/*
	WARNING: This file was generated by dkct.
	Changes you make here will be lost if dkct is run again!
	You should modify the original source and run dkct on it.
	Original source: lprngcl.ctr
*/

/*
Copyright (C) 2013, Dirk Krause

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above opyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used
  to endorse or promote products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**	@file lprngcl.c The lprngcl module.
*/


#line 8 "lprngcl.ctr"

#include "dk3all.h"
#include "dk3sock.h"
#include "dk3print.h"
#include "dk3prcfg.h"
#include "dkt-version.h"




#line 17 "lprngcl.ctr"



#ifndef LPRNGCL_MAX_QUEUENAME_SIZE
/**	Maximum length of queue name.
*/
#define	LPRNGCL_MAX_QUEUENAME_SIZE	256
#endif

#ifndef LPRNGCL_REQUEST_SIZE
/**	Maximum length of request.
*/
#define	LPRNGCL_REQUEST_SIZE		256
#endif

#ifndef LPRNGCL_RECEIVE_BUFFER_SIZE
/**	Buffer size for response from server.
*/
#define	LPRNGCL_RECEIVE_BUFFER_SIZE	4096
#endif

/**	Job structure.
*/
typedef struct {
  dk3_app_t		*app;	/**< Application structure. */
  dkChar const * const	*msg;	/**< Localized texts. */
  dkChar const * const	*nlmsg;	/**< Not localized texts. */
  dkChar const		*tmpfn;	/**< Name for temporary file. */
  dk3_option_set_t	*opt;	/**< Options. */
  dk3_print_conf_t	*pc;	/**< Print configuration. */
  dk3_sto_t		*sph;	/**< Print host storage. */
  dk3_sto_it_t		*iph;	/**< Iterator through print hosts. */
  int			 cmd;	/**< Command to execute. */
  int			 comp;	/**< Flag: Only jobs from current computer. */
  int			 exval;	/**< Exit status code. */
  int			 havep;	/**< Flag: Have printers. */
} LCL_JOB;



/**	Print host structure.
*/
typedef struct {
  dkChar const		*hn;		/**< Host name. */
  dk3_sto_t		*sQueues;	/**< Print queues. */
  dk3_sto_it_t		*iQueues;	/**< Queues iterator. */
  double		 toc;		/**< Timeout for connect. */
  double		 tos;		/**< Timeout for send. */
  double		 tor;		/**< Timeout for receive. */
} LCL_HOST;



/**	Print queue.
*/
typedef struct {
  dkChar const		*qn;		/**< Queue name. */
  double		 toc;		/**< Timeout for connect. */
  double		 tos;		/**< Timeout for send. */
  double		 tor;		/**< Timeout for receive. */
} LCL_QUEUE;



/**	Texts used by the program, not localized.
*/
static dkChar const * const	lprngcl_nlmsg[] = {
/* 0 */
dkT("dkt-3"),

/* 1 */
dkT("lprngcl.str"),

/* 2 */
dkT("lprngcl.txt"),

/* 3 */
dkT("wb"),

NULL


#line 102 "lprngcl.ctr"
};



/**	8-bit character strings used by the module, not localized.
*/
char const * const	lprngcl_c8_kw[] = {
/* 0 */
" ",

/* 1 */
"\n",

/* 2 */
"@",

NULL


#line 123 "lprngcl.ctr"
};



/**	Texts used by the program, replaced by localized versions.
*/
static dkChar const * const	lprngcl_msg[] = {
/* 0 */
dkT("Queue name not configured for \""),

/* 1 */
dkT("\"!"),

/* 2 */
dkT("Print host name not configured for \""),

/* 3 */
dkT("\"!"),

/* 4 */
dkT("Printer/user names too long for LPRng request!"),

/* 5 */
dkT("Failed to convert \""),

/* 6 */
dkT("\" to print host encoding!"),

/* 7 */
dkT("Failed to find users login name!"),

/* 8 */
dkT("No queues configured to clean up."),

/* 9 */
dkT("Printer \""),

/* 10 */
dkT("\" not found!"),

/* 11 */
dkT("Printer name \""),

/* 12 */
dkT("\" too long!"),

NULL


#line 169 "lprngcl.ctr"
};


#define LPRNGCL_USE_C	0


/**	Options for the lprngcl program.
*/
static dk3_option_t	lprngcl_options[] = {
  { dkT('h'), dkT("help"), 0 },
  { dkT('v'), dkT("version"), 0 },
  { dkT('L'), dkT("license"), 0 }
#if LPRNGCL_USE_C
  ,
  { dkT('c'), dkT("computer"), 0 }
#endif
};


/**	Number of options in lprngcl_options.
*/
static size_t const	lprngcl_sz_options =
sizeof(lprngcl_options)/sizeof(dk3_option_t);



