1/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of
5 * the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15 * MA 02111-1307 USA
16 */
17/***************************************************************************
18 * LPRng - An Extended Print Spooler System
19 *
20 * Copyright 1988-2003, Patrick Powell, San Diego, CA
21 *     papowell@lprng.com
22 * See LICENSE for conditions of use.
23 *
24 ***************************************************************************/
25
26 static char *const _id =
27"$Id: lpf.c,v 1.1.1.1 2008/10/15 03:28:27 james26_jang Exp $";
28
29
30/***************************************************************************
31 *  Filter template and frontend.
32 *
33 *	A filter is invoked with the following parameters,
34 *  which can be in any order, and perhaps some missing.
35 *
36 *  filtername arguments \   <- from PRINTCAP entry
37 *      -PPrinter -wwidth -llength -xwidth -ylength [-c] \
38 *      -Kcontrolfilename -Lbnrname \
39 *      [-iindent] \
40 *		[-Zoptions] [-Cclass] [-Jjob] [-Raccntname] -nlogin -hHost  \
41 *      -Fformat [-Tcrlf] [-Tdebug] [affile]
42 *
43 *  1. Parameters can be in different order than the above.
44 *  2. Optional parameters can be missing
45 *  3. Values specified for the width, length, etc., are from PRINTCAP
46 *     or from the overridding user specified options.
47 *
48 *  This program provides a common front end for most of the necessary
49 *  grunt work.  This falls into the following classes:
50 * 1. Parameter extraction.
51 * 2. Suspension when used as the "of" filter.
52 * 3. Termination and accounting
53 *  The front end will extract parameters,  then call the filter_pgm()
54 *  routine,  which is responsible for carrying out the required filter
55 *  actions. filter_pgm() is invoked with the printer device on fd 1,
56 *	and error log on fd 2.  The npages variable is used to record the
57 *  number of pages that were used.
58 *  The "halt string", which is a sequence of characters that
59 *  should cause the filter to suspend itself, is passed to filter.
60 *  When these characters are detected,  the "suspend_ofilter()" routine should be
61 *  called.
62 *
63 *  On successful termination,  the accounting file will be updated.
64 *
65 *  The filter_pgm() routine should return 0 (success), 1 (retry) or 2 (abort).
66 *
67 * Parameter Extraction
68 *	The main() routine will extract parameters
69 *  whose values are placed in the appropriate variables.  This is done
70 *  by using the ParmTable[], which has entries for each valid letter
71 *  parmeter, such as the letter flag, the type of variable,
72 *  and the address of the variable.
73 *  The following variables are provided as a default set.
74 *      -PPrinter -wwidth -llength -xwidth -ylength [-c] [-iindent] \
75 *		[-Zoptions] [-Cclass] [-Jjob] [-Raccntname] -nlogin -hHost  \
76 *      -Fformat [affile]
77 * VARIABLE  FLAG          TYPE    PURPOSE / PRINTCAP ENTRTY
78 * name     name of filter char*    argv[0], program identification
79 * width    -wwidth	       int      PW, width in chars
80 * length   -llength	   int      PL, length in lines
81 * xwidth   -xwidth        int      PX, width in pixels
82 * xlength  -xlength       int      PY, length in pixels
83 * literal  -c	           int      if set, ignore control chars
84 * controlfile -kcontrolfile char*  control file name
85 * bnrname  -Lbnrname      char*    banner name
86 * indent   -iindent       int      indent amount (depends on device)
87 * zopts    -Zoptions      char*    extra options for printer
88 * comment  -Scomment      char*    printer name in comment field
89 * class    -Cclass        char*    classname
90 * job      -Jjob          char*    jobname
91 * accntname -Raccntname   char*    account for billing purposes
92 * login    -nlogin        char*    login name
93 * host     -hhost         char*    host name
94 * format   -Fformat       char*    format
95 * statusfile  -sstatusfile   char*    statusfile
96 * accntfile file          char*    AF, accounting file
97 *
98 * npages    - number of pages for accounting
99 *
100 * -Tdebug - increment debug level
101 * -Tcrlf  - turn LF to CRLF translation off
102 *
103 *	The functions logerr(), and logerr_die() can be used to report
104 *	status. The variable errorcode can be set by the user before calling
105 *	these functions, and will be the exit value of the program. Its default
106 *	value will be 2 (abort status).
107 *	logerr() reports a message, appends information indicated by errno
108 *	(see perror(2) for details), and then returns.
109 *	logerr_die() will call logerr(), and then will exit with errorcode
110 *	status.
111 *
112 * DEBUGGING:  a simple minded debugging version can be enabled by
113 * compiling with the -DDEBUG option.
114 */
115
116#include "portable.h"
117
118/* VARARGS3 */
119#ifdef HAVE_STDARGS
120int	plp_snprintf (char *str, size_t count, const char *fmt, ...);
121int	plp_vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
122#else
123int plp_snprintf ();
124int plp_vsnprintf ();
125#endif
126
127/* VARARGS2 */
128#ifdef HAVE_STDARGS
129 void safefprintf (int fd, char *format,...);
130#else
131 void safefprintf ();
132#endif
133
134#include <sys/types.h>
135#include <stdio.h>
136#include <signal.h>
137#include <string.h>
138#include <ctype.h>
139#include <errno.h>
140#include <time.h>
141
142#ifdef HAVE_STDLIB_H
143#include <stdlib.h>
144#endif
145
146#ifdef HAVE_UNISTD_H
147#include <unistd.h>
148#endif
149
150#ifdef HAVE_SYS_FILE_H
151# include <sys/file.h>
152#endif
153
154#ifndef HAVE_ERRNO_DECL
155extern int errno;
156#endif
157
158#ifdef HAVE_SYS_FCNTL_H
159# include <sys/fcntl.h>
160#endif
161#ifdef HAVE_FCNTL_H
162# include <fcntl.h>
163#endif
164
165/* varargs declarations: */
166
167#if defined(HAVE_STDARG_H)
168# include <stdarg.h>
169# define HAVE_STDARGS    /* let's hope that works everywhere (mj) */
170# define VA_LOCAL_DECL   va_list ap;
171# define VA_START(f)     va_start(ap, f)
172# define VA_SHIFT(v,t)	;	/* no-op for ANSI */
173# define VA_END          va_end(ap)
174#else
175# if defined(HAVE_VARARGS_H)
176#  include <varargs.h>
177#  undef HAVE_STDARGS
178#  define VA_LOCAL_DECL   va_list ap;
179#  define VA_START(f)     va_start(ap)		/* f is ignored! */
180#  define VA_SHIFT(v,t)	v = va_arg(ap,t)
181#  define VA_END		va_end(ap)
182# else
183XX ** NO VARARGS ** XX
184# endif
185#endif
186
187char *Time_str(int shortform, time_t t);
188
189/*
190 * default exit status, causes abort
191 */
192int errorcode;
193char *name;		/* name of filter */
194/* set from flags */
195int debug, width, length, xwidth, ylength, literal, indent;
196char *zopts, *class, *job, *login, *accntname, *host, *accntfile, *format;
197char *printer, *controlfile, *bnrname, *comment;
198char *queuename, *errorfile;
199int npages;	/* number of pages */
200char *statusfile;
201char filter_stop[] = "\031\001";	/* sent to cause filter to suspend */
202int  accounting_fd;
203int crlf;	/* change lf to CRLF */
204
205void getargs( int argc, char *argv[], char *envp[] );
206void logerr( char *msg, ... );
207void logerr_die( char *msg, ... );
208extern void banner( void );
209extern void doaccnt( void );
210extern void filter_pgm( char * );
211int of_filter;
212
213int main( int argc, char *argv[], char *envp[] )
214{
215
216	/* check to see if you have the accounting fd */
217	accounting_fd = dup(3);
218	/* if this works, then you have one */
219	if( accounting_fd >= 0 ){
220		(void)close( accounting_fd );
221		accounting_fd = 3;
222	} else {
223		accounting_fd = -1;
224	}
225	if( fcntl(0,F_GETFL,0) == -1 ){
226		FPRINTF(STDERR,"BAD FD 0\n");
227		exit(2);
228	}
229	if( fcntl(1,F_GETFL,0) == -1 ){
230		FPRINTF(STDERR,"BAD FD 1\n");
231		exit(2);
232	}
233	if( fcntl(2,F_GETFL,0) == -1 ){
234		FPRINTF(STDERR,"BAD FD 2\n");
235		exit(2);
236	}
237	getargs( argc, argv, envp );
238	/*
239	 * Turn off SIGPIPE
240	 */
241	(void)signal( SIGPIPE, SIG_IGN );
242	(void)signal( SIGINT,  SIG_DFL );
243	(void)signal( SIGHUP,  SIG_DFL );
244	(void)signal( SIGQUIT, SIG_DFL );
245	(void)signal( SIGCHLD, SIG_DFL );
246	if( of_filter || (format && format[0] == 'o') ){
247		filter_pgm( filter_stop );
248	} else {
249		filter_pgm( (char *)0 );
250	}
251	return(0);
252}
253
254int Write_fd_str( int fd, const char *msg )
255{
256	int n;
257	n = strlen(msg);
258	return write(fd,msg,n);
259}
260
261/* VARARGS2 */
262#ifdef HAVE_STDARGS
263 void safefprintf (int fd, char *format,...)
264#else
265 void safefprintf (va_alist) va_dcl
266#endif
267{
268#ifndef HAVE_STDARGS
269	int fd;
270    char *format;
271#endif
272	char buf[1024];
273    VA_LOCAL_DECL
274
275    VA_START (format);
276    VA_SHIFT (fd, int);
277    VA_SHIFT (format, char *);
278
279	buf[0] = 0;
280	(void) VSNPRINTF (buf, sizeof(buf)) format, ap);
281	Write_fd_str(fd,buf);
282}
283
284/****************************************************************************
285 * Extract the necessary definitions for error message reporting
286 ****************************************************************************/
287
288#if !defined(HAVE_STRERROR)
289# undef  num_errors
290# if defined(HAVE_SYS_ERRLIST)
291#  if !defined(HAVE_DECL_SYS_ERRLIST)
292     extern const char *const sys_errlist[];
293#  endif
294#  if defined(HAVE_SYS_NERR)
295#   if !defined(HAVE_DECL_SYS_NERR)
296      extern int sys_nerr;
297#   endif
298#   define num_errors    (sys_nerr)
299#  endif
300# endif
301# if !defined(num_errors)
302#   define num_errors   (-1)            /* always use "errno=%d" */
303# endif
304#endif
305
306const char * Errormsg ( int err )
307{
308    const char *cp;
309
310#if defined(HAVE_STRERROR)
311	cp = strerror(err);
312#else
313# if defined(HAVE_SYS_ERRLIST)
314    if (err >= 0 && err <= num_errors) {
315		cp = sys_errlist[err];
316    } else
317# endif
318	{
319		static char msgbuf[32];     /* holds "errno=%d". */
320		(void) SNPRINTF(msgbuf, sizeof(msgbuf)) "errno=%d", err);
321		cp = msgbuf;
322    }
323#endif
324    return (cp);
325}
326
327#ifdef HAVE_STDARGS
328void logerr(char *msg, ...)
329#else
330void logerr( va_alist ) va_dcl
331#endif
332{
333#ifndef HAVE_STDARGS
334	char *msg;
335#endif
336	int err = errno;
337	int n;
338	char buf[1024];
339	VA_LOCAL_DECL
340	VA_START(msg);
341	VA_SHIFT(msg, char *);
342
343	(void)SNPRINTF(buf,sizeof(buf)) "%s: ", name);
344	n = strlen(buf);
345	(void)VSNPRINTF(buf+n,sizeof(buf)-n) msg, ap);
346	n = strlen(buf);
347	(void)SNPRINTF(buf+n,sizeof(buf)-n) "- %s\n", Errormsg(err) );
348	Write_fd_str(2,buf);
349	VA_END;
350}
351
352#ifdef HAVE_STDARGS
353void logerr_die(char *msg, ...)
354#else
355void logerr_die( va_alist ) va_dcl
356#endif
357{
358#ifndef HAVE_STDARGS
359	char *msg;
360#endif
361	int err = errno;
362	int n;
363	char buf[1024];
364	VA_LOCAL_DECL
365	VA_START(msg);
366	VA_SHIFT(msg, char *);
367
368	(void)SNPRINTF(buf,sizeof(buf)) "%s: ", name);
369	n = strlen(buf);
370	(void)VSNPRINTF(buf+n,sizeof(buf)-n) msg, ap);
371	n = strlen(buf);
372	(void)SNPRINTF(buf+n,sizeof(buf)-n) "- %s\n", Errormsg(err) );
373	Write_fd_str(2,buf);
374	VA_END;
375	exit(errorcode);
376}
377
378/*
379 *	doaccnt()
380 *	writes the accounting information to the accounting file
381 *  This has the format: user host printer pages format date
382 */
383void doaccnt(void)
384{
385	time_t t;
386	char buffer[256];
387	FILE *f;
388	int l, len, c;
389
390	t = time((time_t *)0);
391
392	SNPRINTF(buffer, sizeof(buffer)) "%s\t%s\t%s\t%7d\t%s\t%s\n",
393		login? login: "NULL",
394		host? host: "NULL",
395		printer? printer: "NULL",
396		npages,
397		format? format: "NULL",
398		Time_str(0,0));
399	len = strlen( buffer );
400	if( accounting_fd < 0 ){
401		if(accntfile && (f = fopen(accntfile, "a" )) != NULL ) {
402			accounting_fd = fileno( f );
403		}
404	}
405	if( accounting_fd >= 0 ){
406		for( c = l = 0; c >= 0 && l < len; l += c ){
407			c = write( accounting_fd, &buffer[l], len-l );
408		}
409		if( c < 0 ){
410			logerr( "bad write to accounting file" );
411		}
412	}
413}
414
415void getargs( int argc, char *argv[], char *envp[] )
416{
417	int i, c;		/* argument index */
418	char *arg, *optargv;	/* argument */
419	char *s, *end;
420
421	if( (name = argv[0]) == 0 ) name = "FILTER";
422	if( (s = strrchr( name, '/' )) ){
423		++s;
424	} else {
425		s = name;
426	}
427	of_filter =  (strstr( s, "of" ) != 0);
428	for( i = 1; i < argc && (arg = argv[i])[0] == '-'; ++i ){
429		if( (c = arg[1]) == 0 ){
430			FPRINTF( STDERR, "missing option flag");
431			i = argc;
432			break;
433		}
434		if( c == 'c' ){
435			literal = 1;
436			continue;
437		}
438		optargv = &arg[2];
439		if( arg[2] == 0 ){
440			optargv = argv[i++];
441			if( optargv == 0 ){
442				FPRINTF( STDERR, "missing option '%c' value", c );
443				i = argc;
444				break;
445			}
446		}
447		switch(c){
448			case 'C': class = optargv; break;
449			case 'E': errorfile = optargv; break;
450			case 'T':
451					for( s = optargv; s && *s; s = end ){
452						end = strchr( s, ',' );
453						if( end ){
454							*end++ = 0;
455						}
456						if( !strcasecmp( s, "crlf" ) ){
457							crlf = 1;
458						}
459						if( !strcasecmp( s, "debug" ) ){
460							++debug;
461						}
462					}
463					break;
464			case 'F': format = optargv; break;
465			case 'J': job = optargv; break;
466			case 'K': controlfile = optargv; break;
467			case 'L': bnrname = optargv; break;
468			case 'P': printer = optargv; break;
469			case 'Q': queuename = optargv; break;
470			case 'R': accntname = optargv; break;
471			case 'S': comment = optargv; break;
472			case 'Z': zopts = optargv; break;
473			case 'h': host = optargv; break;
474			case 'i': indent = atoi( optargv ); break;
475			case 'l': length = atoi( optargv ); break;
476			case 'n': login = optargv; break;
477			case 's': statusfile = optargv; break;
478			case 'w': width = atoi( optargv ); break;
479			case 'x': xwidth = atoi( optargv ); break;
480			case 'y': ylength = atoi( optargv ); break;
481			default: break;
482		}
483	}
484	if( i < argc ){
485		accntfile = argv[i];
486	}
487	if( errorfile ){
488		int fd;
489		fd = open( errorfile, O_APPEND | O_WRONLY, 0600 );
490		if( fd < 0 ){
491			FPRINTF( STDERR, "cannot open error log file '%s'", errorfile );
492		} else {
493			FPRINTF( STDERR, "using error log file '%s'", errorfile );
494			if( fd != 2 ){
495				dup2(fd, 2 );
496				close(fd);
497			}
498		}
499	}
500	if( debug ){
501		FPRINTF(STDERR, "%s command: ", name );
502		for( i = 0; i < argc; ++i ){
503			FPRINTF(STDERR, "%s ", argv[i] );
504		}
505		FPRINTF( STDERR, "\n" );
506	}
507	if( debug ){
508		FPRINTF(STDERR, "FILTER decoded options: " );
509		FPRINTF(STDERR,"accntfile '%s'\n", accntfile? accntfile : "null" );
510		FPRINTF(STDERR,"accntname '%s'\n", accntname? accntname : "null" );
511		FPRINTF(STDERR,"class '%s'\n", class? class : "null" );
512		FPRINTF(STDERR,"errorfile '%s'\n", errorfile? errorfile : "null" );
513		FPRINTF(STDERR,"format '%s'\n", format? format : "null" );
514		FPRINTF(STDERR,"host '%s'\n", host? host : "null" );
515		FPRINTF(STDERR,"indent, %d\n", indent);
516		FPRINTF(STDERR,"job '%s'\n", job? job : "null" );
517		FPRINTF(STDERR,"length, %d\n", length);
518		FPRINTF(STDERR,"literal, %d\n", literal);
519		FPRINTF(STDERR,"login '%s'\n", login? login : "null" );
520		FPRINTF(STDERR,"printer '%s'\n", printer? printer : "null" );
521		FPRINTF(STDERR,"queuename '%s'\n", queuename? queuename : "null" );
522		FPRINTF(STDERR,"statusfile '%s'\n", statusfile? statusfile : "null" );
523		FPRINTF(STDERR,"width, %d\n", width);
524		FPRINTF(STDERR,"xwidth, %d\n", xwidth);
525		FPRINTF(STDERR,"ylength, %d\n", ylength);
526		FPRINTF(STDERR,"zopts '%s'\n", zopts? zopts : "null" );
527
528		FPRINTF(STDERR, "FILTER environment: " );
529		for( i = 0; (arg = envp[i]); ++i ){
530			FPRINTF(STDERR,"%s\n", arg );
531		}
532		FPRINTF(STDERR, "RUID: %d, EUID: %d\n", (int)getuid(), (int)geteuid() );
533	}
534}
535
536/*
537 * suspend_ofilter():  suspends the output filter, waits for a signal
538 */
539void suspend_ofilter(void)
540{
541	fflush(stdout);
542	fflush(stderr);
543	if(debug)FPRINTF(STDERR,"FILTER suspending\n");
544	kill(getpid(), SIGSTOP);
545	if(debug)FPRINTF(STDERR,"FILTER awake\n");
546}
547/******************************************
548 * prototype filter()
549 * filter will scan the input looking for the suspend string
550 * if any.
551 ******************************************/
552
553void filter_pgm(char *stop)
554{
555	int c;
556	int state, i, xout, lastc;
557	int lines = 0;
558	char inputline[1024];
559	int inputcount = 0;
560
561	/*
562	 * do whatever initializations are needed
563	 */
564	/* FPRINTF(STDERR, "filter ('%s')\n", stop ? stop : "NULL" ); */
565	/*
566	 * now scan the input string, looking for the stop string
567	 */
568	lastc = xout = state = 0;
569	npages = 1;
570
571	inputcount = 0;
572	while( (c = getchar()) != EOF ){
573		if( inputcount < (int)sizeof(inputline) - 3 ) inputline[inputcount++] = c;
574		if( c == '\n' ){
575			inputline[inputcount-1] = 0;
576			if(debug)FPRINTF(STDERR,"INPUTLINE count %d '%s'\n", inputcount, inputline );
577			inputcount = 0;
578			++lines;
579			if( lines > length ){
580				lines -= length;
581				++npages;
582			}
583			if( !literal && crlf == 0 && lastc != '\r' ){
584				putchar( '\r' );
585			}
586		}
587		if( c == '\014' ){
588			++npages;
589			lines = 0;
590			if( !literal && crlf == 0 ){
591				putchar( '\r' );
592			}
593		}
594		if( stop ){
595			if( c == stop[state] ){
596				++state;
597				if( stop[state] == 0 ){
598					state = 0;
599					suspend_ofilter();
600				}
601			} else if( state ){
602				for( i = 0; i < state; ++i ){
603					putchar( stop[i] );
604				}
605				state = 0;
606				putchar( c );
607			} else {
608				putchar( c );
609			}
610		} else {
611			putchar( c );
612		}
613		lastc = c;
614	}
615	if( ferror( stdin ) ){
616		logerr( "error on STDIN");
617	}
618	for( i = 0; i < state; ++i ){
619		putchar( stop[i] );
620	}
621	if( lines > 0 ){
622		++npages;
623	}
624	doaccnt();
625}
626
627/*
628 * Time_str: return "cleaned up" ctime() string...
629 *
630 * in YY/MO/DY/hr:mn:sc
631 * Thu Aug 4 12:34:17 BST 1994 -> 12:34:17
632 */
633
634char *Time_str(int shortform, time_t t)
635{
636    static char buffer[99];
637	struct tm *tmptr;
638	struct timeval tv;
639
640	tv.tv_usec = 0;
641	if( t == 0 ){
642		if( gettimeofday( &tv, 0 ) == -1 ){
643			logerr_die( "Time_str: gettimeofday failed");
644		}
645		t = tv.tv_sec;
646	}
647	tmptr = localtime( &t );
648	if( shortform ){
649		SNPRINTF( buffer, sizeof(buffer))
650			"%02d:%02d:%02d.%03d",
651			tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec,
652			(int)(tv.tv_usec/1000) );
653	} else {
654		SNPRINTF( buffer, sizeof(buffer))
655			"%d-%02d-%02d-%02d:%02d:%02d.%03d",
656			tmptr->tm_year+1900, tmptr->tm_mon+1, tmptr->tm_mday,
657			tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec,
658			(int)(tv.tv_usec/1000) );
659	}
660	/* now format the time */
661	return( buffer );
662}
663