Subject: v16i025: Public lineprinter spooler package, Part12/14 Newsgroups: comp.sources.unix Sender: sources Approved: rsalz@uunet.UU.NET Submitted-by: papowell@julius.cs.umn.edu Posting-number: Volume 16, Issue 25 Archive-name: plp/part12 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'doc/PLP/04.t' <<'END_OF_FILE' X.ig X$Header: 04.t,v 1.1 88/05/21 18:39:37 papowell Locked $ X$log$ X.. X.NH 1 Printcap and PLP Operations X.PP The entries in the printcap file are used to define and control the actions of the PLP software. XEach host machine must have a printcap file that is used to specify the spool queues that are available on the host. Permissions to use a particular spool queue are determined by the printer permissions file. The following chapter discusses the structure of the printcap file, and the purpose of entries in the printcap file. X.NH 2 Printcap Format X.PP The printcap file format is a simplified form of the X.IR termcap (5) data base. XEach spool queue has a corresponding printcap entry. XEach entry for a spool queue with multiple servers also has a printcap entry. XFigure 4.1 is an example of a typical printcap entry. X.KF X.DT X.in +1i X.L X.SM X.nf X# Default line printer is first in printcap entry X# Clunker Printer in Lind 23 clunker_lind23|clunker:\e X :lp=/dev/lp:\e X :st=\et\er\e015:\e X :mx#20:\e X :wt:fl@: X.LG X.in -1i X.R X.ce XFigure 4.1 Sample Printcap Entry X.KE X.IP 1). 3 Blank lines and lines which start with a X.L # are treated as comments and can be used to separate entries. X.IP 2). 3 XEach entry starts in column 1, and is continued across multiple lines by escaping the end of line with a backslash (\c X.BR \e ). XFields of the entry are separated by colons (\c X.BR : ). X.IP 3). 3 The first field of an entry is the entry name followed by a an optional list of aliases. The entry name and aliases are separated by a bars (\c X.BR | ). X.IP 4). 3 Parameter fields start with a two letter parameter name, followed by a flag indicating the parameter type. Parameters can be strings, integers, or flags. String parameters have the form X.L xx=ssss X(\c X.L ssss is the string assigned to the parameter), integer parameters have the form X.L xx#nnnn , and flag or boolean parameters use X.L xx to set X.L xx@ to clear the flag. X.IP 5). 3 Non-printing characters can be represented in a string by using the character escape sequences of the C programming language. X.IP 6). 3 See the X.IR printcap \|(5) man page for a complete list of printcap variables and their default values. X.PP XEach entry in the printcap file specifies either a remote spool queue, a local spool queue with a single printer, a local spool queue with multiple printers, or a printer for a spool queue with multiple printers. The information in the printcap entry determines the type of entry and how the entries in the spool queue are to be handled. X.PP The PLP software searches the printcap file for a printcap entry with a matching printer name or alias. By convention, if a user does not specify a printer, either by using a command line option or setting the X.B PRINTER environment variable, the first printer in the printcap file is used as the default printer. X.NH 2 Spool Queue Directory X.PP The X.B sd X(spool directory) field specifies the path name of the directory which will hold spool queue entries or jobs. The X.B sd parameter must be present for all printcap entries which are used for spooling. XFigure 4.2 is a typical printcap entry for a remote printer spool queue, whose spool directory is specified as X.L /usr/spool/lind33_decwriter . X.KF X.nf X.in +1i X.DT X.L X.SM X# Decwriter III in Lind 33 X# Modified 11 Feb. 1988 Patrick Powell - added fx=n for ditroff X# queue printer permissions file X# maximum job size 2000K bytes, no copies remote\||\|lind33_decwriter:\e X :fx=ilpn:\e X :sd=/usr/spool/lind33_decwriter:\e X :rm=umn-cs.cs.umn.edu:rp=lind33_decrwriter:\e X :xu=perms:\e X :ex=n:ne=/usr/local/lib/ddumb:\e X :mx#2000:sc: X.LG X.R X.in -1i X.ce XFigure 4.2 Remote Printer Printcap Entry X.KE X.NH 2 Job Submission Controls X.PP The following fields are used to control job submission. X.NH 3 Job Formats X.PP XEach job in a spool queue has an associated format, which is represented by a lower case letter\**. X.FS Actually, each file in the job can have a different format, but the current version of X.I lpr does not support this. X.FE There are several X.I lpr options such as X.B -n, X.B -d, etc. which can be used to specify a limited set of formats, or the X.B \-Fx option can be used to specify any format. XFor example, the X.B \-Fn options specifies printing using the X.B n format, as below. X.ti +.5i X.L "ditroff -Tdumb -ms myfile | lpr -Fn -Premote" X.PP The format supported by a spool queue can be restricted by the X.B fx printcap field, which specifies the formats supported by the spool queue. In Figure 4.2, the X.B fx=ilpn field restricts formats to X.L i, X.L l, X.L p, and X.L n . Some formats require that files be printable X(i.e.- contain characters in the ASCII characters set, or other restrictions). By default, only the X.L f and X.L p formats are checked by X.I lpr for printable files. The X.B xt field is used to specify additional formats to be checked for printable files. X.NH 3 Permissions Files X.PP XEach host has a printer permissions file which is checked by the PLP software to determine if actions requested by users or other hosts are permitted. In addition, the X.B xu X(check user) printcap field is used to specify an additional printer permission file which is checked for jobs in the spool queue. XFor example, the X.B xu=perms field causes the X.L perms file in the spool directory to be checked by X.I lpr when the job is submitted. Similarly, the unspooling server process will also check this file when the job is finally printed. X.NH 3 Prefilters X.PP It may be desirable to process or filter a job at the time that it is spooled. The program used to filter a job is specified by a field of the form X.BR X e=filtername , or X.BR X e=filtername,y , where X.L X is the original format, filtername is the pathname of a program, and X.L y is the result format. The default result format is X.L f . In addition, the X.I lpr X.B -p option specifies filtering with the X.I pr program; X.I lpr invokes the program specified by the X.BR pr= filtername X(default is X.L /bin/pr ). X.NH 3 Job Size and Number of Copies X.PP The X.B mx X(maximum size) field is used to specify a maximum file size (in 1 Kbyte blocks) that can be spooled by X.I lpr. Jobs whose total size (including duplicates) exceeds this limit are not spooled. XFor example, the X.B mx#2000 entry restricts jobs to a maximum of 2000 Kbytes. The X.B mc X(maximum copies) value specifies the maximum number of copies allowed and the X.B sc X(suppress copies) flag which suppresses multiple copies. X.NH 2 Remote Printer Spool Queues X.PP Remote spool queues specify the remote host using the X.B rm field and the remote printer using the X.B rp field. The remote host addressing information is obtained using the X.IR gethostbynam (3R) facility. The remote printer must appear in the remote host's printcap data base. The following is an example of an entry for a remote spool queue that will be sent to host X.L umn-cs.cs.umn.edu , and printed on the X.L lind33_decwriter printer. X.NH 2 Local Printer Queues X.PP Unspooling from a local printer queue can be done in two manners: using the unspooling cababilies of the PLP server created for the queue by the lpd, or by having the server process invoke a special X.I "queue handler" process. The latter method is discussed later in this section. X.PP Associated with each local queue is an output device. These can be categorized as serial line or a specialized device with a non-serial interface. If a device is attached to a serial, the printcap can be used to specify how the serial line is to be conditioned in order to communicate with the printer. XFigure 4.3 is a sample printcap entry for a DecWriter III printer connected over a 1200 baud serial line. X.KF X.DT X.in +1i X.L X.nf X.SM X# Local Printer Entry X# Decwriter III in Lind 33 X# Modified 11 Feb. 1988 Patrick Powell - added fx=n for ditroff, max 2 retries lind33_decwriter\||\|clunker\||\|Decwriter Model 3:\e X :fx=ilpn:\e X :sd=/usr/spool/lind33_decwriter:\e X :xu=/usr/adm/perms/lind33_decwriter:\e X :rt=2:\e X :ne=/usr/local/lib/ddumb:\e X :lp=/dev/ttyh8:rw:\e X :br#1200:fs#06320:ty=-nl odd even -tabs tandem new:\e X :lf=error: X.LG X.in -1i X.R X.ce XFigure 4.3 Printcap Entry for Serial Line Device X.LG X.KE X.NH 3 Output Device Pathname X.PP The X.B lp X(line printer) parameter specifies a path name of the device that is to be opened and output sent to. The device is opened for writing only (default), or reading and writing if the X.B rw flag is present. Note that there is no default value for the X.B lp field. If there is no physical device corresponding to the printer, then X.L "lp=/dev/null" should be used to specify a null device. X.NH 3 Retry Limits X.PP The server will attempt to unspool and print a job for a limited number of times. The number of attempts is set by the X.B rt X(retry) parameter; the default value is 3, and a \-1 value specifies unlimited retries. X.NH 3 Serial Line Characteristics X.PP Printers connected via a serial communication line must have the proper baud rate and line characteristics set. X.IP "Setting Bit (Baud) Rates" 3 The X.B br parameter sets the bit (baud) rate for the tty line. XFor example, X300 means 300 BPI, X9600 means 9600 BPI. X.IP "Setting Line Characteristics" 3 The X.B fs X(set bits) and X.B fc X(clear bits) parameter is used to set the serial line characteristics with the TIOCSETP X.IR ioctl \|(2) operation. The X.B fs and X.B fc set and clear bits in the line control status word returned by the TIOGSETP ioctl operation, which is then restored with the ioctl TIOCSETP. In the above example, X.B fs sets CRMOD, no parity, and XTABS (see X.IR tty \|(4) for details). The X.B xs and X.B xc are be used to set and clear local control information using the TIOCCLGET and TIOCCLSET ioctl calls. X.IP "Alternate Method" 3 In case you find the X.B fs , X.B fc , etc., method of setting line characteristics embarassingly user-hostile, you can use the X.B ty X(stty) parameter, which uses (almost) the same keywords and options as the X.I stty (1) command. XFor example, X.L "ty=nl -odd -even -tabs" can be used instead of the above X.B fs values. If you need tandem line control you may have to open the output device for reading and writing using the X.B rw field; this appears to be implementation dependent. The following X.I stty options are recognized by the X.L ty field and have the same meaning as the X.I stty command as supplied for 4.3 UNIX, with some local modifications added from SUN Microsystems UNIX. X.DS X.DT X.nf X.L X.SM X.ta 16n +16n +16n +16n +16n +16n +16n +16n +16n 8i bs0 bs1 [-]cbreak cooked cr0 cr1 cr2 cr3 [-]decctlq [-]echo X[-]even ff0 ff1 [-]lcase [-]litout nl0 nl1 nl2 nl3 [-]nl X[-]noflsh new [-]nohang old [-]odd X[-]raw start stop tab0 tab1 tab2 [-]tabs [-]tandem tek ti700 X[-]tilde tn300 tty33 tty37 vt05 X.DE X.NH 2 Job, Log, Lock, and Status Files X.PP The pathnames for all files associated with a spool queue are relative to the spool queue directory or are absolute paths. The following sections discuss the various files that are used for logging and status reporting. X.NH 3 Job Files X.PP A job consists of a control file and a set of data files. Lines in the control file provide information about the user, the originating hosts, and the names of the data files, and the data file unspooling formats. A job control file name has the form: X.ti +5n X.B cf X.L "[Priority \- A-Z]" X.L "[Job Number \- 000-999]" X.L "[Host Name]" X.br X.ti +5n X.nf XExample: cfA002attila.cs.umn.edu X.fi X.PP The first two letters identify the file as a control file, the next letter is the job's priority level, the next 3 digits are the job number, and the trailing characters are the host name. X.PP The priority level is set by the LPR process at the time that the job is submitted; the X.I lpc program can be used to upgrade a job's priority. The sequence number is obtained from a sequence file, which is locked when in use by X.I lpr in order to avoid duplicate sequence file names. XEach host has a unique sequence file, and the combination of host and sequence number should be unique. This is true as long as there are less than 1000 jobs outstanding in a queue. The host name currently used in the PLP software is currently set to be the network host name, as returned by X.IR gethostnam (3n). X.PP The data files associated with a job have files names with a similar form, as follows: X.ti +5n X.B df X.I [Sequence] X.I [Job Number] X.I [Host] X.br X.ti +5n X.nf XExample: dfA002attila.cs.umn.edu, dfB002attila.cs.umn.edu X.fi X.PP The PLP software checks to ensure that control file and data file names have the same corresponding form. X.NH 3 Control File X.PP A control file contains the information neccessary to print a job. XFigure 4.4 is a typical control file for a job. X.KF X.DT X.L X.SM X.nf X.in +1i Hattila.cs.umn.edu LPatrick Powell,L136 Ppapowell N(stdin) J(stdin) CZ fdfA001attila UdfA001attila X.in -1i X.LG X.R X.ce XFigure 4.4. Job Control File Example X.KE X.PP The first character on each line is a flag character used to decode the remainder of the line. Upper case flags provide information or pass options for the printing actions. Lower case characters are format indicators for specified data files. Note that there is a maximum limit on the size of each line of a control file. Table 4.1 lists the various control flags that are used in the control file. X.KS X.L X.SM X.TS tab(:) box center; l |l |l. XFlag:Parameter:Meaning X_ C:class name:banner page H:host name:originating host I:indent:amount to indent output J:job name:banner page L:user name:name to print on banner M:mail:mail to user when done printing N:name:name of file in job P:Person:user's login name S:flag:no header request R:account:accounting information U:file name:remove file after we print it W:width:page width for PR XX:header:header title for PR Z:extra options:options for filters from lpr f:file name:ordinary file l:file name:text file with control chars p:file name:text file to print with pr(1) t:file name:troff(1) file n:file name:ditroff(1) file d:file name:dvi file g:file name:plot(1G) file v:file name:plain raster file c:file name:cifplot file X.TE X.LG X.R X.ce Table 4.1. Control File Flags X.KE X.NH 3 Log File X.PP The spool queue server process writes error and informational messages to the server log file, which is specified by the X.B lf field; the default log file is X.IR log . If the log file does not exist, then a dummy log file (/dev/null) is opened for logging purposes. Serious error message are written both to the log file and logged using the X.IR syslog (8) facility if it is available, or to X.L /dev/console if it is not. X.NH 3 Lock File X.PP The spool queue lock file is used to control spooling and unspooling operations. The lock file name is specified using the X.B lo X(lock file) field, and the default lock file name is X.IR lock . X.NH 3 Status Files X.PP The server status file X(default file is X.IR status ) contains status information reflecting the operations of the unspooling server processes. The X.B st X(status) field can be used to specify an explicity name for the status file. An additional printer status file can be specified with the X.B ps field. This file can be used by filter programs that generated additional status information that should be available for display. X.NH 2 Multiple Servers X.PP If a spool queue has multiple printers or servers, the server names are specified by the X.B sv X(servers) parameter, which contains a comma separated list of server names. XEach server name has a printcap entry, and each server entry has a corresponding X.B ss X(serves) field which contains the name of the spool queue served by the printer. XEach printer will use the spool queue directory specified by the X.B sd field of the spool queue that it serves. Note that this name corresponds to the name of the queue, not the spool directory. X.KF X.in +1i X.DT X.L X.SM X.nf X# Multiple servers fast:\e X :sd=/usr/spool/fast:\e X :sv=lp1,lp2: lp1:\e X :ss=fast:\e X :lo=lp1:lf=log1:st=st1:lp=/dev/lp1: lp2:\e X :ss=fast:\e X :lo=lp2:lf=log2:st=st2:lp=/dev/lp2: X.LG X.R X.in -1i X.ce XFigure 4.5 Multiple Server Printcap Entry X.KE X.PP In the example in Figure 4.5, the X.L lp1 and X.L lp2 printers are used to serve the X.L fast spool queue. Note that each of them explicitly specified the name of their lock and log files; as they will use the same queue directory the names of the lock, log, and status files must be different. END_OF_FILE if test 16280 -ne `wc -c <'doc/PLP/04.t'`; then echo shar: \"'doc/PLP/04.t'\" unpacked with wrong size! fi # end of 'doc/PLP/04.t' fi if test -f 'src/lpd.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/lpd.c'\" else echo shar: Extracting \"'src/lpd.c'\" \(16723 characters\) sed "s/^X//" >'src/lpd.c' <<'END_OF_FILE' X/*************************************************************************** X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell X *************************************************************************** X * MODULE: lpd.c X * lpd main program X *************************************************************************** X * Revision History: Created Fri Jan 1 16:19:30 CST 1988 X * $Log: lpd.c,v $ X * Revision 3.1 88/07/25 19:51:03 papowell X * Distribution version X * X * X * Revision 3.1 88/06/18 09:34:40 papowell X * Version 3.0- Distributed Sat Jun 18 1988 X * X * Revision 2.3 88/05/19 10:34:08 papowell X * Fixed open() calls to have a 0 parameter, ie: open(f, perms, 0), where needed X * X * Revision 2.2 88/05/14 10:18:06 papowell X * Use long format for job file names; X * Added 'fd', no forward flag; X * Control file has to have hostname and origination agree. X * X * Revision 2.1 88/05/09 10:08:46 papowell X * PLP: Released Version X * X * Revision 1.9 88/04/27 20:26:17 papowell X * Modified to remove unused variables X * X * Revision 1.8 88/04/26 15:51:31 papowell X * Added a Reapchild() call in cleanup and each time a server is started X * This should gather up the orphans that are left in some UNIX implementations X * X * Revision 1.7 88/04/15 13:06:17 papowell X * Std_environ() call added, to ensure that fd 0 (stdin), 1 (stdout), 2(stderr) X * have valid file descriptors; if not open, then /dev/null is used. X * X * Revision 1.6 88/04/07 12:31:11 papowell X * X * Revision 1.5 88/04/06 12:12:53 papowell X * Minor updates, changes in error message formats. X * Elimination of the AF_UNIX connections, use AF_INET only. X * Better error messages. X * X * Revision 1.4 88/03/25 15:00:06 papowell X * Debugged Version: X * 1. Added the PLP control file first transfer X * 2. Checks for MX during file transfers X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities; X * apparently they open files and then assume that they will stay X * open. X * 4. Made sure that stdin, stdout, stderr was available at all times. X * X * Revision 1.3 88/03/11 19:28:58 papowell X * Minor Changes, Updates X * X * Revision 1.2 88/03/05 15:01:20 papowell X * Minor Corrections, Lint Problems X * X * Revision 1.1 88/03/01 11:08:37 papowell X * Initial revision X * X ***************************************************************************/ X#ifndef lint static char id_str1[] = X "$Header: lpd.c,v 3.1 88/07/25 19:51:03 papowell Locked $ PLP Copyright 1988 Patrick Powell"; X#endif lint X/*************************************************************************** X * lpd -- line Printer daemon. X * 1. Get options X * 2. Check for existence of another daemon and exit if it exists. X * Note: this is done by locking a file X * 3. Initialize error logging X * 4. Start up all the queue handlers X * 5. Set up communication sockets X * 6. Loop forever doing: X * Listen for a connection and perform the requested operation. X * Operations are: X * \1Printer\n X * check the queue for jobs and print any found. X * \2Printer\n X * receive a job from another machine and queue it. X * \3Printer [users ...] [jobs ...]\n X * return the current state of the queue (short form). X * \4Printer [users ...] [jobs ...]\n X * return the current state of the queue (long form). X * \5Printer Person [users ...] [jobs ...]\n X * remove jobs from the queue. X * \6Printer Person operation X * enable/disable queueing, etc. X * Note: a process is forked for each operation X * X * Strategy to maintain protected spooling area: X * 1. Spooling area is writable only by daemon and daemon group X * 2. lpr runs setuid ROOT; it uses the user UID X * to access any file it wants (verifying things before X * with access(2)) and sets ownership of files in the spooling X * directory to be owner DAEMON, group DAEMON. X * 3. Critical files in spooling area are owned by DAEMON, group DAEMON X * with mode 660. X * 4. lpd, lpq and lprm run setuid ROOT, and change group to DAEMON X * Users can't get to anything w/o help of lpq and lprm programs. X * 5. LPD forks a server process to service each device X * in the printcap entry. These processes run setuid root X * and setgrp to their PID. They open neccessary files, and X * fork "filter" processes to process the output. Filter X * processes run UID DAEMON. X * 6. The server process group is set to the main server PID. This is used X * by the LPRM program, which signals them with killpg(2) X * X * Error reporting: X * lpd will open a standard logging file. If not already present, X * no messages will be logged. Critial messages will also be logged X * using syslog(8). X * The individual daemon processes will open log files also. If X * they cannot be opened, then no logging will be done for the process. X ***************************************************************************/ X X#include "lp.h" X int cleanup(); /* file system scullery maid, cleans up files */ static int lpdpid; /* lpd process id */ X main(argc, argv) X int argc; X char **argv; X{ X int f; /* Acme Integer, Inc. strikes again */ X int finet; /* Internet socket */ X int defreadfds, readfds; /* select()ed connections */ X int req; /* request on this socket */ X int port_num; /* used to get the inet server information */ X struct sockaddr_in sock_in, frominet; /* networking addresses */ X int fromlen; X int nfds; /* accept return value */ X X /* X * explicitly set umask X */ X (void)umask(0); X X /* X * Set fd 0, 1, 2 to /dev/null if not open. This protects against X * being started in strange environments. X */ X Std_environ(); X /* X * we set up the alternate version if XPERIMENT is defined X */ X# ifdef XPERIMENT X Setup_test(); X# endif XPERIMENT X X /* X * process command line options X */ X Getoptions(argc, argv); X X /* X * set up the pathnames for information files X */ X Tailor_names(); X /* X * check to see that the is not a spooler present. The LO is used X * when we exit to clean out the lock file information. X * Note that we check first, as the initial process, and then we fork X * another process. This allows the output message to be printed X * on the stdout device in a synchronous manner. X */ X LO = Masterlock; X if( (Lfd = Getlockfile(LO, &f, (char *)0, 0, (struct stat *)0)) == NULL ){ X (void)fprintf( stdout, "active LPD %d\n", f ); X exit(0); X } X (void)fclose(Lfd); X /* We can let parent die */ X if ((f = fork()) < 0 ){ X logerr_die( XLOG_INFO, "lpd: fork failed" ); X } else if( f ){ X exit(0); X } X /* X * We can now start logging X */ X Setuplog(Lpdlogf, 0 ); X /* X * Note that we do the locking at this point, as the lock may not X * continue across a fork. X */ X if((Lfd=Getlockfile(LO,&f,(char *)0, 0, (struct stat *)0)) == NULL){ X log(XLOG_INFO, "active LPD %d\n", f ); X exit(0); X } X /* X * save the PID of the LPD process; it will be useful later X */ X lpdpid = getpid(); X /* X * drop the control tty. This prevents the user which started X * LPD from inadvertenly generation a signal and clobbering LPD X */ X if( (f = open("/dev/tty", O_RDWR, 0) ) >= 0) { X (void)ioctl(f, TIOCNOTTY, (struct sgttyb *)0); X (void)close(f); X } X /* set pgrp to pid, so we can kill our kids later */ X (void)setpgrp(0, lpdpid); X X /* put PID and time started in the lockfile */ X Setlockfile(LO, Lfd, lpdpid,Time_str()); X X /* X * set up signals; note that SIGPIPE may not be needed, but it X * never hurts to make sure. X */ X (void)signal(SIGCHLD, Reapchild); X (void)signal(SIGPIPE, SIG_IGN); X (void)signal(SIGHUP, cleanup); X (void)signal(SIGINT, cleanup); X (void)signal(SIGQUIT, cleanup); X (void)signal(SIGTERM, cleanup); X /* X * Restart all the Printers. X */ X Startup(); X X /* X * Set up the IPC. This is very ugly, and is stolen directly from X * the original LPD code and the 4.3 BSD Interprocess Communication X * Tutorial, which seems to have borrowed it from RLOGIN, etc. X * X * Set up INET socket X */ X if( ( finet = socket(AF_INET, SOCK_STREAM, 0) ) < 0 ){ X logerr_die( XLOG_CRIT,"lpd: cannot create AF_INET socket"); X } X port_num = Link_port_num(); X /* X * zero the sockaddr structure before using it X */ X bzero( (char *)&sock_in, sizeof(sock_in) ); X /* X * the INADDR_ANY will allow different nets to be used X */ X sock_in.sin_family = AF_INET; X sock_in.sin_addr.s_addr = INADDR_ANY; X sock_in.sin_port = port_num; X if (bind(finet, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0) { X logerr_die( XLOG_CRIT,"lpd: bind failed internet domain"); X } X if( listen(finet, 5) < 0 ){ X logerr_die( XLOG_CRIT, "lpd: listen failed internet domain" ); X } X if(Debug>2)log( XLOG_DEBUG, "lpd: AF_INET socket %d", finet ); X X /* X * Set up the sockets on which we will do an accept X */ X defreadfds = (1 << finet); X X /* X * Main loop: accept, do a request, continue. X */ X for (;;) { X /* X * Defensive Coding a la Brain Damaged Unix Implementations X * A particular UNIX implementation will not cause SIGCHLD X * signals in a particularly reliable way. Apparently if there X * is a SIGCHLD while it is masked off (error printing does this) X * IT IS IGNORED!!! So we put a Reapchild() here; at least X * we will gather them up each time we start a server X */ X Reapchild(); X if(Debug>2)log( XLOG_DEBUG,"lpd: starting select"); X X readfds = defreadfds; X nfds = select(20, (fd_set *)&readfds, (fd_set *)0, (fd_set *)0, X (struct timeval *)0); /* wait patiently */ X if (nfds <= 0) { X if (nfds < 0 && errno != EINTR) { X logerr_die( XLOG_CRIT,"lpd: select error"); X } X continue; X } X /* X * incredible, but true: could have NO requests X */ X if( readfds ){ X if(Debug>2)log( XLOG_DEBUG,"lpd: doing select 0x%x", readfds ); X /* get the socket causing the select */ X fromlen = sizeof(frominet); X req = accept(finet, (struct sockaddr *)&frominet, &fromlen); X if (req < 0) { X logerr( XLOG_CRIT,"lpd: finet accept"); X continue; X } X if (!From_host(&frominet)){ X logerr(XLOG_INFO,"lpd: From_host failed"); X continue; X } X /* X * Fork process to handle activity requested X */ X (void) fflush(stdout); X (void) fflush(stderr); X if ((f = fork()) == 0) { /* daughter */ X (void) fclose(Lfd); X Lfd = NULL; X if( dup2(req, 1) < 0 ){ X logerr_die( XLOG_CRIT, "lpd: dup2 for server failed" ); X } X (void) close(req); X (void) close(finet); X servicereq(); X exit(0); X } else if( f < 0 ){ X logerr( XLOG_CRIT, "lpd: fork failed" ); X } X (void) close(req); X } X } X} X X/*************************************************************************** X * Getoptions(argv, argc) X * char **argv; int argc; X * Purpose: X * extracts options and arguments from command line using Getopt(2) X * Side Effects: X * -X : calls setup_test() to modify defaults X * -D nn : sets Debug level X * -L file : sets log file X ***************************************************************************/ Getoptions(argc, argv) X int argc; X char **argv; X{ X int c; X while ((c = Getopt(argc, argv, "XD:L:")) != EOF){ X switch(c){ X case 'X': /* test version */ X# ifdef DEBUG X Setup_test(); X Tailor_names(); X# else X (void)fprintf( stderr, "%s: -X not allowed\n", Name ); X exit(1); X# endif DEBUG X break; X case 'D': /* turn on Debugging */ X if( Optarg == NULL ){ X exit(1); X } X Debug= atoi( Optarg ); X break; X case 'L': X if( Optarg == NULL ){ X exit(1); X } X (void)strcpy(Lpdlogf, Optarg); X break; X default: X exit(1); X } X } X if( Optind < argc ){ X (void)fprintf( stderr, "%s: extra argument %s\n", Name, argv[Optind] ); X exit(1); X } X} X/*************************************************************************** X * Setuplog( char *logfile, int saveme ) X * Purpose: to set up a standard error logging environment X * saveme will prevent stdin from being clobbered X * 1. open /dev/null on fd 0; X * 2. if saveme not 1, dup 0 to fd 1 X * 3. If logfile is "-" or NULL, output file is alread opened X * 4. Open logfile; if unable to, then open /dev/null for output X ***************************************************************************/ Setuplog( logfile, saveme ) X int saveme; X char *logfile; X{ X int fd; X X /* X * we want 0 (stdin) to be /dev/null X */ X (void)fflush(stdout); X (void)fflush(stderr); X (void)close(0); X if( (fd = open( "/dev/null", O_RDWR, 0 )) != 0 ){ X logerr_die( XLOG_CRIT, "Setuplog: /dev/null opened as %d", fd); X } X /* X * do the dup if necessary X */ X if( saveme != 1 ){ X (void)close(1); X if( dup2(0,1) < 0){ X logerr_die( XLOG_CRIT, "Setuplog: dup2 failed" ); X } X } X if(Debug>4)log(XLOG_DEBUG,"Setuplog: opening log file %s", logfile ); X /* X * open logfile; if it is "-", use stderr X */ X if( logfile && *logfile && strcmp(logfile, "-") ){ X if( (fd = open_daemon(logfile, O_WRONLY|O_APPEND, 0)) < 0 ){ X if(Debug>0)logerr(XLOG_DEBUG,"cannot open logfile %s",logfile); X /* write to stderr if debugging, /dev/null otherwise */ X if(Debug>0){ X fd = 2; X } else { X fd = 0; X } X } X if( fd != 2 && dup2(fd,2) < 0){ X logerr_die( XLOG_CRIT, "Setuplog: dup2 failed" ); X } X if( fd > 2 ){ X (void)close(fd); X } X } X} X X/* X * When a child dies, it will generate a SIGCHLD, X * and Reapchild() will lay the body to rest with a wait3(). X */ Reapchild() X{ X int pid; X union wait status; X X while ((pid =wait3(&status, WNOHANG, (struct rusage *)0)) > 0){ X if(Debug>3)log(XLOG_DEBUG,"process %d, status %s", pid, X Decode_status( &status ) ); X } X} X X/* X * Clean up; kill off children and remove sockets and files X */ cleanup() X{ X int pid; X X (void)sigblock(sigmask(SIGCHLD)|sigmask(SIGHUP) X |sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM)); X if( Lfd ){ X Closelockfile(LO, Lfd); X Lfd = 0; X } X pid = getpid(); X if(Debug>0)log( XLOG_DEBUG, "cleanup" ); X /* X * if the recfiles server, clean up files X */ X rm_recfiles(); X if( pid == getpgrp(0) ){ X /* Kill children too */ X (void)kill(0, SIGINT); X (void)kill(0, SIGCONT); X if( pid == lpdpid){ X log(XLOG_CRIT, "cleanup: lpd process %d terminating", pid); X } X } X X /* X * Wait for children until the bodies rot X */ X Reapchild(); X exit(Errorcode); X} X X/*********************************************************************** X * From_host( sockaddr_in *f ) X * This routine will extract the remote Host Name and remote Host X * port number information. The remote process must connect on X * a port in a "reserved" range; these are reserved for use X * by priviledged processes (setuid/uid root). X * Side Effects: sets the "from" string to the Name of the remote Host. X * Returns: 1 if privileged and successful, 0 if not. X * NOTE: this code was derived from the orignal 4.2BSD LPR source, X * the 4.3BSD Interprocess Communications Tutorials, and other places. X ***********************************************************************/ X XFrom_host(f) X struct sockaddr_in *f; X{ X struct hostent *hp; /* Host entry */ X extern char *inet_ntoa(); /* inet address translation routine */ X int port; /* from port number */ X X port = ntohs(f->sin_port); X if (f->sin_family != AF_INET ){ X log(XLOG_CRIT, X "From_host: malformed from address, family %d, not AF_INET %d", X f->sin_family, AF_INET); X return(0); X } X if ( port > Maxportno){ X log(XLOG_CRIT,"From_host: from port %d, max is %d",port, Maxportno); X return(0); X } X hp = gethostbyaddr((char *)(&f->sin_addr), sizeof(struct in_addr), X f->sin_family); X if (hp == 0){ X logerr(XLOG_INFO,"From_host: Host Name for address '%s' unknown", X inet_ntoa(f->sin_addr)); X return(0); X } X if( (From = malloc( (unsigned)strlen(hp->h_name)+1 )) == 0 ){ X logerr_die(XLOG_CRIT,"malloc failed in From_host" ); X } X (void)strcpy(From, hp->h_name); X return(1); X} X X/************************************************************************ X * Startup() X * start queue servers X ************************************************************************/ static Startup() X{ X int pid; X char **pr; X union wait status; X X /* X * Restart the server. X * Start scanning the printcap for entries X */ X if ((pid = fork()) < 0) { X logerr( XLOG_CRIT,"server for %s: cannot fork", Printer); X } else if(pid == 0 ) { X if(Debug>4)log( XLOG_DEBUG, "starting servers" ); X for( pr = All_printers(); *pr; ++pr ){ X Printer = *pr; X if ((pid = fork()) < 0) { X logerr( XLOG_CRIT,"server for %s: cannot fork", Printer); X continue; X } else if(pid == 0) { X if(Debug>2)log( XLOG_DEBUG, "started %s server, pid %d", X Printer,getpid()); X (void)fclose(Lfd); X Lfd = 0; X Startprinter(); X exit(0); X } X sleep( (unsigned)1 ); X } X while ((pid=wait(&status)) > 0){ X if(Debug>3)log(XLOG_DEBUG,"process %d, status %s", pid, X Decode_status( &status ) ); X } X if(Debug>4)log( XLOG_DEBUG, "all servers done" ); X exit(0); X } X} END_OF_FILE if test 16723 -ne `wc -c <'src/lpd.c'`; then echo shar: \"'src/lpd.c'\" unpacked with wrong size! fi # end of 'src/lpd.c' fi if test -f 'src/printcap.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/printcap.c'\" else echo shar: Extracting \"'src/printcap.c'\" \(16988 characters\) sed "s/^X//" >'src/printcap.c' <<'END_OF_FILE' X/*************************************************************************** X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell X *************************************************************************** X * MODULE: printcap.c X * Super slick printcap information extraction routines. X * Note: these routines were designed after studying the CURSES routines, X * which are HORRIBLE... These may not be any better, but at least X * they are a lot faster. X * The design is based on the assumption that we will know all the printcap X * entries that we want to know ahead of time, and get them "en mass". X * Further more, these can be placed in sorted order. X * Thus, given a set of these, we scan the printcap entry, extract the X * field name, determine if in the list, and then update the information X * only if in list. Result: X * 1. 1 scan of printcap file per lookup of printer name, finds K entries X * 2. Log N order search of list, for a K log N search of printcap X *************************************************************************** X * Revision History: Created Sun Jan 3 19:12:33 CST 1988 X * $Log: printcap.c,v $ X * Revision 3.1 88/06/18 09:35:22 papowell X * Version 3.0- Distributed Sat Jun 18 1988 X * X * Revision 2.1 88/05/09 10:09:50 papowell X * PLP: Released Version X * X * Revision 1.6 88/04/06 12:13:22 papowell X * Minor updates, changes in error message formats. X * Elimination of the AF_UNIX connections, use AF_INET only. X * Better error messages. X * X * Revision 1.5 88/03/25 15:01:10 papowell X * Debugged Version: X * 1. Added the PLP control file first transfer X * 2. Checks for MX during file transfers X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities; X * apparently they open files and then assume that they will stay X * open. X * 4. Made sure that stdin, stdout, stderr was available at all times. X * X * Revision 1.4 88/03/12 10:04:29 papowell X * Minor Bug Fixes, Better Error Message Logging X * X * Revision 1.3 88/03/11 19:29:48 papowell X * Minor Changes, Updates X * X * Revision 1.2 88/03/05 15:01:37 papowell X * Minor Corrections, Lint Problems X * X * Revision 1.1 88/03/01 11:09:04 papowell X * X ***************************************************************************/ X#ifndef lint static char id_str1[] = X "$Header: printcap.c,v 3.1 88/06/18 09:35:22 papowell Exp $ PLP Copyright 1988 Patrick Powell"; X#endif lint X X#include "lp.h" X/* X * char **All_printers() X * returns a pointer to an array of strings which contains the X * names of the printers in the printcap file. The list is terminated X * with a 0 (NIL) entry. X * X * char *Get_first_printer() X * returns a pointer to the first printer in the printcap file. X * X * init_pc_entry( PC_ENTRY pc[]; int len) X * set the default values for variables listed in pc[] X * X * The following routines return 1 if printcap entry found, 0 otherwise. X * of the printcap entry. X * X * int Set_pc_entry(char *printer; PC_ENTRY pc[]; int len) X * set the values of the variables listed in pc from the printcap X * 1. scan the printcap file and extract the printcap entry for X * printer. Make a copy of the entry. X * 2. find the name of the printer (first entry) and set the First_name X * variable to point to it. X * 3. Initialize the printcap variables according to the defaults in X * the "pc" array. X * 4. Scan the printcap entry, and for each tagged field in the entry: X * a. search the "pc" array for the tag using a binary search. X * b. if the entry is found, determine the type; X * c. if an integer, convert from string to integer and update variable. X * d. if a flag, set or clear it X * e. if a string, copy into a buffer and set the string variable to point X * to it. X * int Get_pc_entry(char *printer; PC_ENTRY pc[]; int len) X * call init_pc_entry() and then Set_pc_entry. X */ X static FILE *pfp = NULL; /* printcap data base file pointer */ X/* X * The capbuf buffer holds printcap, the strbuf holds strings X */ static char capbuf[BUFSIZ]; /* buffer to hold printcap */ static char strbuf[BUFSIZ]; /* buffer to hold strings */ static char *strloc; /* next string location */ X static char *pr_names; /* buffer to hold them */ static int max_names; /* max number of names in printcap */ static char **pr_list; /* printer names in printcap */ static int max_list; /* size of list */ X/* X * All_printers: reads the /etc/printcap file, and forms X * a list of all the printer names in the file. X */ X char ** All_printers() X{ X int i; /* ACME Integer, Inc. */ X char *bp, *cp, *ep; /* ACME Pointer, Inc. */ X loop: X if(Debug>5)log(XLOG_DEBUG,"All_printers: max_names %d, max_list %d", X max_names, max_list); X if( max_names == 0 ){ X max_list = BUFSIZ; X max_names = MAXPCNAMES; X pr_names = (char *)malloc((unsigned)(max_list)); X if( pr_names == NULL ){ X logerr_die( XLOG_INFO, "All_printers: malloc failed" ); X } X pr_list = (char **)malloc((unsigned)(max_names*sizeof(char *))); X if( pr_list == NULL ){ X logerr_die( XLOG_INFO, "All_printers: malloc failed" ); X } X } X i = 0; X bp = pr_names; X ep = pr_names + max_list; X closeprent(); X while( pc_entry( capbuf, sizeof(capbuf)) ){ X init_pc_entry( Status_pc_vars, Status_pc_len); X getpcvars(Status_pc_vars, Status_pc_len); X /* get the printcap variable values */ X if(Debug>6)show_pc( Status_pc_vars, Status_pc_len); X if( SD != 0 && *SD != 0 ){ X pr_list[i] = bp; X if( (cp = index( capbuf, '|')) == 0 ){ X cp = index( capbuf, ':'); X } X *cp = 0; X bp = estrcp( bp, capbuf, ep ); X if( bp ){ X ++bp; X } X ++i; X if( i >= max_names || bp == 0 || bp >= ep){ X if(Debug>1)log(XLOG_DEBUG,"All_printers: need more space" ); X max_list += BUFSIZ; X max_names += MAXPCNAMES; X pr_names = (char *)realloc(pr_names,(unsigned)(max_list)); X if( pr_names == NULL ){ X logerr_die( XLOG_INFO, "All_printers: malloc failed" ); X } X pr_list = (char **) X realloc((char *)pr_list,(unsigned)max_names*sizeof(char *)); X if( pr_list == 0 ){ X logerr_die( XLOG_INFO, "All_printers: malloc failed" ); X } X goto loop; X } X pr_list[i] = 0; X } X } X closeprent(); X if(Debug>5){ X int j; X (void)fprintf(stdout,"All_printers: %d,", i); X for( j = 0; j < i; ++j ){ X (void)fprintf(stdout," %s", pr_list[j] ); X } X (void)fprintf(stdout,"\n"); X } X return( pr_list ); X} X X/* X * First_printer: reads the /etc/printcap file X * and finds the first printer name X */ char * XFirst_printer() X{ X char *bp; X static char first[PRNAMELEN+1]; X X closeprent(); X while( pc_entry( capbuf, sizeof(capbuf)) ){ X init_pc_entry( Status_pc_vars, Status_pc_len); X getpcvars(Status_pc_vars, Status_pc_len); X /* get the printcap variable values */ X if(Debug>6)show_pc( Status_pc_vars, Status_pc_len); X if( SD != 0 && *SD != 0 ){ X if( (bp = index( capbuf, '|')) == 0 ){ X bp = index( capbuf, ':'); X } X *bp = 0; X (void)strcpy( first, capbuf ); X closeprent(); X if(Debug>4)log(XLOG_DEBUG,"First_printer: %s", first ); X return( first ); X } X } X fatal( XLOG_INFO, "First_printer: bad printcap %s", Printcap ); X return( (char *)0 ); X} X X/* X * pc_entry(char *tb; int size) X * Used to scan the printcap file. X * finds the next entry in the printcap file; X * it places it in the tb buffer, and returns X * 1 if entry found, 0 otherwise X */ static int pc_entry(tb, size) X char *tb; X int size; X{ X char buf[BUFSIZ]; /* read buffer */ X char *bp, *cp, *ep; /* next read position */ X int l; /* line length */ X X /* X * get the printcap file X */ X if (pfp == NULL ){ X if( (pfp = fopen_daemon(Printcap, "r")) == NULL){ X logerr_die( XLOG_CRIT, "open %s failed", Printcap ); X } X } X /* X * We read a single printcap entry into the bp buffer. X * We scan lines until we hit one that does not start with a X * # or is a blank line. X */ X while( bp = fgets( buf, sizeof(buf), pfp) ){ X if( buf[0] != '#' && buf[0] != '\n' ){ X break; X } X } X if( bp == 0 ){ X return(0); X } X /* X * read the file in, stripping off the \ X */ X ep = tb + size; X bp = tb; X do{ X cp = index( buf, '\n' ); X if( cp == 0 ){ X fatal( XLOG_INFO, "bad line in printcap file '%s'", buf ); X } X *cp = 0; X if( cp != buf ){ X l = (cp[-1] == '\\'); X if( l ){ X cp[-1] = 0; X } X } else { X l = 0; X } X bp = estrcp( bp, buf, ep ); X if( bp == 0 ){ X fatal( XLOG_INFO, "printcap entry too long %s", tb ); X } X if( l == 0 ){ X if(Debug>8)log(XLOG_DEBUG,"pc_entry: %s", tb ); X return( 1 ); X } X } while( fgets( buf, sizeof(buf), pfp) ); X log( XLOG_INFO,"bad termcap entry %s", tb); X return( 0 ); X} X X/* X * init_pc_entry( PC_ENTRY pc_vars[]; int pc_len) X * initialize the variables in pc_vars with their default values. X * sets the strloc to start of string buffer. X */ init_pc_entry( pc_vars, pc_len) X PC_ENTRY pc_vars[]; /* the printcap vars used for the printer */ X int pc_len; /* the number of printcap vars */ X{ X int i; /* ACME Integer, Inc. */ X /* X * intialize with the defaults X */ X First_name = 0; X for( i = 0; i < pc_len; ++ i ){ X switch( pc_vars[i].kind ){ X case PC_NUM: X *(int *)pc_vars[i].var = pc_vars[i].idefault; X break; X case PC_FLAG: X *(int *)pc_vars[i].var = pc_vars[i].idefault; X break; X case PC_STRING: X *(char **)pc_vars[i].var = pc_vars[i].sdefault; X break; X default: X fatal( XLOG_INFO,"getpcvars; bad kind %d in table entry %d", X pc_vars[i].kind,i); X } X } X strloc = strbuf; X if(Debug>7)log(XLOG_DEBUG,"init_pc_entry: done"); X} X X/* X * char *fix_str( char *str ) X * makes a copy of str in strbuf and returns the start X * Side Effect: modifies strloc X */ static char * fix_str(str) X char *str; X{ X char *cp; /* ACME Pointers, Inc. */ X int l; X X l = strlen(str); X cp = strloc; X if( ! ((strloc+l) < (strbuf+sizeof(strbuf))) ){ X fatal( XLOG_INFO,"fix_str: string table overflow" ); X } X (void)strcpy(strloc,str); X strloc += (l+1); X return(cp); X} X X/* X * Set_pc_entry( char *name; PC_ENTRY pc_vars[]; int pc_len) X * 1. gets the "name" printcap entry; capbuf[] holds it; X * strbuf is used to hold string constants. X * 2. checks to see if the entry has a continuation (tc= field), X * and gets the continuations X * 3. calls getpcvar routine to find fields in the printcap entry and set X * variables. X * Returns: 1 if successful, and printcap entry found; 0 otherwise. X * Side Effect: sets the First_name to the first name in the printcap entry. X */ Set_pc_entry(name, pc_vars, pc_len) X char *name; /* the printer name */ X PC_ENTRY pc_vars[]; /* the printcap vars used for the printer */ X int pc_len; /* the number of printcap vars */ X{ X int found; /* ACME Integers and Pointers */ X char *cp; X X if(Debug>7)log(XLOG_DEBUG,"Set_pc_entry: %s", name); X if( name == 0 || *name == 0 ){ X return( 0 ); X } X found = 0; X while( found == 0 && pc_entry(capbuf, sizeof(capbuf)) > 0 ) { X if(Debug>7)log(XLOG_DEBUG,"Set_pc_entry: pc_entry %s", capbuf); X if (Chk_name(capbuf, name)) { X found = 1; X } X } X closeprent(); X if( found == 0 ){ X return(0); X } X if( First_name == 0 ){ X if( (cp = index( capbuf, '|')) == 0 ){ X cp = index( capbuf, ':'); X } X found = *cp; X *cp = 0; X First_name = fix_str( capbuf ); X *cp = found; X } X if(Debug>8){ X (void)fprintf(stderr,"printer %s, %s, len %d printcap ", X name,First_name,pc_len); X (void)fprintf(stderr,capbuf); X (void)fprintf(stderr,"\n"); X } X /* X * get the printcap variable values X */ X getpcvars(pc_vars, pc_len); /* get the printcap variable values */ X if(Debug>6)show_pc( pc_vars, pc_len); X return(1); X} X X/* X * Get_pc_entry( char *name; PC_ENTRY pc_vars[]; int pc_len) X * 1. calls init_pc_entry() to initialize variables X * 2. calls Set_pc_entry() to set them X */ Get_pc_entry(name, pc_vars, pc_len) X char *name; /* the printer name */ X PC_ENTRY pc_vars[]; /* the printcap vars used for the printer */ X{ X if(Debug>7)log(XLOG_DEBUG,"Get_pc_entry: %s", name); X init_pc_entry( pc_vars, pc_len); X return( Set_pc_entry(name, pc_vars, pc_len) ); X} X X X/* X * Chk_name deals with name matching. The first line of the printcap X * entry is a sequence of names separated by |'s, so we compare X * against each such name. The normal : terminator after the last X * name (before the first field) stops us. X */ static int Chk_name(buf, name) X char *buf, *name; X{ X int l; X X l = strlen(name); X X while( buf && *buf ){ X if( strncmp( name, buf, l ) == 0 && (buf[l] == ':' || buf[l] == '|')) { X /* match */ X return(1); X } X if( buf = index(buf, '|' ) ){ X buf = buf + 1; X } X } X /* no match */ X return(0); X} X X/* X * Return the (numeric) option id. X * Numeric options look like X * li#80 X * i.e. the option string is separated from the numeric value by X * a # character. If the option is not found we return -1. X * Note that we handle octal numbers beginning with 0. X */ static int getnum(bp) X char *bp; X{ X int i, base; X X if (*bp != '#'){ X return(-1); X } X bp = bp + 1; X i = 0; X base = 10; X if (*bp == '0'){ X base = 8; X } X while(isdigit(*bp)){ X i = i*base + (*bp - '0'); X ++bp; X } X return (i); X} X X/* X * Handle a flag option. X * Flag options are given "naked", i.e. followed by a :, @ or end X * of the buffer. Return 1 if we find the option, or 0 if it is X * not given. X */ static int getflag(bp) X char *bp; X{ X if (*bp == 0 || *bp == ':'){ X return (1); X } X return(0); X} X X/* X * Get a string valued option. X * These are given as xx=string X * There is a limited amount of decoding done. X * \n -> '\n', \nnn -> '\nnn' X * ^ -> CTRL-CHAR X */ static char * getstr(str) X char *str; /* points to string entry */ X{ X char buf[BUFSIZ]; X char *bp, *cp, *op; /* ACME Chain and Pointers, Inc */ X int c, i; /* ACME Integers, Inc */ X static char norm[] = "E^\\:nrtbf"; X static char esc[] = "\033^\\:\n\r\t\b\f"; X X bp = str; X op = buf; /* output area */ X if (*bp != '=') X return( (char *)0 ); X bp++; X X while ((c = *bp++) && c != ':') { X switch (c) { X case '^': X c = *bp++; X if( c == 0 ){ X fatal(XLOG_INFO,"getstr: bad escape string in printcap" ); X } X c = c & 037; X break; X case '\\': X c = *bp++; X if( c == 0 ){ X fatal(XLOG_INFO,"getstr: bad escape string in printcap" ); X } X cp = index( norm, c ); X if( cp ){ X c = esc[cp - norm]; X } else if (isdigit(c)) { X c = c - '0'; X for( i = 0; i < 3 && isdigit(*bp); ++i ){ X c = 8*c + *bp - '0'; X ++bp; X } X } X break; X } X *op++ = c; X } X *op++ = 0; X if( strlen( buf ) > 0 ){ X return( fix_str(buf) ); X } else { X return( (char *)0 ); X } X} X X/* X * getpcvars- passed a table of entries, tries to find them all X */ getpcvars(pc_vars, pc_len) X PC_ENTRY *pc_vars; X int pc_len; X{ X int h, l, m; /* high, low, middle */ X char *cp; /* string value */ X int dir; /* direction */ X X /* X * now scan the printcap entry, getting the variables X */ X cp = capbuf; X while( cp && (cp = index(cp, ':' ))){ X /* X * we are now positioned at the ":" X */ X cp = cp+1; X if( cp[-2] == '\\' ){ X continue; X } X /* note: islower is a macro! do not combine the above statement! */ X if( !islower( cp[0] ) || !islower( cp[1] )){ X continue; X } X if(Debug>7)log(XLOG_DEBUG,"get_pc_vars: entry %c%c", cp[0], cp[1]); X /* X * binary search the table X */ X l = 0; h = pc_len-1; X dir = 1; X while( dir && l <= h ){ X m = (l+h)/2; X if(Debug>8)log( XLOG_DEBUG, "get_pc_vars: l %d, m %d, h %d",l,m,h); X dir = strncmp( cp, pc_vars[m].pc_name, 2 ); X if( dir < 0 ){ X h = m-1; /* too high */ X } else { X l = m + 1; /* too low */ X } X } X if( dir == 0 ){ X /* bingo! we found it */ X if(Debug>7)log(XLOG_DEBUG,"get_pc_vars: found %c%c%c%c%c", X cp[0], cp[1], cp[2], cp[3], cp[4] ); X fixentry( &pc_vars[m], cp+2 ); X } X /* X * we finished, on to next X */ X } X} X X/* X * fixentry: fix up the value for the entry in the printcap X */ X fixentry( pc_vars, cp ) X PC_ENTRY *pc_vars; X char *cp; X{ X if(Debug>6)log(XLOG_DEBUG,"found %c%c", cp[-2], cp[-1] ); X switch( pc_vars->kind ){ X case PC_NUM: X *(int *)pc_vars->var = getnum(cp); X break; X case PC_FLAG: X *(int *)pc_vars->var = getflag(cp); X break; X case PC_STRING: X *(char **)pc_vars->var = getstr(cp); X break; X default: X fatal( XLOG_INFO, "fixentry: bad kind in the pc_vars" ); X break; X } X} X X/* X * Debugging and display information X */ X show_pc( pc_vars, pc_len ) X PC_ENTRY *pc_vars; X int pc_len; X{ X int i; X X (void)fprintf(stderr, "printcap entry %s: \n", First_name ); X for( i = 0; i < pc_len; ++i ){ X (void)fprintf(stderr, "%s: ", pc_vars[i].pc_name ); X switch( pc_vars[i].kind ){ X case PC_NUM: X (void)fprintf(stderr, "PC_NUM %d\n", *(int *)pc_vars[i].var ); X break; X case PC_FLAG: X (void)fprintf(stderr, "PC_FLAG %d\n", *(int *)pc_vars[i].var ); X break; X case PC_STRING: X (void)fprintf(stderr, "PC_STRING %s\n", *(char **)pc_vars[i].var ); X break; X } X } X (void)fflush(stderr); X} X/*************************************************************************** X * closeprent X * closes the file that the printcap was read from X ***************************************************************************/ closeprent() X{ X if (pfp != NULL) X (void) fclose(pfp); X pfp = NULL; X} END_OF_FILE if test 16988 -ne `wc -c <'src/printcap.c'`; then echo shar: \"'src/printcap.c'\" unpacked with wrong size! fi # end of 'src/printcap.c' fi echo shar: End of archive 12 \(of 16\). cp /dev/null ark12isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 16 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0