/**	Default help text shown if help file not found.
*/
static dkChar const * const	lprngcl_text_help[] = {
dkT(""),
dkT("NAME"),
dkT(""),
dkT("  lprngcl - Clean LPRng print queues"),
dkT(""),
dkT("SYNOPSIS"),
dkT(""),
dkT("  lprngcl [<printers>]"),
dkT("  lprngcl [-h] [-v] [-L]"),
dkT(""),
dkT("DESCRIPTION"),
dkT(""),
dkT("The lprngcl program deletes all print jobs created by the current user"),
dkT("from the named LPD/LPRng print queues or from all found LPD/LPRng print"),
dkT("queues if no printers are specified."),
dkT("It is intended for use in computer labs or PC classrooms in schools and"),
dkT("universities."),
dkT(""),
dkT("OPTIONS"),
dkT(""),
dkT("-h"),
dkT("--help"),
dkT("\tshows this help text."),
dkT(""),
dkT("-v"),
dkT("--version"),
dkT("\tshows version information."),
dkT(""),
dkT("-L"),
dkT("--license"),
dkT("\tshows the license terms."),
dkT(""),
dkT("RETURN VALUE"),
dkT(""),
dkT("The program returns exit status code 0 on success, all other status"),
dkT("codes indicate an error."),
dkT(""),
dkT("FILES"),
dkT(""),
dkT("The dk3print.conf file must be used to configure"),
dkT("- type  (\"lprng\" or \"lpd\")"),
dkT("- host  (print host running the LPD or LPRng software)"),
dkT("- queue (Queue name on the remote print host)."),
dkT("Additionally you can configure short alias names for the print queues."),
dkT(""),
dkT("EXAMPLES"),
dkT(""),
dkT("Here is an example of a dk3print.conf file:"),
dkT(""),
dkT("  [host ps]"),
dkT("  encoding\t=\tplain"),
dkT("  alias\t\t=\tps.my-domain.org"),
dkT("  alias\t\t=\t192.168.3.4"),
dkT("  "),
dkT("  [printer Xerox Phaser 4500DP PS]"),
dkT("  alias\t\t=\tlp"),
dkT("  type\t\t=\tlprng"),
dkT("  host\t\t=\tps"),
dkT("  queue\t\t=\tlp"),
dkT("  "),
dkT("  [printer HP Laserjet 4M]"),
dkT("  alias\t\t=\thplj"),
dkT("  type\t\t=\tlprng"),
dkT("  host\t\t=\tps"),
dkT("  queue\t\t=\thplj"),
dkT(""),
dkT("AUTHOR"),
dkT(""),
dkT("Dirk Krause"),
dkT(""),
dkT("COPYRIGHT AND LICENSE"),
dkT(""),
dkT("Run"),
dkT("  lprngcl --license"),
dkT("to see the license conditions."),
dkT(""),
dkT("SEE ALSO"),
dkT(""),
dkT("http://dktools.sourceforge.net"),
dkT(""),
NULL


#line 280 "lprngcl.ctr"
};



/**	License conditions.
*/
static dkChar const * const	lprngcl_text_license[] = {
dkT(""),
dkT("Copyright (c) 2013, Dirk Krause"),
dkT("All rights reserved."),
dkT(""),
dkT("Redistribution and use in source and binary forms, with or without"),
dkT("modification, are permitted provided that the following conditions are met:"),
dkT(""),
dkT("* Redistributions of source code must retain the above copyright notice,"),
dkT("  this list of conditions and the following disclaimer."),
dkT("* Redistributions in binary form must reproduce the above copyright"),
dkT("  notice, this list of conditions and the following disclaimer in the"),
dkT("  documentation and/or other materials provided with the distribution."),
dkT("* Neither the name of the copyright holder(s) nor the names of"),
dkT("  contributors may be used to endorse or promote products derived from "),
dkT("  this software without specific prior written permission."),
dkT(""),
dkT("THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS"),
dkT("\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT"),
dkT("LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR"),
dkT("A PARTICULAR PURPOSE ARE DISCLAIMED."),
dkT(""),
dkT("IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY"),
dkT("DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL"),
dkT("DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS"),
dkT("OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)"),
dkT("HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,"),
dkT("STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING"),
dkT("IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE"),
dkT("POSSIBILITY OF SUCH DAMAGE."),
dkT(""),
NULL


#line 319 "lprngcl.ctr"
};



/**	Version information.
*/
static dkChar const lprngcl_version[] = { DKT_VERSION };



/**	Delete queue structure.
	@param	qptr	Queue to delete.
*/
static
void
lprngcl_delete_queue(LCL_QUEUE *qptr)
{
  

#line 337 "lprngcl.ctr"
  if(qptr) {
    

#line 339 "lprngcl.ctr"
    

#line 340 "lprngcl.ctr"
    qptr->toc = qptr->tos = qptr->tor = 0.0;
    dk3_release(qptr->qn)
    dk3_delete(qptr)
  } 

#line 344 "lprngcl.ctr"
}



/**	Create new queue entry.
	@param	qn	Queue name.
	@param	toc	Connect timeout.
	@param	tos	Send timeout.
	@param	tor	Receive timeout.
	@param	app	Application structure for diagnostics.
	@return	Pointer to new queue structure on success, NULL on error.
*/
static
LCL_QUEUE *
lprngcl_new_queue(
  dkChar const	*qn,
  double	 toc,
  double	 tos,
  double	 tor,
  dk3_app_t	*app
)
{
  LCL_QUEUE		*back = NULL;
  if(qn) {
    back = dk3_new_app(LCL_QUEUE,1,app);
    if(back) {
      back->toc = toc;
      back->tos = tos;
      back->tor = tor;
      back->qn = dk3str_dup_app(qn, app);
      if(!(back->qn)) {
        lprngcl_delete_queue(back);
	back = NULL;
      }
    }
  }
  return back;
}



