Subject: v16i027: Public lineprinter spooler package, Part14/16 Newsgroups: comp.sources.unix Sender: sources Approved: rsalz@uunet.UU.NET Submitted-by: papowell@julius.cs.umn.edu Posting-number: Volume 16, Issue 27 Archive-name: plp/part14 #! /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/02.t' <<'END_OF_FILE' X.ig X$Header: 02.t,v 1.1 88/05/21 18:39:32 papowell Locked $ X$log$ X.. X.NH 1 PLP Programs X.PP The following section outlines the operation of the X.IR lpd \|(8) line printer X.IR daemon , and the X.IR lpr \|(1), X.IR lpq \|(1), X.IR lprm \|(1), and X.IR lpc \|(1) utility programs. X.NH 2 lpd \- Line Printer Dameon Program X.PP The X.IR lpd \|(8) program is usually invoked at boot time from the X.L /etc/rc or X.L /etc/rc.local startup file, and acts as a server or X.I daemon for coordinating and controlling the spooling queues configured in the printcap file. XFigure 2.1 is a typically used to start the X.I lpd daemon. X.KF X.in +1i X.L X.SM X.nf X.ta 4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n 8i X# Start up the lpd daemon if [ -f /usr/lib/lpd ]; then X /usr/lib/lpd & (echo -n ' printer') >/dev/console X.LG X.in -1i X.R X.ce XFigure 2.1 Startup File Command for LPD X.KE X.PP When X.I lpd is started, it checks to see if there is an active X.I lpd daemon already running by trying to lock the X.L /usr/spool/lpd.lock file using the X.IR flock \|(2) or X.IR lockf \|(3) facilities. If no daemon is present, X.I lpd forks a server process for each non-empty spool queue in the X.IR printcap \|(5) data base, and then listens for service requests on specific Internet domain port. The Internet port name is found using the X.IR getservbyname \|(3) facility to get the X.I printer service information. The X.I "Advanced Tuturial on Interprocess Communications" distributed with the Berkeley UNIX 4.3 BSD X(and reprinted by SUN Microsystems) provides an excellent tutorial on interprocess communications. See the manual pages for X.IR socket \|(2) and X.IR services \|(5) for more information on sockets and service specifications. X.PP When a connection is made to the X.I lpd port by a requesting process (usually called a client), X.I lpd forks a server process to carry out the request and continues to listen for new requests. The Table 2.1 shows the requests understood by X.IR lpd . The first byte of a request is a binary number specifying the request type, and the following characters up to a terminating a new line ('\en') character are the request. X.KF X.TS box center; l | l. Request Interpretation X_ X^Aprinter\en start a server for the queue X^Bprinter\en receive and queue a job from another host X^Cprinter [users ...] [jobs ...]\en return short form of printer queue status X^Dprinter [users ...] [jobs ...]\en return long form of printer queue status X^Eprinter person [users ...] [jobs ...]\en remove jobs from a queue X^Fperson command [printers]\en perform lpc control function X.TE X.R X.ce 1 Table 2.1 LPD Request Message Format X.KE X.PP After reading the request, the server process will the printer permissions file to determine if the originating host has permissions to access the specified printer. Next, the X.I printcap data base is scanned determine the set of options and restrictions that apply to the spool queue or printer. XFinally, the server will use this information to perform the requested action. X.PP In response to a start printer message, the server process will check to see if a server is present, and if not, will undertake to start unspooling activities. The two forms of status requests will cause the server to check the spool queue and report on the current activities. X.PP The file transfer request will cause the server and client process to start transferring control and data files that make up a print job. This activity is described in detail in Section 5. The remove job request will cause the server to check that the user has suitable permissions for the particular queue, and will then remove the specified jobs. X.PP The control request will cause the server to perform control functions similar to the X.I lpd program. This function allows control over spooling operations to be done using the X.I lpc program on a remote host. X.NH 3 Security Issues In a Networked Environment X.PP The permissions and restriction checks performed by X.I lpd are based on the assumption that the client host machine is well known by the network support software, and that the process originating a request authenticates the user requesting the service. The port from which the client request can be originated is restricted to a value which is reserved for users with system or X.I "superuser" permissions. In a UNIX environment, this means that the originating program must be running X.I setuid X(SUID) X.I root or the originating user is X.IR root . X.PP In an environment where users can implement their own support for TCP/IP facilities, these security checks may not be sufficient. In fact, given access to the network communications hardware, it is possible to forge messages that appear to be originating from any valid site. The problem of authentication of request is currently open; as part of the PLP development a facility for network wide authentication is under development. X.NH 3 Spool Queue Access and Permissions X.PP As discussed in the previous section, there are problems in controlling access to the PLP software by clients on remote hosts. As a preliminary step towards a more general authentication procedure, PLP uses a X.I printer_perms or printer permissions file contains a list of machines, users, spool queues and associated privileges. Authentication of remote hosts is based on the network connection information provided by the X.IR gethostent \|(3N) networking function. In the future, authentication of this information will be done before using it to determine associated PLP capabilities. X.KF X.L X.SM X.TS tab(:) center box; l s s s s s s s l l l l l l l l. X# -- Line printer permissions X# host:name:queue:Ops:Pr:Max:Done:#Comment X~*.umn.edu:*:*:C:A:0:0:# filter out non-local sites central.*.umn.edu:root:*:C:A:0:0:# root@central can do anything X*.umn.edu:root:lp:C:A:0:100:# root@* can access lp, priority A server.adm.umn.edu:admin:lp:R:C:1000:200:# admin@server, lp priority C X!*:CS*:laser*:R:Z:*:*:# CS accounts, no laser queues X*.umn.edu:*:lp:R:Z:*:*:# the great unwashed have priority Z X.TE X.LG X.R X.ce Table 2.2 Example Printer Permissions File X.KE X.PP XEach entry in the printer permissions file specifies a host machine, a userid, a printer or spool queue name, a set of operations (R or C), a maximum priority (A-Z), and a maximum and current page counts associated with use of a spool queue. Blank lines and lines that start with X.L # X(comment) are ignored. XFields are separated with one or more spaces or tabs. There is a simple wildcard match facility similar to the UNIX shell metacharacter match. The X.L * character will match 0 or more characters in the string, and the X.L ? will match exactly one character. Lines that start with a ! (not) indicate that permissions are denied for queries that would otherwise match. X.PP Given a host, user, and spool queue, the printer permissions file is searched for a matching entry in first line to last line, left to right order. The host, user, and spool queue fields are checked for a match; the first line found which matches these will terminate the search. If an tilde X.B (~) is first character on the line, the fields are checked for a match. If the match fails, then permissions are refused. If the match succeeds, then further searching will be done. This facility allows only sites with suitable path names, etc., to have access to a spool queue. If an exclamation mark X.B (!) is first character on the line, all permissions are refused; this allows X.I filtering out request that are to be rejected in a simple manner. If this facility is used, entries should be ordered so that refusals X(starting with !) precede any matching entries. X.PP XFor example, the printer permissions file in Table 2.1 allows X.L root on host X.L central X(i.e.- X.L root@central ) to access all queues, X.L root on any machine to access the lp queue, and X.L admin on X.L server to access the lp queue. Userids starting with CS are denied permission to use spool queues whose names start with laser. Note that the host name used for checking is the full name returned by the X.I gethostent (3N) function. X.PP Spool queue operations, spooling priorities, and other information are set by the matching line. The X.I Ops field controls the allowed operations; a X.L C value stands for Control, and means that the user has permissions to use the LPC and LPRM programs to manage a queue as well as spool jobs to the queue. An X.L R value stands for LPR, and means that the user can only spool to the queue and remove his own jobs. Control functions can be exercised from remote hosts using the X.I remote command of the X.I lpc program. X.PP The Priority field determines the maximum priority available to a user. Users can prioritize jobs; the default is X.B Z , or lowest priority, and the highest is X.B A . X.PP The maximum and current page count fields are used for accounting and other purposes. They are checked when a job is queued by X.I lpr and when the job is unqueued for printing. If the current maximum page count is exceeded by the user, the job will be refused. X.PP The server process checks the printer permissions file to determine if a requesting host is allowed access to a particular queue; if so, it will perform the requested actions as detailed in the following sections. X.NH 3 Unspooler Servers X.PP During initialization and in response to X.I "start printer" requests, X.I lpd forks an unspooler or server process that processes jobs in the spool queue. Jobs are placed in the spool queue by X.I lpr or are transferred from a remote site and removed by the server process. XEach spool queue has a X.I lock file which is used to control spooling and unspooling activities. The lock file X.I owner , X.I group and X.I execute permissions are used to control spooling and unspooling actions; if the owner execute permission is set then unspooling is disabled and if the group execute permission is set then spooling is disabled. If unspooling is enabled, the server tries to lock the X.I lock file; if it cannot be locked, then another server is active and the process will exit. X.PP Once the server has gained control of the queue, it finds the entries in the queue, sort them in order of priority and time, and then unspools them according to specifications in the X.I printcap file. When a job unspooled, the server will lock the job's control file in order to prevent other processes from manipulating the job. If it cannot lock the control file, then the job is currently being manipulated by another PLP program and the server will process another job in the queue. X.NH 3 Local Printer Queues X.PP If the spool queue is for a local printer X(a printer attached to the host processor), then the server process will print the job according to specifications in the X.I printcap file. The printcap file specify the physcial printer device, any serial line characteristics which need to be set, and any X.I filter programs used to print the job. X.NH 3 Remote Printer Queues X.PP If the spool queue is for a printer attached to another host, the server attempts to connect to the remote host and transfer the jobs in the spool queue to the remote host. If the file system used to store the job is flagged as being shared by the local host and the remote host, then it is not neccessary to transfer files but only to inform the remote host that there are jobs in the spool queue. X.NH 3 Multiple Printers For a Spool Queue X.PP If there are multiple unspooling devices or printers for a local queue, the server process checks to see if unspooling is enabled, and then forks individual unspooler process for each printer. The printer processes will read the printcap entry controlling the individual actions of each printer, and then check the status of a printer lock file which controls the action of the printer. Thus, individual printers can be enabled or disabled as well as the entire spool queue. X.NH 2 lpr \- Job Submission Program X.PP The X.I lpr (1) program is used to place a print job in a spool queue and to then send a X.I "start printer" message to the X.I lpd process. The X.I lpd process is then responsible for printing the job or forwarding it to the correct destination. X.PP The X.I lpr program uses the printcap database to determine the actions required for spooling a job. Spooling is enabled by the state of the spool queue lock file permissions; if the owner permission is set, then spooling is disabled. X.PP Permission to use a spool queue is determined by using the printer permissions file. X.I Lpr will determine the user name X(by using the X.I getuid (2) and X.I getpwent(3) functions), and then searches the printer permission file to see if the user can submit jobs to the spool queue (has R or C privileges). If the user can use the spool queue, X.I lpr will then copy the users files to the spool queue. On systems that support symbolic links, X.I lpr can make symbolic links to the original files rather than copying them. After copying a file, X.I lpr can be requested to remove the file. Note that there are security loopholes in the use of the last two facilities that restrict their use only to trusted users. X.PP Before a job is placed in the spool queue, it may be desirable to do some local processing or prefiltering of the files. XFor example, running a formatter such as X.IR pr , translating a typesetter format file into a different form, etc, can be done by lpr. These actions are controlled by X.I prefilter information specified in the printcap entry for the spool queue. X.PP Associated with each job is a format that specifies how the job is to be processed. XFor example, the X.B f format is the default format, and is used when files contain printable information. XEach format can have a prefilter that is to be applied to the job before it is placed in the spool queue. The prefilter facility is usually used when files must have some further form of processing carried out, but the remote site where they are to be printed cannot perform the processing. X.PP Once the job has been placed in a queue, lpr will request the lpd daemon to start a spool queue server. X.NH 2 lpq \- Spool Queue Status Display X.PP The X.IR lpq \|(1) program displays the status and contents of selected spool queues. X.I Lpq has two forms of output: the (default) long format which displays a complete summary of the current spool queue activities, and a short format which simply lists the jobs that are outstanding, The output format of X.I lpq is extraordinarily verbose, and attempts to give a full picture of the total state of the line printer and its associated queues, servers, and other information. The following is an example of the output produced by X.I lpq when displaying the status of a remote spool queue. X.DS X.L X.SM X.nf Printer 'diablo' (julius.cs.umn.edu): X processing, active job started Wed May 11 15:20:54 1988, attempt 1 X Rank Owner Pr Job Host Files Size active root A 15 julius /tmp/test5 132 X 2nd root Z 14 julius /tmp/test2 15 Remote printer 'lp2' (umn-cs.cs.umn.edu): Warning: no server present X work done at Wed May 11 15:16:43 1988 X Rank Owner Pr Job Host Files Size X 1st root Z 13 julius /tmp/test3 30 X 2nd root Z 14 julius /tmp/test4 9 X.LG X.DE X.PP The first line identifies the name of the local printer, and the host. It is followed by an informative message generated by the currently active server process, which is transferring a file to the remote site. Next is a list of the files in the local queue, ranked according to their processing order. The next line identifies the remote printer and site. The warning message indicates that no server process is present, even though there are jobs in the spool queue. This is a transitory condition, because the server process will be started as soon as the file transfers are completed. X.DS X.L X.SM X.nf imagen_lind27: 0 jobs imagen_ld27: 3 jobs X1 cfZ022julius: root X2 cfZ023julius: root X3 cfZ024julius: root X.LG X.DE The short form of status display illustrated above merely gives a summary of the numbers of jobs and their order in the queue. X.NH 2 lprm \- remove jobs from a queue X.PP The X.IR lprm \|(1) command deletes jobs from a spooling queue. If necessary, X.I lprm will kill off a server process which is working on the job. After removing any files, it will then restart the queue by sending a request to the lpd daemon. X.I Lprm will lock a job control file before removing the job, preventing a server from trying to unspool the job while it is being removed. X.PP If a user can submit jobs to a queue, he can remove jobs that he has submitted. The root or superuser for a host can remove any job submitted from a particular host. XFinally, the printer permission file will allow any use with control permissions for a spool queue to remove any job from the spool queue. X.NH 2 lpc \- line printer control program X.PP The X.IR lpc \|(1) program is used to monitor and control the operation of the Line Printer Software by manipulating the status of spool queue lock files, and killing active servers. It can send messages to PLP daemons requesting that they carry out similar actions, controlling both local and remote PLP daemons. It is not necessary to log onto a remote machine to perform control actions, as long as the printer permissions file has the appropriate entries. The X.I lpc program has the following commands. Unless specified as unpriviledged, they can only be used by a user with control permissions for the specified spool queue, and on the current or local host machine. X.I All or individual queues can be operated on. X.IP "disable/enable [queue* | all]" 3 Disable or enable spooling to list of queues or all queues. X.IP "start/restart/stop [queue* | all]" 3 The start command enables unspooling from a queue, and starts a server; restart is unprivileged and simply request the X.I lpd process to start a server. The stop command disables any further unspooling. X.IP "abort/kill [queue* | all]" 3 The abort command does a stop, and then kills off any server process that is active for the queue. The kill command does an abort followed by a start; this last command is handy for restarting a queue whose server process is hung due to problems with a job or hardware. X.IP "status [queue*]" 3 Display a short form of status information about the spool queues. The following is a typical status report. X.DS X.SM X.L X.nf Queue Jobs Queueing Printing imagen_lind27 1 enabled enabled (server 15817, job cfZ025julius) X checking queue at Thu May 12 11:19:22 1988 diablo_lind136 0 enabled enabled X work done at Wed May 11 15:21:04 1988 X.LG X.DE X.IP "topq queue [job*]" 3 Rearrange the order of jobs in a spool queue, placing specified jobs at the head of the queue. X.IP "lprm queue [job*]" 3 Use lprm to remove a job from the specified queue. X.IP "lpq [parms*]" 3 Use lpq to display queue status. X.IP "lpd\ \ \ " 3 Report the status of the lpd daemon. This is useful when you wish to monitor the status of the daemon process. X.IP "remote {command}" 3 Perform the command for the specified spool queue at the remote host. The start, stop, abort, etc., commands will be executed by the remote X.I lpd daemon. This command is useful for network wide management of the PLP spooler. END_OF_FILE if test 19343 -ne `wc -c <'doc/PLP/02.t'`; then echo shar: \"'doc/PLP/02.t'\" unpacked with wrong size! fi # end of 'doc/PLP/02.t' fi if test -f 'src/banner.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'src/banner.c'\" else echo shar: Extracting \"'src/banner.c'\" \(21457 characters\) sed "s/^X//" >'src/banner.c' <<'END_OF_FILE' X/*************************************************************************** X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell X *************************************************************************** X * MODULE: banner.c X *************************************************************************** X * Revision History: Fri Jan 15 12:13:50 CST 1988 X * $Log: banner.c,v $ X * Revision 3.1 88/06/18 09:36:50 papowell X * Version 3.0- Distributed Sat Jun 18 1988 X * X * Revision 3.1 88/06/18 09:30:10 papowell X * Version 3.0- Distributed Sat Jun 18 1988 X * X * Revision 2.1 88/05/09 10:07:30 papowell X * PLP: Released Version X * X * Revision 1.5 88/05/05 20:09:07 papowell X * Modified short banner format X * X * Revision 1.4 88/04/21 21:51:21 papowell X * Removed spurious line feeds X * X * Revision 1.3 88/03/25 14:58:55 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.2 88/03/05 15:01:32 papowell X * Minor Corrections, Lint Problems X * X * Revision 1.1 88/03/01 11:08:11 papowell X * Initial revision X * X * banner printing routine. X * some of the code (the tables for the banner headers) was stolen from the X * old LPD program. But this in turn was snarfed from a banner X * printing games program. Which snarfed it from an old card deck... etc. X * X ***************************************************************************/ X#ifndef lint static char id_str1[] = X "$Header: banner.c,v 3.1 88/06/18 09:36:50 papowell Exp $ PLP Copyright 1988 Patrick Powell"; X#endif lint X X#include "lp.h" X X/* X * Print a banner X * The large character fonts have been stolen from the X * BANNER program, which stole them from an old card deck... X * X * banner( fd ) X * will print the banner X * 1. form feed if needed X * 2. print banner X * 3. another form feed X */ X#define HEIGHT 9 /* height of representation of characters */ X#define WIDTH 8 /* width of characters */ X#define DROP 3 /* offset to drop characters with descenders */ X#define BLAST 10 /* 10 lines to separate things */ X#define SEP 3 /* 3 lines to separate BLAST from printing */ X static int linecnt; static int fail; extern char chartable[][HEIGHT]; static char *bline; static unsigned blen; static int fd; X int banner( pr ) X int pr; X{ X linecnt = 0; X /* X * print the banner X */ X X fd = pr; X fail = 0; X if( bline == 0 ){ X blen = BUFSIZ; X if( (bline = malloc( blen )) == 0 ){ X logerr_die( XLOG_INFO, "banner: malloc failed" ); X } X } X if( blen < PW+1 ){ X blen = PW+2; X if( (bline = realloc( bline, blen )) == 0 ){ X logerr_die( XLOG_INFO, "banner: realloc failed" ); X } X } X if( SB ) { X shortbanner(); X } else { X longbanner(); X } X if(Debug>4)log( XLOG_DEBUG, "banner: printed, fail %d", fail); X if( fail ){ X return( JFAIL ); X } else { X return( JSUCC ); X } X} X X/* X * shortbanner X * single line, has format: X * adobe:shore Job: test.data Date: Tue Sep 18 16:22:33 1984 X */ shortbanner() X{ X (void)sprintf(bline, "%s:%s Job: %s Date: %s", LOGNAME, FROMHOST, JOBNAME, Time_str()); X Out_line(); X} X/* X * userinfo X * just print the information X */ userinfo() X{ X (void)sprintf(bline, "User: %s@%s", LOGNAME, FROMHOST); X Out_line(); X (void)sprintf(bline, "Name: %s", BNRNAME); X Out_line(); X (void)sprintf(bline, "Date: %s", Time_str()); X Out_line(); X (void)sprintf(bline, "Job: %s", JOBNAME ); X Out_line(); X (void)sprintf(bline, "Class: %s", CLASSNAME ); X Out_line(); X} X/* X * long banner X * get fancy and print separators, etc. X */ longbanner() X{ X int i; /* ACME integers, INC */ X X for( i = 0; i < BLAST; ++i ){ X breakline( '*'); X } X for( i = 0; i < SEP; ++i ){ X breakline( 0 ); X } X /* X * print the Name and Host in BIG letters X */ X bigprint( LOGNAME); X bigprint( FROMHOST); X userinfo(); X for( i = 0; i < SEP; ++i ){ X breakline( 0); X } X if( PL > 0 ){ X /* separator from banner */ X while( (linecnt + BLAST) % PL ){ X breakline( 0 ); X } X /* page bottom marker */ X while( linecnt % PL ){ X breakline( '*' ); X } X } X} X breakline( c ) X int c; X{ X int i; /* ACME Integers, Inc. */ X X i = 0; X if( c ){ X for( i = 0; i < PW; ++i ){ X bline[i] = c; X } X } X bline[i] = 0; X Out_line(); X} X X/*************************************************************************** X * bigprint( char * line ) X * print the line in big characters X * for i = topline to bottomline do X * for each character in the line do X * get the scan line representation for the character X * foreach bit in the string do X * if the bit is set, print X, else print ' '; X * endfor X * endfor X * endfor X ***************************************************************************/ X bigprint( line ) X char *line; X{ X int i, j; /* ACME Integers, Inc. */ X int cnt; /* number of characters */ X X if(Debug>5)log(XLOG_DEBUG,"bigprint: '%s'", line ); X cnt = strlen(line); X i = PW/WIDTH; X if( cnt > i ){ X cnt = i; X } X for( i = 0; i < HEIGHT+DROP; ++i ){ X for( j = 0; j < cnt; ++j ){ X do_char( line[j], i, bline+(j*WIDTH) ); X } X bline[j*WIDTH] = 0; X Out_line(); X } X} X X/*************************************************************************** X * do_char( c, line, buf ) X * print out the raster line corresponding to character c, line l X * If a character is not a descender, you can use the direct mapping. X * If it is a descender, you have to shift down by the descender amount. X * 1. for lines above the "top" of a descender line X * if the character is a descender, print a space X * otherwise print normally X * 2. for lines at and below the top of a descender X * if the character is a descender, print line X * otherwise print normally X * 3. for lines below the baseline X * if a descender, print line X * otherwise print blank X ***************************************************************************/ static int is_descender(c) X char c; X{ X switch(c) { X case '_': case ';': case ',': case 'g': case 'j': case 'p': X case 'q': case 'y': X return(1); X default: X break; X } X return (0); X} X do_char( c, line, buf ) X int c; /* character */ X int line; X char *buf; X{ X int i; /* ACME Integer, Inc. */ X int position; /* index into chartable */ X int rep; /* representation of character scan line */ X X if( !isprint( c ) ){ X c = ' '; X } X if(Debug>5)log(XLOG_DEBUG,"do_char: %c, line %d", c, line ); X position = c - ' '; X if( is_descender(c) ){ X if( line >= DROP ){ X line = line - DROP; X } else { X position = 0; X line = 0; X } X } X if( line >= HEIGHT ){ X position = 0; X line = 0; X } X X rep = chartable[position][line]; X if(Debug>5)log(XLOG_DEBUG,"do_char: position %d, line %d, rep 0x%x", X position, line, rep ); X for( i = 6; i >= 0; --i ){ X if( rep & (1 << i)){ X buf[6-i] = 'o'; X } else { X buf[6-i] = ' '; X } X } X for( i = 7; i < WIDTH; ++i ){ X buf[i] = ' '; X } X} X X X/*************************************************************************** X * Out_line( char* buf ) X * write the buffer out to the file descriptor. X * don;t do if fail is invalid. X ***************************************************************************/ Out_line() X{ X int i; X i = strlen(bline); X X if( fail == 0 ){ X if( (i > 0 && write( fd, bline, i) != i) || write( fd, "\n", 1) != 1){ X logerr( XLOG_INFO, "Out_line: write failed" ); X fail = 1; X } X } X ++linecnt; X} X X#define c_______ 0 X#define c______1 01 X#define c_____1_ 02 X#define c____1__ 04 X#define c____11_ 06 X#define c___1___ 010 X#define c___1__1 011 X#define c___1_1_ 012 X#define c___11__ 014 X#define c__1____ 020 X#define c__1__1_ 022 X#define c__1_1__ 024 X#define c__11___ 030 X#define c__111__ 034 X#define c__111_1 035 X#define c__1111_ 036 X#define c__11111 037 X#define c_1_____ 040 X#define c_1____1 041 X#define c_1___1_ 042 X#define c_1__1__ 044 X#define c_1_1___ 050 X#define c_1_1__1 051 X#define c_1_1_1_ 052 X#define c_11____ 060 X#define c_11_11_ 066 X#define c_111___ 070 X#define c_111__1 071 X#define c_111_1_ 072 X#define c_1111__ 074 X#define c_1111_1 075 X#define c_11111_ 076 X#define c_111111 077 X#define c1______ 0100 X#define c1_____1 0101 X#define c1____1_ 0102 X#define c1____11 0103 X#define c1___1__ 0104 X#define c1___1_1 0105 X#define c1___11_ 0106 X#define c1__1___ 0110 X#define c1__1__1 0111 X#define c1__11_1 0115 X#define c1__1111 0117 X#define c1_1____ 0120 X#define c1_1___1 0121 X#define c1_1_1_1 0125 X#define c1_1_11_ 0126 X#define c1_111__ 0134 X#define c1_1111_ 0136 X#define c11____1 0141 X#define c11___1_ 0142 X#define c11___11 0143 X#define c11_1___ 0150 X#define c11_1__1 0151 X#define c111_11_ 0166 X#define c1111___ 0170 X#define c11111__ 0174 X#define c111111_ 0176 X#define c1111111 0177 X char chartable[][HEIGHT] = /* this is relatively easy to modify */ X /* just look: */ X{ X { c_______, X c_______, X c_______, X c_______, X c_______, X c_______, X c_______, X c_______, X c_______ }, /* */ X X { c__11___, X c__11___, X c__11___, X c__11___, X c__11___, X c_______, X c_______, X c__11___, X c__11___ }, /* ! */ X X { c_1__1__, X c_1__1__, X c_______, X c_______, X c_______, X c_______, X c_______, X c_______, X c_______ }, /* " */ X X { c_______, X c__1_1__, X c__1_1__, X c1111111, X c__1_1__, X c1111111, X c__1_1__, X c__1_1__, X c_______ }, /* # */ X X { c___1___, X c_11111_, X c1__1__1, X c1__1___, X c_11111_, X c___1__1, X c1__1__1, X c_11111_, X c___1___ }, /* $ */ X X { c_1_____, X c1_1___1, X c_1___1_, X c____1__, X c___1___, X c__1____, X c_1___1_, X c1___1_1, X c_____1_ }, /* % */ X X { c_11____, X c1__1___, X c1___1__, X c_1_1___, X c__1____, X c_1_1__1, X c1___11_, X c1___11_, X c_111__1 }, /* & */ X X { c___11__, X c___11__, X c___1___, X c__1____, X c_______, X c_______, X c_______, X c_______, X c_______ }, /* ' */ X X { c____1__, X c___1___, X c__1____, X c__1____, X c__1____, X c__1____, X c__1____, X c___1___, X c____1__ }, /* ( */ X X { c__1____, X c___1___, X c____1__, X c____1__, X c____1__, X c____1__, X c____1__, X c___1___, X c__1____ }, /* ) */ X X { c_______, X c___1___, X c1__1__1, X c_1_1_1_, X c__111__, X c_1_1_1_, X c1__1__1, X c___1___, X c_______ }, /* * */ X X { c_______, X c___1___, X c___1___, X c___1___, X c1111111, X c___1___, X c___1___, X c___1___, X c_______ }, /* + */ X X { c_______, X c_______, X c_______, X c_______, X c__11___, X c__11___, X c__1____, X c_1_____, X c_______ }, /* , */ X X { c_______, X c_______, X c_______, X c_______, X c1111111, X c_______, X c_______, X c_______, X c_______ }, /* - */ X X { c_______, X c_______, X c_______, X c_______, X c_______, X c_______, X c_______, X c__11___, X c__11___ }, /* . */ X X { c_______, X c______1, X c_____1_, X c____1__, X c___1___, X c__1____, X c_1_____, X c1______, X c_______ }, /* / */ X X { c_11111_, X c1_____1, X c1____11, X c1___1_1, X c1__1__1, X c1_1___1, X c11____1, X c1_____1, X c_11111_ }, /* 0 */ X X { c___1___, X c__11___, X c_1_1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c_11111_ }, /* 1 */ X X { c_11111_, X c1_____1, X c______1, X c_____1_, X c__111__, X c_1_____, X c1______, X c1______, X c1111111 }, /* 2 */ X X { c_11111_, X c1_____1, X c______1, X c______1, X c__1111_, X c______1, X c______1, X c1_____1, X c_11111_ }, /* 3 */ X X { c_____1_, X c____11_, X c___1_1_, X c__1__1_, X c_1___1_, X c1____1_, X c1111111, X c_____1_, X c_____1_ }, /* 4 */ X X { c1111111, X c1______, X c1______, X c11111__, X c_____1_, X c______1, X c______1, X c1____1_, X c_1111__ }, /* 5 */ X X { c__1111_, X c_1_____, X c1______, X c1______, X c1_1111_, X c11____1, X c1_____1, X c1_____1, X c_11111_ }, /* 6 */ X X { c1111111, X c1_____1, X c_____1_, X c____1__, X c___1___, X c__1____, X c__1____, X c__1____, X c__1____ }, /* 7 */ X X { c_11111_, X c1_____1, X c1_____1, X c1_____1, X c_11111_, X c1_____1, X c1_____1, X c1_____1, X c_11111_ }, /* 8 */ X X { c_11111_, X c1_____1, X c1_____1, X c1_____1, X c_111111, X c______1, X c______1, X c1_____1, X c_1111__ }, /* 9 */ X X { c_______, X c_______, X c_______, X c__11___, X c__11___, X c_______, X c_______, X c__11___, X c__11___ }, /* : */ X X X { c__11___, X c__11___, X c_______, X c_______, X c__11___, X c__11___, X c__1____, X c_1_____, X c_______ }, /* ; */ X X { c____1__, X c___1___, X c__1____, X c_1_____, X c1______, X c_1_____, X c__1____, X c___1___, X c____1__ }, /* < */ X X { c_______, X c_______, X c_______, X c1111111, X c_______, X c1111111, X c_______, X c_______, X c_______ }, /* = */ X X { c__1____, X c___1___, X c____1__, X c_____1_, X c______1, X c_____1_, X c____1__, X c___1___, X c__1____ }, /* > */ X X { c__1111_, X c_1____1, X c_1____1, X c______1, X c____11_, X c___1___, X c___1___, X c_______, X c___1___ }, /* ? */ X X { c__1111_, X c_1____1, X c1__11_1, X c1_1_1_1, X c1_1_1_1, X c1_1111_, X c1______, X c_1____1, X c__1111_ }, /* @ */ X X { c__111__, X c_1___1_, X c1_____1, X c1_____1, X c1111111, X c1_____1, X c1_____1, X c1_____1, X c1_____1 }, /* A */ X X { c111111_, X c_1____1, X c_1____1, X c_1____1, X c_11111_, X c_1____1, X c_1____1, X c_1____1, X c111111_ }, /* B */ X X { c__1111_, X c_1____1, X c1______, X c1______, X c1______, X c1______, X c1______, X c_1____1, X c__1111_ }, /* C */ X X { c11111__, X c_1___1_, X c_1____1, X c_1____1, X c_1____1, X c_1____1, X c_1____1, X c_1___1_, X c11111__ }, /* D */ X X { c1111111, X c1______, X c1______, X c1______, X c111111_, X c1______, X c1______, X c1______, X c1111111 }, /* E */ X X { c1111111, X c1______, X c1______, X c1______, X c111111_, X c1______, X c1______, X c1______, X c1______ }, /* F */ X X { c__1111_, X c_1____1, X c1______, X c1______, X c1______, X c1__1111, X c1_____1, X c_1____1, X c__1111_ }, /* G */ X X { c1_____1, X c1_____1, X c1_____1, X c1_____1, X c1111111, X c1_____1, X c1_____1, X c1_____1, X c1_____1 }, /* H */ X X { c_11111_, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c_11111_ }, /* I */ X X { c__11111, X c____1__, X c____1__, X c____1__, X c____1__, X c____1__, X c____1__, X c1___1__, X c_111___ }, /* J */ X X { c1_____1, X c1____1_, X c1___1__, X c1__1___, X c1_1____, X c11_1___, X c1___1__, X c1____1_, X c1_____1 }, /* K */ X X { c1______, X c1______, X c1______, X c1______, X c1______, X c1______, X c1______, X c1______, X c1111111 }, /* L */ X X { c1_____1, X c11___11, X c1_1_1_1, X c1__1__1, X c1_____1, X c1_____1, X c1_____1, X c1_____1, X c1_____1 }, /* M */ X X { c1_____1, X c11____1, X c1_1___1, X c1__1__1, X c1___1_1, X c1____11, X c1_____1, X c1_____1, X c1_____1 }, /* N */ X X { c__111__, X c_1___1_, X c1_____1, X c1_____1, X c1_____1, X c1_____1, X c1_____1, X c_1___1_, X c__111__ }, /* O */ X X { c111111_, X c1_____1, X c1_____1, X c1_____1, X c111111_, X c1______, X c1______, X c1______, X c1______ }, /* P */ X X { c__111__, X c_1___1_, X c1_____1, X c1_____1, X c1_____1, X c1__1__1, X c1___1_1, X c_1___1_, X c__111_1 }, /* Q */ X X { c111111_, X c1_____1, X c1_____1, X c1_____1, X c111111_, X c1__1___, X c1___1__, X c1____1_, X c1_____1 }, /* R */ X X { c_11111_, X c1_____1, X c1______, X c1______, X c_11111_, X c______1, X c______1, X c1_____1, X c_11111_ }, /* S */ X X { c1111111, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___ }, /* T */ X X { c1_____1, X c1_____1, X c1_____1, X c1_____1, X c1_____1, X c1_____1, X c1_____1, X c1_____1, X c_11111_ }, /* U */ X X { c1_____1, X c1_____1, X c1_____1, X c_1___1_, X c_1___1_, X c__1_1__, X c__1_1__, X c___1___, X c___1___ }, /* V */ X X { c1_____1, X c1_____1, X c1_____1, X c1_____1, X c1__1__1, X c1__1__1, X c1_1_1_1, X c11___11, X c1_____1 }, /* W */ X X { c1_____1, X c1_____1, X c_1___1_, X c__1_1__, X c___1___, X c__1_1__, X c_1___1_, X c1_____1, X c1_____1 }, /* X */ X X { c1_____1, X c1_____1, X c_1___1_, X c__1_1__, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___ }, /* Y */ X X { c1111111, X c______1, X c_____1_, X c____1__, X c___1___, X c__1____, X c_1_____, X c1______, X c1111111 }, /* Z */ X X { c_1111__, X c_1_____, X c_1_____, X c_1_____, X c_1_____, X c_1_____, X c_1_____, X c_1_____, X c_1111__ }, /* [ */ X X { c_______, X c1______, X c_1_____, X c__1____, X c___1___, X c____1__, X c_____1_, X c______1, X c_______ }, /* \ */ X X { c__1111_, X c_____1_, X c_____1_, X c_____1_, X c_____1_, X c_____1_, X c_____1_, X c_____1_, X c__1111_ }, /* ] */ X X { c___1___, X c__1_1__, X c_1___1_, X c1_____1, X c_______, X c_______, X c_______, X c_______ }, /* ^ */ X X { c_______, X c_______, X c_______, X c_______, X c_______, X c_______, X c_______, X c1111111, X c_______ }, /* _ */ X X { c__11___, X c__11___, X c___1___, X c____1__, X c_______, X c_______, X c_______, X c_______, X c_______ }, /* ` */ X X { c_______, X c_______, X c_______, X c_1111__, X c_____1_, X c_11111_, X c1_____1, X c1____11, X c_1111_1 }, /* a */ X X { c1______, X c1______, X c1______, X c1_111__, X c11___1_, X c1_____1, X c1_____1, X c11___1_, X c1_111__ }, /* b */ X X { c_______, X c_______, X c_______, X c_1111__, X c1____1_, X c1______, X c1______, X c1____1_, X c_1111__ }, /* c */ X X { c_____1_, X c_____1_, X c_____1_, X c_111_1_, X c1___11_, X c1____1_, X c1____1_, X c1___11_, X c_111_1_ }, /* d */ X X { c_______, X c_______, X c_______, X c_1111__, X c1____1_, X c111111_, X c1______, X c1____1_, X c_1111__ }, /* e */ X X { c___11__, X c__1__1_, X c__1____, X c__1____, X c11111__, X c__1____, X c__1____, X c__1____, X c__1____ }, /* f */ X X { c_111_1_, X c1___11_, X c1____1_, X c1____1_, X c1___11_, X c_111_1_, X c_____1_, X c1____1_, X c_1111__ }, /* g */ X X { c1______, X c1______, X c1______, X c1_111__, X c11___1_, X c1____1_, X c1____1_, X c1____1_, X c1____1_ }, /* h */ X X { c_______, X c___1___, X c_______, X c__11___, X c___1___, X c___1___, X c___1___, X c___1___, X c__111__ }, /* i */ X X { c____11_, X c_____1_, X c_____1_, X c_____1_, X c_____1_, X c_____1_, X c_____1_, X c_1___1_, X c__111__ }, /* j */ X X { c1______, X c1______, X c1______, X c1___1__, X c1__1___, X c1_1____, X c11_1___, X c1___1__, X c1____1_ }, /* k */ X X { c__11___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c__111__ }, /* l */ X X { c_______, X c_______, X c_______, X c1_1_11_, X c11_1__1, X c1__1__1, X c1__1__1, X c1__1__1, X c1__1__1 }, /* m */ X X { c_______, X c_______, X c_______, X c1_111__, X c11___1_, X c1____1_, X c1____1_, X c1____1_, X c1____1_ }, /* n */ X X { c_______, X c_______, X c_______, X c_1111__, X c1____1_, X c1____1_, X c1____1_, X c1____1_, X c_1111__ }, /* o */ X X { c1_111__, X c11___1_, X c1____1_, X c1____1_, X c11___1_, X c1_111__, X c1______, X c1______, X c1______ }, /* p */ X X { c_111_1_, X c1___11_, X c1____1_, X c1____1_, X c1___11_, X c_111_1_, X c_____1_, X c_____1_, X c_____1_ }, /* q */ X X { c_______, X c_______, X c_______, X c1_111__, X c11___1_, X c1______, X c1______, X c1______, X c1______ }, /* r */ X X { c_______, X c_______, X c_______, X c_1111__, X c1____1_, X c_11____, X c___11__, X c1____1_, X c_1111__ }, /* s */ X X { c_______, X c__1____, X c__1____, X c11111__, X c__1____, X c__1____, X c__1____, X c__1__1_, X c___11__ }, /* t */ X X { c_______, X c_______, X c_______, X c1____1_, X c1____1_, X c1____1_, X c1____1_, X c1___11_, X c_111_1_ }, /* u */ X X { c_______, X c_______, X c_______, X c1_____1, X c1_____1, X c1_____1, X c_1___1_, X c__1_1__, X c___1___ }, /* v */ X X { c_______, X c_______, X c_______, X c1_____1, X c1__1__1, X c1__1__1, X c1__1__1, X c1__1__1, X c_11_11_ }, /* w */ X X { c_______, X c_______, X c_______, X c1____1_, X c_1__1__, X c__11___, X c__11___, X c_1__1__, X c1____1_ }, /* x */ X X { c1____1_, X c1____1_, X c1____1_, X c1____1_, X c1___11_, X c_111_1_, X c_____1_, X c1____1_, X c_1111__ }, /* y */ X X { c_______, X c_______, X c_______, X c111111_, X c____1__, X c___1___, X c__1____, X c_1_____, X c111111_ }, /* z */ X X { c___11__, X c__1____, X c__1____, X c__1____, X c_1_____, X c__1____, X c__1____, X c__1____, X c___11__ }, /* } */ X X { c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___, X c___1___ }, /* | */ X X { c__11___, X c____1__, X c____1__, X c____1__, X c_____1_, X c____1__, X c____1__, X c____1__, X c__11___ }, /* } */ X X { c_11____, X c1__1__1, X c____11_, X c_______, X c_______, X c_______, X c_______, X c_______, X c_______ }, /* ~ */ X X { c_1__1__, X c1__1__1, X c__1__1_, X c_1__1__, X c1__1__1, X c__1__1_, X c_1__1__, X c1__1__1, X c__1__1_ } /* rub-out */ X}; END_OF_FILE if test 21457 -ne `wc -c <'src/banner.c'`; then echo shar: \"'src/banner.c'\" unpacked with wrong size! fi # end of 'src/banner.c' fi echo shar: End of archive 14 \(of 16\). cp /dev/null ark14isdone 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