home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume16 / plp / part05 < prev    next >
Text File  |  1988-09-14  |  54KB  |  1,939 lines

  1. Subject:  v16i018:  Public lineprinter spooler package, Part05/16
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: papowell@julius.cs.umn.edu
  7. Posting-number: Volume 16, Issue 18
  8. Archive-name: plp/part05
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 5 (of 16)."
  17. # Contents:  doc/PLP/05.t doc/PLP/09.t filters/lpf.c man/lpd.8
  18. #   man/lpr.1 src/mexecv.c src/rmjob.c
  19. # Wrapped by papowell@attila on Wed Aug 10 10:44:53 1988
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'doc/PLP/05.t' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'doc/PLP/05.t'\"
  23. else
  24. echo shar: Extracting \"'doc/PLP/05.t'\" \(7584 characters\)
  25. sed "s/^X//" >'doc/PLP/05.t' <<'END_OF_FILE'
  26. X.ig
  27. X$Header: 05.t,v 1.1 88/05/21 18:39:40 papowell Locked $
  28. X$log$
  29. X..
  30. X.bp
  31. X.NH 1
  32. Remote Spool Queue
  33. X.PP
  34. XEntries in a remote spool queue are transfered to the remote host
  35. by a server process forked by the
  36. X.I lpd
  37. daemon.
  38. The server process checks the spool queue for entries
  39. and then initiates communication with the remote host and the 
  40. line printer daemon on the remote end.
  41. The following section discusses the protocol used to transfer files
  42. to the remote hosts.
  43. X.NH 2
  44. XEstablishing Communication
  45. X.PP
  46. Interhost job transfer is done by using the Internet (Inet)
  47. interprocess communication facilities.
  48. XFor an excellent tutorial on this,
  49. see the 4.3BSD Advanced Tutorial On Interprocess Communications.
  50. The client or process that wishes to send files to a remote host
  51. establishes communication using the following procedure.
  52. X.IP 1). 3
  53. The client opens an AF_INET socket,
  54. using the SOCK_STREAM protocol.
  55. The socket will use a reliable message delivery method for
  56. communication.
  57. Note that the socket is written to send a message,
  58. read to receive a message.
  59. X.IP 2). 3
  60. The client socket is bound to a reserved port on the local host,
  61. that is,
  62. a port on the local host whose port number is less than 1024.
  63. These are reserved ports and can only be bound to by
  64. root processes or a process which
  65. is SUID root.
  66. The server on the remote machine will check to ensure that the
  67. client originated his request from a reserved port.
  68. X.IP 3). 3
  69. The client determines the remote host addressing information using the
  70. X.IR gethostbynam (3)
  71. facility.
  72. This returns the network address of the remote host.
  73. X.IP 4). 3
  74. The client determines the port number that the remote
  75. X.I lpd
  76. server is listening on by using the
  77. X.IR getservent (3)
  78. facility;
  79. for example,
  80. X.L getservent("printer","tcp")
  81. will return the port number and other needed information.
  82. X.IP 5). 3
  83. Using the above information,
  84. the client process will use
  85. X.IR connect (2)
  86. to establish a connection to the remote host.
  87. If the connect call fails,
  88. then the client may retry a couple of times until successful,
  89. or terminate with an error condition.
  90. X.NH 2
  91. Sending Requests
  92. X.PP
  93. After establishing connection with the remote host,
  94. the
  95. X.I lpd
  96. daemon will fork a server process to deal with the request.
  97. The format of the requests is specified in
  98. Table 1.1.
  99. Requests consist of a simple message consisting of a flag
  100. X(unsigned character value) followed by a string terminated with a
  101. new line character.
  102. XFor example,
  103. the request to transfer files to the remote host consists of a line of the
  104. form:
  105. X.ti +5n
  106. X\&^Bprinter
  107. X.PP
  108. The remote server process uses network supplied information to
  109. determine the name of the client host machine,
  110. and the port that originated the message.
  111. It will check the local
  112. printer permissions file to determine if the host is allowed to access
  113. the specified printer spool queue.
  114. After checking this basic permission,
  115. the remote host will then perform any requested actions.
  116. X.NH 2
  117. XFile Transfers
  118. X.PP
  119. A job is transferred between the local and remote hosts by using one
  120. of two methods.
  121. The first method is to send the data files of a job first,
  122. followed by the control file (Berkeley Protocol);
  123. the second method is to send the control file,
  124. followed by the data files (PLP Protocol).
  125. The
  126. X.B fj
  127. flag is used to select the PLP (control file first) protocol to be used
  128. in sending jobs.
  129. The default protocol is the Berkeley (data files first) protocol.
  130. X.PP
  131. The file transfer protocol consists of sending a line identifying
  132. the type of protocol being used,
  133. the size of the file (in bytes),
  134. and the name of the file.
  135. The remote host will return a single
  136. X0 (zero)
  137. confirmation byte if it agrees to accept the file;
  138. any other value is a fatal error.
  139. After confirmation,
  140. the file is transferred to the other end,
  141. followed by a single
  142. X0 (zero) byte.
  143. If the remote end receives the file and the terminating 0 byte,
  144. it will return a confirming
  145. X0 (zero) byte.
  146. The next file may then be transferred.
  147. After all files have been transferred,
  148. a single
  149. X0 (zero) byte will terminate the file transfer.
  150. X.PP
  151. The file transferred to the remote site will be placed in the
  152. remote site spool queue directory,
  153. with the same path name it had at the local site.
  154. Both the local and remote sites will lock any files being
  155. transfered in order to prevent other PLP processes from
  156. manipulating them.
  157. Table 5.1 specifies the values of the flags used in the protocol.
  158. X.KF
  159. X.TS
  160. tab(:) box center;
  161. l | l | l.
  162. Protocol:Flag:Meaning
  163. X_
  164. Berkeley:2 (^B):control file
  165. Both:3 (^C):data file
  166. PLP:4 (^D):control file
  167. PLP:5 (^E):last file
  168. X.TE
  169. X.ce
  170. Table 5.1. File Transfer Protocol Flags
  171. X.KE
  172. X.PP
  173. To use the Berkeley Protocol,
  174. the data files would be transferred first using the 3 (^B) flag,
  175. followed by the control file using the 2 (^C) flag.
  176. To use the PLP Protocol,
  177. the control file would be transferred first
  178. using the 4 (^D) flag,
  179. followed by the data files.
  180. When all of the data files had been transferred,
  181. a confirming message would be sent using the 5 (^E) flag,
  182. indicating that the job had been transferred.
  183. X.PP
  184. XFor example,
  185. assume that we have a job with a control file
  186. X.L cfA123attila.cs.umn.edu ,
  187. and a data file
  188. X.L dfA123attila.cs.umn.edu .
  189. Using the Berkeley Protocol,
  190. the client would first send the message
  191. X.ti +5n
  192. X.L "^C1293 dfA123attila.cs.umn.edu" ,
  193. X.br
  194. requesting transfer of a 1293 byte data file,
  195. whose name is
  196. X.L dfA123attila .
  197. After confirmation,
  198. the data file followed by a single 0 byte would be sent,
  199. and a confirmation should be received.
  200. Next,
  201. the message
  202. X.L "^B343 cfA123attila.cs.umn.edu" ,
  203. would be sent,
  204. and then the control file followed by a single 0 byte would be sent.
  205. Having received the control file,
  206. the remote end would acknowledge the reception of the job,
  207. allowing the client to delete the job and send another one.
  208. When control file is transferred,
  209. the remote host will check the job to make sure that all data files have
  210. been transferred,
  211. and enable it for printing or further processing.
  212. If anything happens to disturb this communication,
  213. the connection is terminated and any partially transferred jobs are
  214. removed.
  215. X.PP
  216. To prevent interaction between an unspooler server and a file
  217. transfer server,
  218. the job control file is locked by the file transfer server until it
  219. has been completely transferred.
  220. X.NH 2
  221. XError Recovery and Retry
  222. X.PP
  223. When either end of the file transfer protocol detects an error,
  224. they will shut down the link.
  225. It is the responsibility of the client process to retry sending jobs.
  226. X.NH 2
  227. Security Checks and Authorizations
  228. X.PP
  229. The PLP file transfer protocol restricts the names of control and
  230. data files to a fixed format.
  231. In addition,
  232. the
  233. X.B U
  234. X(unlink) flag has no effect on a remote host.
  235. The enforcement of the fixed format of data and control file names
  236. makes it simple to remove all files associated with a job on
  237. completion.
  238. X.PP
  239. Before placing a job in the spool queue,
  240. the user name and hostname are checked to see if permissions are
  241. acceptible.
  242. The security of this check is based on the assumption that the client
  243. host is a trusted originator of PLP jobs.
  244. In a networked environment with many different users,
  245. this may not be the case,
  246. as it is possible to forge information in a control file.
  247. The
  248. X.B fd
  249. X(no forward)
  250. flag in a printcap entry prevents the acceptance of jobs from other than
  251. the current client.
  252. This is intended to prevent clients from attempting to transfer control
  253. files that would appear to have originated from another
  254. host.
  255. While this will provide verification at the host level,
  256. assuming that network level software will uniquely identify a host,
  257. it still does not provide a secure user identification.
  258. END_OF_FILE
  259. if test 7584 -ne `wc -c <'doc/PLP/05.t'`; then
  260.     echo shar: \"'doc/PLP/05.t'\" unpacked with wrong size!
  261. fi
  262. # end of 'doc/PLP/05.t'
  263. fi
  264. if test -f 'doc/PLP/09.t' -a "${1}" != "-c" ; then 
  265.   echo shar: Will not clobber existing file \"'doc/PLP/09.t'\"
  266. else
  267. echo shar: Extracting \"'doc/PLP/09.t'\" \(6569 characters\)
  268. sed "s/^X//" >'doc/PLP/09.t' <<'END_OF_FILE'
  269. X.ig
  270. X$Header: 09.t,v 1.1 88/05/21 18:39:48 papowell Locked $
  271. X$log$
  272. X..
  273. X.NH 1
  274. Line Printer Spooler Administration
  275. X.PP
  276. Most of the administration of PLP commands consists of
  277. printcap entries,
  278. permissions entries,
  279. and spooler queue management.
  280. In addition,
  281. killing and restarting the
  282. X.I lpd
  283. daemon is sometimes needed.
  284. X.NH 2
  285. Printcap File Management
  286. X.PP
  287. XEach spool queue or printer must have an entry in a printcap file.
  288. In a single,
  289. non-networked environment,
  290. this is usually a simple and easily managed system,
  291. but in a highly networked environment it may be a difficult procedure.
  292. In order to simply the generation of printcap files,
  293. the PLP distribution has a set of programs that allow a simple printcap
  294. data base to be set up,
  295. and a set of programs to generate printcaps for different hosts.
  296. X.NH 3
  297. Printer Names
  298. X.PP
  299. Printer names and aliases are a major problem faced by administrators.
  300. The convention adopted by many sites is to make the primary (first)
  301. name of the printer correspond to the physical printer type and its
  302. location.
  303. XFor exmple,
  304. X.L lw_lind23
  305. is the name of the Apple LaserWriter in Lind Hall 23.
  306. Additional aliases or names can then be added.
  307. X.PP
  308. In performing permissions checking,
  309. the name supplied by the user is checked against the information in the
  310. printer permissions file,
  311. and also checked in the printcap database.
  312. XFor this reason,
  313. the name used in the printer permissions file and the
  314. name of the printer should be identical.
  315. When sending files to a remote site,
  316. the printer name found in the printcap entry and not the
  317. alias provided by a user is used to determine permissions.
  318. X.NH 3
  319. Printcap File Generation
  320. X.PP
  321. XEach printer usually has three forms of printcap entries:
  322. remote,
  323. forward,
  324. and actual device.
  325. The remote entry is used by a system that will treat the spool queue
  326. as a remote site and forward all jobs to a remote site.
  327. The forwarding entry is usually used for sites that accept jobs
  328. from other sites,
  329. and then forward them to a remote site.
  330. The forwarding is usually done by a specialized file transfer program.
  331. XFinally,
  332. a local or actual device entry is used for the host which has the
  333. actual printer attached.
  334. XFiles with the appropriate form printcap entry can be prepared.
  335. As part of the PLP software,
  336. a set of programs to generate printcap files tailored to a
  337. particular host has been developed,
  338. and is available in the
  339. X.L printcap
  340. directory.
  341. X.NH 3
  342. Printer Permissions File
  343. X.PP
  344. The main printer permissions file is intended to act as a filter for
  345. general user permissions.
  346. As described elsewhere,
  347. each line is checked for a matching set of user permissions,
  348. and the first matching line determines the available permissions.
  349. It is strongly suggested that the set of entries in the printcap
  350. file be used to filter out unauthorized users,
  351. and the remaining set of entries used to determine the appropriate
  352. permissions.
  353. X.PP
  354. If restrictions on the number of pages are desired,
  355. the printer permissions file can be used on a global or local basis.
  356. The current page count field is compared against the maximum
  357. field at the time a job is spooled or printed.
  358. In order to keep this file concurrent,
  359. some form of simple permissions file updating must be done.
  360. This can be done by using the
  361. X.I pac
  362. X(printer accounting) program and a suitable shell script.
  363. Due to the wide divergence of different sites,
  364. an accounting package to do this automatically has not been
  365. provided.
  366. X.NH 2
  367. Using LPC
  368. X.PP
  369. The
  370. X.I lpc
  371. program provides control over line printer activity.
  372. The major commands and their intended use are described in this section.
  373. See 
  374. X.IR lpc (8)
  375. for details.
  376. X.LP
  377. X.B
  378. status and lpq
  379. X.R
  380. X.IP
  381. Status is used to display the current status of various line printers.
  382. The lpq function invokes lpq with various parameters.
  383. This is useful to monitor various printer activities.
  384. X.LP
  385. X.B
  386. start, abort, kill
  387. X.R
  388. X.IP
  389. X.I Start
  390. enables printing and requests 
  391. X.I lpd
  392. to start printing jobs.
  393. X.sp
  394. X.I Abort
  395. disables unspooling and terminates an active server and its
  396. filters.
  397. This is normally used to kill a
  398. catatonic filter or spooler process
  399. X(i.e.,
  400. X.I lpq
  401. reports that there is a daemon present but nothing is
  402. happening).
  403. It does not remove any jobs from the queue.
  404. X.sp
  405. X.I Kill
  406. does an abort followed by a start.
  407. This is handy to kill a server and start another.
  408. Note that there is an upper limit on the numbers of times a server will
  409. attempt to print a job.
  410. X.B
  411. enable and disable
  412. X.R
  413. X.IP
  414. X.I Enable
  415. and 
  416. X.I disable
  417. control spooling to a queue.
  418. This is used to prevent
  419. X.I lpr
  420. from putting new jobs in the spool queue.
  421. The main use is to prevent users from putting jobs in the queue
  422. when the printer is expected to be unavailable for a long time.
  423. X.LP
  424. X.B restart
  425. X.IP
  426. X.I Restart
  427. allows mere mortals users to restart printer daemons when
  428. X.I lpq
  429. reports that there is no daemon present.
  430. X.LP
  431. X.B stop
  432. X.IP
  433. X.I Stop
  434. disables unspooling,
  435. but does not kill off the server.
  436. This is a clean way to shutdown a printer in order to perform
  437. maintenence, etc.
  438. Note that users can still enter jobs in a
  439. spool queue while a printer is
  440. X.IR stopped .
  441. X.LP
  442. X.B topq
  443. X.IP
  444. X.I Topq
  445. places jobs at the top of a printer queue.
  446. This can be used to reorder high priority jobs.
  447. X.LP
  448. X.B
  449. remote (command)
  450. X.R
  451. X.IP
  452. X.I remote
  453. is used to send a command to the remote site for processing.
  454. This is useful to control remote line printer queues.
  455. X.LP
  456. X.B clean
  457. X.IP
  458. X.I clean
  459. totally purges a queue.
  460. This functionality is needed very infrequently,
  461. and perhaps should be removed.
  462. X.NH 2
  463. Starting the LPD Daemon
  464. X.PP
  465. Under various circumstances it may be necessary to kill and/or
  466. restart the
  467. X.I lpd
  468. daemon.
  469. The currently executing daemon writes
  470. its process ID and the time it was started in the
  471. X.L /usr/spool/lock.lpd 
  472. file.
  473. The
  474. program in Figure 9.1,
  475. usually stored in
  476. X.L /usr/etc/lpd.kill ,
  477. can be used to kill and restart the LPD daemon.
  478. X.KF
  479. X.in +1i
  480. X.SM
  481. X.L
  482. X.nf
  483. X.ta 4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n 8i
  484. X#!/bin/csh -f
  485. set p=`head -1 /usr/spool/lpd.lock`
  486. set h=`hostname`
  487. if ( "$p" == "" ) then
  488. X    echo $h - no lpd daemon
  489. else
  490. X    echo $h - daemon $p
  491. X    kill $p
  492. X    sleep 1
  493. X    /usr/lib/lpd
  494. endif
  495. X.fi
  496. X.LG
  497. X.R
  498. X.in -1i
  499. X.ce
  500. XFigure 9.1 lpd.kill Program
  501. X.KE
  502. X.PP
  503. In a heavily networked system,
  504. it may be desirable to kill and restart daemons on a set of machines.
  505. The
  506. program in Figure 9.2,
  507. usually stored in
  508. X.L /usr/etc/lpd.all ,
  509. can be used to kill and restart remote daemons.
  510. X.KF
  511. X.in +1i
  512. X.SM
  513. X.L
  514. X.nf
  515. X.ta 4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n +4n 8i
  516. X#!/bin/csh -f
  517. set l=(`cat /etc/remote.lpd`)
  518. foreach  i ($l)
  519. X    rsh $i /usr/etc/lpd.kill
  520. X    echo done $i
  521. endfor
  522. X.LG
  523. X.R
  524. X.in -1i
  525. X.ce
  526. XFigure 9.2 lpd.all Program
  527. X.KE
  528. END_OF_FILE
  529. if test 6569 -ne `wc -c <'doc/PLP/09.t'`; then
  530.     echo shar: \"'doc/PLP/09.t'\" unpacked with wrong size!
  531. fi
  532. # end of 'doc/PLP/09.t'
  533. fi
  534. if test -f 'filters/lpf.c' -a "${1}" != "-c" ; then 
  535.   echo shar: Will not clobber existing file \"'filters/lpf.c'\"
  536. else
  537. echo shar: Extracting \"'filters/lpf.c'\" \(6527 characters\)
  538. sed "s/^X//" >'filters/lpf.c' <<'END_OF_FILE'
  539. X/***************************************************************************
  540. X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
  541. X ***************************************************************************
  542. X * MODULE:  lpf.c
  543. X ***************************************************************************
  544. X * Revision History: Created Fri Mar  4 19:05:43 CST 1988
  545. X * $Log:    lpf.c,v $
  546. X * Revision 2.2  88/05/19  07:35:38  papowell
  547. X * fixed minor bug with overstrikes
  548. X * 
  549. X * Revision 2.1  88/05/09  10:12:08  papowell
  550. X * *** empty log message ***
  551. X * 
  552. X ***************************************************************************/
  553. X#ifndef lint
  554. static char id_str1[] =
  555. X    "$Header: lpf.c,v 2.2 88/05/19 07:35:38 papowell Locked $ PLP Copyright 1988 Patrick Powell";
  556. X#endif lint
  557. X/***************************************************************************
  558. X *    LPF and VPF filters.
  559. X *     Filter which converts lines with ^H's to overwritten lines.
  560. X *    Thus this works like 'ul' but is much better; it can handle
  561. X *    more than 2 overwrites and it is written with some style.
  562. X *
  563. X *    Also used as the versatec driver, with the VERSATEC defined
  564. X *  Original source for this was the 4.2BSD release,  but there have
  565. X *    been substantial modifications since.  This version has been derived 
  566. X *    from a total rewrite done in 1985.
  567. X ***************************************************************************/
  568. X
  569. X#include <stdio.h>
  570. X#include <ctype.h>
  571. X#include <signal.h>
  572. X#include <sys/types.h>
  573. X#include <sys/time.h>
  574. X
  575. extern int errorcode;
  576. X/* set from flags */
  577. extern int debug, width, length, xwidth, ylength, literal, indent;
  578. extern char *zopts, *class, *job, *login, *accntname, *host, *accntfile;
  579. extern char *printer, *format, *name;
  580. extern int npages;    /* number of pages */
  581. extern char *calloc();    /* memory allocation */
  582. X
  583. X#ifdef VERSATEC
  584. X#include <sys/vcmd.h>
  585. X
  586. X
  587. int    pltmode[] = {VPLOT};
  588. int    prtmode[] = {VPRINT};
  589. X#define MAXREP        10
  590. X
  591. X#else  VERSATEC
  592. X/* line printer */
  593. X
  594. X#define MAXREP        10
  595. X#endif VERSATEC
  596. X
  597. X#define    TIMEOUT        (5*60)    /*    5 minute time out    */
  598. X
  599. char    *buf;
  600. int    *maxcol;
  601. int    lineno;
  602. X
  603. X
  604. int    timeout();
  605. X
  606. filter(stop)
  607. X    char *stop;
  608. X{
  609. X    register FILE *p = stdin, *o = stdout;
  610. X    register int i, j, col;
  611. X    register char *cp;
  612. X    int done, linedone, maxrep;
  613. X    unsigned int bufsize;
  614. X    char ch, *limit;
  615. X    int state, partial;
  616. X
  617. X    partial = 0;
  618. X    state = 0;
  619. X    if( xwidth <= 0 ){
  620. X        xwidth = width;
  621. X        /* fatal( "bad xwidth value %d", xwidth ); */
  622. X    }
  623. X    if( width > xwidth || width <= 0 ){
  624. X        fatal( "width (%d) out of range, max %d", width, xwidth );
  625. X    }
  626. X    /*
  627. X     * check on width 
  628. X     */
  629. X    bufsize =  (unsigned)( MAXREP * xwidth );
  630. X    if( (buf = calloc( bufsize, sizeof(char) )) == 0 
  631. X        || (maxcol = (int *)calloc( (unsigned)( MAXREP ), sizeof(int) )) == 0 ){
  632. X        fatal( "malloc failed");
  633. X    }
  634. X    maxcol[0] = -1;
  635. X    state = 0;
  636. X    /*
  637. X     * set up timeout
  638. X     */
  639. X
  640. X#    ifdef VERSATEC
  641. X    (void) signal( SIGALRM, timeout );
  642. X    errorcode = 1;    /* set retry on failure */
  643. X    if( ioctl(1, VSETSTATE, prtmode) < 0 ){
  644. X        logerr_die( "ioctl failed" );
  645. X    }
  646. X#    endif VERSATEC
  647. X
  648. X
  649. X    for( i = 0; i < MAXREP; ++i ){
  650. X        maxcol[i] = -1;
  651. X    }
  652. X    for( i = 0; i < bufsize; ++i ){
  653. X        buf[i] = ' ';
  654. X    }
  655. X    done = 0;
  656. X
  657. X    while (!done) {
  658. X        col = indent;
  659. X        maxrep = -1;
  660. X        linedone = 0;
  661. X        while (!linedone) {
  662. X            if( state ){
  663. X                /* we read a partial stop and want to process it normally */
  664. X                ch = stop[partial];
  665. X                ++partial;
  666. X                if( partial == state ){
  667. X                    state = 0;
  668. X                }
  669. X            } else {
  670. X                ch = getc(p);
  671. X                if( stop && ch == *stop){
  672. X                    /* we have hit the first character in the stop sequence */
  673. X                    for(state = 1;
  674. X                        stop[state] && (ch = getc(p)) == stop[state];
  675. X                        ++state );
  676. X                    /* we either have last, or we have a bad character */
  677. X                    if( stop[state] == 0 ){
  678. X                        state = 0; /* suspend yourself!! */
  679. X                        /*
  680. X                         * if this filter is running as the OF
  681. X                         * filter, it is used only for banners.
  682. X                         * lpd needs to use a different filter to
  683. X                         * print data so stop what we are doing and
  684. X                         * wait for lpd to restart us.
  685. X                         */
  686. X                        (void)alarm( TIMEOUT );
  687. X                        if( fflush(stdout) < 0 ){
  688. X                            logerr_die( "fflush failed" );
  689. X                        }
  690. X                        (void)alarm( 0 );
  691. X                        suspend();
  692. X#                        ifdef VERSATEC
  693. X                        if( ioctl(1, VSETSTATE, prtmode) < 0 ){
  694. X                            logerr_die( "ioctl failed" );
  695. X                        }
  696. X#                        endif VERSATEC
  697. X                    } else {
  698. X                        /* we don't have all of the characters */
  699. X                        partial = 0;
  700. X                        if( ch != EOF ){
  701. X                            (void)ungetc(ch, stdin);
  702. X                        }
  703. X                    }
  704. X                    /* we want to iterate, using new characters */
  705. X                    continue;
  706. X                }
  707. X            }
  708. X            switch (ch) {
  709. X            case EOF:
  710. X                linedone = done = 1;
  711. X                ch = '\n';
  712. X                break;
  713. X
  714. X            case '\f':
  715. X                lineno = length;
  716. X            case '\n':
  717. X                if (maxrep < 0)
  718. X                    maxrep = 0;
  719. X                linedone = 1;
  720. X                break;
  721. X
  722. X            case '\b':
  723. X                --col;
  724. X                if( col < indent)
  725. X                    col = indent;
  726. X                break;
  727. X
  728. X            case '\r':
  729. X                col = indent;
  730. X                break;
  731. X
  732. X            case '\t':
  733. X                col = ((col - indent) | 07) + indent + 1;
  734. X                break;
  735. X
  736. X            default:
  737. X                if (col >= width || !literal && !isprint(ch)) {
  738. X                    col++;
  739. X                    break;
  740. X                }
  741. X                if(debug)fprintf(stderr,"ch '%c', col %d\n", ch, col );
  742. X                cp = buf+col;
  743. X                for (i = 0; i < MAXREP; i++) {
  744. X                    if (i > maxrep)
  745. X                        maxrep = i;
  746. X                    if (*cp == ' ') {
  747. X                        *cp = ch;
  748. X                        if (col > maxcol[i])
  749. X                            maxcol[i] = col;
  750. X                        break;
  751. X                    }
  752. X                    cp += xwidth;
  753. X                }
  754. X                col++;
  755. X            }
  756. X        }
  757. X
  758. X        /* print out lines */
  759. X        (void)alarm( TIMEOUT );
  760. X        /* print out the lines in order of last to first */
  761. X        if(debug){
  762. X            fprintf(stderr,"maxrep %d\n");
  763. X        }
  764. X        for (i = maxrep; i >= 0;  --i) {
  765. X            cp = buf+i*xwidth;
  766. X            if(debug){
  767. X                fprintf(stderr,"line %d, col %d, '%s'\n",i,
  768. X                    maxcol[i], cp);
  769. X            }
  770. X            if( (col = maxcol[i]+1) > 0 ){
  771. X                if(col != fwrite(cp,1,col,o)){
  772. X                    logerr_die( "write failed" );
  773. X                }
  774. X            }
  775. X            for( j = 0; j < xwidth; ++j ){
  776. X                cp[j] = ' ';
  777. X            }
  778. X            maxcol[i] = -1;
  779. X#            ifdef PRINTRONIX
  780. X            if( putc('\r', o) < 0 ){
  781. X                logerr_die( "putc failed" );
  782. X            }
  783. X#            else  PRINTRONIX
  784. X            if( i > 0 ){
  785. X                if( putc('\r', o) < 0 ){
  786. X                    logerr_die( "putc failed" );
  787. X                }
  788. X            }
  789. X#            endif PRINTRONIX
  790. X        }
  791. X        if( putc(ch, o) < 0 ){
  792. X            logerr_die( "write failed" );
  793. X        }
  794. X        (void)alarm( 0 );
  795. X
  796. X        /*
  797. X         * update page count
  798. X         */
  799. X        if(++lineno >= length){
  800. X            npages++;
  801. X            lineno = 0;
  802. X        }
  803. X    }
  804. X
  805. X    (void)alarm( TIMEOUT );    /* set up timeout */
  806. X    if (lineno) {        /* be sure to end on a page boundary */
  807. X        if( putc('\f', o) < 0 ){
  808. X            logerr_die( "putc failed" );
  809. X        }
  810. X        npages++;
  811. X    }
  812. X    (void)fflush( o );
  813. X    if( ferror(o) ){
  814. X        logerr_die( "write failed" );
  815. X    }
  816. X    (void)alarm( 0 );
  817. X    }
  818. X
  819. X
  820. timeout()
  821. X    {
  822. X    fatal( "timeout" );
  823. X    }
  824. X
  825. X/*
  826. X * do nothing on cleanup
  827. X */
  828. X
  829. cleanup()
  830. X    {}
  831. END_OF_FILE
  832. if test 6527 -ne `wc -c <'filters/lpf.c'`; then
  833.     echo shar: \"'filters/lpf.c'\" unpacked with wrong size!
  834. fi
  835. # end of 'filters/lpf.c'
  836. fi
  837. if test -f 'man/lpd.8' -a "${1}" != "-c" ; then 
  838.   echo shar: Will not clobber existing file \"'man/lpd.8'\"
  839. else
  840. echo shar: Extracting \"'man/lpd.8'\" \(6781 characters\)
  841. sed "s/^X//" >'man/lpd.8' <<'END_OF_FILE'
  842. X.TH LPD 8 "19 Mar 1988" "U-MN PLP"
  843. X.ig
  844. X$Header: lpd.8,v 2.1 88/05/09 10:08:31 papowell Exp $
  845. X$Log:    lpd.8,v $
  846. Revision 2.1  88/05/09  10:08:31  papowell
  847. PLP: Released Version
  848. X
  849. Revision 1.1  88/04/28  10:58:50  papowell
  850. Initial revision
  851. X
  852. X..
  853. X.SH NAME
  854. lpd \- line printer daemon
  855. X.SH SYNOPSIS
  856. X.B /usr/lib/lpd
  857. X[\-Llogfile] [\-D[n]] [\-X]
  858. X.SH DESCRIPTION
  859. X.I Lpd
  860. is the line printer daemon (spool queue handler) and is normally invoked
  861. at boot time from the
  862. X.IR rc (8)
  863. file.  It makes a single pass through the
  864. X.IR printcap (5)
  865. file to find out about the existing printers and
  866. starts spool queue servers.
  867. It then uses the system calls
  868. X.IR listen (2)
  869. and
  870. X.IR accept (2)
  871. to receive requests to print files in the queue,
  872. transfer files to the spooling area,
  873. display the queue,
  874. remove jobs from the queue,
  875. or perform a spool queue control function.
  876. In each case it creates a server process to handle
  877. the request and the lpd process will listen for more requests.
  878. The
  879. X.B \-L
  880. option specifies an alternate file to be used for logging error messages.
  881. The
  882. X.IR syslog (8)
  883. facility is used to log critical messages as well.
  884. The
  885. X.B \-D
  886. flag enables generation of debugging messages,
  887. and the 
  888. X.B \-X
  889. flag forces use of an Xperimental version of the software if it
  890. is avilable.
  891. X.PP
  892. Access control is provided by two means:
  893. a general set of printer permissions determined by the
  894. X.I /etc/printer_perm
  895. file,
  896. and an optional additional set of restrictions set by the
  897. X.B XU
  898. X(check user) permissions file specified in the printcap entry.
  899. XEach entry in the /etc/printer_perm file specifies a machine,
  900. user,
  901. printer,
  902. and maximum priority allowed;
  903. the ``*'' is a wildcard entry and matches anything.
  904. XFor example,
  905. the following entry allows root on any machine,
  906. to have to access to all queues and any priority,
  907. all users to have access to the lp queue but only a max of
  908. C priority,
  909. and admin on central to have access to the pr queue.
  910. The max and current fields are used to determine the maximum
  911. and current number of pages used by a particular user;
  912. this information is checked by the particular device
  913. to determine if a user or group of users have exceeded their
  914. limits,
  915. and is usually present only in the device specific permissions file.
  916. X.RS
  917. X.nf
  918. X.ta 1.i +1.i +1.i +.5i +.5i +.5i +.5i +.5i +.5i +.5i +.5i
  919. X#host    user    queue    priority    max    current
  920. X*    root    *    A    0    0
  921. X*    *    lp    C    0    0
  922. central    admin    lp    C    0    0
  923. X.fi
  924. X.RE
  925. X.PP
  926. The spool queue
  927. X.I lock
  928. file in each spool directory is used to
  929. control spooling activities,
  930. prevent multiple servers from becoming active simultaneously,
  931. and to store information about the server process.
  932. The
  933. X.IR lpr (1),
  934. X.IR lpq (1),
  935. X.IR lpc (1)
  936. and
  937. X.IR lprm (1)
  938. programs use this file to report on queue status.
  939. To gain control of a spool queue,
  940. a server process locks the control file,
  941. and then unspools the jobs.
  942. XEach job has a control file and a set of data files.
  943. Lines in the control file
  944. file specify files to be printed or non-printing actions to be
  945. performed.
  946. XEach line consists of a flag character and a parameter.
  947. Lower case letters are reserved for the format
  948. indication,
  949. specified by the 
  950. X\-F
  951. option of LPR.
  952. the following is a partial list of the flag characters and their uses.
  953. X.in +3
  954. X.IP J
  955. Job Name.  String to be used for the job name on the burst page.
  956. X.IP C
  957. Classification.
  958. String to be used for the classification line
  959. on the burst page and to determine job priority.
  960. The first letter of the string sets the level;
  961. A is highest, Z is lowest.
  962. The maximum level that can be used is determined by the
  963. printer_perm database and/or the
  964. X.B XU
  965. printcap entry.
  966. X.IP L
  967. Literal.
  968. The line contains identification info from
  969. the password file and appears on the banner page.
  970. X.IP T
  971. Title.  String to be used as the title for
  972. X.IR pr (1).
  973. X.IP H
  974. Host Name.  Name of the machine where
  975. X.I lpr
  976. was invoked.
  977. X.IP P
  978. Person.  Login name of the person who invoked
  979. X.IR lpr .
  980. This is used to verify ownership by
  981. X.IR lprm .
  982. X.IP M
  983. Send mail to the specified user when the current print job completes.
  984. X.IP Z
  985. zoptions. Options passed by
  986. X.IR lpr
  987. X.IR -Zzoptions.
  988. These are passed to output filters.
  989. X.IP f
  990. XFormatted File.  Name of a file to print which is already formatted.
  991. X.IP l
  992. Like ``f'' but passes control characters and does not make page breaks.
  993. X.IP p
  994. Name of a file to print using
  995. X.IR pr (1)
  996. as a filter.
  997. X.IP t
  998. Troff File.  The file contains
  999. X.IR troff (1)
  1000. output (cat phototypesetter commands).
  1001. X.IP d
  1002. DVI File.  The file contains
  1003. X.IR Tex (l)
  1004. output (DVI format from Stanford).
  1005. X.IP g
  1006. Graph File.  The file contains data produced by
  1007. X.IR plot (3X).
  1008. X.IP c
  1009. Cifplot File. The file contains data produced by
  1010. X.IR cifplot .
  1011. X.IP v
  1012. The file contains a raster image.
  1013. X.IP r
  1014. The file contains text data with FORTRAN carriage control characters.
  1015. X.IP 1
  1016. Troff Font R. Name of the font file to use instead of the default.
  1017. X(Obsolete, not used)
  1018. X.IP 2
  1019. Troff Font I. Name of the font file to use instead of the default.
  1020. X(Obsolete, not used)
  1021. X.IP 3
  1022. Troff Font B. Name of the font file to use instead of the default.
  1023. X(Obsolete, not used)
  1024. X.IP 4
  1025. Troff Font S. Name of the font file to use instead of the default.
  1026. X(Obsolete, not used)
  1027. X.IP W
  1028. Width. Changes the page width (in characters) used by
  1029. X.IR pr (1)
  1030. and the text filters.
  1031. X.IP I
  1032. Indent.  The number of characters to indent the output by (in ascii).
  1033. X.IP U
  1034. Unlink.  Name of file to remove upon completion of printing.
  1035. X.IP N
  1036. XFile name.  The name of the file which is being printed, or a blank
  1037. for the standard input (when 
  1038. X.I lpr
  1039. is invoked in a pipeline).
  1040. X.in -5
  1041. X.PP
  1042. The spool server will attempt to print a job a limited number of times
  1043. before abandoning it.
  1044. X.PP
  1045. X.I Lpd
  1046. uses
  1047. X.IR flock (2)
  1048. to provide exclusive access to the lock file and to prevent multiple
  1049. deamons from becoming active simultaneously.  If the daemon should be killed
  1050. or die unexpectedly, the lock file need not be removed.
  1051. The lock file is kept in a readable ASCII form
  1052. and contains two lines.
  1053. The first is the process id of the daemon and the second is the control
  1054. file name of the current job being printed.  The second line is updated to
  1055. reflect the current status of
  1056. X.I lpd
  1057. for the programs
  1058. X.IR lpq (1)
  1059. and
  1060. X.IR lprm (1).
  1061. X.SH FILES
  1062. X.nf
  1063. X.ta \w'/etc/printcap           'u
  1064. X/etc/printcap    printer description file
  1065. X/usr/spool/*    spool directories
  1066. X/dev/lp*    line printer devices
  1067. X/dev/printer    socket for local requests
  1068. X/etc/hosts.equiv    lists machine names allowed printer access
  1069. X/etc/printer_perms    permissions
  1070. X.fi
  1071. X.SH "SEE ALSO"
  1072. lpc(8),
  1073. pac(1),
  1074. lpr(1),
  1075. lpq(1),
  1076. lprm(1),
  1077. printcap(5)
  1078. X.br
  1079. X.I "PLP - The Public Line Printer Spooler",
  1080. by
  1081. Patrick Powell,
  1082. University of Minnesota.
  1083. X.fi
  1084. X.SH "HISTORY"
  1085. X.PP
  1086. The PLP is a reverse engineered version of the Berkeley 4.3BSD Line Printer
  1087. Spooler.
  1088. It has many advanced features which are described in
  1089. X.I "PLP - The Public Line Printer Spooler"
  1090. by
  1091. Patrick Powell,
  1092. University of Minnesota.
  1093. END_OF_FILE
  1094. if test 6781 -ne `wc -c <'man/lpd.8'`; then
  1095.     echo shar: \"'man/lpd.8'\" unpacked with wrong size!
  1096. fi
  1097. # end of 'man/lpd.8'
  1098. fi
  1099. if test -f 'man/lpr.1' -a "${1}" != "-c" ; then 
  1100.   echo shar: Will not clobber existing file \"'man/lpr.1'\"
  1101. else
  1102. echo shar: Extracting \"'man/lpr.1'\" \(7221 characters\)
  1103. sed "s/^X//" >'man/lpr.1' <<'END_OF_FILE'
  1104. X.TH LPR 1 "19 Mar 1988" "U-MN PLP"
  1105. X.ig
  1106. X$Header: lpr.1,v 2.2 88/05/19 07:43:00 papowell Locked $
  1107. X$Log:    lpr.1,v $
  1108. Revision 2.2  88/05/19  07:43:00  papowell
  1109. XFixed up the -H, -h descriptions
  1110. X
  1111. Revision 2.1  88/05/09  10:08:40  papowell
  1112. PLP: Released Version
  1113. X
  1114. Revision 1.2  88/05/09  10:01:51  papowell
  1115. added -h option
  1116. X
  1117. Revision 1.1  88/04/28  10:58:53  papowell
  1118. Initial revision
  1119. X
  1120. X..
  1121. X.SH NAME
  1122. lpr \- off line print
  1123. X.SH SYNOPSIS
  1124. X.B lpr
  1125. X[
  1126. X.BI \-P printer
  1127. X] [
  1128. X.BI \-# num
  1129. X] [
  1130. X.B \-C
  1131. X.I class
  1132. X] [
  1133. X.B \-J
  1134. X.I job
  1135. X] [
  1136. X.BI \-R remoteAccount
  1137. X] [
  1138. X.BI \-m \fR[\fImailTo\fR]\fI
  1139. X] [
  1140. X.B \-T
  1141. X.I title
  1142. X]
  1143. X[\fB\-i\fP[\fInumcols\fP]]
  1144. X[
  1145. X.BI \-w num
  1146. X] [
  1147. X.BI \-Z zoptions
  1148. X] [
  1149. X.BI \-U user
  1150. X] [
  1151. X.BI \-F filter
  1152. X] [
  1153. X.B \-bhrs
  1154. X] [
  1155. X.BI \-D n
  1156. X] [
  1157. X.B \-X
  1158. X] [
  1159. filename ...
  1160. X]
  1161. X.SH DESCRIPTION
  1162. X.B Lpr
  1163. uses a spooling daemon to print the named files when facilities
  1164. become available.  If no names appear, the standard input is assumed.
  1165. X.IP "\fB\-P\fIprinter\fR" 5
  1166. Output to the specific printer;
  1167. the default is to use
  1168. the value of the
  1169. environment variable PRINTER and
  1170. and then the default (site dependent) printer.
  1171. X.IP "\fB\-F\fIf\fR" 5
  1172. XFilter or format specification.
  1173. By default,
  1174. input is assumed to a standard text file and the
  1175. X.I f
  1176. format is used;
  1177. the output device is assmed to be a simple line printer.
  1178. Therer are other formats available,
  1179. listed below.
  1180. Not all formats may be available on all printers;
  1181. see
  1182. X.IR printcap (5)
  1183. for details.
  1184. XFormats are single lower case letters;
  1185. the following are the valid arguments for
  1186. X.B \-F
  1187. together with the assumed type of data.
  1188. XFor compatibility with previous versions of
  1189. X.BR lpr ,
  1190. the format types can be used as options themselves
  1191. X(i.e. by omitting the
  1192. X.BR F )
  1193. except where noted below,
  1194. a warning may be issued in such cases.
  1195. X.IP \fBp\fP 5
  1196. text to be printed using
  1197. X.IR pr (1)
  1198. to format the files.
  1199. The output is then formatted using the
  1200. X.I f
  1201. format.
  1202. X.IP \fBl\fP 5
  1203. text with control characters to be printed,
  1204. and page breaks suppressed.
  1205. X.IP \fBt\fP 5
  1206. output from
  1207. X.IR troff (1)
  1208. X(originally cat phototypesetter commands,
  1209. but now we assume the same as the
  1210. X.B n
  1211. format).
  1212. X.IP \fBn\fP 5
  1213. output from (device independent)
  1214. X.IR troff .
  1215. X.IP \fBd\fP 5
  1216. output from
  1217. X.IR tex (l)
  1218. X(DVI format from Stanford).
  1219. X.IP \fBg\fP 5
  1220. standard plot data as produced by the
  1221. X.IR plot (3X)
  1222. routines (see also
  1223. X.IR plot (1G)
  1224. for the filters used by the printer spooler).
  1225. X.IP \fBv\fP 5
  1226. a raster image for devices like the Benson Varian.
  1227. X.IP \fBc\fP 5
  1228. data produced by
  1229. X.IR cifplot (l).
  1230. X.IP \fBr\fP 5
  1231. text in which the first character of each line is interpreted as a
  1232. standard FORTRAN carriage control character.
  1233. The effect of this format used to be obtained with the
  1234. X.B \-f
  1235. option.
  1236. X.PP
  1237. The remaining single letter options have the following meaning.
  1238. X.IP \fB\-m\fP[\fImailTo\fP] 5
  1239. Send mail upon completion to user
  1240. X.I mailTo
  1241. X(default is to the submitter).
  1242. If the \-m flag is followed by another flag or a single \-,
  1243. the default submitter name will be used.
  1244. X.IP \fB\-h\fP 5
  1245. Request no banner or header for this job.
  1246. X.IP \fB\-s\fP 5
  1247. Use symbolic links.
  1248. Usually files are copied to the spool directory.
  1249. This flag will cause a symbolic link to be made to the file,
  1250. and the file should not be modified or removed until it has been printed.
  1251. Primarily useful for printing very large files,
  1252. and its use is restricted due to security loopholes.
  1253. X.IP \fB\-r\fP 5
  1254. Remove the file after printing it.
  1255. This option is also restricted due to security loopholes.
  1256. X.IP \fB\-J\fP\ \fIjobname\fP 5
  1257. Specify the job name to print on the burst page;
  1258. defaults to the name of the first file.
  1259. X.IP \fB\-T\fP\ \fItitle\fP 5
  1260. Specify the title used by
  1261. X.IR pr (1);
  1262. defaults to the file name.
  1263. X.IP \fB\-w\fP\fIwidth\fP 5
  1264. Specify the page width for
  1265. X.IR pr .
  1266. X.IP \fB\-C\fP\ \fIclass\fP 5
  1267. Specify the job classification for use on the burst page and to
  1268. set the priority.
  1269. Priorities range from A (highest) to Z (lowest);
  1270. the default priority is Z.
  1271. XFor example,
  1272. X.br
  1273. X.ti +0.5i
  1274. lpr \-C A foo.c
  1275. X.br
  1276. sets the priority to A and the file foo.c to be printed.
  1277. X.IP \fB\-R\fP\ \fIremoteAccount\fP 5
  1278. Specify accounting information to be used by a remote system that prints
  1279. your output.
  1280. X.sp
  1281. XFor some printers,
  1282. such as the Imagen,
  1283. it can be used to specify a real money billing code to be charged for the
  1284. printing.  This is only needed if the user has more than one
  1285. real money billing code.
  1286. X.IP \fB\-#\fP\fInum\fP 5
  1287. Specify the number of copies of each file to be printed.
  1288. Note that this option is NOT SUPPORTED on all printers,
  1289. and its use is strongly discouraged.
  1290. X.IP \fB\-i\fP[\fInumcols\fP] 5 
  1291. Cause the output to be indented;
  1292. X\fInumcols\fP defaults to 8 blanks.
  1293. Note that this option is NOT SUPPORTED on all printers,
  1294. and thus its use is discouraged.
  1295. X.IP \fB\-b\fP 5
  1296. The files are assumed to contain binary data. This option allows the
  1297. X.I lpr-spooler
  1298. to be used for file transfers.
  1299. X.IP "\fB\-Z\fP\ \fIextra\ options\fP" 5
  1300. This option is used to pass extra options to the input filter for the printer
  1301. in question and is mainly used when spooling to non-Unix printers or when
  1302. using the spooler to to do file transfers to non-Unix machines.
  1303. X.IP "\fB\-D\fR[\fIn\fR]"
  1304. XEnables display of debugging information.
  1305. The 
  1306. X.B \-D
  1307. selects level 1;
  1308. X.B \-D\fIn\fR
  1309. X\fRselects level
  1310. X.I n
  1311. X(n is a single digit).
  1312. X.IP "\fB\-X"
  1313. Use an Xperimental version of LPD if the software has been compiled
  1314. with the appropriate support;
  1315. ignored otherwise.
  1316. X.PP
  1317. The
  1318. X.B \-U
  1319. option is used to specify a user name
  1320. for the purpose of accounting and banners.
  1321. X.PP
  1322. The old LPR options
  1323. X.B \-1234
  1324. are obsolete;
  1325. they were used to specify a font to be mounted on font position \fIi\fR.
  1326. The 
  1327. X.B \-r
  1328. X(delete files on job completion)
  1329. and
  1330. X.B \-s
  1331. options are restricted to users who are in a group specified by the
  1332. X.B ln
  1333. X(allowed to use links) printcap entry for the printer.
  1334. X.SH FILES
  1335. X.nf
  1336. X.ta \w'/usr/spool/*/cf*       'u
  1337. X/etc/passwd    personal identification
  1338. X/etc/printcap    printer capabilities data base
  1339. X/etc/printer_perms    printer permissions
  1340. X/usr/lib/lpd*    line printer daemons
  1341. X/usr/spool/*    directories used for spooling
  1342. X/usr/spool/*/cf*    daemon control files
  1343. X/usr/spool/*/df*    data files specified in "cf" files
  1344. X/usr/spool/*/tf*    temporary copies of "cf" files
  1345. X.fi
  1346. X.SH DIAGNOSTICS
  1347. The diagnostics from
  1348. X.I lpr
  1349. are extremely verbose.
  1350. If you try to spool too large a file
  1351. or too many files,
  1352. the job will be rejected.
  1353. If this is the case,
  1354. use
  1355. X.I cat
  1356. to append the files and then pipe them to LPR.
  1357. X.I Lpr
  1358. will object to printing binary files,
  1359. as determined by a crude sanity and a.out header check.
  1360. If a connection to the
  1361. X.I lpd
  1362. daemon socket /dev/printer cannot be made,
  1363. X.I lpr
  1364. will say that the server cannot be started.
  1365. X.PP
  1366. XEntries in the printcap will determine the exact actions taken by
  1367. X.I lpr
  1368. in spooling files;
  1369. see the
  1370. X.IR printcap (5)
  1371. man pages for details.
  1372. X.IR lpd .
  1373. X.SH "SEE ALSO"
  1374. lpq(1),
  1375. lprm(1),
  1376. pr(1),
  1377. printcap(5),
  1378. lpc(8),
  1379. lpd(8),
  1380. X.br
  1381. X.I "PLP - The Public Line Printer Spooler",
  1382. by
  1383. Patrick Powell,
  1384. University of Minnesota.
  1385. X.fi
  1386. X.SH "HISTORY"
  1387. X.PP
  1388. The PLP is a reverse engineered version of the Berkeley 4.3BSD Line Printer
  1389. Spooler,
  1390. done in 1988 at the University of Minnesota.
  1391. It has many advanced features which are described in
  1392. X.I "PLP - The Public Line Printer Spooler"
  1393. by
  1394. Patrick Powell,
  1395. Department of Computer Science,
  1396. University of Minnesota.
  1397. END_OF_FILE
  1398. if test 7221 -ne `wc -c <'man/lpr.1'`; then
  1399.     echo shar: \"'man/lpr.1'\" unpacked with wrong size!
  1400. fi
  1401. # end of 'man/lpr.1'
  1402. fi
  1403. if test -f 'src/mexecv.c' -a "${1}" != "-c" ; then 
  1404.   echo shar: Will not clobber existing file \"'src/mexecv.c'\"
  1405. else
  1406. echo shar: Extracting \"'src/mexecv.c'\" \(6769 characters\)
  1407. sed "s/^X//" >'src/mexecv.c' <<'END_OF_FILE'
  1408. X/***************************************************************************
  1409. X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
  1410. X ***************************************************************************
  1411. X * MODULE: mexecv.c
  1412. X * ExecrV a la 4 BSD for brain damaged System V
  1413. X ***************************************************************************
  1414. X * Revision History: Created Sat Jan  9 15:23:23 CST 1988
  1415. X * $Log:    mexecv.c,v $
  1416. X * Revision 3.1  88/06/18  09:35:11  papowell
  1417. X * Version 3.0- Distributed Sat Jun 18 1988
  1418. X * 
  1419. X * Revision 2.1  88/05/09  10:09:36  papowell
  1420. X * PLP: Released Version
  1421. X * 
  1422. X * Revision 1.6  88/04/28  11:02:48  papowell
  1423. X * removed unused variables,  shuts up lint
  1424. X * 
  1425. X * Revision 1.5  88/04/27  20:24:08  papowell
  1426. X * Modified the SYSV Braindamaged mode to invoke shells better.
  1427. X * The invocation is rather odd, but appears to work.
  1428. X * 
  1429. X * Revision 1.4  88/03/25  15:00:52  papowell
  1430. X * Debugged Version:
  1431. X * 1. Added the PLP control file first transfer
  1432. X * 2. Checks for MX during file transfers
  1433. X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
  1434. X *     apparently they open files and then assume that they will stay
  1435. X *     open.
  1436. X * 4. Made sure that stdin, stdout, stderr was available at all times.
  1437. X * 
  1438. X * Revision 1.3  88/03/11  19:29:44  papowell
  1439. X * Minor Changes, Updates
  1440. X * 
  1441. X * Revision 1.2  88/03/05  15:00:56  papowell
  1442. X * Minor Corrections,  Lint Problems
  1443. X * 
  1444. X * Revision 1.1  88/03/01  11:08:56  papowell
  1445. X * Initial revision
  1446. X * 
  1447. X ***************************************************************************/
  1448. X#ifndef lint
  1449. static char id_str1[] =
  1450. X    "$Header: mexecv.c,v 3.1 88/06/18 09:35:11 papowell Exp $ PLP Copyright 1988 Patrick Powell";
  1451. X#endif lint
  1452. X
  1453. X/***************************************************************************
  1454. X * ExecrV a la 4 BSD for brain damaged System V
  1455. X * Sun Dec  6 20:04:57 CST 1987 Patrick Powell
  1456. X * This is a massive hack, based on all sorts of checks and balances.
  1457. X *
  1458. X * 1. First, we try blinding using exev()
  1459. X * 2. Next, we check the perms; if not executable, tough.
  1460. X * 3. Next we try to read the file;  if not readable, tough.
  1461. X * 4. We read the first character of the file.
  1462. X * 5. If it is not #, use Bourne shell;
  1463. X * 6. We check for the #!; if not, we use CSH
  1464. X * 7. We get the pathname of the file, and args, and use that.
  1465. X */
  1466. X#include "lp.h"
  1467. X
  1468. mexecv( command)
  1469. X    char *command;
  1470. X{
  1471. X    int i;        /* ACME Integer, Inc. */
  1472. X    struct stat statb;
  1473. X    FILE *fp;
  1474. X    char buf[BUFSIZ];
  1475. X    char cmd[BUFSIZ];
  1476. X    char *cp;
  1477. X    char *args[100];
  1478. X    char **argv = args+2;
  1479. X    char *option = 0;
  1480. X    char *fname;
  1481. X    char *path;
  1482. X
  1483. X    /*
  1484. X     * close all the file descriptors
  1485. X     */
  1486. X    for( i = 3; i < NOFILE; ++i){
  1487. X        (void)close(i);
  1488. X    }
  1489. X    /*
  1490. X     * split command line up
  1491. X     */
  1492. X    (void)strcpy(cmd,command);
  1493. X    if( getwords(cmd, argv, 98) == 0 ){
  1494. X        log(XLOG_INFO,"mexecv: invalid argv passed, command %s",command);
  1495. X        return;
  1496. X    }
  1497. X    if(Debug>4){
  1498. X        char **s, b[BUFSIZ];
  1499. X        (void)sprintf(b,"mexecv: ");
  1500. X        for( s = argv; *s; ++s){
  1501. X            (void)sprintf(b+strlen(b),"'%s' ",*s);
  1502. X        }
  1503. X        log(XLOG_DEBUG,"%s",b);
  1504. X    }
  1505. X    execv( argv[0],argv );
  1506. X
  1507. X    if(Debug>4)logerr(XLOG_DEBUG,"mexecv: execv failed" );
  1508. X
  1509. X    /*
  1510. X     * well, that didn't work, lets try the shell options
  1511. X     */
  1512. X    fname = argv[0];
  1513. X    if( stat(fname, &statb) < 0 ){
  1514. X        logerr( XLOG_INFO,"mexecv: cannot stat %s", fname );
  1515. X        return;
  1516. X    }
  1517. X    if(Debug>4)log(XLOG_DEBUG,
  1518. X        "mexecv: %s, perms %o, st_uid %d, st_gid %d, uid %d, gid %d",
  1519. X        fname,statb.st_mode &0777, statb.st_uid, statb.st_gid,
  1520. X            getegid(), geteuid());
  1521. X    if(! (statb.st_mode & 0001)
  1522. X        && !((statb.st_mode & 0010) && statb.st_gid == getegid())
  1523. X        && !((statb.st_mode & 0100) && statb.st_uid == geteuid())){
  1524. X        log( XLOG_INFO,"mexecv: %s has no valid execute perms",fname);
  1525. X        return;
  1526. X    }
  1527. X    fp = fopen( fname, "r" );
  1528. X    if( fp == NULL ){
  1529. X        logerr(XLOG_INFO,"mexecv: cannot open %s for reading and failed execv",
  1530. X            fname);
  1531. X        return;
  1532. X    }
  1533. X    if( fgets( buf, sizeof(buf), fp ) == NULL
  1534. X        || (cp = index(buf, '\n')) == NULL ){
  1535. X        /* empty file , bad format */
  1536. X        log( XLOG_INFO,"mexecv: bad format %s", fname );
  1537. X        return;
  1538. X    }
  1539. X    (void)fclose(fp);
  1540. X    *cp = 0;
  1541. X    if( buf[0] != '#' ){
  1542. X        path = "/bin/sh";
  1543. X    } else {
  1544. X        /* check for the #! magic number */
  1545. X        if( buf[1] != '!' ){
  1546. X            /* use csh, be paranoid, and sure */
  1547. X            path = "/bin/csh";
  1548. X        } else {
  1549. X            /* we have an explicit path Name formed */
  1550. X            for( path = &buf[2]; *path && isspace(*path); ++path);
  1551. X            /* path points to the start of the command */
  1552. X            if( *path == 0 ){
  1553. X                log( XLOG_INFO,"mexecv: bad format %s", path );
  1554. X                return;
  1555. X            }
  1556. X            /* look for the end of the command */
  1557. X            for( cp = path; *cp && !isspace(*cp); ++cp );
  1558. X            if( *cp ){
  1559. X                /* we have options */
  1560. X                *cp = 0;
  1561. X                for( ++cp; *cp && isspace(*cp); ++cp);
  1562. X                option = cp;
  1563. X                ++cp;
  1564. X                for( ++cp; *cp && !isspace(*cp); ++cp);
  1565. X                *cp = 0;
  1566. X            }
  1567. X        }
  1568. X    }
  1569. X    if( strcmp( path, "/bin/csh" ) == 0 ){
  1570. X        /* we are using csh */
  1571. X        if( option == 0 ){
  1572. X            option = "-f";
  1573. X        } else {
  1574. X            (void)strcat(option, "f");
  1575. X        }
  1576. X    } else if (strcmp( path, "/bin/sh") == 0 ){
  1577. X        /*
  1578. X         * nothing
  1579. X         */
  1580. X        ;
  1581. X    } else {
  1582. X        fatal( XLOG_INFO,"mexecv: shell (%s) not /bin/csh or /bin/sh", path );
  1583. X    }
  1584. X    if( option ){
  1585. X        --argv;
  1586. X        argv[0] = option;
  1587. X    }
  1588. X    --argv;
  1589. X    argv[0] = fname;
  1590. X    if(Debug>4){
  1591. X        char **s, b[BUFSIZ];
  1592. X        (void)sprintf(b,"mexecv: path '%s'", path);
  1593. X        for( s = argv; *s; ++s){
  1594. X            (void)sprintf(b+strlen(b),"'%s' ",*s);
  1595. X        }
  1596. X        log(XLOG_DEBUG,"%s",b);
  1597. X    }
  1598. X    execv( path,argv );
  1599. X    logerr_die(XLOG_INFO,"mexecv: execv failed '%s'", path);
  1600. X}
  1601. X
  1602. X
  1603. X/*
  1604. X * getwords splits a string up into words, and returns a non-null
  1605. X * pointer if there are any.
  1606. X * Note that splitting is done on a crude basis of matched quotes only
  1607. X */
  1608. int
  1609. getwords(s, argv, maxargc)
  1610. X    char *s;    /* string containing isspace separated words */
  1611. X    char *argv[];
  1612. X    int maxargc;
  1613. X{
  1614. X    char **w;
  1615. X    char *tp = s;
  1616. X    int num_words = 0;
  1617. X
  1618. X    if (s==NULL || *s == 0 )
  1619. X        return(0);
  1620. X    while (*tp) {
  1621. X        while(*tp && isspace(*tp))
  1622. X            ++tp;
  1623. X        if (*tp == 0) continue;
  1624. X        num_words++;
  1625. X        if( *tp == '\'' ){
  1626. X            if( (tp = index( tp+1, '\'')) == 0 ){
  1627. X                log(XLOG_INFO,"getwords: unmatched ' in string: %s", s );
  1628. X                return(0);
  1629. X            }
  1630. X            ++tp;
  1631. X        } else if( *tp == '\"' ){
  1632. X            if( (tp = index( tp+1, '\"')) == 0 ){
  1633. X                log(XLOG_INFO,"getwords: unmatched ' in string: %s", s );
  1634. X                return(0);
  1635. X            }
  1636. X            ++tp;
  1637. X        } else  {
  1638. X            while ((*tp) && !isspace(*tp))
  1639. X            ++tp;
  1640. X        }
  1641. X    }
  1642. X    if (num_words==0)
  1643. X        return(0);
  1644. X    if( num_words > maxargc ){
  1645. X        log(XLOG_INFO,"getwords: more than %d arguments" );
  1646. X        return(0);
  1647. X    }
  1648. X    w = argv;
  1649. X    while(*s) {
  1650. X        while(*s && isspace(*s))
  1651. X            ++s;
  1652. X        if (*s == 0) {
  1653. X            *w = NULL;
  1654. X            return(1);
  1655. X        }
  1656. X        if( *s == '\'' ){
  1657. X            *w++ = s+1;
  1658. X            s = index( s+1, '\'');
  1659. X            *s++ = 0;
  1660. X        } else if( *s == '\"' ){
  1661. X            *w++ = s+1;
  1662. X            s = index( s+1, '\"');
  1663. X            *s++ = 0;
  1664. X        } else  {
  1665. X            *w++ = s;
  1666. X            while ((*s) && !isspace(*s))
  1667. X                ++s;
  1668. X            if( *s ) *s++ = '\0';
  1669. X        }
  1670. X    }
  1671. X    *w = NULL;
  1672. X    return(1);
  1673. X}
  1674. END_OF_FILE
  1675. if test 6769 -ne `wc -c <'src/mexecv.c'`; then
  1676.     echo shar: \"'src/mexecv.c'\" unpacked with wrong size!
  1677. fi
  1678. # end of 'src/mexecv.c'
  1679. fi
  1680. if test -f 'src/rmjob.c' -a "${1}" != "-c" ; then 
  1681.   echo shar: Will not clobber existing file \"'src/rmjob.c'\"
  1682. else
  1683. echo shar: Extracting \"'src/rmjob.c'\" \(7416 characters\)
  1684. sed "s/^X//" >'src/rmjob.c' <<'END_OF_FILE'
  1685. X/***************************************************************************
  1686. X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
  1687. X ***************************************************************************
  1688. X * MODULE: rmjob.c
  1689. X * Remove jobs from a printer queue
  1690. X ***************************************************************************
  1691. X * Revision History: Created Sat Jan  9 20:08:26 CST 1988
  1692. X * $Log:    rmjob.c,v $
  1693. X * Revision 3.1  88/06/18  09:35:34  papowell
  1694. X * Version 3.0- Distributed Sat Jun 18 1988
  1695. X * 
  1696. X * Revision 2.2  88/05/14  10:21:11  papowell
  1697. X * Modified -X flag handling
  1698. X * 
  1699. X * Revision 2.1  88/05/09  10:10:09  papowell
  1700. X * PLP: Released Version
  1701. X * 
  1702. X * Revision 1.4  88/03/25  15:01:32  papowell
  1703. X * Debugged Version:
  1704. X * 1. Added the PLP control file first transfer
  1705. X * 2. Checks for MX during file transfers
  1706. X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
  1707. X *     apparently they open files and then assume that they will stay
  1708. X *     open.
  1709. X * 4. Made sure that stdin, stdout, stderr was available at all times.
  1710. X * 
  1711. X * Revision 1.3  88/03/11  19:29:16  papowell
  1712. X * Minor Changes, Updates
  1713. X * 
  1714. X * Revision 1.2  88/03/05  15:01:42  papowell
  1715. X * Minor Corrections,  Lint Problems
  1716. X * 
  1717. X * Revision 1.1  88/03/01  11:09:11  papowell
  1718. X * Initial revision
  1719. X * 
  1720. X ***************************************************************************/
  1721. X#ifndef lint
  1722. static char id_str1[] =
  1723. X    "$Header: rmjob.c,v 3.1 88/06/18 09:35:34 papowell Exp $ PLP Copyright 1988 Patrick Powell";
  1724. X#endif lint
  1725. X
  1726. X#include "lp.h"
  1727. X
  1728. X/***************************************************************************
  1729. X * rmjob()
  1730. X *  1. get the printcap entries
  1731. X *  2. get the queue entries
  1732. X *  3. get the active server files and see if they are in queue
  1733. X *  4. scan the queue, checking each job for removal
  1734. X *
  1735. X *  user   local  job   from  remove_all    perms inlist    remove?
  1736. X *  ===============================================================
  1737. X *  root   yes    *     *     yes           *     *         yes
  1738. X *  root   yes    *     *     no            *     yes       yes
  1739. X *  root   no     *     mach  yes           *     *         yes
  1740. X *  root   no     *     mach  no            *     yes       yes
  1741. X *  user   -      user  mach  -             R     yes       yes
  1742. X *  user   -      *     *     -             C     yes       yes
  1743. X *
  1744. X *  5. remove the job;  this may necessitate stopping or killing a deamon
  1745. X ***************************************************************************/
  1746. static int remove_all;    /* remove all jobs */
  1747. static int control_perms;    /* has C perms */
  1748. X
  1749. rmjob()
  1750. X{
  1751. X    int i;                /* ACME Integers, Inc. */
  1752. X    struct queue *q;    /* job entry */
  1753. X    int perms;            /* hold perms values */
  1754. X
  1755. X    /*
  1756. X     * get the printcap entry
  1757. X     */
  1758. X    if(Get_pc_entry(Printer, Status_pc_vars, Status_pc_len) == 0){
  1759. X        (void)fprintf(stdout, "Printer %s does not exist\n", Printer );
  1760. X        (void)fflush(stdout);
  1761. X        return;
  1762. X    }
  1763. X    if( SD == 0 || *SD == 0 ){
  1764. X        if(Debug>2)log(XLOG_DEBUG,"not a Printer");
  1765. X        return;
  1766. X    }
  1767. X    /* chdir to spool directory */
  1768. X    if (chdir(SD) < 0) {
  1769. X        logerr_die( XLOG_NOTICE,"cannot chdir to %s", SD);
  1770. X    }
  1771. X    /*
  1772. X     * set the flags needed
  1773. X     */
  1774. X    Is_local = strcmp( From, Host ) == 0;
  1775. X    Is_root  = strcmp( Person, "root" );
  1776. X    remove_all = (Parmcount > 0 && (strcmp( Parms[0].str, "-all" ) == 0));
  1777. X    /*
  1778. X     * check to see that the user has RMJOB privs on this machine
  1779. X     */
  1780. X    perms = 'R';    /* must be able to at least use the Printer */
  1781. X    if( !Is_root && (
  1782. X        (Permfile && *Permfile
  1783. X            && !Checkperm( Permfile,From,Person,First_name,&perms,(int *)0,0 ))
  1784. X        || (XU && *XU
  1785. X            && !Checkperm( XU,From,Person,First_name,&perms,(int *)0,0 )))) {
  1786. X        (void)fprintf(stdout, "No remove permission on %s", First_name);
  1787. X        return;
  1788. X    }
  1789. X    perms = 'C';    /* check for control perms */
  1790. X    control_perms = 0;
  1791. X    if( !Is_root && (
  1792. X        (Permfile && *Permfile
  1793. X            && Checkperm( Permfile,From,Person,First_name,&perms,(int *)0,0 ))
  1794. X        || (XU && *XU
  1795. X            && Checkperm( XU,From,Person,First_name,&perms,(int *)0,0 )))) {
  1796. X        control_perms = 1;
  1797. X    }
  1798. X    if(Debug>4)log(XLOG_DEBUG,
  1799. X        "rmjob: Is_root %d, Is_local %d, remove_all %d, control_perms %d",
  1800. X        Is_root, Is_local, remove_all, control_perms );
  1801. X    /*
  1802. X     * check for remote machine and networked file system
  1803. X     */
  1804. X    if( RM && NW ){
  1805. X        Remote_remove();
  1806. X        return;
  1807. X    }
  1808. X    /*
  1809. X     * get the job queue
  1810. X     */
  1811. X    Jobcount = Getq();
  1812. X    (void)Checkactive();
  1813. X    /*
  1814. X     * run down list
  1815. X     */
  1816. X    (void)fprintf(stdout,"Printer '%s' (%s):\n", Printer, Host );
  1817. X    (void)fflush(stdout);
  1818. X    for( i = 0; i < Jobcount; ++i ){
  1819. X        q = &Queue[i];
  1820. X        if( shouldremove( q ) ){
  1821. X            (void)fprintf(stdout,"removing %s, job %d owner %s\n",
  1822. X                q->q_name, q->q_num, q->q_user);
  1823. X            (void)fflush(stdout);
  1824. X            doremove(q);
  1825. X        }
  1826. X    }
  1827. X    /*
  1828. X     * check for remote machine
  1829. X     */
  1830. X    if( RM ){
  1831. X        Remote_remove();
  1832. X    }
  1833. X    /*
  1834. X     * give the server a kick
  1835. X     */
  1836. X    (void)Startserver();
  1837. X}
  1838. X
  1839. X/***********************************************************************
  1840. X * shouldremove( q )
  1841. X *  a simple application of the removal decision table
  1842. X *
  1843. X *  user   local  job   from  remove_all    perms inlist    remove?
  1844. X *  ===============================================================
  1845. X *  root   yes    *     *     yes           *     *         yes
  1846. X *  root   yes    *     *     no            *     yes       yes
  1847. X *  root   no     *     mach  yes           *     *         yes
  1848. X *  root   no     *     mach  no            *     yes       yes
  1849. X *  user   -      user  mach  -             R     yes       yes
  1850. X *  user   -      *     *     -             C     yes       yes
  1851. X *
  1852. X * Returns: 1 if removal is indicated, 0 otherwise
  1853. X ***********************************************************************/
  1854. int
  1855. shouldremove( q )
  1856. X    struct queue *q;
  1857. X{
  1858. X    int i, same_host;
  1859. X
  1860. X    same_host = (strcmp( From, &q->q_from ) == 0);
  1861. X    i = Match_entry(q);
  1862. X    if( Is_root && Is_local && remove_all ) return(1);
  1863. X    if( Is_root && Is_local && !remove_all && i ) return(1);
  1864. X    if( Is_root && !Is_local && same_host && remove_all ) return(1);
  1865. X    if( Is_root && !Is_local && same_host && !remove_all && i ) return(1);
  1866. X    if( strcmp( Person, q->q_user) == 0 && same_host && i ) return(1);
  1867. X    if( i && control_perms ) return(1);
  1868. X    return( 0 );
  1869. X}
  1870. X
  1871. X/***************************************************************************
  1872. X * doremove(struct queue *q)
  1873. X * remove the job
  1874. X * 1. Lock the control file.
  1875. X * 2. If unsuccessful, find the server PID and kill it off.
  1876. X * 3. Use brute force and remove the files.
  1877. X ***************************************************************************/
  1878. X
  1879. doremove( q )
  1880. X    struct queue *q;
  1881. X{
  1882. X    FILE *cfp;
  1883. X
  1884. X    if( (cfp = Lockcf( q->q_name )) == NULL ){
  1885. X        /* hmmm... looks like an active server */
  1886. X        if( (cfp = fopen_daemon( q->q_name, "r" )) == NULL ){
  1887. X            /* nope, the file has really gone */
  1888. X            logerr(XLOG_INFO,"control file %s not readable", q->q_name );
  1889. X            return;
  1890. X        }
  1891. X        /* well, we will just have to kill of the server */
  1892. X        if( q->q_daemon == 0){
  1893. X            /*
  1894. X             * Hmmm... we have this fellow running the file, and it is
  1895. X             * locked.  That means that it just started running this
  1896. X             * guy. Better check again.
  1897. X             */
  1898. X            (void)Checkactive();
  1899. X        }
  1900. X        if( q->q_daemon ){
  1901. X            (void)fprintf( stdout, "killing off %s server %d",
  1902. X                q->q_server,q->q_daemon );
  1903. X            if( killpg( q->q_daemon, SIGINT) < 0 ){
  1904. X                if(Debug>2)log(XLOG_DEBUG,
  1905. X                        "server %s (%d) was not alive",
  1906. X                            q->q_server,q->q_daemon );
  1907. X            }
  1908. X        }
  1909. X    }
  1910. X    /* use brute force;  we simply remove files */
  1911. X    if(Debug>3)log(XLOG_DEBUG,"removing files for job %s",q->q_name);
  1912. X    Remove_job( cfp, q );
  1913. X    (void)fclose( cfp );
  1914. X}
  1915. END_OF_FILE
  1916. if test 7416 -ne `wc -c <'src/rmjob.c'`; then
  1917.     echo shar: \"'src/rmjob.c'\" unpacked with wrong size!
  1918. fi
  1919. # end of 'src/rmjob.c'
  1920. fi
  1921. echo shar: End of archive 5 \(of 16\).
  1922. cp /dev/null ark5isdone
  1923. MISSING=""
  1924. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
  1925.     if test ! -f ark${I}isdone ; then
  1926.     MISSING="${MISSING} ${I}"
  1927.     fi
  1928. done
  1929. if test "${MISSING}" = "" ; then
  1930.     echo You have unpacked all 16 archives.
  1931.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1932. else
  1933.     echo You still need to unpack the following archives:
  1934.     echo "        " ${MISSING}
  1935. fi
  1936. ##  End of shell archive.
  1937. exit 0
  1938.  
  1939.