/**	Compare two queues.
	@param	l	Left object.
	@param	r	Right object or name.
	@param	cr	Comparison criteria (0=queue/queue, 1=queue/name).
	@return	Comparison result.
*/
static
int
lprngcl_compare_queues(void *l, void *r, int cr)
{
  int		 	 back = 0;
  LCL_QUEUE		*pl;		/* Left queue. */
  LCL_QUEUE		*pr;		/* Right queue. */
  if(l) {
    if(r) {
      pl = (LCL_QUEUE *)l;
      pr = (LCL_QUEUE *)r;
      switch(cr) {
        case 1: {
	  if(pl->qn) {
	    back = dk3str_cmp(pl->qn, (dkChar *)r);
	    if(back < 0) back = -1;
	    if(back > 0) back =  1;
	  } else {
	    back = -1;
	  }
	} break;
	default: {
	  if(pl->qn) {
	    if(pr->qn) {
	      back = dk3str_cmp(pl->qn, pr->qn);
	      if(back < 0) back = -1;
	      if(back > 0) back =  1;
	    } else back = 1;
	  } else {
	    if(pr->qn) back = -1;
	  }
	} break;
      }
    } else back = 1;
  } else {
    if(r) back = -1;
  }
  return back;
}



/**	Destroy LCL_HOST, release memory.
	@param	ptr	Host structure to destroy.
*/
static
void
lprngcl_delete_host(LCL_HOST *ptr)
{
  void		*vptr;		/* Current queue as returned from iterator. */
  LCL_QUEUE	*queueptr;	/* Current queue to delete. */
#if TRACE_DEBUG
  dkChar const *qptr;		/* Queue name to report. */
#endif
  

#line 446 "lprngcl.ctr"
  if(ptr) {	

#line 447 "lprngcl.ctr"
    dk3_release(ptr->hn)
    if(ptr->sQueues) {
      if(ptr->iQueues) {
        dk3sto_it_reset(ptr->iQueues);
	while(NULL != (vptr = dk3sto_it_next(ptr->iQueues))) {
	  queueptr = (LCL_QUEUE *)vptr;
#if TRACE_DEBUG
	  qptr = queueptr->qn;
#endif
	  

#line 457 "lprngcl.ctr"
	  lprngcl_delete_queue(queueptr);
	}
	dk3sto_it_close(ptr->iQueues);
      }
      dk3sto_close(ptr->sQueues);
    } ptr->sQueues = NULL; ptr->iQueues = NULL;
    dk3_delete(ptr)
  } 

#line 465 "lprngcl.ctr"
}



/**	Compare two host entries.
	@param	l	Left object.
	@param	r	Right object or name.
	@param	cr	Comparison criteria (0=host/host, 1=host/name).
	@return	Comparison result.
*/
static
int
lprngcl_compare_hosts(void *l, void *r, int cr)
{
  LCL_HOST		*pl;		/* Left host. */
  LCL_HOST		*pr;		/* Right host. */
  dkChar const		*name;		/* Right host name. */
  int			 back = 0;

  if(l) {
    if(r) {
      pl = (LCL_HOST *)l;
      switch(cr) {
        case 1: {
	  name = (dkChar const *)r;
	  if(pl->hn) {
	    back = dk3str_cmp(pl->hn, name);
	    if(back < 0) back = -1;
	    if(back > 0) back =  1;
	  } else {
	    back = -1;
	  }
	} break;
	default: {
	  pr = (LCL_HOST *)r;
	  if(pl->hn) {
	    if(pr->hn) {
	      back = dk3str_cmp(pl->hn, pr->hn);
	      if(back < 0) back = -1;
	      if(back > 0) back =  1;
	    } else {
	      back = 1;
	    }
	  } else {
	    if(pr->hn) {
	      back = -1;
	    }
	  }
	} break;
      }
    } else {
      back = 1;
    }
  } else {
    if(r) {
      back = -1;
    }
  }
  return back;
}


/**	Create new host entry.
	@param	hn	Host name.
	@param	app	Application structure.
	@return	Pointer to new structure on success, NULL on error.
*/
static
LCL_HOST *
lprngcl_new_host(
  dkChar const *hn,
  dk3_app_t *app
)
{
  LCL_HOST		*back	= NULL;
  int			 ok	= 0;	/* Flag: Success. */
  

#line 542 "lprngcl.ctr"
  if(hn) {
    back = dk3_new_app(LCL_HOST,1,app);
    if(back) {
      back->sQueues = NULL;
      back->iQueues = NULL;
      back->hn = dk3str_dup_app(hn, app);
      if(back->hn) {
        back->sQueues = dk3sto_open_app(app);
	if(back->sQueues) {
	  dk3sto_set_comp(back->sQueues, lprngcl_compare_queues, 0);
	  back->iQueues = dk3sto_it_open(back->sQueues);
	  if(back->iQueues) {
	    ok = 1;
	  }
	}
      }
      if(!(ok)) {
        lprngcl_delete_host(back); back = NULL;
      }
    }
  } 

#line 563 "lprngcl.ctr"
  return back;
}



