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

  1. Subject:  v16i025:  Public lineprinter spooler package, Part12/14
  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 25
  8. Archive-name: plp/part12
  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 12 (of 16)."
  17. # Contents:  doc/PLP/04.t src/lpd.c src/printcap.c
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. if test -f 'doc/PLP/04.t' -a "${1}" != "-c" ; then 
  20.   echo shar: Will not clobber existing file \"'doc/PLP/04.t'\"
  21. else
  22. echo shar: Extracting \"'doc/PLP/04.t'\" \(16280 characters\)
  23. sed "s/^X//" >'doc/PLP/04.t' <<'END_OF_FILE'
  24. X.ig
  25. X$Header: 04.t,v 1.1 88/05/21 18:39:37 papowell Locked $
  26. X$log$
  27. X..
  28. X.NH 1
  29. Printcap and PLP Operations
  30. X.PP
  31. The entries in the printcap file are used to define and control
  32. the actions of the PLP software.
  33. XEach host machine must have a printcap file that is used to specify
  34. the spool queues that are available on the host.
  35. Permissions to use a particular spool queue are determined by the
  36. printer permissions file.
  37. The following chapter discusses the structure of the printcap file,
  38. and the purpose of entries in the printcap file.
  39. X.NH 2
  40. Printcap Format
  41. X.PP
  42. The printcap file format is a simplified form of the
  43. X.IR termcap (5)
  44. data base.
  45. XEach spool queue has a corresponding printcap entry.
  46. XEach entry for a spool queue with multiple servers also has a printcap entry.
  47. XFigure 4.1 is an example of a typical printcap entry.
  48. X.KF
  49. X.DT
  50. X.in +1i
  51. X.L
  52. X.SM
  53. X.nf
  54. X#    Default line printer is first in printcap entry
  55. X#   Clunker Printer in Lind 23
  56. clunker_lind23|clunker:\e
  57. X    :lp=/dev/lp:\e
  58. X    :st=\et\er\e015:\e
  59. X    :mx#20:\e
  60. X    :wt:fl@:
  61. X.LG
  62. X.in -1i
  63. X.R
  64. X.ce
  65. XFigure 4.1  Sample Printcap Entry
  66. X.KE
  67. X.IP 1). 3
  68. Blank lines and lines which start with a
  69. X.L #
  70. are treated as comments and can be used to separate entries.
  71. X.IP 2). 3
  72. XEach entry starts in column 1,
  73. and is continued across multiple lines by escaping the end of line with a
  74. backslash (\c
  75. X.BR \e ).
  76. XFields of the entry are separated by colons (\c
  77. X.BR : ).
  78. X.IP 3). 3
  79. The first field of an entry is the entry name followed by a
  80. an optional list of aliases.
  81. The entry name and aliases are separated by a bars (\c
  82. X.BR | ).
  83. X.IP 4). 3
  84. Parameter fields start with a two letter parameter name,
  85. followed by a flag indicating the parameter type.
  86. Parameters can be strings,
  87. integers,
  88. or flags.
  89. String parameters have the form
  90. X.L xx=ssss 
  91. X(\c
  92. X.L ssss
  93. is the string assigned to the parameter),
  94. integer parameters have the form
  95. X.L xx#nnnn ,
  96. and flag or boolean parameters use
  97. X.L xx
  98. to set
  99. X.L xx@
  100. to clear the flag.
  101. X.IP 5). 3
  102. Non-printing characters can be represented in a string by using
  103. the character escape sequences of the C programming language.
  104. X.IP 6). 3
  105. See the
  106. X.IR printcap \|(5)
  107. man page for a complete list of printcap variables and their default values.
  108. X.PP
  109. XEach entry in the printcap file specifies either a remote spool queue,
  110. a local spool queue with a single printer,
  111. a local spool queue with multiple printers,
  112. or a printer for a spool queue with multiple printers.
  113. The information in the printcap entry determines the type of entry and
  114. how the entries in the spool queue are to be handled.
  115. X.PP
  116. The PLP software searches the printcap file for a printcap entry with
  117. a matching printer name or alias.
  118. By convention,
  119. if a user does not specify a printer,
  120. either by using a command line option or setting the
  121. X.B PRINTER
  122. environment variable,
  123. the first printer in the printcap file is used as the default printer.
  124. X.NH 2
  125. Spool Queue Directory
  126. X.PP
  127. The
  128. X.B sd
  129. X(spool directory) field specifies the path name of the directory
  130. which will hold spool queue entries or jobs.
  131. The
  132. X.B sd
  133. parameter must be present for all printcap entries which are
  134. used for spooling.
  135. XFigure 4.2  is a typical printcap entry for
  136. a remote printer spool queue,
  137. whose spool directory is specified as
  138. X.L /usr/spool/lind33_decwriter .
  139. X.KF
  140. X.nf
  141. X.in +1i
  142. X.DT
  143. X.L
  144. X.SM
  145. X# Decwriter III in Lind 33
  146. X# Modified 11 Feb. 1988 Patrick Powell - added fx=n for ditroff
  147. X# queue printer permissions file
  148. X# maximum job size 2000K bytes, no copies
  149. remote\||\|lind33_decwriter:\e
  150. X    :fx=ilpn:\e
  151. X    :sd=/usr/spool/lind33_decwriter:\e
  152. X    :rm=umn-cs.cs.umn.edu:rp=lind33_decrwriter:\e
  153. X    :xu=perms:\e
  154. X    :ex=n:ne=/usr/local/lib/ddumb:\e
  155. X    :mx#2000:sc:
  156. X.LG
  157. X.R
  158. X.in -1i
  159. X.ce
  160. XFigure 4.2  Remote Printer Printcap Entry
  161. X.KE
  162. X.NH 2
  163. Job Submission Controls
  164. X.PP
  165. The following fields are used to control job submission.
  166. X.NH 3
  167. Job Formats
  168. X.PP
  169. XEach job in a spool queue has an associated format,
  170. which is represented by a lower case letter\**.
  171. X.FS
  172. Actually,
  173. each file in the job can have a different format,
  174. but the current version of
  175. X.I lpr
  176. does not support this.
  177. X.FE
  178. There are several
  179. X.I lpr
  180. options such as 
  181. X.B -n,
  182. X.B -d,
  183. etc. which can be used to specify a limited set of formats,
  184. or the
  185. X.B \-Fx
  186. option can be used to specify any format.
  187. XFor example,
  188. the
  189. X.B \-Fn
  190. options specifies printing using the
  191. X.B n
  192. format,
  193. as below.
  194. X.ti +.5i
  195. X.L "ditroff -Tdumb -ms myfile | lpr -Fn -Premote"
  196. X.PP
  197. The format supported by a spool queue can be restricted by the
  198. X.B fx
  199. printcap field,
  200. which specifies the formats supported by the spool queue.
  201. In Figure 4.2,
  202. the
  203. X.B fx=ilpn
  204. field restricts formats to
  205. X.L i,
  206. X.L l,
  207. X.L p,
  208. and
  209. X.L n .
  210. Some formats require that files be printable
  211. X(i.e.- contain characters in the ASCII characters set,
  212. or other restrictions).
  213. By default,
  214. only the
  215. X.L f
  216. and
  217. X.L p
  218. formats are checked by
  219. X.I lpr
  220. for printable files.
  221. The
  222. X.B xt
  223. field is used to specify additional formats to be checked for printable files.
  224. X.NH 3
  225. Permissions Files
  226. X.PP
  227. XEach host has a
  228. printer permissions file which is checked by the
  229. PLP software to determine if actions requested by users or other hosts
  230. are permitted.
  231. In addition,
  232. the
  233. X.B xu
  234. X(check user) printcap
  235. field is used to specify an additional printer permission
  236. file which is checked for jobs in the spool queue.
  237. XFor example,
  238. the
  239. X.B xu=perms
  240. field causes the
  241. X.L perms
  242. file in the spool directory to be checked by
  243. X.I lpr
  244. when  the job is submitted.
  245. Similarly,
  246. the unspooling server process will also check this file
  247. when the job is finally printed.
  248. X.NH 3
  249. Prefilters
  250. X.PP
  251. It may be desirable to process or filter a job at the time that it is
  252. spooled.
  253. The program used to filter a job is specified by a field of the form
  254. X.BR X e=filtername ,
  255. or
  256. X.BR X e=filtername,y ,
  257. where
  258. X.L X
  259. is the original format,
  260. filtername is the pathname of a program,
  261. and
  262. X.L y
  263. is the result format.
  264. The default result format is
  265. X.L f .
  266. In addition,
  267. the
  268. X.I lpr
  269. X.B -p
  270. option specifies filtering with the
  271. X.I pr
  272. program;
  273. X.I lpr
  274. invokes the program specified by the
  275. X.BR pr= filtername
  276. X(default is
  277. X.L /bin/pr ).
  278. X.NH 3
  279. Job Size and Number of Copies
  280. X.PP
  281. The
  282. X.B mx
  283. X(maximum size)
  284. field is used to specify a maximum file size (in 1 Kbyte blocks)
  285. that can be spooled by
  286. X.I lpr.
  287. Jobs whose total size (including duplicates) exceeds this limit are
  288. not spooled.
  289. XFor example,
  290. the
  291. X.B mx#2000
  292. entry restricts jobs to a maximum of 2000 Kbytes.
  293. The
  294. X.B mc
  295. X(maximum copies) value specifies the maximum number of copies allowed
  296. and the
  297. X.B sc
  298. X(suppress copies) flag which suppresses multiple copies.
  299. X.NH 2
  300. Remote Printer Spool Queues
  301. X.PP
  302. Remote spool queues specify the remote host using the
  303. X.B rm
  304. field and the remote printer using the
  305. X.B rp
  306. field.
  307. The remote host addressing information is obtained using the
  308. X.IR gethostbynam (3R)
  309. facility.
  310. The remote printer must appear in the remote host's
  311. printcap data base.
  312. The following is an example of an entry for a remote spool queue
  313. that will be sent to host
  314. X.L umn-cs.cs.umn.edu ,
  315. and printed on the
  316. X.L lind33_decwriter
  317. printer.
  318. X.NH 2
  319. Local Printer Queues
  320. X.PP
  321. Unspooling from a local printer queue can be done in two manners:
  322. using the unspooling cababilies of the PLP server created for the
  323. queue by the lpd,
  324. or by having the server process invoke a special
  325. X.I "queue handler"
  326. process.
  327. The latter method is discussed later in this section.
  328. X.PP
  329. Associated with each local queue is an output device.
  330. These can be categorized as serial line
  331. or a specialized device with a non-serial interface.
  332. If a device is attached to a serial,
  333. the printcap can be used to specify how the serial line
  334. is to be conditioned in order to communicate with the printer.
  335. XFigure 4.3 is a sample printcap entry for a DecWriter III printer connected
  336. over a 1200 baud serial line.
  337. X.KF
  338. X.DT
  339. X.in +1i
  340. X.L
  341. X.nf
  342. X.SM
  343. X# Local Printer Entry
  344. X# Decwriter III in Lind 33
  345. X# Modified 11 Feb. 1988 Patrick Powell - added fx=n for ditroff, max 2 retries
  346. lind33_decwriter\||\|clunker\||\|Decwriter Model 3:\e
  347. X    :fx=ilpn:\e
  348. X    :sd=/usr/spool/lind33_decwriter:\e
  349. X    :xu=/usr/adm/perms/lind33_decwriter:\e
  350. X    :rt=2:\e
  351. X    :ne=/usr/local/lib/ddumb:\e
  352. X    :lp=/dev/ttyh8:rw:\e
  353. X    :br#1200:fs#06320:ty=-nl odd even -tabs tandem new:\e
  354. X    :lf=error:
  355. X.LG
  356. X.in -1i
  357. X.R
  358. X.ce
  359. XFigure 4.3 Printcap Entry for Serial Line Device
  360. X.LG
  361. X.KE
  362. X.NH 3
  363. Output Device Pathname
  364. X.PP
  365. The
  366. X.B lp
  367. X(line printer) parameter specifies a path name of the device that is
  368. to be opened and output sent to.
  369. The device is opened for writing only (default),
  370. or reading and writing if the
  371. X.B rw
  372. flag is  present.
  373. Note that there is no default value for the
  374. X.B lp
  375. field.
  376. If there is no physical device corresponding to the printer,
  377. then
  378. X.L "lp=/dev/null"
  379. should be used to specify a null device.
  380. X.NH 3
  381. Retry Limits
  382. X.PP
  383. The server will attempt to unspool and print a job for a limited number
  384. of times.
  385. The number of attempts is set by the
  386. X.B rt
  387. X(retry) parameter;
  388. the default value is 3,
  389. and a \-1 value specifies unlimited retries.
  390. X.NH 3
  391. Serial Line Characteristics
  392. X.PP
  393. Printers connected via a serial communication line
  394. must have the proper baud rate and line characteristics set.
  395. X.IP "Setting Bit (Baud) Rates" 3
  396. The
  397. X.B br
  398. parameter sets the bit (baud) rate for the tty line.
  399. XFor example,
  400. X300 means 300 BPI,
  401. X9600 means 9600 BPI.
  402. X.IP "Setting Line Characteristics" 3
  403. The
  404. X.B fs
  405. X(set bits) and
  406. X.B fc
  407. X(clear bits) parameter is used to set the serial line characteristics with the
  408. TIOCSETP
  409. X.IR ioctl \|(2)
  410. operation.
  411. The
  412. X.B fs
  413. and
  414. X.B fc
  415. set and clear bits in the line control status word returned by the
  416. TIOGSETP ioctl operation,
  417. which is then restored with the ioctl TIOCSETP.
  418. In the above example,
  419. X.B fs
  420. sets CRMOD,
  421. no parity,
  422. and XTABS (see
  423. X.IR tty \|(4)
  424. for details).
  425. The
  426. X.B xs
  427. and
  428. X.B xc
  429. are be used to set and clear local control  information
  430. using the TIOCCLGET and TIOCCLSET ioctl calls.
  431. X.IP "Alternate Method" 3
  432. In case you find the
  433. X.B fs ,
  434. X.B fc ,
  435. etc.,
  436. method of setting line characteristics embarassingly user-hostile,
  437. you can use the
  438. X.B ty
  439. X(stty) parameter,
  440. which uses (almost) the same keywords and options as the
  441. X.I stty (1)
  442. command.
  443. XFor example,
  444. X.L "ty=nl -odd -even -tabs" 
  445. can be used instead of the above
  446. X.B fs
  447. values.
  448. If you need tandem line control you
  449. may have to open the output device for reading and writing using the
  450. X.B rw
  451. field;
  452. this appears to be implementation dependent.
  453. The following 
  454. X.I stty
  455. options are recognized by the
  456. X.L ty
  457. field and have the same meaning as the
  458. X.I stty
  459. command as supplied for 4.3 UNIX,
  460. with some local modifications added from SUN Microsystems UNIX.
  461. X.DS
  462. X.DT
  463. X.nf
  464. X.L
  465. X.SM
  466. X.ta 16n +16n +16n +16n +16n +16n +16n +16n +16n 8i
  467. bs0    bs1    [-]cbreak    cooked    cr0    
  468. cr1    cr2    cr3    [-]decctlq    [-]echo    
  469. X[-]even    ff0    ff1    [-]lcase    [-]litout    
  470. nl0    nl1    nl2    nl3    [-]nl    
  471. X[-]noflsh    new    [-]nohang    old    [-]odd    
  472. X[-]raw    start    stop    tab0    tab1    
  473. tab2    [-]tabs    [-]tandem    tek    ti700    
  474. X[-]tilde    tn300    tty33    tty37    vt05    
  475. X.DE
  476. X.NH 2
  477. Job, Log, Lock, and Status Files
  478. X.PP
  479. The pathnames for all files associated with a spool queue are
  480. relative to the spool queue directory or are absolute paths.
  481. The following sections discuss the various files that are used
  482. for logging and status reporting.
  483. X.NH 3
  484. Job Files
  485. X.PP
  486. A job consists of a control file and a set of data files.
  487. Lines in the control file provide information about the user,
  488. the originating hosts,
  489. and the names of the data files,
  490. and the data file unspooling formats.
  491. A job control file name has the form:
  492. X.ti +5n
  493. X.B cf
  494. X.L "[Priority \- A-Z]"
  495. X.L "[Job Number \- 000-999]"
  496. X.L "[Host Name]"
  497. X.br
  498. X.ti +5n
  499. X.nf
  500. XExample:   cfA002attila.cs.umn.edu
  501. X.fi
  502. X.PP
  503. The first two letters identify the file as a control file,
  504. the next letter is the job's priority level,
  505. the next 3 digits are the job number,
  506. and the trailing characters are the host name.
  507. X.PP
  508. The priority level is set by the LPR process at the time that the job is
  509. submitted;
  510. the
  511. X.I lpc
  512. program can be used to upgrade a job's priority.
  513. The sequence number is obtained from a sequence file,
  514. which is locked when in use by
  515. X.I lpr
  516. in order to avoid duplicate sequence file names.
  517. XEach host has a unique sequence file,
  518. and the combination of host and sequence number should be unique.
  519. This is true as long as there are less than 1000 jobs outstanding in
  520. a queue.
  521. The host name currently used in the PLP software is currently set to be the
  522. network host name,
  523. as returned by
  524. X.IR gethostnam (3n).
  525. X.PP
  526. The data files associated with a job have files names with a similar form,
  527. as follows:
  528. X.ti +5n
  529. X.B df
  530. X.I [Sequence]
  531. X.I [Job Number]
  532. X.I [Host]
  533. X.br
  534. X.ti +5n
  535. X.nf
  536. XExample:  dfA002attila.cs.umn.edu, dfB002attila.cs.umn.edu
  537. X.fi
  538. X.PP
  539. The PLP software checks to ensure that control file and data file
  540. names have the same corresponding form.
  541. X.NH 3
  542. Control File
  543. X.PP
  544. A control file contains the information neccessary to print a job.
  545. XFigure 4.4 is a typical control file for a job.
  546. X.KF
  547. X.DT
  548. X.L
  549. X.SM
  550. X.nf
  551. X.in +1i
  552. Hattila.cs.umn.edu
  553. LPatrick Powell,L136
  554. Ppapowell
  555. N(stdin)
  556. J(stdin)
  557. CZ
  558. fdfA001attila
  559. UdfA001attila
  560. X.in -1i
  561. X.LG
  562. X.R
  563. X.ce
  564. XFigure 4.4. Job Control File Example
  565. X.KE
  566. X.PP
  567. The first character on each line is a flag character
  568. used to decode the remainder of the line.
  569. Upper case flags provide information or pass options for the
  570. printing actions.
  571. Lower  case characters are format indicators for specified data files.
  572. Note that there is a maximum limit on the size of each line of a
  573. control file.
  574. Table 4.1 lists the various control flags that are used
  575. in the control file.
  576. X.KS
  577. X.L
  578. X.SM
  579. X.TS
  580. tab(:) box center;
  581. l |l |l.
  582. XFlag:Parameter:Meaning
  583. X_
  584. C:class name:banner page
  585. H:host name:originating host
  586. I:indent:amount to indent output
  587. J:job name:banner page
  588. L:user name:name to print on banner
  589. M:mail:mail to user when done printing
  590. N:name:name of file in job
  591. P:Person:user's login name
  592. S:flag:no header request
  593. R:account:accounting information
  594. U:file name:remove file after we print it
  595. W:width:page width for PR
  596. XX:header:header title for PR
  597. Z:extra options:options for filters from lpr
  598. f:file name:ordinary file
  599. l:file name:text file with control chars
  600. p:file name:text file to print with pr(1)
  601. t:file name:troff(1) file
  602. n:file name:ditroff(1) file
  603. d:file name:dvi file
  604. g:file name:plot(1G) file
  605. v:file name:plain raster file
  606. c:file name:cifplot file
  607. X.TE
  608. X.LG
  609. X.R
  610. X.ce
  611. Table 4.1. Control File Flags
  612. X.KE
  613. X.NH 3
  614. Log File
  615. X.PP
  616. The spool queue server process writes error and informational messages to 
  617. the server log file,
  618. which is specified by the
  619. X.B lf
  620. field;
  621. the default log file is
  622. X.IR log .
  623. If the log file does not exist,
  624. then a dummy log file (/dev/null) is opened for logging purposes.
  625. Serious error message are written both to the log file and
  626. logged using the
  627. X.IR syslog (8)
  628. facility if it is available,
  629. or to
  630. X.L /dev/console
  631. if it is not.
  632. X.NH 3
  633. Lock File
  634. X.PP
  635. The spool queue lock file
  636. is used to control spooling and unspooling  operations.
  637. The lock file name is specified using the
  638. X.B lo
  639. X(lock file)
  640. field,
  641. and the default lock file name is
  642. X.IR lock .
  643. X.NH 3
  644. Status Files
  645. X.PP
  646. The server status file
  647. X(default file is
  648. X.IR status )
  649. contains status information reflecting the operations of the unspooling
  650. server processes.
  651. The
  652. X.B st
  653. X(status) field can be used to specify an explicity name for the status file.
  654. An additional printer status file can be specified with the
  655. X.B ps
  656. field.
  657. This file can be used by filter programs that generated additional status
  658. information that should be available for display.
  659. X.NH 2
  660. Multiple Servers
  661. X.PP
  662. If a spool queue has multiple printers or servers,
  663. the server names are specified by the 
  664. X.B sv
  665. X(servers) parameter,
  666. which contains a comma separated list of server names.
  667. XEach server name has a printcap entry,
  668. and each server entry has a corresponding
  669. X.B ss
  670. X(serves) field
  671. which contains the name of the spool queue served by the printer.
  672. XEach printer will use the spool queue directory specified by the
  673. X.B sd
  674. field of the spool queue that it serves.
  675. Note that this name corresponds to the name of the queue,
  676. not the spool directory.
  677. X.KF
  678. X.in +1i
  679. X.DT
  680. X.L
  681. X.SM
  682. X.nf
  683. X# Multiple servers
  684. fast:\e
  685. X    :sd=/usr/spool/fast:\e
  686. X    :sv=lp1,lp2:
  687. lp1:\e
  688. X    :ss=fast:\e
  689. X    :lo=lp1:lf=log1:st=st1:lp=/dev/lp1:
  690. lp2:\e
  691. X    :ss=fast:\e
  692. X    :lo=lp2:lf=log2:st=st2:lp=/dev/lp2:
  693. X.LG
  694. X.R
  695. X.in -1i
  696. X.ce
  697. XFigure 4.5  Multiple Server Printcap Entry
  698. X.KE
  699. X.PP
  700. In the example in Figure 4.5,
  701. the
  702. X.L lp1
  703. and
  704. X.L lp2
  705. printers are used to serve the
  706. X.L fast
  707. spool queue.
  708. Note that each of them explicitly specified the name of their lock and
  709. log files;
  710. as they will use the same queue directory the names of the lock,
  711. log,
  712. and status files must be different.
  713. END_OF_FILE
  714. if test 16280 -ne `wc -c <'doc/PLP/04.t'`; then
  715.     echo shar: \"'doc/PLP/04.t'\" unpacked with wrong size!
  716. fi
  717. # end of 'doc/PLP/04.t'
  718. fi
  719. if test -f 'src/lpd.c' -a "${1}" != "-c" ; then 
  720.   echo shar: Will not clobber existing file \"'src/lpd.c'\"
  721. else
  722. echo shar: Extracting \"'src/lpd.c'\" \(16723 characters\)
  723. sed "s/^X//" >'src/lpd.c' <<'END_OF_FILE'
  724. X/***************************************************************************
  725. X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
  726. X ***************************************************************************
  727. X * MODULE: lpd.c
  728. X * lpd main program
  729. X ***************************************************************************
  730. X * Revision History: Created Fri Jan  1 16:19:30 CST 1988
  731. X * $Log:    lpd.c,v $
  732. X * Revision 3.1  88/07/25  19:51:03  papowell
  733. X * Distribution version
  734. X * 
  735. X * 
  736. X * Revision 3.1  88/06/18  09:34:40  papowell
  737. X * Version 3.0- Distributed Sat Jun 18 1988
  738. X * 
  739. X * Revision 2.3  88/05/19  10:34:08  papowell
  740. X * Fixed open() calls to have a 0 parameter, ie: open(f, perms, 0), where needed
  741. X * 
  742. X * Revision 2.2  88/05/14  10:18:06  papowell
  743. X * Use long format for job file names;
  744. X * Added 'fd', no forward flag;
  745. X * Control file has to have hostname and origination agree.
  746. X * 
  747. X * Revision 2.1  88/05/09  10:08:46  papowell
  748. X * PLP: Released Version
  749. X * 
  750. X * Revision 1.9  88/04/27  20:26:17  papowell
  751. X * Modified to remove unused variables
  752. X * 
  753. X * Revision 1.8  88/04/26  15:51:31  papowell
  754. X * Added a Reapchild() call in cleanup and each time a server is started
  755. X * This should gather up the orphans that are left in some UNIX implementations
  756. X * 
  757. X * Revision 1.7  88/04/15  13:06:17  papowell
  758. X * Std_environ() call added, to ensure that fd 0 (stdin), 1 (stdout), 2(stderr)
  759. X * have valid file descriptors;  if not open, then /dev/null is used.
  760. X * 
  761. X * Revision 1.6  88/04/07  12:31:11  papowell
  762. X * 
  763. X * Revision 1.5  88/04/06  12:12:53  papowell
  764. X * Minor updates, changes in error message formats.
  765. X * Elimination of the AF_UNIX connections, use AF_INET only.
  766. X * Better error messages.
  767. X * 
  768. X * Revision 1.4  88/03/25  15:00:06  papowell
  769. X * Debugged Version:
  770. X * 1. Added the PLP control file first transfer
  771. X * 2. Checks for MX during file transfers
  772. X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
  773. X *     apparently they open files and then assume that they will stay
  774. X *     open.
  775. X * 4. Made sure that stdin, stdout, stderr was available at all times.
  776. X * 
  777. X * Revision 1.3  88/03/11  19:28:58  papowell
  778. X * Minor Changes, Updates
  779. X * 
  780. X * Revision 1.2  88/03/05  15:01:20  papowell
  781. X * Minor Corrections,  Lint Problems
  782. X * 
  783. X * Revision 1.1  88/03/01  11:08:37  papowell
  784. X * Initial revision
  785. X * 
  786. X ***************************************************************************/
  787. X#ifndef lint
  788. static char id_str1[] =
  789. X    "$Header: lpd.c,v 3.1 88/07/25 19:51:03 papowell Locked $ PLP Copyright 1988 Patrick Powell";
  790. X#endif lint
  791. X/***************************************************************************
  792. X * lpd -- line Printer daemon.
  793. X * 1. Get options 
  794. X * 2. Check for existence of another daemon and exit if it exists.
  795. X *    Note: this is done by locking a file
  796. X * 3. Initialize error logging
  797. X * 4. Start up all the queue handlers
  798. X * 5. Set up communication sockets
  799. X * 6. Loop forever doing:
  800. X *     Listen for a connection and perform the requested operation.
  801. X *     Operations are:
  802. X *        \1Printer\n
  803. X *            check the queue for jobs and print any found.
  804. X *        \2Printer\n
  805. X *            receive a job from another machine and queue it.
  806. X *        \3Printer [users ...] [jobs ...]\n
  807. X *            return the current state of the queue (short form).
  808. X *        \4Printer [users ...] [jobs ...]\n
  809. X *            return the current state of the queue (long form).
  810. X *        \5Printer Person [users ...] [jobs ...]\n
  811. X *            remove jobs from the queue.
  812. X *        \6Printer Person operation
  813. X *            enable/disable queueing, etc.
  814. X *    Note: a process is forked for each operation
  815. X *
  816. X * Strategy to maintain protected spooling area:
  817. X *    1. Spooling area is writable only by daemon and daemon group
  818. X *    2. lpr runs setuid ROOT; it uses the user UID
  819. X *       to access any file it wants (verifying things before
  820. X *       with access(2)) and sets ownership of files in the spooling
  821. X *     directory to be owner DAEMON, group DAEMON.
  822. X *    3. Critical files in spooling area are owned by DAEMON, group DAEMON
  823. X *       with mode 660.
  824. X *    4. lpd, lpq and lprm run setuid ROOT, and change group to DAEMON
  825. X *       Users can't get to anything w/o help of lpq and lprm programs.
  826. X *    5. LPD forks a server process to service each device
  827. X *       in the printcap entry.  These processes run setuid root
  828. X *       and setgrp to their PID.  They open neccessary files, and
  829. X *       fork "filter" processes to process the output.  Filter
  830. X *       processes run UID DAEMON.
  831. X *    6. The server process group is set to the main server PID.  This is used
  832. X *       by the LPRM program, which signals them with killpg(2)
  833. X *
  834. X * Error reporting:
  835. X *    lpd will open a standard logging file. If not already present,
  836. X *  no messages will be logged.  Critial messages will also be logged
  837. X *  using syslog(8).
  838. X *    The individual daemon processes will open log files also.  If
  839. X *    they cannot be opened, then no logging will be done for the process.
  840. X ***************************************************************************/
  841. X
  842. X#include "lp.h"
  843. X
  844. int    cleanup();        /* file system scullery maid, cleans up files */
  845. static int lpdpid;            /* lpd process id */
  846. X
  847. main(argc, argv)
  848. X    int argc;
  849. X    char **argv;
  850. X{
  851. X    int f;                        /* Acme Integer, Inc. strikes again */
  852. X    int finet;                    /* Internet socket */
  853. X    int defreadfds, readfds;    /* select()ed connections */
  854. X    int    req;                    /* request on this socket */
  855. X    int port_num;                /* used to get the inet server information */
  856. X    struct sockaddr_in sock_in, frominet; /* networking addresses */
  857. X    int fromlen;
  858. X    int nfds;                    /* accept return value */
  859. X
  860. X    /*
  861. X     * explicitly set umask
  862. X     */
  863. X    (void)umask(0);
  864. X
  865. X    /*
  866. X     * Set fd 0, 1, 2 to /dev/null if not open.  This protects against
  867. X     * being started in strange environments.
  868. X     */
  869. X    Std_environ();
  870. X    /*
  871. X     * we set up the alternate version if XPERIMENT is defined
  872. X     */
  873. X#    ifdef XPERIMENT
  874. X        Setup_test();
  875. X#    endif XPERIMENT
  876. X
  877. X    /*
  878. X     * process command line options
  879. X     */
  880. X    Getoptions(argc, argv);
  881. X
  882. X    /*
  883. X     * set up the pathnames for information files
  884. X     */
  885. X    Tailor_names();
  886. X    /*
  887. X     * check to see that the is not a spooler present.  The LO is used
  888. X     * when we exit to clean out the lock file information.
  889. X     * Note that we check first, as the initial process, and then we fork
  890. X     * another process.  This allows the output message to be printed
  891. X     * on the stdout device in a synchronous manner.
  892. X     */
  893. X    LO = Masterlock;
  894. X    if( (Lfd = Getlockfile(LO, &f, (char *)0, 0, (struct stat *)0)) == NULL ){
  895. X        (void)fprintf( stdout, "active LPD %d\n", f );
  896. X        exit(0);
  897. X    }
  898. X    (void)fclose(Lfd);
  899. X    /* We can let parent die */
  900. X    if ((f = fork()) < 0 ){
  901. X        logerr_die( XLOG_INFO, "lpd: fork failed" );
  902. X    } else if( f ){
  903. X        exit(0);
  904. X    }
  905. X    /*
  906. X     * We can now start logging
  907. X     */
  908. X    Setuplog(Lpdlogf, 0 );
  909. X    /*
  910. X     * Note that we do the locking at this point, as the lock may not
  911. X     * continue across a fork.
  912. X     */
  913. X    if((Lfd=Getlockfile(LO,&f,(char *)0, 0, (struct stat *)0)) == NULL){
  914. X        log(XLOG_INFO, "active LPD %d\n", f );
  915. X        exit(0);
  916. X    }
  917. X    /*
  918. X     * save the PID of the LPD process;  it will be useful later
  919. X     */
  920. X    lpdpid = getpid();
  921. X    /*
  922. X     * drop the control tty.  This prevents the user which started
  923. X     * LPD from inadvertenly generation a signal and clobbering LPD
  924. X     */
  925. X    if( (f = open("/dev/tty", O_RDWR, 0) ) >= 0) {
  926. X        (void)ioctl(f, TIOCNOTTY, (struct sgttyb *)0);
  927. X        (void)close(f);
  928. X    }
  929. X    /* set pgrp to pid, so we can kill our kids later */
  930. X    (void)setpgrp(0, lpdpid);
  931. X
  932. X    /* put PID and time started in the lockfile */
  933. X    Setlockfile(LO, Lfd, lpdpid,Time_str());
  934. X
  935. X    /*
  936. X     * set up signals; note that SIGPIPE may not be needed, but it
  937. X     * never hurts to make sure.
  938. X     */
  939. X    (void)signal(SIGCHLD, Reapchild);
  940. X    (void)signal(SIGPIPE, SIG_IGN);
  941. X    (void)signal(SIGHUP, cleanup);
  942. X    (void)signal(SIGINT, cleanup);
  943. X    (void)signal(SIGQUIT, cleanup);
  944. X    (void)signal(SIGTERM, cleanup);
  945. X    /*
  946. X     * Restart all the Printers.
  947. X     */
  948. X    Startup();
  949. X
  950. X    /*
  951. X     * Set up the IPC.  This is very ugly,  and is stolen directly from
  952. X     * the original LPD code and the 4.3 BSD Interprocess Communication
  953. X     * Tutorial, which seems to have borrowed it from RLOGIN, etc.
  954. X     *
  955. X     * Set up INET socket
  956. X     */
  957. X    if( ( finet = socket(AF_INET, SOCK_STREAM, 0) ) < 0 ){
  958. X        logerr_die( XLOG_CRIT,"lpd: cannot create AF_INET socket");
  959. X    }
  960. X    port_num = Link_port_num();
  961. X    /*
  962. X     * zero the sockaddr structure before using it
  963. X     */
  964. X    bzero( (char *)&sock_in, sizeof(sock_in) );
  965. X    /*
  966. X     * the INADDR_ANY will allow different nets to be used
  967. X     */
  968. X    sock_in.sin_family = AF_INET;
  969. X    sock_in.sin_addr.s_addr = INADDR_ANY;
  970. X    sock_in.sin_port = port_num;
  971. X    if (bind(finet, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0) {
  972. X        logerr_die( XLOG_CRIT,"lpd: bind failed internet domain");
  973. X    } 
  974. X    if( listen(finet, 5) < 0 ){
  975. X        logerr_die( XLOG_CRIT, "lpd: listen failed internet domain" );
  976. X    }
  977. X    if(Debug>2)log( XLOG_DEBUG, "lpd: AF_INET socket %d", finet );
  978. X
  979. X    /*
  980. X     * Set up the sockets on which we will do an accept
  981. X     */
  982. X    defreadfds = (1 << finet);
  983. X
  984. X    /*
  985. X     * Main loop: accept, do a request, continue.
  986. X     */
  987. X    for (;;) {
  988. X        /*
  989. X         * Defensive Coding a la Brain Damaged Unix Implementations
  990. X         * A particular UNIX implementation will not cause SIGCHLD
  991. X         * signals in a particularly reliable way.  Apparently if there
  992. X         * is a SIGCHLD while it is masked off (error printing does this)
  993. X         * IT IS IGNORED!!!  So we put a Reapchild() here;  at least
  994. X         * we will gather them up each time we start a server
  995. X         */
  996. X        Reapchild();
  997. X        if(Debug>2)log( XLOG_DEBUG,"lpd: starting select");
  998. X
  999. X        readfds = defreadfds;
  1000. X        nfds = select(20, (fd_set *)&readfds, (fd_set *)0, (fd_set *)0,
  1001. X            (struct timeval *)0); /* wait patiently */
  1002. X        if (nfds <= 0) {
  1003. X            if (nfds < 0 && errno != EINTR) {
  1004. X                logerr_die( XLOG_CRIT,"lpd: select error");
  1005. X            }
  1006. X            continue;
  1007. X        }
  1008. X        /*
  1009. X         * incredible, but true: could have NO requests
  1010. X         */
  1011. X        if( readfds ){
  1012. X            if(Debug>2)log( XLOG_DEBUG,"lpd: doing select 0x%x", readfds );
  1013. X            /* get the socket causing the select */
  1014. X            fromlen = sizeof(frominet);
  1015. X            req = accept(finet, (struct sockaddr *)&frominet, &fromlen);
  1016. X            if (req < 0) {
  1017. X                logerr( XLOG_CRIT,"lpd: finet accept");
  1018. X                continue;
  1019. X            }
  1020. X            if (!From_host(&frominet)){
  1021. X                logerr(XLOG_INFO,"lpd: From_host failed");
  1022. X                continue;
  1023. X            }
  1024. X            /*
  1025. X             * Fork process to handle activity requested
  1026. X             */
  1027. X            (void) fflush(stdout);
  1028. X            (void) fflush(stderr);
  1029. X            if ((f = fork()) == 0) {    /* daughter */
  1030. X                (void) fclose(Lfd);
  1031. X                Lfd = NULL;
  1032. X                if( dup2(req, 1) < 0 ){
  1033. X                    logerr_die( XLOG_CRIT, "lpd: dup2 for server failed" );
  1034. X                }
  1035. X                (void) close(req);
  1036. X                (void) close(finet);
  1037. X                servicereq();
  1038. X                exit(0);
  1039. X            } else if( f < 0 ){
  1040. X                logerr( XLOG_CRIT, "lpd: fork failed" );
  1041. X            }
  1042. X            (void) close(req);
  1043. X        }
  1044. X    }
  1045. X}
  1046. X
  1047. X/***************************************************************************
  1048. X * Getoptions(argv, argc)
  1049. X *    char **argv; int argc;
  1050. X * Purpose:
  1051. X * extracts options and arguments from command line using Getopt(2)
  1052. X * Side Effects:
  1053. X *    -X : calls setup_test() to modify defaults 
  1054. X *  -D nn : sets Debug level
  1055. X *  -L file : sets log file
  1056. X ***************************************************************************/
  1057. Getoptions(argc, argv)
  1058. X    int argc;
  1059. X    char **argv;
  1060. X{
  1061. X    int c;
  1062. X    while ((c = Getopt(argc, argv, "XD:L:")) != EOF){
  1063. X        switch(c){
  1064. X            case 'X':    /* test version */
  1065. X#            ifdef DEBUG
  1066. X                Setup_test();
  1067. X                Tailor_names();
  1068. X#            else
  1069. X                (void)fprintf( stderr, "%s: -X not allowed\n", Name );
  1070. X                exit(1);
  1071. X#            endif DEBUG
  1072. X                break;
  1073. X            case 'D':        /* turn on Debugging */
  1074. X                if( Optarg == NULL ){
  1075. X                    exit(1);
  1076. X                }
  1077. X                Debug= atoi( Optarg );
  1078. X                break;
  1079. X            case 'L':
  1080. X                if( Optarg == NULL ){
  1081. X                    exit(1);
  1082. X                }
  1083. X                (void)strcpy(Lpdlogf, Optarg);
  1084. X                break;
  1085. X            default:
  1086. X                exit(1);
  1087. X        }
  1088. X    }
  1089. X    if( Optind < argc ){
  1090. X        (void)fprintf( stderr, "%s: extra argument %s\n", Name, argv[Optind] );
  1091. X        exit(1);
  1092. X    }
  1093. X}
  1094. X/***************************************************************************
  1095. X * Setuplog( char *logfile, int saveme )
  1096. X * Purpose: to set up a standard error logging environment
  1097. X * saveme will prevent stdin from being clobbered
  1098. X *     1.  open /dev/null on fd 0;
  1099. X *   2.  if saveme not 1, dup 0 to fd 1
  1100. X *   3.  If logfile is "-" or NULL, output file is alread opened
  1101. X *   4.  Open logfile; if unable to, then open /dev/null for output
  1102. X ***************************************************************************/
  1103. Setuplog( logfile, saveme )
  1104. X    int saveme;
  1105. X    char *logfile;
  1106. X{
  1107. X    int fd;
  1108. X
  1109. X    /*
  1110. X     * we want 0 (stdin) to be /dev/null
  1111. X     */
  1112. X    (void)fflush(stdout);
  1113. X    (void)fflush(stderr);
  1114. X    (void)close(0);
  1115. X    if( (fd = open( "/dev/null", O_RDWR, 0 )) != 0 ){
  1116. X        logerr_die( XLOG_CRIT, "Setuplog: /dev/null opened as %d", fd);
  1117. X    }
  1118. X    /*
  1119. X     * do the dup if necessary
  1120. X     */
  1121. X    if( saveme != 1 ){
  1122. X        (void)close(1);
  1123. X        if( dup2(0,1) < 0){
  1124. X            logerr_die( XLOG_CRIT, "Setuplog: dup2 failed" );
  1125. X        }
  1126. X    }
  1127. X    if(Debug>4)log(XLOG_DEBUG,"Setuplog: opening log file %s", logfile );
  1128. X    /*
  1129. X     * open logfile;  if it is "-", use stderr
  1130. X     */
  1131. X    if( logfile && *logfile && strcmp(logfile, "-") ){
  1132. X        if( (fd = open_daemon(logfile, O_WRONLY|O_APPEND, 0)) < 0 ){
  1133. X            if(Debug>0)logerr(XLOG_DEBUG,"cannot open logfile %s",logfile);
  1134. X            /* write to stderr if debugging, /dev/null otherwise */
  1135. X            if(Debug>0){
  1136. X                fd = 2;
  1137. X            } else {
  1138. X                fd = 0;
  1139. X            }
  1140. X        }
  1141. X        if( fd != 2 && dup2(fd,2) < 0){
  1142. X            logerr_die( XLOG_CRIT, "Setuplog: dup2 failed" );
  1143. X        }
  1144. X        if( fd > 2 ){
  1145. X            (void)close(fd);
  1146. X        }
  1147. X    }
  1148. X}
  1149. X
  1150. X/*
  1151. X *    When a child dies, it will generate a SIGCHLD,
  1152. X *    and Reapchild() will lay the body to rest with a wait3().
  1153. X */
  1154. Reapchild()
  1155. X{
  1156. X    int pid;
  1157. X    union wait status;
  1158. X
  1159. X    while ((pid =wait3(&status, WNOHANG, (struct rusage *)0)) > 0){
  1160. X        if(Debug>3)log(XLOG_DEBUG,"process %d, status %s", pid,
  1161. X            Decode_status( &status ) );
  1162. X    }
  1163. X}
  1164. X
  1165. X/*
  1166. X *    Clean up; kill off children and remove sockets and files
  1167. X */
  1168. cleanup()
  1169. X{
  1170. X    int pid;
  1171. X
  1172. X    (void)sigblock(sigmask(SIGCHLD)|sigmask(SIGHUP)
  1173. X        |sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM));
  1174. X    if( Lfd ){
  1175. X        Closelockfile(LO, Lfd);
  1176. X        Lfd = 0;
  1177. X    }
  1178. X    pid = getpid();
  1179. X    if(Debug>0)log( XLOG_DEBUG, "cleanup" );
  1180. X    /*
  1181. X     * if the recfiles server, clean up files
  1182. X     */
  1183. X    rm_recfiles();
  1184. X    if( pid == getpgrp(0) ){
  1185. X        /* Kill children too */
  1186. X        (void)kill(0, SIGINT);
  1187. X        (void)kill(0, SIGCONT);
  1188. X        if( pid == lpdpid){
  1189. X            log(XLOG_CRIT, "cleanup: lpd process %d terminating", pid);
  1190. X        }
  1191. X    }
  1192. X
  1193. X    /*
  1194. X     * Wait for children until the bodies rot
  1195. X     */
  1196. X    Reapchild();
  1197. X    exit(Errorcode);
  1198. X}
  1199. X
  1200. X/***********************************************************************
  1201. X * From_host( sockaddr_in *f )
  1202. X *   This routine will extract the remote Host Name and remote Host
  1203. X *   port number information.  The remote process must connect on
  1204. X *   a port in a "reserved" range;  these are reserved for use
  1205. X *   by priviledged processes (setuid/uid root).
  1206. X * Side Effects: sets the "from" string to the Name of the remote Host.
  1207. X * Returns: 1 if privileged and successful, 0 if not.
  1208. X * NOTE: this code was derived from the orignal 4.2BSD LPR source,
  1209. X *   the 4.3BSD Interprocess Communications Tutorials, and other places.
  1210. X ***********************************************************************/
  1211. X
  1212. XFrom_host(f)
  1213. X    struct sockaddr_in *f;
  1214. X{
  1215. X    struct hostent *hp;            /* Host entry */
  1216. X    extern char *inet_ntoa();    /* inet address translation routine */
  1217. X    int port;                    /* from port number */
  1218. X
  1219. X    port = ntohs(f->sin_port);
  1220. X    if (f->sin_family != AF_INET ){
  1221. X        log(XLOG_CRIT,
  1222. X        "From_host: malformed from address, family %d, not AF_INET %d",
  1223. X        f->sin_family, AF_INET);
  1224. X        return(0);
  1225. X    }
  1226. X    if ( port > Maxportno){
  1227. X        log(XLOG_CRIT,"From_host: from port %d, max is %d",port, Maxportno);
  1228. X        return(0);
  1229. X    }
  1230. X    hp = gethostbyaddr((char *)(&f->sin_addr), sizeof(struct in_addr),
  1231. X                f->sin_family);
  1232. X    if (hp == 0){
  1233. X        logerr(XLOG_INFO,"From_host: Host Name for address '%s' unknown",
  1234. X            inet_ntoa(f->sin_addr));
  1235. X        return(0);
  1236. X    }
  1237. X    if( (From = malloc( (unsigned)strlen(hp->h_name)+1 )) == 0 ){
  1238. X        logerr_die(XLOG_CRIT,"malloc failed in From_host" );
  1239. X    }
  1240. X    (void)strcpy(From, hp->h_name);
  1241. X    return(1);
  1242. X}
  1243. X
  1244. X/************************************************************************
  1245. X * Startup()
  1246. X * start queue servers
  1247. X ************************************************************************/
  1248. static
  1249. Startup()
  1250. X{
  1251. X    int pid;
  1252. X    char **pr;
  1253. X    union wait status;
  1254. X
  1255. X    /*
  1256. X     * Restart the server.
  1257. X     * Start scanning the printcap for entries
  1258. X     */
  1259. X    if ((pid = fork()) < 0) {
  1260. X        logerr( XLOG_CRIT,"server for %s: cannot fork", Printer);
  1261. X    } else if(pid == 0 ) {
  1262. X        if(Debug>4)log( XLOG_DEBUG, "starting servers" );
  1263. X        for( pr = All_printers(); *pr; ++pr ){
  1264. X            Printer = *pr;
  1265. X            if ((pid = fork()) < 0) {
  1266. X                logerr( XLOG_CRIT,"server for %s: cannot fork", Printer);
  1267. X                continue;
  1268. X            } else if(pid == 0) {
  1269. X                if(Debug>2)log( XLOG_DEBUG, "started %s server, pid %d",
  1270. X                    Printer,getpid());
  1271. X                (void)fclose(Lfd);
  1272. X                Lfd = 0;
  1273. X                Startprinter();
  1274. X                exit(0);
  1275. X            }
  1276. X            sleep( (unsigned)1 );
  1277. X        }
  1278. X        while ((pid=wait(&status)) > 0){
  1279. X            if(Debug>3)log(XLOG_DEBUG,"process %d, status %s", pid,
  1280. X                Decode_status( &status ) );
  1281. X        }
  1282. X        if(Debug>4)log( XLOG_DEBUG, "all servers done" );
  1283. X        exit(0);
  1284. X    }
  1285. X}
  1286. END_OF_FILE
  1287. if test 16723 -ne `wc -c <'src/lpd.c'`; then
  1288.     echo shar: \"'src/lpd.c'\" unpacked with wrong size!
  1289. fi
  1290. # end of 'src/lpd.c'
  1291. fi
  1292. if test -f 'src/printcap.c' -a "${1}" != "-c" ; then 
  1293.   echo shar: Will not clobber existing file \"'src/printcap.c'\"
  1294. else
  1295. echo shar: Extracting \"'src/printcap.c'\" \(16988 characters\)
  1296. sed "s/^X//" >'src/printcap.c' <<'END_OF_FILE'
  1297. X/***************************************************************************
  1298. X * U. Minnesota LPD Software * Copyright 1987, 1988, Patrick Powell
  1299. X ***************************************************************************
  1300. X * MODULE: printcap.c
  1301. X * Super slick printcap information extraction routines.
  1302. X * Note: these routines were designed after studying the CURSES routines,
  1303. X * which are HORRIBLE...  These may not be any better, but at least
  1304. X * they are a lot faster.
  1305. X * The design is based on the assumption that we will know all the printcap
  1306. X * entries that we want to know ahead of time,  and get them "en mass".
  1307. X * Further more,  these can be placed in sorted order.
  1308. X * Thus,  given a set of these,  we scan the printcap entry, extract the
  1309. X * field name,  determine if in the list,  and then update the information
  1310. X * only if in list.  Result:
  1311. X * 1.  1 scan of printcap file per lookup of printer name, finds K entries
  1312. X * 2.  Log N order search of list,  for a K log N search of printcap
  1313. X ***************************************************************************
  1314. X * Revision History: Created Sun Jan  3 19:12:33 CST 1988
  1315. X * $Log:    printcap.c,v $
  1316. X * Revision 3.1  88/06/18  09:35:22  papowell
  1317. X * Version 3.0- Distributed Sat Jun 18 1988
  1318. X * 
  1319. X * Revision 2.1  88/05/09  10:09:50  papowell
  1320. X * PLP: Released Version
  1321. X * 
  1322. X * Revision 1.6  88/04/06  12:13:22  papowell
  1323. X * Minor updates, changes in error message formats.
  1324. X * Elimination of the AF_UNIX connections, use AF_INET only.
  1325. X * Better error messages.
  1326. X * 
  1327. X * Revision 1.5  88/03/25  15:01:10  papowell
  1328. X * Debugged Version:
  1329. X * 1. Added the PLP control file first transfer
  1330. X * 2. Checks for MX during file transfers
  1331. X * 3. Found and fixed a mysterious bug involving the SYSLOG facilities;
  1332. X *     apparently they open files and then assume that they will stay
  1333. X *     open.
  1334. X * 4. Made sure that stdin, stdout, stderr was available at all times.
  1335. X * 
  1336. X * Revision 1.4  88/03/12  10:04:29  papowell
  1337. X * Minor Bug Fixes,  Better Error Message Logging
  1338. X * 
  1339. X * Revision 1.3  88/03/11  19:29:48  papowell
  1340. X * Minor Changes, Updates
  1341. X * 
  1342. X * Revision 1.2  88/03/05  15:01:37  papowell
  1343. X * Minor Corrections,  Lint Problems
  1344. X * 
  1345. X * Revision 1.1  88/03/01  11:09:04  papowell
  1346. X * 
  1347. X ***************************************************************************/
  1348. X#ifndef lint
  1349. static char id_str1[] =
  1350. X    "$Header: printcap.c,v 3.1 88/06/18 09:35:22 papowell Exp $ PLP Copyright 1988 Patrick Powell";
  1351. X#endif lint
  1352. X
  1353. X#include "lp.h"
  1354. X/*
  1355. X * char **All_printers()
  1356. X *  returns a pointer to an array of strings which contains the
  1357. X *  names of the printers in the printcap file.  The list is terminated
  1358. X *  with a 0 (NIL) entry.
  1359. X *
  1360. X * char *Get_first_printer()
  1361. X *  returns a pointer to the first printer in the printcap file.
  1362. X *
  1363. X * init_pc_entry( PC_ENTRY pc[]; int len)
  1364. X *     set the default values for variables listed in pc[]
  1365. X *
  1366. X * The following routines return 1 if printcap entry found, 0 otherwise.
  1367. X *     of the printcap entry.
  1368. X *
  1369. X * int Set_pc_entry(char *printer; PC_ENTRY pc[]; int len)
  1370. X *     set the values of the variables listed in pc from the printcap
  1371. X *  1. scan the printcap file and extract the printcap entry for
  1372. X *     printer.  Make a copy of the entry.
  1373. X *  2. find the name of the printer (first entry) and set the First_name
  1374. X *     variable to point to it.
  1375. X *  3. Initialize the printcap variables according to the defaults in
  1376. X *     the "pc" array.
  1377. X *  4. Scan the printcap entry, and for each tagged field in the entry:
  1378. X *     a. search the "pc" array for the tag using a binary search.
  1379. X *     b. if the entry is found, determine the type;
  1380. X *     c. if an integer, convert from string to integer and update variable.
  1381. X *     d. if a flag, set or clear it
  1382. X *     e. if a string, copy into a buffer and set the string variable to point
  1383. X *        to it.
  1384. X * int Get_pc_entry(char *printer; PC_ENTRY pc[]; int len)
  1385. X *     call init_pc_entry() and then Set_pc_entry.
  1386. X */
  1387. X
  1388. static    FILE *pfp = NULL;    /* printcap data base file pointer */
  1389. X/*
  1390. X * The capbuf buffer holds printcap, the strbuf holds strings
  1391. X */
  1392. static    char    capbuf[BUFSIZ]; /* buffer to hold printcap */
  1393. static    char    strbuf[BUFSIZ]; /* buffer to hold strings */
  1394. static  char   *strloc;        /* next string location */
  1395. X
  1396. static  char *pr_names;        /* buffer to hold them */
  1397. static  int max_names;        /* max number of names in printcap */
  1398. static  char **pr_list;        /* printer names in printcap */
  1399. static  int max_list;        /* size of list */
  1400. X/*
  1401. X * All_printers: reads the /etc/printcap file, and forms
  1402. X * a list of all the printer names in the file.
  1403. X */
  1404. X
  1405. char **
  1406. All_printers()
  1407. X{
  1408. X    int i;        /* ACME Integer, Inc. */
  1409. X    char *bp, *cp, *ep;    /* ACME Pointer, Inc. */
  1410. X
  1411. loop:
  1412. X    if(Debug>5)log(XLOG_DEBUG,"All_printers: max_names %d, max_list %d",
  1413. X        max_names, max_list);
  1414. X    if( max_names == 0 ){
  1415. X        max_list = BUFSIZ;
  1416. X        max_names = MAXPCNAMES;
  1417. X        pr_names = (char *)malloc((unsigned)(max_list));
  1418. X        if( pr_names == NULL ){
  1419. X            logerr_die( XLOG_INFO, "All_printers: malloc failed" );
  1420. X        }
  1421. X        pr_list = (char **)malloc((unsigned)(max_names*sizeof(char *)));
  1422. X        if( pr_list == NULL ){
  1423. X            logerr_die( XLOG_INFO, "All_printers: malloc failed" );
  1424. X        }
  1425. X    }
  1426. X    i = 0;
  1427. X    bp = pr_names;
  1428. X    ep = pr_names + max_list;
  1429. X    closeprent();
  1430. X    while( pc_entry( capbuf, sizeof(capbuf)) ){
  1431. X        init_pc_entry( Status_pc_vars, Status_pc_len);
  1432. X        getpcvars(Status_pc_vars, Status_pc_len);
  1433. X            /* get the printcap variable values */
  1434. X        if(Debug>6)show_pc( Status_pc_vars, Status_pc_len);
  1435. X        if( SD != 0 && *SD != 0 ){
  1436. X            pr_list[i] = bp;
  1437. X            if( (cp = index( capbuf, '|')) == 0 ){
  1438. X                cp = index( capbuf, ':');
  1439. X            }
  1440. X            *cp = 0;
  1441. X            bp = estrcp( bp, capbuf, ep );
  1442. X            if( bp ){
  1443. X                ++bp;
  1444. X            }
  1445. X            ++i;
  1446. X            if( i >= max_names || bp == 0 || bp >= ep){
  1447. X                if(Debug>1)log(XLOG_DEBUG,"All_printers: need more space" );
  1448. X                max_list += BUFSIZ;
  1449. X                max_names += MAXPCNAMES;
  1450. X                pr_names = (char *)realloc(pr_names,(unsigned)(max_list));
  1451. X                if( pr_names == NULL ){
  1452. X                    logerr_die( XLOG_INFO, "All_printers: malloc failed" );
  1453. X                }
  1454. X                pr_list = (char **)
  1455. X                    realloc((char *)pr_list,(unsigned)max_names*sizeof(char *));
  1456. X                if( pr_list == 0 ){
  1457. X                    logerr_die( XLOG_INFO, "All_printers: malloc failed" );
  1458. X                }
  1459. X                goto loop;
  1460. X            }
  1461. X            pr_list[i] = 0;
  1462. X        }
  1463. X    }
  1464. X    closeprent();
  1465. X    if(Debug>5){
  1466. X        int j;
  1467. X        (void)fprintf(stdout,"All_printers: %d,", i);
  1468. X        for( j = 0; j < i; ++j ){
  1469. X            (void)fprintf(stdout," %s", pr_list[j] );
  1470. X        }
  1471. X        (void)fprintf(stdout,"\n"); 
  1472. X    }
  1473. X    return( pr_list );
  1474. X}
  1475. X
  1476. X/*
  1477. X * First_printer: reads the /etc/printcap file
  1478. X * and finds the first printer name
  1479. X */
  1480. char *
  1481. XFirst_printer()
  1482. X{
  1483. X    char *bp;
  1484. X    static char first[PRNAMELEN+1];
  1485. X
  1486. X    closeprent();
  1487. X    while( pc_entry( capbuf, sizeof(capbuf)) ){
  1488. X        init_pc_entry( Status_pc_vars, Status_pc_len);
  1489. X        getpcvars(Status_pc_vars, Status_pc_len);
  1490. X            /* get the printcap variable values */
  1491. X        if(Debug>6)show_pc( Status_pc_vars, Status_pc_len);
  1492. X        if( SD != 0 && *SD != 0 ){
  1493. X            if( (bp = index( capbuf, '|')) == 0 ){
  1494. X                bp = index( capbuf, ':');
  1495. X            }
  1496. X            *bp = 0;
  1497. X            (void)strcpy( first, capbuf );
  1498. X            closeprent();
  1499. X            if(Debug>4)log(XLOG_DEBUG,"First_printer: %s", first );
  1500. X            return( first );
  1501. X        }
  1502. X    }
  1503. X    fatal( XLOG_INFO, "First_printer: bad printcap %s", Printcap );
  1504. X    return( (char *)0 );
  1505. X}
  1506. X
  1507. X/*
  1508. X * pc_entry(char *tb; int size)
  1509. X * Used to scan the printcap file.
  1510. X * finds the next entry in the printcap file;
  1511. X * it places it in the tb buffer, and returns
  1512. X * 1 if entry found, 0 otherwise
  1513. X */
  1514. static int
  1515. pc_entry(tb, size)
  1516. X    char *tb;
  1517. X    int size;
  1518. X{
  1519. X    char buf[BUFSIZ];    /* read buffer */
  1520. X    char *bp, *cp, *ep;    /* next read position */
  1521. X    int l;                /* line length */
  1522. X
  1523. X    /*
  1524. X     * get the printcap file
  1525. X     */
  1526. X    if (pfp == NULL ){
  1527. X        if( (pfp = fopen_daemon(Printcap, "r")) == NULL){
  1528. X            logerr_die( XLOG_CRIT, "open %s failed", Printcap );
  1529. X        }
  1530. X    }
  1531. X    /*
  1532. X     * We read a single printcap entry into the bp buffer.
  1533. X     * We scan lines until we hit one that does not start with a
  1534. X     * # or is a blank line.
  1535. X     */
  1536. X    while( bp = fgets( buf, sizeof(buf), pfp) ){
  1537. X        if( buf[0] != '#' && buf[0] != '\n' ){
  1538. X            break;
  1539. X        }
  1540. X    }
  1541. X    if( bp == 0 ){
  1542. X        return(0);
  1543. X    }
  1544. X    /*
  1545. X     * read the file in, stripping off the \<newlines>
  1546. X     */
  1547. X    ep = tb + size;
  1548. X    bp = tb;
  1549. X    do{
  1550. X        cp = index( buf, '\n' );
  1551. X        if( cp == 0 ){
  1552. X            fatal( XLOG_INFO, "bad line in printcap file '%s'", buf );
  1553. X        }
  1554. X        *cp = 0;
  1555. X        if( cp != buf ){
  1556. X            l = (cp[-1] == '\\');
  1557. X            if( l ){
  1558. X                cp[-1] = 0;
  1559. X            }
  1560. X        } else {
  1561. X            l = 0;
  1562. X        }
  1563. X        bp = estrcp( bp, buf, ep );
  1564. X        if( bp == 0 ){
  1565. X            fatal( XLOG_INFO, "printcap entry too long %s", tb );
  1566. X        }
  1567. X        if( l == 0 ){
  1568. X            if(Debug>8)log(XLOG_DEBUG,"pc_entry: %s", tb );
  1569. X            return( 1 );
  1570. X        }
  1571. X    } while( fgets( buf, sizeof(buf), pfp) );
  1572. X    log( XLOG_INFO,"bad termcap entry %s", tb);
  1573. X    return( 0 );
  1574. X}
  1575. X
  1576. X/*
  1577. X * init_pc_entry(  PC_ENTRY pc_vars[]; int pc_len)
  1578. X *   initialize the variables in pc_vars with their default values.
  1579. X *   sets the strloc to start of string buffer.
  1580. X */
  1581. init_pc_entry( pc_vars, pc_len)
  1582. X    PC_ENTRY pc_vars[];    /* the printcap vars used for the printer */
  1583. X    int pc_len;    /* the number of printcap vars */
  1584. X{
  1585. X    int i;        /* ACME Integer, Inc. */
  1586. X    /*
  1587. X     * intialize with the defaults
  1588. X     */
  1589. X    First_name = 0;
  1590. X    for( i = 0; i < pc_len; ++ i ){
  1591. X        switch( pc_vars[i].kind ){
  1592. X        case PC_NUM:
  1593. X            *(int *)pc_vars[i].var = pc_vars[i].idefault;
  1594. X            break;
  1595. X        case PC_FLAG:
  1596. X            *(int *)pc_vars[i].var = pc_vars[i].idefault;
  1597. X            break;
  1598. X        case PC_STRING:
  1599. X            *(char **)pc_vars[i].var = pc_vars[i].sdefault;
  1600. X            break;
  1601. X        default:
  1602. X            fatal( XLOG_INFO,"getpcvars; bad kind %d in table entry %d",
  1603. X                pc_vars[i].kind,i);
  1604. X        }
  1605. X    }
  1606. X    strloc = strbuf;
  1607. X    if(Debug>7)log(XLOG_DEBUG,"init_pc_entry: done");
  1608. X}
  1609. X
  1610. X/*
  1611. X * char *fix_str( char *str )
  1612. X *   makes a copy of str in strbuf and returns the start
  1613. X * Side Effect: modifies strloc
  1614. X */
  1615. static char *
  1616. fix_str(str)
  1617. X    char *str;
  1618. X{
  1619. X    char *cp;    /* ACME Pointers, Inc. */
  1620. X    int l;
  1621. X
  1622. X    l = strlen(str);
  1623. X    cp = strloc;
  1624. X    if( ! ((strloc+l) < (strbuf+sizeof(strbuf))) ){
  1625. X        fatal( XLOG_INFO,"fix_str: string table overflow" );
  1626. X    }
  1627. X    (void)strcpy(strloc,str);
  1628. X    strloc += (l+1);
  1629. X    return(cp);
  1630. X}
  1631. X
  1632. X/*
  1633. X * Set_pc_entry( char *name; PC_ENTRY pc_vars[]; int pc_len)
  1634. X *  1. gets the "name" printcap entry; capbuf[] holds it;
  1635. X *     strbuf is used to hold string constants.
  1636. X *  2. checks to see if the entry has a continuation (tc= field),
  1637. X *     and gets the continuations
  1638. X *  3. calls getpcvar routine to find fields in the printcap entry and set 
  1639. X *     variables.
  1640. X * Returns: 1 if successful, and printcap entry found; 0 otherwise.
  1641. X * Side Effect: sets the First_name to the first name in the printcap entry.
  1642. X */
  1643. Set_pc_entry(name, pc_vars, pc_len)
  1644. X    char *name;        /* the printer name */
  1645. X    PC_ENTRY pc_vars[];    /* the printcap vars used for the printer */
  1646. X    int pc_len;    /* the number of printcap vars */
  1647. X{
  1648. X    int found;        /* ACME Integers and Pointers */
  1649. X    char *cp;
  1650. X
  1651. X    if(Debug>7)log(XLOG_DEBUG,"Set_pc_entry: %s", name);
  1652. X    if( name == 0 || *name == 0 ){
  1653. X        return( 0 );
  1654. X    }
  1655. X    found = 0;
  1656. X    while( found == 0 && pc_entry(capbuf, sizeof(capbuf)) > 0 ) {
  1657. X        if(Debug>7)log(XLOG_DEBUG,"Set_pc_entry: pc_entry %s", capbuf);
  1658. X        if (Chk_name(capbuf, name)) {
  1659. X            found = 1;
  1660. X        }
  1661. X    }
  1662. X    closeprent();
  1663. X    if( found == 0 ){
  1664. X        return(0);
  1665. X    }
  1666. X    if( First_name == 0 ){
  1667. X        if( (cp = index( capbuf, '|')) == 0 ){
  1668. X            cp = index( capbuf, ':');
  1669. X        }
  1670. X        found = *cp;
  1671. X        *cp = 0;
  1672. X        First_name = fix_str( capbuf );
  1673. X        *cp = found;
  1674. X    }
  1675. X    if(Debug>8){
  1676. X        (void)fprintf(stderr,"printer %s, %s, len %d printcap ",
  1677. X            name,First_name,pc_len);
  1678. X        (void)fprintf(stderr,capbuf);
  1679. X        (void)fprintf(stderr,"\n");
  1680. X    }
  1681. X    /*
  1682. X     * get the printcap variable values
  1683. X     */
  1684. X    getpcvars(pc_vars, pc_len);    /* get the printcap variable values */
  1685. X    if(Debug>6)show_pc( pc_vars, pc_len);
  1686. X    return(1);
  1687. X}
  1688. X
  1689. X/*
  1690. X * Get_pc_entry( char *name; PC_ENTRY pc_vars[]; int pc_len)
  1691. X *  1. calls init_pc_entry() to initialize variables
  1692. X *  2. calls Set_pc_entry() to set them
  1693. X */
  1694. Get_pc_entry(name, pc_vars, pc_len)
  1695. X    char *name;        /* the printer name */
  1696. X    PC_ENTRY pc_vars[];    /* the printcap vars used for the printer */
  1697. X{
  1698. X    if(Debug>7)log(XLOG_DEBUG,"Get_pc_entry: %s", name);
  1699. X    init_pc_entry( pc_vars, pc_len);
  1700. X    return( Set_pc_entry(name, pc_vars, pc_len) );
  1701. X}
  1702. X
  1703. X
  1704. X/*
  1705. X * Chk_name deals with name matching.  The first line of the printcap
  1706. X * entry is a sequence of names separated by |'s, so we compare
  1707. X * against each such name.  The normal : terminator after the last
  1708. X * name (before the first field) stops us.
  1709. X */
  1710. static int
  1711. Chk_name(buf, name)
  1712. X    char *buf, *name;
  1713. X{
  1714. X    int l;
  1715. X
  1716. X    l = strlen(name);
  1717. X
  1718. X    while( buf && *buf ){
  1719. X        if( strncmp( name, buf, l ) == 0 && (buf[l] == ':' || buf[l] == '|')) {
  1720. X            /* match */
  1721. X            return(1);
  1722. X        }
  1723. X        if( buf = index(buf, '|' ) ){
  1724. X            buf = buf + 1;
  1725. X        }
  1726. X    }
  1727. X    /* no match */
  1728. X    return(0);
  1729. X}
  1730. X
  1731. X/*
  1732. X * Return the (numeric) option id.
  1733. X * Numeric options look like
  1734. X *    li#80
  1735. X * i.e. the option string is separated from the numeric value by
  1736. X * a # character.  If the option is not found we return -1.
  1737. X * Note that we handle octal numbers beginning with 0.
  1738. X */
  1739. static int
  1740. getnum(bp)
  1741. X    char *bp;
  1742. X{
  1743. X    int i, base;
  1744. X
  1745. X    if (*bp != '#'){
  1746. X        return(-1);
  1747. X    }
  1748. X    bp = bp + 1;
  1749. X    i = 0;
  1750. X    base = 10;
  1751. X    if (*bp == '0'){
  1752. X        base = 8;
  1753. X    } 
  1754. X    while(isdigit(*bp)){
  1755. X        i = i*base + (*bp - '0');
  1756. X        ++bp;
  1757. X    }
  1758. X    return (i);
  1759. X}
  1760. X
  1761. X/*
  1762. X * Handle a flag option.
  1763. X * Flag options are given "naked", i.e. followed by a :, @ or end
  1764. X * of the buffer.  Return 1 if we find the option, or 0 if it is
  1765. X * not given.
  1766. X */
  1767. static int
  1768. getflag(bp)
  1769. X    char *bp;
  1770. X{
  1771. X    if (*bp == 0 || *bp == ':'){
  1772. X        return (1);
  1773. X    }
  1774. X    return(0);
  1775. X}
  1776. X
  1777. X/*
  1778. X * Get a string valued option.
  1779. X * These are given as xx=string
  1780. X * There is a limited amount of decoding done.
  1781. X * \n -> '\n', \nnn -> '\nnn'
  1782. X * ^<CHAR> -> CTRL-CHAR
  1783. X */
  1784. static char *
  1785. getstr(str)
  1786. X    char *str;    /* points to string entry */
  1787. X{
  1788. X    char buf[BUFSIZ];
  1789. X    char *bp, *cp, *op;        /* ACME Chain and Pointers, Inc */
  1790. X    int c, i;            /* ACME Integers, Inc */
  1791. X    static char norm[] = "E^\\:nrtbf";
  1792. X    static char esc[] = "\033^\\:\n\r\t\b\f";
  1793. X
  1794. X    bp = str;
  1795. X    op = buf;    /* output area */
  1796. X    if (*bp != '=')
  1797. X        return( (char *)0 );
  1798. X    bp++;
  1799. X
  1800. X    while ((c = *bp++) && c != ':') {
  1801. X        switch (c) {
  1802. X        case '^':
  1803. X            c = *bp++;
  1804. X            if( c == 0 ){
  1805. X                fatal(XLOG_INFO,"getstr: bad escape string in printcap" );
  1806. X            }
  1807. X            c = c & 037;
  1808. X            break;
  1809. X        case '\\':
  1810. X            c = *bp++;
  1811. X            if( c == 0 ){
  1812. X                fatal(XLOG_INFO,"getstr: bad escape string in printcap" );
  1813. X            }
  1814. X            cp = index( norm, c );
  1815. X            if( cp ){
  1816. X                c = esc[cp - norm];
  1817. X            } else if (isdigit(c)) {
  1818. X                c = c - '0';
  1819. X                for( i = 0; i < 3 && isdigit(*bp); ++i ){
  1820. X                    c = 8*c + *bp - '0';
  1821. X                    ++bp;
  1822. X                }
  1823. X            }
  1824. X            break;
  1825. X        }
  1826. X        *op++ = c;
  1827. X    }
  1828. X    *op++ = 0;
  1829. X    if( strlen( buf ) > 0 ){
  1830. X        return( fix_str(buf) );
  1831. X    } else {
  1832. X        return( (char *)0 );
  1833. X    }
  1834. X}
  1835. X
  1836. X/*
  1837. X * getpcvars- passed a table of entries, tries to find them all
  1838. X */
  1839. getpcvars(pc_vars, pc_len)
  1840. X    PC_ENTRY *pc_vars;
  1841. X    int pc_len;
  1842. X{
  1843. X    int h, l, m;    /* high, low, middle */
  1844. X    char *cp;    /* string value */
  1845. X    int dir;    /* direction */
  1846. X    
  1847. X    /*
  1848. X     * now scan the printcap entry, getting the variables
  1849. X     */
  1850. X    cp = capbuf;
  1851. X    while( cp && (cp = index(cp, ':' ))){
  1852. X        /*
  1853. X         * we are now positioned at the ":"
  1854. X         */
  1855. X        cp = cp+1;
  1856. X        if( cp[-2] == '\\' ){
  1857. X            continue;
  1858. X        }
  1859. X        /* note: islower is a macro! do not combine the above statement! */
  1860. X        if( !islower( cp[0] ) || !islower( cp[1] )){
  1861. X            continue;
  1862. X        }
  1863. X        if(Debug>7)log(XLOG_DEBUG,"get_pc_vars: entry %c%c", cp[0], cp[1]);
  1864. X        /*
  1865. X         * binary search the table
  1866. X         */
  1867. X        l = 0; h = pc_len-1;
  1868. X        dir = 1;
  1869. X        while( dir && l <= h ){
  1870. X            m = (l+h)/2;
  1871. X            if(Debug>8)log( XLOG_DEBUG, "get_pc_vars: l %d, m %d, h %d",l,m,h);
  1872. X            dir = strncmp( cp, pc_vars[m].pc_name, 2 );
  1873. X            if( dir < 0 ){
  1874. X                h = m-1; /* too high */
  1875. X            } else {
  1876. X                l = m + 1;    /* too low */
  1877. X            }
  1878. X        }
  1879. X        if( dir == 0 ){
  1880. X            /* bingo! we found it */
  1881. X            if(Debug>7)log(XLOG_DEBUG,"get_pc_vars: found %c%c%c%c%c",
  1882. X                cp[0], cp[1], cp[2], cp[3], cp[4] );
  1883. X            fixentry( &pc_vars[m], cp+2 );
  1884. X        }
  1885. X        /*
  1886. X         * we finished, on to next
  1887. X         */
  1888. X    }
  1889. X}
  1890. X
  1891. X/*
  1892. X * fixentry: fix up the value for the entry in the printcap
  1893. X */
  1894. X
  1895. fixentry( pc_vars, cp )
  1896. X    PC_ENTRY *pc_vars;
  1897. X    char *cp;
  1898. X{
  1899. X    if(Debug>6)log(XLOG_DEBUG,"found %c%c", cp[-2], cp[-1] );
  1900. X    switch( pc_vars->kind ){
  1901. X    case PC_NUM:
  1902. X        *(int *)pc_vars->var = getnum(cp);
  1903. X        break;
  1904. X    case PC_FLAG:
  1905. X        *(int *)pc_vars->var = getflag(cp);
  1906. X        break;
  1907. X    case PC_STRING:
  1908. X        *(char **)pc_vars->var = getstr(cp);
  1909. X        break;
  1910. X    default:
  1911. X        fatal( XLOG_INFO, "fixentry: bad kind in the pc_vars" );
  1912. X        break;
  1913. X    }
  1914. X}
  1915. X
  1916. X/*
  1917. X * Debugging and display information
  1918. X */
  1919. X
  1920. show_pc( pc_vars, pc_len )
  1921. X    PC_ENTRY *pc_vars;
  1922. X    int pc_len;
  1923. X{
  1924. X    int i;
  1925. X
  1926. X    (void)fprintf(stderr, "printcap entry %s: \n", First_name );
  1927. X    for( i = 0; i < pc_len; ++i ){
  1928. X        (void)fprintf(stderr, "%s: ", pc_vars[i].pc_name );
  1929. X        switch( pc_vars[i].kind ){
  1930. X        case PC_NUM:
  1931. X            (void)fprintf(stderr, "PC_NUM %d\n", *(int *)pc_vars[i].var );
  1932. X            break;
  1933. X        case PC_FLAG:
  1934. X            (void)fprintf(stderr, "PC_FLAG %d\n", *(int *)pc_vars[i].var );
  1935. X            break;
  1936. X        case PC_STRING:
  1937. X            (void)fprintf(stderr, "PC_STRING %s\n", *(char **)pc_vars[i].var );
  1938. X            break;
  1939. X        }
  1940. X    }
  1941. X    (void)fflush(stderr);
  1942. X}
  1943. X/***************************************************************************
  1944. X * closeprent
  1945. X * closes the file that the printcap was read from
  1946. X ***************************************************************************/
  1947. closeprent()
  1948. X{
  1949. X    if (pfp != NULL)
  1950. X        (void) fclose(pfp);
  1951. X    pfp = NULL;
  1952. X}
  1953. END_OF_FILE
  1954. if test 16988 -ne `wc -c <'src/printcap.c'`; then
  1955.     echo shar: \"'src/printcap.c'\" unpacked with wrong size!
  1956. fi
  1957. # end of 'src/printcap.c'
  1958. fi
  1959. echo shar: End of archive 12 \(of 16\).
  1960. cp /dev/null ark12isdone
  1961. MISSING=""
  1962. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
  1963.     if test ! -f ark${I}isdone ; then
  1964.     MISSING="${MISSING} ${I}"
  1965.     fi
  1966. done
  1967. if test "${MISSING}" = "" ; then
  1968.     echo You have unpacked all 16 archives.
  1969.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1970. else
  1971.     echo You still need to unpack the following archives:
  1972.     echo "        " ${MISSING}
  1973. fi
  1974. ##  End of shell archive.
  1975. exit 0
  1976.  
  1977.