/***************************************************************************
* LPRng - An Extended Print Spooler System
*
* Copyright 1988-1995 Patrick Powell, San Diego State University
*
[email protected]
* See LICENSE for conditions of use.
* From the original PLP Software distribution
*
***************************************************************************
* MODULE: main.c for filter
* PURPOSE: setup a filter environment
**************************************************************************/
/***************************************************************************
* MODULE: main.c for filters
***************************************************************************
*
* Wed Aug 16 09:35:24 PDT 1995 Patrick Powell
* Added new filter options -s, etc.
*
* Revision History: Baseline Fri Jul 14 16:16:34 PDT 1995
*
* Patrick Powell Fri Jul 14 16:16:13 PDT 1995
*
***************************************************************************/
#ifndef lint
static char _id[] =
"main.c,v 1.1 1997/01/05 04:57:02 papowell Exp";
#endif
/***************************************************************************
* Filter template and frontend.
*
* A filter is invoked with the following parameters,
* which can be in any order, and perhaps some missing.
*
* filtername arguments \ <- from PRINTCAP entry
* -PPrinter -wwidth -llength -xwidth -ylength [-c] \
* -kcontrolfilename -Lbnrname \
* [-iindent] \
* [-Zoptions] [-Cclass] [-Jjob] [-Raccntname] -nlogin -hHost \
* -Fformat -Ddebug [affile]
*
* 1. Parameters can be in different order than the above.
* 2. Optional parameters can be missing
* 3. Values specified for the width, length, etc., are from PRINTCAP
* or from the overridding user specified options.
*
* This program provides a common front end for most of the necessary
* grunt work. This falls into the following classes:
* 1. Parameter extraction.
* 2. Suspension when used as the "of" filter.
* 3. Termination and accounting
* The front end will extract parameters, then call the filter()
* routine, which is responsible for carrying out the required filter
* actions. filter() is invoked with the printer device on fd 1,
* and error log on fd 2. The npages variable is used to record the
* number of pages that were used.
* The "halt string", which is a sequence of characters that
* should cause the filter to suspend itself, is passed to filter.
* When these characters are detected, the "suspend()" routine should be
* called.
*
* On successful termination, the accounting file will be updated.
*
* The filter() routine should return 0 (success), 1 (retry) or 2 (abort).
*
* Parameter Extraction
* The main() routine will extract parameters
* whose values are placed in the appropriate variables. This is done
* by using the ParmTable[], which has entries for each valid letter
* parmeter, such as the letter flag, the type of variable,
* and the address of the variable.
* The following variables are provided as a default set.
*
* VARIABLE FLAG TYPE PURPOSE / PRINTCAP ENTRTY
* name filterparm char* argv[0], program identification
* accntfile -aaccnting char* AF, accounting file
* bytecount -bbytecount int size of job in bytes
* literal -c int if set, ignore control chars
* ctrldir -dctrldir char* control directory for status information
* dfilename -edfilename char* datafile name in spool directory
* filename -ffilename char* original file name of data file
* host -hhost char* host name
* indent -iindent int indent amount (depends on device)
* jobnum -jjobnum int job number from cfNNNhost, jobnum = NNN
* cffile -kcffile char* job control file (cfNNNhost) name
* length -llength int PL, length in lines
* login -nlogin char* login name
* statusfile -sstatusfile char* statusfile
* timestr -ttimestr char* time of printing
* width -wwidth int PW, width in chars
* xwidth -xwidth int PX, width in pixels
* ylength -ylength int PY, length in pixels
* class -Cclass char* classname
* format -Fformat char* format
* job -Jjob char* jobname
* bnrname -Lbnrname char* banner name
* printer -Printer char* printer name
* accntname -Raccntname char* name to be used for accounting
* comment -Scomment char* comment field in printcap
* accntname -Raccntname char* account for billing purposes
* zopts -Zoptions char* extra options for printer
* accntfile file char* AF, accounting file
*
* Additional Variables - used for the following
* npages - number of pages for accounting
* debug - sets debug level
* verbose - echo to a log file
*
* The functions fatal(), logerr(), and logerr_die() can be used to report
* status. The variable errorcode can be set by the user before calling
* these functions, and will be the exit value of the program. Its default
* value will be 2 (abort status).
* fatal() reports a fatal message, and terminates.
* logerr() reports a message, appends information indicated by errno
* (see perror(2) for details), and then returns.
* logerr_die() will call logerr(), and then will exit with errorcode
* status.
* Both fatal() and logerr_die() call the cleanup() function before exit.
*
* DEBUGGING: a simple minded debugging version can be enabled by
* compiling with the -DDEBUG option.
* NB: check error code status with LPRng documentation.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_FILE_H
# include <sys/file.h>
#endif
#ifndef HAVE_ERRNO_DECL
extern int errno;
#endif
#ifdef HAVE_SYS_FCNTL_H
# include <sys/fcntl.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
/* varargs declarations: */
#if defined(HAVE_STDARG_H)
# include <stdarg.h>
# define HAVE_STDARGS /* let's hope that works everywhere (mj) */
# define VA_LOCAL_DECL va_list ap;
# define VA_START(f) va_start(ap, f)
# define VA_SHIFT(v,t) ; /* no-op for ANSI */
# define VA_END va_end(ap)
#else
# if defined(HAVE_VARARGS_H)
# include <varargs.h>
# undef HAVE_STDARGS
# define VA_LOCAL_DECL va_list ap;
# define VA_START(f) va_start(ap) /* f is ignored! */
# define VA_SHIFT(v,t) v = va_arg(ap,t)
# define VA_END va_end(ap)
# else
XX ** NO VARARGS ** XX
# endif
#endif
/*
* default exit status, causes abort
*/
int errorcode;
char *name; /* name of filter */
int debug, verbose, width, length, xwidth, ylength, literal, indent;
int bytecount, jobnum;
char *zopts, *class, *job, *login, *accntname, *host, *accntfile, *format;
char *printer, *controlfile, *bnrname, *comment;
char *queuename, *errorfile;
char *ctrldir, *dfilename, *filename, *cffile, *timestr;
int npages; /* number of pages */
char *statusfile;
char filter_stop[] = "\031\001"; /* sent to cause filter to suspend */
void getargs( int argc, char *argv[], char *envp[] );
#ifdef HAVE_STDARGS
void log( char *msg, ... );
void logerr( char *msg, ... );
void logerr_die( char *msg, ... );
void fatal( char *msg, ... );
#else
void log();
void logerr();
void logerr_die();
void fatal();
#endif
extern void banner( void );
extern void cleanup( void );
extern void doaccnt( void );
extern void filter( char * );
int main( int argc, char *argv[], char *envp[] )
{
getargs( argc, argv, envp );
/*
* Turn off SIGPIPE
*/
(void)signal( SIGPIPE, SIG_IGN );
if( format && format[0] == 'o' ){
filter( filter_stop );
} else {
filter( (char *)0 );
}
return(0);
}
/****************************************************************************
* Extract the necessary definitions for error message reporting
****************************************************************************/
#if !defined(HAVE_STRERROR)
# if defined(HAVE_SYS_NERR)
# if !defined(HAVE_SYS_NERR_DEF)
extern int sys_nerr;
# endif
# define num_errors (sys_nerr)
# else
# define num_errors (-1) /* always use "errno=%d" */
# endif
# if defined(HAVE_SYS_ERRLIST)
# if !defined(HAVE_SYS_ERRLIST_DEF)
extern const char *const sys_errlist[];
# endif
# else
# undef num_errors
# define num_errors (-1) /* always use "errno=%d" */
# endif
#endif
const char * Errormsg ( int err )
{
const char *cp;
#if defined(HAVE_STRERROR)
cp = strerror(err);
#else
# if defined(HAVE_SYS_ERRLIST)
if (err >= 0 && err <= num_errors) {
cp = sys_errlist[err];
} else
# endif
{
static char msgbuf[32]; /* holds "errno=%d". */
/* SAFE use of sprintf */
(void) sprintf (msgbuf, "errno=%d", err);
cp = msgbuf;
}
#endif
return (cp);
}
#ifdef HAVE_STDARGS
void log(char *msg, ...)
#else
void log( va_alist ) va_dcl
#endif
{
#ifndef HAVE_STDARGS
char *msg;
#endif
VA_LOCAL_DECL
VA_START(msg);
VA_SHIFT(msg, char *);
(void)vfprintf(stderr, msg, ap);
(void)fprintf(stderr, "\n" );
VA_END;
(void)fflush(stderr);
}
#ifdef HAVE_STDARGS
void fatal(char *msg, ...)
#else
void fatal( va_alist ) va_dcl
#endif
{
#ifndef HAVE_STDARGS
char *msg;
#endif
VA_LOCAL_DECL
VA_START(msg);
VA_SHIFT(msg, char *);
(void)fprintf(stderr, "%s: ", name);
(void)vfprintf(stderr, msg, ap);
(void)fprintf(stderr, "\n" );
VA_END;
(void)fflush(stderr);
cleanup();
exit(errorcode);
}
#ifdef HAVE_STDARGS
void logerr(char *msg, ...)
#else
void logerr( va_alist ) va_dcl
#endif
{
#ifndef HAVE_STDARGS
char *msg;
#endif
int err = errno;
VA_LOCAL_DECL
VA_START(msg);
VA_SHIFT(msg, char *);
(void)fprintf(stderr, "%s: ", name);
(void)vfprintf(stderr, msg, ap);
(void)fprintf(stderr, "- %s\n", Errormsg(err) );
VA_END;
(void)fflush(stderr);
}
#ifdef HAVE_STDARGS
void logerr_die(char *msg, ...)
#else
void logerr_die( va_alist ) va_dcl
#endif
{
#ifndef HAVE_STDARGS
char *msg;
#endif
int err = errno;
VA_LOCAL_DECL
VA_START(msg);
VA_SHIFT(msg, char *);
(void)fprintf(stderr, "%s: ", name);
(void)vfprintf(stderr, msg, ap);
(void)fprintf(stderr, "- %s\n", Errormsg(err) );
VA_END;
(void)fflush(stderr);
cleanup();
exit(errorcode);
}
/*
* doaccnt()
* writes the accounting information to the accounting file
* This has the format: user host printer pages format date
*/
void doaccnt()
{
time_t t, time();
char *ctime();
FILE *f;
t = time((time_t *)0);
if(accntfile && (f = fopen(accntfile, "a" )) != NULL ) {
fprintf(f,"%s\t%s\t%s\t%7d\t%s\t%s",
login? login: "NULL",
host? host: "NULL",
printer? printer: "NULL",
npages,
format? format: "NULL",
ctime(&t));
(void)fclose(f);
}
}
void getargs( int argc, char *argv[], char *envp[] )
{
int i, c; /* argument index */
char *arg, *optarg; /* argument */
if( (name = argv[0]) == 0 ) name = "FILTER";
for( i = 1; i < argc && (arg = argv[i])[0] == '-'; ++i ){
if( (c = arg[1]) == 0 ){
fprintf( stderr, "missing option flag");
i = argc;
break;
}
if( c == 'c' ){
literal = 1;
continue;
}
if( arg[2] == 0 ){
optarg = argv[i++];
if( optarg == 0 || optarg[0] == '-' ){
fprintf( stderr, "missing option '%c' value", c );
i = argc;
break;
}
}
optarg = &arg[2];
switch(c){
case 'C': class = optarg; break;
case 'E': errorfile = optarg; break;
case 'D': debug = atoi( optarg ); break;
case 'F': format = optarg; break;
case 'J': job = optarg; break;
case 'K': controlfile = optarg; break;
case 'L': bnrname = optarg; break;
case 'P': printer = optarg; break;
case 'Q': queuename = optarg; break;
case 'R': accntname = optarg; break;
case 'S': comment = optarg; break;
case 'Z': zopts = optarg; break;
case 'a': accntfile = optarg; break;
case 'b': bytecount = atoi(optarg); break;
case 'd': ctrldir = optarg; break;
case 'e': dfilename = optarg; break;
case 'f': filename = optarg; break;
case 'h': host = optarg; break;
case 'i': indent = atoi( optarg ); break;
case 'j': jobnum = atoi( optarg ); break;
case 'k': cffile = optarg; break;
case 'l': length = atoi( optarg ); break;
case 'n': login = optarg; break;
case 's': statusfile = optarg; break;
case 't': timestr = optarg; break;
case 'v': verbose = atoi( optarg ); break;
case 'w': width = atoi( optarg ); break;
case 'x': xwidth = atoi( optarg ); break;
case 'y': ylength = atoi( optarg ); break;
default: break;
}
}
if( i < argc ){
accntfile = argv[i];
}
if( errorfile ){
int fd;
fd = open( errorfile, O_APPEND | O_WRONLY );
if( fd < 0 ){
fprintf( stderr, "cannot open error log file '%s'", errorfile );
} else {
fprintf( stderr, "using error log file '%s'", errorfile );
if( fd != 2 ){
dup2(fd, 2 );
close(fd);
}
}
}
if( verbose || debug ){
fprintf(stderr, "%s command: ", name );
for( i = 0; i < argc; ++i ){
fprintf(stderr, "%s ", argv[i] );
}
fprintf( stderr, "\n" );
fflush(stderr);
}
if( debug ){
fprintf(stderr, "FILTER decoded options: " );
fprintf(stderr,"login '%s'\n", login? login : "null" );
fprintf(stderr,"host '%s'\n", host? host : "null" );
fprintf(stderr,"class '%s'\n", class? class : "null" );
fprintf(stderr,"format '%s'\n", format? format : "null" );
fprintf(stderr,"job '%s'\n", job? job : "null" );
fprintf(stderr,"printer '%s'\n", printer? printer : "null" );
fprintf(stderr,"queuename '%s'\n", queuename? queuename : "null" );
fprintf(stderr,"accntname '%s'\n", accntname? accntname : "null" );
fprintf(stderr,"zopts '%s'\n", zopts? zopts : "null" );
fprintf(stderr,"literal, %d\n", literal);
fprintf(stderr,"indent, %d\n", indent);
fprintf(stderr,"length, %d\n", length);
fprintf(stderr,"width, %d\n", width);
fprintf(stderr,"xwidth, %d\n", xwidth);
fprintf(stderr,"ylength, %d\n", ylength);
fprintf(stderr,"accntfile '%s'\n", accntfile? accntfile : "null" );
fprintf(stderr,"errorfile '%s'\n", errorfile? errorfile : "null" );
fprintf(stderr,"statusfile '%s'\n", statusfile? statusfile : "null" );
fprintf(stderr, "FILTER environment: " );
for( i = 0; (arg = envp[i]); ++i ){
fprintf(stderr,"%s\n", arg );
}
fprintf(stderr, "RUID: %d, EUID: %d\n", (int)getuid(), (int)geteuid() );
fflush(stderr);
}
}
/*
* suspend(): suspends the output filter, waits for a signal
*/
void suspend()
{
if(debug)fprintf(stderr,"FILTER suspending\n");
fflush(stderr);
kill(getpid(), SIGSTOP);
if(debug)fprintf(stderr,"FILTER awake\n");
fflush(stderr);
}
/******************************************
* prototype filter() and cleanup() functions;
* filter will scan the input looking for the suspend string
* if any.
******************************************/
void cleanup() {}
#ifdef DEBUG
void filter(char *stop)
{
int c;
int state, i;
int lines = 0;
/*
* do whatever initializations are needed
*/
/* fprintf(stderr, "filter ('%s')\n", stop ? stop : "NULL" ); */
/*
* now scan the input string, looking for the stop string
*/
state = 0;
npages = 1;
while( (c = getchar()) != EOF ){
if( c == '\n' ){
++lines;
if( lines > length ){
lines -= length;
++npages;
}
}
if( (stop || state) && c == stop[state] ){
++state;
if( stop[state] == 0 ){
state = 0;
if( fflush(stdout) ){
logerr( "fflush returned error" );
break;
}
suspend();
}
} else {
for( i = 0; i < state; ++i ){
putchar( stop[i] );
}
state = 0;
putchar( c );
}
}
if( ferror( stdin ) ){
logerr( "read error on stdin");
}
for( i = 0; i < state; ++i ){
putchar( stop[i] );
}
if( lines > 0 ){
++npages;
}
if( fflush(stdout) ){
logerr( "fflush returned error" );
}
doaccnt();
}
#endif /* DEBUG */