/**	Get host timeouts.
	@param	hptr	Destination host to configure.
	@param	job	Job structure.
	@param	hn	Host name.
*/
static
void
lprngcl_transfer_host_to(
  LCL_HOST		*hptr,
  LCL_JOB		*job,
  dkChar const		*hn
)
{
  dk3_print_host_t	*ph;		/* Host to search for configuration. */
  ph = dk3print_get_host(job->pc, hn);
  if(ph) {
    hptr->toc = ph->to_c;
    hptr->tos = ph->to_s;
    hptr->tor = ph->to_r;
  }
}


/**	Add host and queue to list of cleanup queues.
	@param	job		Job structure.
	@param	prname		Printer name, may be NULL.
	@param	hostname	Print host name.
	@param	queuename	Name of queue on host.
	@param	toc		Timeout for connection attempts.
	@param	tos		Timeout for send operations.
	@param	tor		Timeout for receive operations.
*/
static
void
lprngcl_add_queue(
  LCL_JOB		*job,
  dkChar const		*prname,
  dkChar const		*hostname,
  dkChar const		*queuename,
  double		 toc,
  double		 tos,
  double		 tor
)
{
  LCL_HOST		*hptr;			/* Server host for queue. */
  LCL_QUEUE		*qptr;			/* New queue to add. */
  

#line 615 "lprngcl.ctr"
  hptr = (LCL_HOST *)dk3sto_it_find_like(job->iph, (void *)hostname, 1);
  if(!(hptr)) {				

#line 617 "lprngcl.ctr"
    hptr = lprngcl_new_host(hostname, job->app);
    if(hptr) {				

#line 619 "lprngcl.ctr"
      if(!dk3sto_add(job->sph, (void *)hptr)) {
        lprngcl_delete_host(hptr);	

#line 621 "lprngcl.ctr"
	hptr = NULL;
	job->exval = 1;
      } else {				

#line 624 "lprngcl.ctr"
        lprngcl_transfer_host_to(hptr, job, hostname);
      }
    } else {				

#line 627 "lprngcl.ctr"
      job->exval = 1;
    }
  } else {				

#line 630 "lprngcl.ctr"
  }
  if(hptr) {				

#line 632 "lprngcl.ctr"
    qptr = (LCL_QUEUE *)dk3sto_it_find_like(
      hptr->iQueues, (void *)queuename, 1
    );
    if(qptr) {				

#line 636 "lprngcl.ctr"
      job->havep = 1;
    } else {				

#line 638 "lprngcl.ctr"
      qptr = lprngcl_new_queue(queuename, toc, tos, tor, job->app);
      if(qptr) {
        if(dk3sto_add(hptr->sQueues, (void *)qptr)) {
	  job->havep = 1;
	} else {
	  lprngcl_delete_queue(qptr);
	  qptr = NULL;
	  job->exval = 1;
	}
      } else {
        job->exval = 1;
      }
    }
  } 

#line 652 "lprngcl.ctr"
}



/**	Add one queue to list of cleanup queues.
	@param	job	Job structure.
	@param	pr	Printer to add.
	@param	det	LPRng queue details.
*/
static
void
lprngcl_add_printer(
  LCL_JOB		*job,
  dk3_printer_t		*pr,
  dk3_printer_details_t	*det
)
{
  

#line 670 "lprngcl.ctr"
  if((det->lprng).hostname) {
    if((det->lprng).queuename) {
      lprngcl_add_queue(
        job, pr->name, (det->lprng).hostname, (det->lprng).queuename,
	(det->lprng).to_c, (det->lprng).to_s, (det->lprng).to_r
      );
    } else {			

#line 677 "lprngcl.ctr"
      /* ERROR: Queue name not configured! */
      dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 0, 1, pr->name);
    }
  } else {			

#line 681 "lprngcl.ctr"
    /* ERROR: Hostname not configured! */
    dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 2, 3, pr->name);
  } 

#line 684 "lprngcl.ctr"
}



/**	Write one file line to standard output, tabulator at start of line.
	@param	obj	Object modified by handler, NULL here.
	@param	il	Input line.
	@return	1.
*/
static
int
lprngcl_response_line_handler(void *obj, dkChar *il)
{
  dk3str_delnl(il);
  dk3sf_fputc(dkT('\t'), stdout);
  dk3sf_fputs(il, stdout);
  dk3sf_fputc(dkT('\n'), stdout);
  return 1;
}



/**	Split double timeout value to seconds and microseconds.
	@param	secs	Destination variable for seconds.
	@param	usecs	Destination variable for microseconds.
	@param	value	Timeout value.
*/
static
void
lprngcl_set_timeout(
  long		*secs,
  long		*usecs,
  double	 value
)
{
  double	ds;	/* Seconds. */
  double	du;	/* Microseconds. */

  *secs = 0L;
  *usecs = 0L;
  if(value > 0.0) {
    ds = floor(value);
    du = value - ds;
    du = 1000000.0 * du;
    *secs = (long)ds;
    *usecs = (long)du;
  }
}



/**	Process one request (send request to host and show response).
	@param	job	Job structure.
	@param	hptr	Host structure.
	@param	rq	Request data.
	@param	enc	Encoding used on server.
	@param	toc	Timeout for connection attempts.
	@param	tos	Timeout for send operations.
	@param	tor	Timeout for receive operations.
	@return	1 if we can do further connections to the same host,
	0 otherwise.
*/
static
int
lprngcl_process_request(
  LCL_JOB	*job,
  LCL_HOST	*hptr,
  char const	*rq,
  int		 enc,
  double	 toc,
  double	 tos,
  double	 tor
)
{
  char			 buf[LPRNGCL_RECEIVE_BUFFER_SIZE];
  			 /* Buffer for response from print server. */
  dkChar		 dbuf[LPRNGCL_RECEIVE_BUFFER_SIZE];
  			 /* Buffer to process response file line by line. */
  FILE			*tmpf	= NULL;			/* Temporary file. */
  dk3_socket_t		 sock	= INVALID_SOCKET;	/* Socket. */
  long			 tocs = 0L;	/* Connect timeout seconds. */
  long			 tocu = 0L;	/* Connect timeout microseconds. */
  long			 toss = 0L;	/* Send timeout seconds. */
  long			 tosu = 0L;	/* Send timeout microseconds. */
  long			 tors = 0L;	/* Receive timeout seconds. */
  long			 toru = 0L;	/* Receive timeout microseconds. */
  int			 back	= 1;	
#if 0
  int			 bufl	= 0;	/* Buffer length. */
#endif
  int			 szwr	= 0;	/* Size written. */
  int			 show	= 0;	/* Show temporary file. */
  

#line 777 "lprngcl.ctr"
  tmpf = dk3sf_fopen_app(job->tmpfn, lprngcl_nlmsg[3], job->app);
  if(tmpf) {
    show = 1;
  }

  lprngcl_set_timeout(&tocs, &tocu, toc);
  lprngcl_set_timeout(&toss, &tosu, tos);
  lprngcl_set_timeout(&tors, &toru, tor);
  

#line 786 "lprngcl.ctr"
  

#line 787 "lprngcl.ctr"
  

#line 788 "lprngcl.ctr"
  sock = dk3socket_dkchar_open_net_stream_client(
    hptr->hn, 515, 0, tocs, tocu, NULL, job->app
  );
  if(dk3socket_check(sock)) {
    szwr = dk3socket_send(sock, rq, strlen(rq), toss, tosu, NULL, job->app);
    if((int)strlen(rq) != szwr) {
      /* Fewer bytes written than expected! */
      job->exval = 1;
    }
    szwr = dk3socket_shutdown(sock, DK3_TCPIP_SHUTDOWN_WRITE, NULL, job->app);
    if(szwr) {
      do {
        szwr = dk3socket_recv(
	  sock, buf, sizeof(buf), tors, toru, NULL, job->app
	);
	if((szwr > 0) && (tmpf)) {
	  dk3sf_fwrite_app(buf, 1, (size_t)szwr, tmpf, job->app);
	}
      } while(szwr > 0);
    } else {
      /* Shutdown failed */
    }
    dk3socket_close(sock, NULL, job->app); sock = INVALID_SOCKET;
  } else {
    /* Failed to connect to server! */
    back = 0;
    job->exval = 1;
  }
  if(tmpf) {
    fclose(tmpf);
  }

  if(show) {
    dk3stream_process_filename_lines_app(
      NULL, lprngcl_response_line_handler,
      job->tmpfn,
      dbuf, DK3_SIZEOF(dbuf,dkChar),
      dk3app_get_encoding(job->app), enc,
      job->app
    );
  } 

#line 829 "lprngcl.ctr"
  return back;
}



/**	Do cleanup for one remote host.
	@param	job	Job structure.
	@param	hptr	Current host to process.
*/
static
void
lprngcl_cleanup_for_host(LCL_JOB *job, LCL_HOST *hptr)
{
  char				 rq[LPRNGCL_REQUEST_SIZE];
  				 /* Buffer to construct request. */
  char				 bu[LPRNGCL_REQUEST_SIZE];
  				 /* Buffer for temporary conversions. */
  LCL_QUEUE			*qptr;		/* Current queue. */
  dk3_print_host_t		*ph;		/* Current print host. */
  dk3_print_host_alias_t	*al;		/* Print host alias. */
  dkChar const			*queuename;	/* Current queue name. */
  dkChar const			*logname;	/* Current users login. */
  dkChar const			*hostname;	/* Current computer name. */
  double			 toc;		/* Timeout for connect. */
  double			 tos;		/* Timeout for send. */
  double			 tor;		/* Timeout for receive. */
  size_t			 sl;		/* Required string length. */
  int				 cc;		/* Flag: Can continue. */
  int				 encoding;	/* Print host encoding. */
  int				 res;		/* Operation result. */
  int				 ie;		/* Input encoding. */
  int				 rqok;		/* Flag: Request constructed. */
  

#line 862 "lprngcl.ctr"

  /*	Find encoding used by the server.
  */
  ie = dk3app_get_encoding(job->app);
  encoding = DK3_ENCODING_UTF8;
  ph = (dk3_print_host_t *)dk3sto_it_find_like(
    (job->pc)->iPrintHosts, (void *)(hptr->hn), 1
  );
  if(!(ph)) {
    al = (dk3_print_host_alias_t *)dk3sto_it_find_like(
      (job->pc)->iHostAliases, (void *)(hptr->hn), 1
    );
    if(al) {
      ph = al->host;
    }
  }
  if(ph) {
    if(DK3_ENCODING_UTF8 == ph->enc) {
      encoding = DK3_ENCODING_UTF8;
    } else {
      encoding = DK3_ENCODING_PLAIN;
    }
  }

  /*	Process print queues.
  */
  logname = dk3app_get_logname(job->app);
  if(logname) {
    dk3sto_it_reset(hptr->iQueues);
    cc = 1;
    do {
      qptr = (LCL_QUEUE *)dk3sto_it_next(hptr->iQueues);
      if(qptr) {
        queuename = qptr->qn;

	/*	Set timeouts.
	*/
        toc = qptr->toc; tos = qptr->tos; tor = qptr->tor;
	if(toc < 0.0) {
	  toc = hptr->toc;
	}
	if(tos < 0.0) {
	  tos = hptr->tos;
	}
	if(tor < 0.0) {
	  tor = hptr->tor;
	}

        /*	Print queue name.
	*/
	dk3sf_fputs(queuename, stdout);
	dk3sf_fputc(dkT('@'), stdout);
	dk3sf_fputs(hptr->hn, stdout);
	dk3sf_fputc(dkT('\n'), stdout);

        /*	Construct request.
        */
        rq[0] = ' '; rq[1] = '\0';
        switch(encoding) {
          case DK3_ENCODING_UTF8: {
	    res = dk3str_to_c8u_app(bu, sizeof(bu), queuename, ie, job->app);
	  } break;
	  default: {
	    res = dk3str_to_c8p_app(bu, sizeof(bu), queuename, ie, job->app);
	  } break;
        }
        if(res) {
          if(sizeof(rq) > (strlen(rq) + strlen(bu))) {
	    strcat(rq, bu);
	    if(sizeof(rq) > (strlen(rq) + strlen(lprngcl_c8_kw[0]))) {
	      strcat(rq, lprngcl_c8_kw[0]);
	      switch(encoding) {
	        case DK3_ENCODING_UTF8: {
		  res = dk3str_to_c8u_app(
		    bu, sizeof(bu), logname, ie, job->app
		  );
		} break;
		default: {
		  res = dk3str_to_c8p_app(
		    bu, sizeof(bu), logname, ie, job->app
		  );
		} break;
	      }
	      if(res) {
	        if(sizeof(rq) > (strlen(rq) + strlen(bu))) {
		  strcat(rq, bu);
		  if(sizeof(rq) > (strlen(rq) + strlen(lprngcl_c8_kw[0]))) {
		    strcat(rq, lprngcl_c8_kw[0]);
		    if(sizeof(rq) > (strlen(rq) + strlen(bu))) {
		      strcat(rq, bu);
		      rqok = 1;
#if LPRNGCL_USE_C
		      if(job->comp) {
		        rqok = 0;
			if(sizeof(rq) > (strlen(rq)+strlen(lprngcl_c8_kw[2]))) {
			  hostname = dk3app_get_hostname(job->app);
			  switch(encoding) {
			    case DK3_ENCODING_UTF8: {
			      res = dk3str_to_c8u_app(
			        bu, sizeof(bu), hostname, ie, job->app
			      );
			    } break;
			    default: {
			      res = dk3str_to_c8p_app(
			        bu, sizeof(bu), hostname, ie, job->app
			      );
			    } break;
			  }
			  if(res) {
			    sl = strlen(rq) + strlen(bu);
			    if(sizeof(rq) > sl) {
			      rqok = 1;
			      strcat(rq, lprngcl_c8_kw[2]);
			      strcat(rq, bu);
			    } else {
			      /* ERROR: Names too long! */
			      dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4);
			    }
			  }
			} else {
			  /* ERROR: Names too long! */
			  dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4);
			}
		      }
#endif
		      if(rqok) {
		        rqok = 0;
		        if(sizeof(rq) > (strlen(rq)+strlen(lprngcl_c8_kw[1]))) {
		          rqok = 1;
		          strcat(rq, lprngcl_c8_kw[1]);
		        } else {
		          /* ERROR: Names too long! */
			  dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4);
		        }
		      }
		    } else {
		      /* ERROR: User name too long! */
		      dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4);
		    }
		  } else {
		    /* ERRROR: User name too long! */
		    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4);
		  }
		} else {
		  /* ERROR: User name too long! */
		  dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4);
		}
	      } else {
	        /* ERROR: Failed to convert log name! */
		dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 5, 6, logname);
	      }
	    } else {
	      /* ERROR: Printer name too long! */
	      dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4);
	    }
	  } else {
	    /* ERROR: Printer name too long! */
	    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4);
	  }
        } else {
          job->exval = 1;
        }

	/*	Process request.
	*/
	if(rqok) {	

#line 1028 "lprngcl.ctr"
	  rq[0] = 0x05;
	  if(!lprngcl_process_request(job, hptr, rq, encoding, toc, tos, tor)) {
	    cc = 0;
	  }
	}
      } else {
        cc = 0;
      }
    } while((qptr) && (cc));
  } else {
    /* ERROR: Log name not found! */
    dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 7);
  }
  

#line 1042 "lprngcl.ctr"
}



/**	Cleanup up all queues marked for cleanup.
	@param	job	Job structure.
*/
static
void
lprngcl_cleanup_listed_printers(LCL_JOB *job)
{
  LCL_HOST		*hptr;	/* Current host to process. */
  void			*vptr;	/* Host as returned from iterator. */
  

#line 1056 "lprngcl.ctr"
  if(job->havep) {			

#line 1057 "lprngcl.ctr"
    if(dk3socket_up(NULL, job->app)) {	

#line 1058 "lprngcl.ctr"
      dk3sto_it_reset(job->iph);
      while(NULL != (vptr = dk3sto_it_next(job->iph))) {
        hptr = (LCL_HOST *)vptr;
        lprngcl_cleanup_for_host(job, hptr);
      }
      dk3socket_down(NULL, job->app);
    } else {				

#line 1065 "lprngcl.ctr"
      job->exval = 1;
    }
  } else {
    /* WARNING: No queues configured to clean up. */
    dk3app_log_1(job->app, DK3_LL_WARNING, job->msg, 8);
  } 

#line 1071 "lprngcl.ctr"
}



/**	Clean up all printers on the system.
	@param	job	Job structure.
*/
static
void
lprngcl_cleanup_all_printers(LCL_JOB *job)
{
  void			*vptr;	/* Printer as returned from iterator. */
  dk3_printer_t		*pr;	/* Current printer. */
  

#line 1085 "lprngcl.ctr"
  dk3sto_it_reset((job->pc)->iPrinters);
  while(NULL != (vptr = dk3sto_it_next((job->pc)->iPrinters))) {
    pr = (dk3_printer_t *)vptr;
    switch(pr->t_p) {
      case DK3_PRINTER_TYPE_WINDOWS: {
        switch(pr->t_s) {
	  case DK3_PRINTER_TYPE_LPD: case DK3_PRINTER_TYPE_LPRNG: {
	    lprngcl_add_printer(job, pr, &(pr->det_s));
	  } break;
	}
      } break;
      case DK3_PRINTER_TYPE_LPD: case DK3_PRINTER_TYPE_LPRNG: {
        lprngcl_add_printer(job, pr, &(pr->det_p));
      } break;
    }
  }
  lprngcl_cleanup_listed_printers(job);
  

#line 1103 "lprngcl.ctr"
}



/**	Clean up the named printers.
	@param	job	Job structure.
	@param	numargs	Number of command line arguments.
*/
static
void
lprngcl_cleanup_named_printers(LCL_JOB *job, int numargs)
{
  dkChar		 buf[LPRNGCL_MAX_QUEUENAME_SIZE];
  			 /* Temporary buffer for queue name. */
  dkChar const		*pname;	/* Print queue name. */
  dkChar		*p1;	/* Print host name. */
  dk3_printer_t		*pr;	/* Printer entry. */
  dk3_printer_alias_t	*al;	/* Printer alias. */
  

#line 1122 "lprngcl.ctr"
  int			 i;
  for(i = 0; i < numargs; i++) {
    pname = dk3opt_get_arg(job->opt, i);
    if(pname) {				

#line 1126 "lprngcl.ctr"
      if(dk3str_len(pname) < DK3_SIZEOF(buf,dkChar)) {
        dk3str_cpy(buf, pname);
	p1 = dk3str_chr(buf, dkT('@'));
	if(p1) {			

#line 1130 "lprngcl.ctr"
	  *(p1++) = dkT('\0');
	  lprngcl_add_queue(job, pname, p1, buf, -1.0, -1.0, -1.0);
	} else {			

#line 1133 "lprngcl.ctr"
	  pr = (dk3_printer_t *)dk3sto_it_find_like(
	    (job->pc)->iPrinters, (void *)buf, 1
	  );
	  if(!(pr)) {
	    al = (dk3_printer_alias_t *)dk3sto_it_find_like(
	      (job->pc)->iPrintAliases, (void *)buf, 1
	    );
	    if(al) {
	      pr = al->printer;
	    }
	  }
	  if(pr) {
	    switch(pr->t_p) {
	      case DK3_PRINTER_TYPE_WINDOWS: {
	        switch(pr->t_s) {
		  case DK3_PRINTER_TYPE_LPD: case DK3_PRINTER_TYPE_LPRNG: {
		    lprngcl_add_printer(job, pr, &(pr->det_s));
		  } break;
		}
	      } break;
              case DK3_PRINTER_TYPE_LPD: case DK3_PRINTER_TYPE_LPRNG: {
	        lprngcl_add_printer(job, pr, &(pr->det_p));
	      } break;
	    }
	  } else {
	    /* ERROR: Printer not found! */
	    dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 9, 10, pname);
	  }
	}
      } else {
        /* ERROR: Printer name too long! */
	dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 11, 12, pname);
      }
    }
  }
  lprngcl_cleanup_listed_printers(job);
  

#line 1170 "lprngcl.ctr"
}



/**	Do cleanup.
	@param	job	Job structure.
*/
static
void
lprngcl_do_cleanup(LCL_JOB *job)
{
  dkChar	tmpfn[DK3_MAX_PATH];	/* File name for temporary file. */
  int		numargs;		/* Number of command line arguments. */
  

#line 1184 "lprngcl.ctr"
  if(dk3app_get_temp_file_name(job->app, tmpfn, DK3_SIZEOF(tmpfn,dkChar))) {
    job->tmpfn = dk3str_dup_app(tmpfn, job->app);
    if(job->tmpfn) {
      job->pc = dk3print_conf_open(job->app, 1);
      if(job->pc) {
        job->sph = dk3sto_open_app(job->app);
	if(job->sph) {
	  dk3sto_set_comp(job->sph, lprngcl_compare_hosts, 0);
	  job->iph = dk3sto_it_open(job->sph);
	  if(job->iph) {
            numargs = dk3opt_get_num_args(job->opt);
            job->exval = 0;
            if(0 == numargs) {
              lprngcl_cleanup_all_printers(job);
            } else {
              lprngcl_cleanup_named_printers(job, numargs);
            }
            dk3print_conf_close(job->pc); job->pc = NULL;
	  }
	}
      }
    }
  } 

#line 1207 "lprngcl.ctr"
}



/**	Run with a configured job structure.
	@param	job	Job structure.
*/
static
void
lprngcl_run(LCL_JOB *job)
{
  

#line 1219 "lprngcl.ctr"
  job->opt = dk3opt_open_from_app(
    lprngcl_options, lprngcl_sz_options,
    dkT('\0'), NULL,
    job->app
  );
  if(job->opt) {
    if(0 == dk3opt_get_error_code(job->opt)) {
      if(dk3opt_is_set(job->opt, dkT('h'))) {
        job->cmd |= DK3_APP_CMD_HELP;
      }
      if(dk3opt_is_set(job->opt, dkT('v'))) {
        job->cmd |= DK3_APP_CMD_VERSION;
      }
      if(dk3opt_is_set(job->opt, dkT('L'))) {
        job->cmd |= DK3_APP_CMD_LICENSE;
      }
      if(dk3opt_is_set(job->opt, dkT('c'))) {
        job->comp = 1;
      }
      if(0 == job->cmd) {	

#line 1239 "lprngcl.ctr"
        lprngcl_do_cleanup(job);
      } else {
        job->exval = 0;
	dk3sf_initialize_stdout();
	if((job->cmd) & DK3_APP_CMD_VERSION) {
	  dk3sf_fputs(lprngcl_version, stdout);
	  dk3sf_fputc(dkT('\n'), stdout);
	}
	if((job->cmd) & DK3_APP_CMD_LICENSE) {
	  dk3sf_fputt(lprngcl_text_license, stdout);
	}
	if((job->cmd) & DK3_APP_CMD_HELP) {
	  dk3app_help(job->app, lprngcl_nlmsg[2], lprngcl_text_help);
	}
      }
    }
    dk3opt_close(job->opt); job->opt = NULL;
  } 

#line 1257 "lprngcl.ctr"
}



/**	Program entry point.
	@param	argc	Number of command line arguments.
	@param	argv	Command line arguments array.
	@return	0 on success, any other value indicates an error.
*/
DK3_MAIN
{
  LCL_JOB	 job;		/* Cleanup job. */
  void		*vptr;		/* Host to clean up. */
  int		 exval = 1;	/* Exit status code. */

  

#line 1273 "lprngcl.ctr"
  

#line 1274 "lprngcl.ctr"
  job.app = dk3app_open_command(
    argc, (dkChar const * const *)argv, lprngcl_nlmsg[0]
  );
  if(job.app) {

    /*	Initialize job structure.
    */
    job.msg = dk3app_messages(
      job.app, lprngcl_nlmsg[1], (dkChar const **)lprngcl_msg
    );
    job.nlmsg = lprngcl_nlmsg;
    job.cmd = 0;
    job.comp = 0;
    job.exval = 1;
    job.pc = NULL;
    job.tmpfn = NULL;
    job.sph = NULL;
    job.iph = NULL;
    job.havep = 0;

    /*	Run print queue cleanup
    */
    lprngcl_run(&job);

    /*	Clean up job structure.
    */
    if(job.sph) {
      if(job.iph) {
        dk3sto_it_reset(job.iph);
	while(NULL != (vptr = dk3sto_it_next(job.iph))) {
	  lprngcl_delete_host((LCL_HOST *)vptr);
	}
	dk3sto_it_close(job.iph);
      }
      dk3sto_close(job.sph);
    } job.sph = NULL;
    if(job.tmpfn) {
      dk3sf_remove_file_app(job.tmpfn, NULL);
      dk3_delete(job.tmpfn); job.tmpfn = NULL;
    }
    exval = job.exval;
    dk3app_close(job.app);

  } else {
    fputs("lprngcl: ERROR: Not enough memory!\n", stderr);
    fflush(stderr);
  }
  

#line 1322 "lprngcl.ctr"
  

#line 1323 "lprngcl.ctr"
  exit(exval); return exval;
}

