postio.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212
  1. /*
  2. *
  3. * postio - RS-232 serial interface for PostScript printers
  4. *
  5. * A simple program that manages input and output for PostScript printers. Much
  6. * has been added and changed from early versions of the program, but the basic
  7. * philosophy is still the same. Don't send real data until we're certain we've
  8. * connected to a PostScript printer that's in the idle state and try to hold the
  9. * connection until the job is completely done. It's more work than you might
  10. * expect is necessary, but should provide a reasonably reliable spooler interface
  11. * that can return error indications to the caller via the program's exit status.
  12. *
  13. * I've added code that will let you split the program into separate read/write
  14. * processes. Although it's not the default it should be useful if you have a file
  15. * that will be returning useful data from the printer. The two process stuff was
  16. * laid down on top of the single process code and both methods still work. The
  17. * implementation isn't as good as it could be, but didn't require many changes
  18. * to the original program (despite the fact that there are now many differences).
  19. *
  20. * By default the program still runs as a single process. The -R2 option forces
  21. * separate read and write processes after the intial connection is made. If you
  22. * want that as the default initialize splitme (below) to TRUE. In addition the
  23. * -t option that's used to force stuff not recognized as status reports to stdout
  24. * also tries to run as two processes (by setting splitme to TRUE). It will only
  25. * work if the required code (ie. resetline() in ifdef.c) has been implemented
  26. * for your Unix system. I've only tested the System V code.
  27. *
  28. * Code needed to support interactive mode has also been added, although again it's
  29. * not as efficient as it could be. It depends on the system dependent procedures
  30. * resetline() and setupstdin() (file ifdef.c) and for now is only guaranteed to
  31. * work on System V. Can be requested using the -i option.
  32. *
  33. * Quiet mode (-q option) is also new, but was needed for some printers connected
  34. * to RADIAN. If you're running in quiet mode no status requests will be sent to
  35. * the printer while files are being transmitted (ie. in send()).
  36. *
  37. * The program expects to receive printer status lines that look like,
  38. *
  39. * %%[ status: idle; source: serial 25 ]%%
  40. * %%[ status: waiting; source: serial 25 ]%%
  41. * %%[ status: initializing; source: serial 25 ]%%
  42. * %%[ status: busy; source: serial 25 ]%%
  43. * %%[ status: printing; source: serial 25 ]%%
  44. * %%[ status: PrinterError: out of paper; source: serial 25 ]%%
  45. * %%[ status: PrinterError: no paper tray; source: serial 25 ]%%
  46. *
  47. * although this list isn't complete. Sending a '\024' (control T) character forces
  48. * the return of a status report. PostScript errors detected on the printer result
  49. * in the immediate transmission of special error messages that look like,
  50. *
  51. * %%[ Error: undefined; OffendingCommand: xxx ]%%
  52. * %%[ Flushing: rest of job (to end-of-file) will be ignored ]%%
  53. *
  54. * although we only use the Error and Flushing keywords. Finally conditions, like
  55. * being out of paper, result in other messages being sent back from the printer
  56. * over the communications line. Typical PrinterError messages look like,
  57. *
  58. * %%[ PrinterError: out of paper; source: serial 25 ]%%
  59. * %%[ PrinterError: paper jam; source: serial 25 ]%%
  60. *
  61. * although we only use the PrinterError keyword rather than trying to recognize
  62. * all possible printer errors.
  63. *
  64. * The implications of using one process and only flow controlling data going to
  65. * the printer are obvious. Job transmission should be reliable, but there can be
  66. * data loss in stuff sent back from the printer. Usually that only caused problems
  67. * with jobs designed to run on the printer and return useful data back over the
  68. * communications line. If that's the kind of job you're sending call postio with
  69. * the -t option. That should force the program to split into separate read and
  70. * write processes and everything not bracketed by "%%[ " and " ]%%" strings goes
  71. * to stdout. In otherwords the data you're expecting should be separated from the
  72. * status stuff that goes to the log file (or stderr). The -R2 option does almost
  73. * the same thing (ie. separate read and write processes), but everything that
  74. * comes back from the printer goes to the log file (stderr by default) and you'll
  75. * have to separate your data from any printer messages.
  76. *
  77. * A typical command line might be,
  78. *
  79. * postio -l /dev/tty01 -b 9600 -L log file1 file2
  80. *
  81. * where -l selects the line, -b sets the baud rate, and -L selects the printer
  82. * log file. Since there's no default line, at least not right now, you'll always
  83. * need to use the -l option, and if you don't choose a log file stderr will be
  84. * used. If you have a program that will be returning data the command line might
  85. * look like,
  86. *
  87. * postio -t -l/dev/tty01 -b9600 -Llog file >results
  88. *
  89. * Status stuff goes to file log while the data you're expecting back from the
  90. * printer gets put in file results.
  91. *
  92. */
  93. #include <stdio.h>
  94. #include <ctype.h>
  95. #include <fcntl.h>
  96. #include <signal.h>
  97. #include <sys/types.h>
  98. #include <errno.h>
  99. #include "ifdef.h" /* conditional compilation stuff */
  100. #include "gen.h" /* general purpose definitions */
  101. #include "postio.h" /* some special definitions */
  102. char **argv; /* global so everyone can use them */
  103. int argc;
  104. char *prog_name = ""; /* really just for error messages */
  105. int x_stat = 0; /* program exit status */
  106. int debug = OFF; /* debug flag */
  107. int ignore = OFF; /* what's done for FATAL errors */
  108. char *line = NULL; /* printer is on this tty line */
  109. short baudrate = BAUDRATE; /* and running at this baud rate */
  110. Baud baudtable[] = BAUDTABLE; /* converts strings to termio values */
  111. int stopbits = 1; /* number of stop bits */
  112. int tostdout = FALSE; /* non-status stuff goes to stdout? */
  113. int quiet = FALSE; /* no status queries in send() if TRUE */
  114. int interactive = FALSE; /* interactive mode */
  115. char *postbegin = POSTBEGIN; /* preceeds all the input files */
  116. int useslowsend = FALSE; /* not recommended! */
  117. int sendctrlC = TRUE; /* interrupt with ctrl-C when BUSY */
  118. int window_size = -1; /* for Datakit - use -w */
  119. char *block = NULL; /* input file buffer */
  120. int blocksize = BLOCKSIZE; /* and its size in bytes */
  121. int head = 0; /* block[head] is the next character */
  122. int tail = 0; /* one past the last byte in block[] */
  123. int splitme = FALSE; /* into READ and WRITE processes if TRUE */
  124. int whatami = READWRITE; /* a READ or WRITE process - or both */
  125. int canread = TRUE; /* allow reads */
  126. int canwrite = TRUE; /* and writes if TRUE */
  127. int otherpid = -1; /* who gets signals if greater than 1 */
  128. int joinsig = SIGTRAP; /* reader gets this when writing is done */
  129. int writedone = FALSE; /* and then sets this to TRUE */
  130. char mesg[MESGSIZE]; /* exactly what came back on ttyi */
  131. char sbuf[MESGSIZE]; /* for parsing the message */
  132. int next = 0; /* next character goes in mesg[next] */
  133. char *mesgptr = NULL; /* printer message starts here in mesg[] */
  134. char *endmesg = NULL; /* as far as readline() can go in mesg[] */
  135. Status status[] = STATUS; /* for converting status strings */
  136. int nostatus = NOSTATUS; /* default getstatus() return value */
  137. int currentstate = NOTCONNECTED; /* what's happening START, SEND, or DONE */
  138. int ttyi = 0; /* input */
  139. int ttyo = 2; /* and output file descriptors */
  140. FILE *fp_log = stderr; /* log file for stuff from the printer */
  141. /*****************************************************************************/
  142. main(agc, agv)
  143. int agc;
  144. char *agv[];
  145. {
  146. /*
  147. *
  148. * A simple program that manages input and output for PostScript printers. Can run
  149. * as a single process or as separate read/write processes. What's done depends on
  150. * the value assigned to splitme when split() is called.
  151. *
  152. */
  153. argc = agc; /* other routines may want them */
  154. argv = agv;
  155. prog_name = argv[0]; /* really just for error messages */
  156. init_signals(); /* sets up interrupt handling */
  157. options(); /* get command line options */
  158. initialize(); /* must be done after options() */
  159. start(); /* make sure the printer is ready */
  160. split(); /* into read/write processes - maybe */
  161. arguments(); /* then send each input file */
  162. done(); /* wait until the printer is finished */
  163. cleanup(); /* make sure the write process stops */
  164. exit(x_stat); /* everything probably went OK */
  165. } /* End of main */
  166. /*****************************************************************************/
  167. init_signals()
  168. {
  169. void interrupt(); /* handles them if we catch signals */
  170. /*
  171. *
  172. * Makes sure we handle interrupts. The proper way to kill the program, if
  173. * necessary, is to do a kill -15. That forces a call to interrupt(), which in
  174. * turn tries to reset the printer and then exits with a non-zero status. If the
  175. * program is running as two processes, sending SIGTERM to either the parent or
  176. * child should clean things up.
  177. *
  178. */
  179. if ( signal(SIGINT, interrupt) == SIG_IGN ) {
  180. signal(SIGINT, SIG_IGN);
  181. signal(SIGQUIT, SIG_IGN);
  182. signal(SIGHUP, SIG_IGN);
  183. } else {
  184. signal(SIGHUP, interrupt);
  185. signal(SIGQUIT, interrupt);
  186. } /* End else */
  187. signal(SIGTERM, interrupt);
  188. } /* End of init_sig */
  189. /*****************************************************************************/
  190. options()
  191. {
  192. int ch; /* return value from getopt() */
  193. char *optnames = "b:cil:qs:tw:B:L:P:R:SDI";
  194. extern char *optarg; /* used by getopt() */
  195. extern int optind;
  196. /*
  197. *
  198. * Reads and processes the command line options. The -R2, -t, and -i options all
  199. * force separate read and write processes by eventually setting splitme to TRUE
  200. * (check initialize()). The -S option is not recommended and should only be used
  201. * as a last resort!
  202. *
  203. */
  204. while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
  205. switch ( ch ) {
  206. case 'b': /* baud rate string */
  207. baudrate = getbaud(optarg);
  208. break;
  209. case 'c': /* no ctrl-C's */
  210. sendctrlC = FALSE;
  211. break;
  212. case 'i': /* interactive mode */
  213. interactive = TRUE;
  214. break;
  215. case 'l': /* printer line */
  216. line = optarg;
  217. break;
  218. case 'q': /* no status queries - for RADIAN? */
  219. quiet = TRUE;
  220. break;
  221. case 's': /* use 2 stop bits - for UNISON? */
  222. if ( (stopbits = atoi(optarg)) < 1 || stopbits > 2 )
  223. stopbits = 1;
  224. break;
  225. case 't': /* non-status stuff goes to stdout */
  226. tostdout = TRUE;
  227. break;
  228. case 'w': /* Datakit window size */
  229. window_size = atoi(optarg);
  230. break;
  231. case 'B': /* set the job buffer size */
  232. if ( (blocksize = atoi(optarg)) <= 0 )
  233. blocksize = BLOCKSIZE;
  234. break;
  235. case 'L': /* printer log file */
  236. if ( (fp_log = fopen(optarg, "w")) == NULL ) {
  237. fp_log = stderr;
  238. error(NON_FATAL, "can't open log file %s", optarg);
  239. } /* End if */
  240. break;
  241. case 'P': /* initial PostScript code */
  242. postbegin = optarg;
  243. break;
  244. case 'R': /* run as one or two processes */
  245. if ( atoi(optarg) == 2 )
  246. splitme = TRUE;
  247. else splitme = FALSE;
  248. break;
  249. case 'S': /* slow and kludged up version of send */
  250. useslowsend = TRUE;
  251. break;
  252. case 'D': /* debug flag */
  253. debug = ON;
  254. break;
  255. case 'I': /* ignore FATAL errors */
  256. ignore = ON;
  257. break;
  258. case '?': /* don't understand the option */
  259. error(FATAL, "");
  260. break;
  261. default: /* don't know what to do for ch */
  262. error(FATAL, "missing case for option %c\n", ch);
  263. break;
  264. } /* End switch */
  265. } /* End while */
  266. argc -= optind; /* get ready for non-option args */
  267. argv += optind;
  268. } /* End of options */
  269. /*****************************************************************************/
  270. getbaud(rate)
  271. char *rate; /* string representing the baud rate */
  272. {
  273. int i; /* for looking through baudtable[] */
  274. /*
  275. *
  276. * Called from options() to convert a baud rate string into an appropriate termio
  277. * value. *rate is looked up in baudtable[] and if it's found, the corresponding
  278. * value is returned to the caller.
  279. *
  280. */
  281. for ( i = 0; baudtable[i].rate != NULL; i++ )
  282. if ( strcmp(rate, baudtable[i].rate) == 0 )
  283. return(baudtable[i].val);
  284. error(FATAL, "don't recognize baud rate %s", rate);
  285. } /* End of getbaud */
  286. /*****************************************************************************/
  287. initialize()
  288. {
  289. /*
  290. *
  291. * Initialization, a few checks, and a call to setupline() (file ifdef.c) to open
  292. * and configure the communications line. Settings for interactive mode always
  293. * take precedence. The setupstdin() call with an argument of 0 saves the current
  294. * terminal settings if interactive mode has been requested - otherwise nothing's
  295. * done. Unbuffering stdout (via the setbuf() call) isn't really needed on System V
  296. * since it's flushed whenever terminal input is requested. It's more efficient if
  297. * we buffer the stdout (on System V) but safer (for other versions of Unix) if we
  298. * include the setbuf() call.
  299. *
  300. */
  301. whatami = READWRITE; /* always run start() as one process */
  302. canread = canwrite = TRUE;
  303. if ( tostdout == TRUE ) /* force separate read/write processes */
  304. splitme = TRUE;
  305. if ( interactive == TRUE ) { /* interactive mode settings always win */
  306. quiet = FALSE;
  307. tostdout = FALSE;
  308. splitme = TRUE;
  309. blocksize = 1;
  310. postbegin = NULL;
  311. useslowsend = FALSE;
  312. nostatus = INTERACTIVE;
  313. setbuf(stdout, NULL);
  314. } /* End if */
  315. if ( useslowsend == TRUE ) { /* last resort only - not recommended */
  316. quiet = FALSE;
  317. splitme = FALSE;
  318. if ( blocksize > 1024 ) /* don't send too much all at once */
  319. blocksize = 1024;
  320. } /* End if */
  321. if ( tostdout == TRUE && fp_log == stderr )
  322. fp_log = NULL;
  323. if ( line == NULL && (interactive == TRUE || tostdout == TRUE) )
  324. error(FATAL, "a printer line must be supplied - use the -l option");
  325. if ( (block = malloc(blocksize)) == NULL )
  326. error(FATAL, "no memory");
  327. endmesg = mesg + sizeof mesg - 2; /* one byte from last position in mesg */
  328. setupline(); /* configure the communications line */
  329. setupstdin(0); /* save current stdin terminal settings */
  330. } /* End of initialize */
  331. /*****************************************************************************/
  332. start()
  333. {
  334. /*
  335. *
  336. * Tries to put the printer in the IDLE state before anything important is sent.
  337. * Run as a single process no matter what has been assigned to splitme. Separate
  338. * read and write processes, if requested, will be created after we're done here.
  339. *
  340. */
  341. logit("printer startup\n");
  342. currentstate = START;
  343. clearline();
  344. while ( 1 )
  345. switch ( getstatus(1) ) {
  346. case IDLE:
  347. case INTERACTIVE:
  348. if ( postbegin != NULL && *postbegin != '\0' )
  349. Write(ttyo, postbegin, strlen(postbegin));
  350. clearline();
  351. return;
  352. case BUSY:
  353. if ( sendctrlC == TRUE ) {
  354. Write(ttyo, "\003", 1);
  355. Rest(1);
  356. } /* End if */
  357. break;
  358. case WAITING:
  359. case ERROR:
  360. case FLUSHING:
  361. Write(ttyo, "\004", 1);
  362. Rest(1);
  363. break;
  364. case PRINTERERROR:
  365. Rest(15);
  366. break;
  367. case DISCONNECT:
  368. error(FATAL, "Disconnected - printer may be offline");
  369. break;
  370. case ENDOFJOB:
  371. case UNKNOWN:
  372. clearline();
  373. break;
  374. default:
  375. Rest(1);
  376. break;
  377. } /* End switch */
  378. } /* End of start */
  379. /*****************************************************************************/
  380. split()
  381. {
  382. int pid;
  383. void interrupt();
  384. /*
  385. *
  386. * If splitme is TRUE we fork a process, make the parent handle reading, and let
  387. * the child take care of writing. resetline() (file ifdef.c) contains all the
  388. * system dependent code needed to reset the communications line for separate
  389. * read and write processes. For now it's expected to return TRUE or FALSE and
  390. * that value controls whether we try the fork. I've only tested the two process
  391. * stuff for System V. Other versions of resetline() may just be dummy procedures
  392. * that always return FALSE. If the fork() failed previous versions continued as
  393. * a single process, although the implementation wasn't quite right, but I've now
  394. * decided to quit. The main reason is a Datakit channel may be configured to
  395. * flow control data in both directions, and if we run postio over that channel
  396. * as a single process we likely will end up in deadlock.
  397. *
  398. */
  399. if ( splitme == TRUE )
  400. if ( resetline() == TRUE ) {
  401. pid = getpid();
  402. signal(joinsig, interrupt);
  403. if ( (otherpid = fork()) == -1 )
  404. error(FATAL, "can't fork");
  405. else if ( otherpid == 0 ) {
  406. whatami = WRITE;
  407. nostatus = WRITEPROCESS;
  408. otherpid = pid;
  409. setupstdin(1);
  410. } else whatami = READ;
  411. } else if ( interactive == TRUE || tostdout == TRUE )
  412. error(FATAL, "can't create two process - check resetline()");
  413. else error(NON_FATAL, "running as a single process - check resetline()");
  414. canread = (whatami & READ) ? TRUE : FALSE;
  415. canwrite = (whatami & WRITE) ? TRUE : FALSE;
  416. } /* End of split */
  417. /*****************************************************************************/
  418. arguments()
  419. {
  420. int fd_in; /* next input file */
  421. /*
  422. *
  423. * Makes sure all the non-option command line arguments are processed. If there
  424. * aren't any arguments left when we get here we'll send stdin. Input files are
  425. * only read and sent to the printer if canwrite is TRUE. Checking it here means
  426. * we won't have to do it in send(). If interactive mode is TRUE we'll stay here
  427. * forever sending stdin when we run out of files - exit with a break. Actually
  428. * the loop is bogus and used at most once when we're in interactive mode because
  429. * stdin is in a pseudo raw mode and the read() in readblock() should never see
  430. * the end of file.
  431. *
  432. */
  433. if ( canwrite == TRUE )
  434. do /* loop is for interactive mode */
  435. if ( argc < 1 )
  436. send(fileno(stdin), "pipe.end");
  437. else {
  438. while ( argc > 0 ) {
  439. if ( (fd_in = open(*argv, O_RDONLY)) == -1 )
  440. error(FATAL, "can't open %s", *argv);
  441. send(fd_in, *argv);
  442. close(fd_in);
  443. argc--;
  444. argv++;
  445. } /* End while */
  446. } /* End else */
  447. while ( interactive == TRUE );
  448. } /* End of arguments */
  449. /*****************************************************************************/
  450. send(fd_in, name)
  451. int fd_in; /* next input file */
  452. char *name; /* and it's pathname */
  453. {
  454. /*
  455. *
  456. * Sends file *name to the printer. There's nothing left here that depends on
  457. * sending and receiving status reports, although it can be reassuring to know
  458. * the printer is responding and processing our job. Only the writer gets here
  459. * in the two process implementation, and in that case split() has reset nostatus
  460. * to WRITEPROCESS and that's what getstatus() always returns. For now we accept
  461. * the IDLE state and ENDOFJOB as legitimate and ignore the INITIALIZING state.
  462. *
  463. */
  464. if ( interactive == FALSE )
  465. logit("sending file %s\n", name);
  466. currentstate = SEND;
  467. if ( useslowsend == TRUE ) {
  468. slowsend(fd_in);
  469. return;
  470. } /* End if */
  471. while ( readblock(fd_in) )
  472. switch ( getstatus(0) ) {
  473. case IDLE:
  474. case BUSY:
  475. case WAITING:
  476. case PRINTING:
  477. case ENDOFJOB:
  478. case PRINTERERROR:
  479. case UNKNOWN:
  480. case NOSTATUS:
  481. case WRITEPROCESS:
  482. case INTERACTIVE:
  483. writeblock();
  484. break;
  485. case ERROR:
  486. fprintf(stderr, "%s", mesg); /* for csw */
  487. error(USER_FATAL, "PostScript Error");
  488. break;
  489. case FLUSHING:
  490. error(USER_FATAL, "Flushing Job");
  491. break;
  492. case DISCONNECT:
  493. error(FATAL, "Disconnected - printer may be offline");
  494. break;
  495. } /* End switch */
  496. } /* End of send */
  497. /*****************************************************************************/
  498. done()
  499. {
  500. int sleeptime = 15; /* for 'out of paper' etc. */
  501. /*
  502. *
  503. * Tries to stay connected to the printer until we're reasonably sure the job is
  504. * complete. It's the only way we can recover error messages or data generated by
  505. * the PostScript program and returned over the communication line. Actually doing
  506. * it correctly for all possible PostScript jobs is more difficult that it might
  507. * seem. For example if we've sent several jobs, each with their own EOF mark, then
  508. * waiting for ENDOFJOB won't guarantee all the jobs have completed. Even waiting
  509. * for IDLE isn't good enough. Checking for the WAITING state after all the files
  510. * have been sent and then sending an EOF may be the best approach, but even that
  511. * won't work all the time - we could miss it or might not get there. Even sending
  512. * our own special PostScript job after all the input files has it's own different
  513. * set of problems, but probably could work (perhaps by printing a fake status
  514. * message or just not timing out). Anyway it's probably not worth the trouble so
  515. * for now we'll quit if writedone is TRUE and we get ENDOFJOB or IDLE.
  516. *
  517. * If we're running separate read and write processes the reader gets here after
  518. * after split() while the writer goes to send() and only gets here after all the
  519. * input files have been transmitted. When they're both here the writer sends the
  520. * reader signal joinsig and that forces writedone to TRUE in the reader. At that
  521. * point the reader can begin looking for an indication of the end of the job.
  522. * The writer hangs around until the reader kills it (usually in cleanup()) sending
  523. * occasional status requests.
  524. *
  525. */
  526. if ( canwrite == TRUE )
  527. logit("waiting for end of job\n");
  528. currentstate = DONE;
  529. writedone = (whatami == READWRITE) ? TRUE : FALSE;
  530. while ( 1 ) {
  531. switch ( getstatus(1) ) {
  532. case WRITEPROCESS:
  533. if ( writedone == FALSE ) {
  534. sendsignal(joinsig);
  535. Write(ttyo, "\004", 1);
  536. writedone = TRUE;
  537. sleeptime = 1;
  538. } /* End if */
  539. Rest(sleeptime++);
  540. break;
  541. case WAITING:
  542. Write(ttyo, "\004", 1);
  543. Rest(1);
  544. sleeptime = 15;
  545. break;
  546. case IDLE:
  547. case ENDOFJOB:
  548. if ( writedone == TRUE ) {
  549. logit("job complete\n");
  550. return;
  551. } /* End if */
  552. break;
  553. case BUSY:
  554. case PRINTING:
  555. case INTERACTIVE:
  556. sleeptime = 15;
  557. break;
  558. case PRINTERERROR:
  559. Rest(sleeptime++);
  560. break;
  561. case ERROR:
  562. fprintf(stderr, "%s", mesg); /* for csw */
  563. error(USER_FATAL, "PostScript Error");
  564. return;
  565. case FLUSHING:
  566. error(USER_FATAL, "Flushing Job");
  567. return;
  568. case DISCONNECT:
  569. error(FATAL, "Disconnected - printer may be offline");
  570. return;
  571. default:
  572. Rest(1);
  573. break;
  574. } /* End switch */
  575. if ( sleeptime > 60 )
  576. sleeptime = 60;
  577. } /* End while */
  578. } /* End of done */
  579. /*****************************************************************************/
  580. cleanup()
  581. {
  582. int w;
  583. /*
  584. *
  585. * Only needed if we're running separate read and write processes. Makes sure the
  586. * write process is killed after the read process has successfully finished with
  587. * all the jobs. sendsignal() returns a -1 if there's nobody to signal so things
  588. * work when we're running a single process.
  589. *
  590. */
  591. while ( sendsignal(SIGKILL) != -1 && (w = wait((int *)0)) != otherpid && w != -1 ) ;
  592. } /* End of cleanup */
  593. /*****************************************************************************/
  594. readblock(fd_in)
  595. int fd_in; /* current input file */
  596. {
  597. static long blocknum = 1;
  598. /*
  599. *
  600. * Fills the input buffer with the next block, provided we're all done with the
  601. * last one. Blocks from fd_in are stored in array block[]. head is the index
  602. * of the next byte in block[] that's supposed to go to the printer. tail points
  603. * one past the last byte in the current block. head is adjusted in writeblock()
  604. * after each successful write, while head and tail are reset here each time
  605. * a new block is read. Returns the number of bytes left in the current block.
  606. * Read errors cause the program to abort. The fake status message that's put out
  607. * in quiet mode is only so you can look at the log file and know something's
  608. * happening - take it out if you want.
  609. *
  610. */
  611. if ( head >= tail ) { /* done with the last block */
  612. if ( (tail = read(fd_in, block, blocksize)) == -1 )
  613. error(FATAL, "error reading input file");
  614. if ( quiet == TRUE && tail > 0 ) /* put out a fake message? */
  615. logit("%%%%[ status: busy; block: %d ]%%%%\n", blocknum++);
  616. head = 0;
  617. } /* End if */
  618. return(tail - head);
  619. } /* End of readblock */
  620. /*****************************************************************************/
  621. writeblock()
  622. {
  623. int count; /* bytes successfully written */
  624. /*
  625. *
  626. * Called from send() when it's OK to send the next block to the printer. head
  627. * is adjusted after the write, and the number of bytes that were successfully
  628. * written is returned to the caller.
  629. *
  630. */
  631. if ( (count = write(ttyo, &block[head], tail - head)) == -1 )
  632. error(FATAL, "error writing to %s", line);
  633. else if ( count == 0 )
  634. error(FATAL, "printer appears to be offline");
  635. head += count;
  636. return(count);
  637. } /* End of writeblock */
  638. /*****************************************************************************/
  639. getstatus(t)
  640. int t; /* sleep time after sending '\024' */
  641. {
  642. int gotline = FALSE; /* value returned by readline() */
  643. int state = nostatus; /* representation of the current state */
  644. int mesgch; /* to restore mesg[] when tostdout == TRUE */
  645. static int laststate = NOSTATUS; /* last state recognized */
  646. /*
  647. *
  648. * Looks for things coming back from the printer on the communications line, parses
  649. * complete lines retrieved by readline(), and returns an integer representation
  650. * of the current printer status to the caller. If nothing was available a status
  651. * request (control T) is sent to the printer and nostatus is returned to the
  652. * caller (provided quiet isn't TRUE). Interactive mode either never returns from
  653. * readline() or returns FALSE.
  654. *
  655. */
  656. if ( canread == TRUE && (gotline = readline()) == TRUE ) {
  657. state = parsemesg();
  658. if ( state != laststate || state == UNKNOWN || mesgptr != mesg || debug == ON )
  659. logit("%s", mesg);
  660. if ( tostdout == TRUE && currentstate != START ) {
  661. mesgch = *mesgptr;
  662. *mesgptr = '\0';
  663. fprintf(stdout, "%s", mesg);
  664. fflush(stdout);
  665. *mesgptr = mesgch; /* for ERROR in send() and done() */
  666. } /* End if */
  667. return(laststate = state);
  668. } /* End if */
  669. if ( (quiet == FALSE || currentstate != SEND) &&
  670. (tostdout == FALSE || currentstate == START) && interactive == FALSE ) {
  671. if ( Write(ttyo, "\024", 1) != 1 )
  672. error(FATAL, "printer appears to be offline");
  673. if ( t > 0 ) Rest(t);
  674. } /* End if */
  675. return(nostatus);
  676. } /* End of getstatus */
  677. /*****************************************************************************/
  678. parsemesg()
  679. {
  680. char *e; /* end of printer message in mesg[] */
  681. char *key, *val; /* keyword/value strings in sbuf[] */
  682. char *p; /* for converting to lower case etc. */
  683. int i; /* where *key was found in status[] */
  684. /*
  685. *
  686. * Parsing the lines that readline() stores in mesg[] is messy, and what's done
  687. * here isn't completely correct nor as fast as it could be. The general format
  688. * of lines that come back from the printer (assuming no data loss) is:
  689. *
  690. * str%%[ key: val; key: val; key: val ]%%\n
  691. *
  692. * where str can be most anything not containing a newline and printer reports
  693. * (eg. status or error messages) are bracketed by "%%[ " and " ]%%" strings and
  694. * end with a newline. Usually we'll have the string or printer report but not
  695. * both. For most jobs the leading string will be empty, but could be anything
  696. * generated on a printer and returned over the communications line using the
  697. * PostScript print operator. I'll assume PostScript jobs are well behaved and
  698. * never bracket their messages with "%%[ " and " ]%%" strings that delimit status
  699. * or error messages.
  700. *
  701. * Printer reports consist of one or more key/val pairs, and what we're interested
  702. * in (status or error indications) may not be the first pair in the list. In
  703. * addition we'll sometimes want the value associated with a keyword (eg. when
  704. * key = status) and other times we'll want the keyword (eg. when key = Error or
  705. * Flushing). The last pair isn't terminated by a semicolon and a value string
  706. * often contains many space separated words and it can even include colons in
  707. * meaningful places. I've also decided to continue converting things to lower
  708. * case before doing the lookup in status[]. The isupper() test is for Berkeley
  709. * systems.
  710. *
  711. */
  712. if ( *(mesgptr = find("%%[ ", mesg)) != '\0' && *(e = find(" ]%%", mesgptr+4)) != '\0' ) {
  713. strcpy(sbuf, mesgptr+4); /* don't change mesg[] */
  714. sbuf[e-mesgptr-4] = '\0'; /* ignore the trailing " ]%%" */
  715. for ( key = strtok(sbuf, " :"); key != NULL; key = strtok(NULL, " :") ) {
  716. if ( (val = strtok(NULL, ";")) != NULL && strcmp(key, "status") == 0 )
  717. key = val;
  718. for ( ; *key == ' '; key++ ) ; /* skip any leading spaces */
  719. for ( p = key; *p; p++ ) /* convert to lower case */
  720. if ( *p == ':' ) {
  721. *p = '\0';
  722. break;
  723. } else if ( isupper(*p) ) *p = tolower(*p);
  724. for ( i = 0; status[i].state != NULL; i++ )
  725. if ( strcmp(status[i].state, key) == 0 )
  726. return(status[i].val);
  727. } /* End for */
  728. } else if ( strcmp(mesg, "CONVERSATION ENDED.\n") == 0 )
  729. return(DISCONNECT);
  730. return(mesgptr == '\0' ? nostatus : UNKNOWN);
  731. } /* End of parsemesg */
  732. /*****************************************************************************/
  733. char *find(str1, str2)
  734. char *str1; /* look for this string */
  735. char *str2; /* in this one */
  736. {
  737. char *s1, *s2; /* can't change str1 or str2 too fast */
  738. /*
  739. *
  740. * Looks for *str1 in string *str2. Returns a pointer to the start of the substring
  741. * if it's found or to the end of string str2 otherwise.
  742. *
  743. */
  744. for ( ; *str2 != '\0'; str2++ ) {
  745. for ( s1 = str1, s2 = str2; *s1 != '\0' && *s1 == *s2; s1++, s2++ ) ;
  746. if ( *s1 == '\0' )
  747. break;
  748. } /* End for */
  749. return(str2);
  750. } /* End of find */
  751. /*****************************************************************************/
  752. clearline()
  753. {
  754. /*
  755. *
  756. * Reads characters from the input line until nothing's left. Don't do anything if
  757. * we're currently running separate read and write processes.
  758. *
  759. */
  760. if ( whatami == READWRITE )
  761. while ( readline() != FALSE ) ;
  762. } /* End of clearline */
  763. /*****************************************************************************/
  764. sendsignal(sig)
  765. int sig; /* this goes to the other process */
  766. {
  767. /*
  768. *
  769. * Sends signal sig to the other process if we're running as separate read and
  770. * write processes. Returns the result of the kill if there's someone else to
  771. * signal or -1 if we're running alone.
  772. *
  773. */
  774. if ( whatami != READWRITE && otherpid > 1 )
  775. return(kill(otherpid, sig));
  776. return(-1);
  777. } /* End of sendsignal */
  778. /*****************************************************************************/
  779. void interrupt(sig)
  780. int sig; /* signal that we caught */
  781. {
  782. /*
  783. *
  784. * Caught a signal - all except joinsig cause the program to quit. joinsig is the
  785. * signal sent by the writer to the reader after all the jobs have been transmitted.
  786. * Used to tell the read process when it can start looking for the end of the job.
  787. *
  788. */
  789. signal(sig, SIG_IGN);
  790. if ( sig != joinsig ) {
  791. x_stat |= FATAL;
  792. if ( canread == TRUE )
  793. if ( interactive == FALSE )
  794. error(NON_FATAL, "signal %d abort", sig);
  795. else error(NON_FATAL, "quitting");
  796. quit(sig);
  797. } /* End if */
  798. writedone = TRUE;
  799. signal(joinsig, interrupt);
  800. } /* End of interrupt */
  801. /*****************************************************************************/
  802. logit(mesg, a1, a2, a3)
  803. char *mesg; /* control string */
  804. unsigned a1, a2, a3; /* and possible arguments */
  805. {
  806. /*
  807. *
  808. * Simple routine that's used to write a message to the log file.
  809. *
  810. */
  811. if ( mesg != NULL && fp_log != NULL ) {
  812. fprintf(fp_log, mesg, a1, a2, a3);
  813. fflush(fp_log);
  814. } /* End if */
  815. } /* End of logit */
  816. /*****************************************************************************/
  817. error(kind, mesg, a1, a2, a3)
  818. int kind; /* FATAL or NON_FATAL error */
  819. char *mesg; /* error message control string */
  820. unsigned a1, a2, a3; /* control string arguments */
  821. {
  822. FILE *fp_err;
  823. /*
  824. *
  825. * Called when we've run into some kind of program error. First *mesg is printed
  826. * using the control string arguments a?. If kind is FATAL and we're not ignoring
  827. * errors the program will be terminated. If mesg is NULL or *mesg is the NULL
  828. * string nothing will be printed.
  829. *
  830. */
  831. fp_err = (fp_log != NULL) ? fp_log : stderr;
  832. if ( mesg != NULL && *mesg != '\0' ) {
  833. fprintf(fp_err, "%s: ", prog_name);
  834. fprintf(fp_err, mesg, a1, a2, a3);
  835. putc('\n', fp_err);
  836. } /* End if */
  837. x_stat |= kind;
  838. if ( kind != NON_FATAL && ignore == OFF )
  839. quit(SIGTERM);
  840. } /* End of error */
  841. /*****************************************************************************/
  842. quit(sig)
  843. int sig;
  844. {
  845. int w;
  846. /*
  847. *
  848. * Makes sure everything is properly cleaned up if there's a signal or FATAL error
  849. * that should cause the program to terminate. The sleep by the write process is
  850. * to help give the reset sequence a chance to reach the printer before we break
  851. * the connection - primarily for printers connected to Datakit. There's a very
  852. * slight chance the reset sequence that's sent to the printer could get us stuck
  853. * here. Simplest solution is don't bother to send it - everything works without it.
  854. * Flushing ttyo would be better, but means yet another system dependent procedure
  855. * in ifdef.c! I'll leave things be for now.
  856. *
  857. * Obscure problem on PS-810 turbos says wait a bit after sending an interrupt.
  858. * Seem to remember the printer getting into a bad state immediately after the
  859. * top was opened when the toner light was on. A sleep after sending the ctrl-C
  860. * seemed to fix things.
  861. *
  862. */
  863. signal(sig, SIG_IGN);
  864. ignore = ON;
  865. while ( sendsignal(sig) != -1 && (w = wait((int *)0)) != otherpid && w != -1 ) ;
  866. setupstdin(2);
  867. if ( currentstate != NOTCONNECTED ) {
  868. if ( sendctrlC == TRUE ) {
  869. Write(ttyo, "\003", 1);
  870. Rest(1); /* PS-810 turbo problem?? */
  871. } /* End if */
  872. Write(ttyo, "\004", 1);
  873. } /* End if */
  874. alarm(0); /* prevents sleep() loop on V9 systems */
  875. Rest(2);
  876. exit(x_stat);
  877. } /* End of quit */
  878. /*****************************************************************************/
  879. Rest(t)
  880. int t;
  881. {
  882. /*
  883. *
  884. * Used to replace sleep() calls. Only needed if we're running the program as
  885. * a read and write process and don't want to have the read process sleep. Most
  886. * sleeps are in the code because of the non-blocking read used by the single
  887. * process implementation. Probably should be a macro.
  888. *
  889. */
  890. if ( t > 0 && canwrite == TRUE )
  891. sleep(t);
  892. } /* End of Rest */
  893. /*****************************************************************************/
  894. Read(fd, buf, n)
  895. int fd;
  896. char *buf;
  897. int n;
  898. {
  899. int count;
  900. /*
  901. *
  902. * Used to replace some of the read() calls. Only needed if we're running separate
  903. * read and write processes. Should only be used to replace read calls on ttyi.
  904. * Always returns 0 to the caller if the process doesn't have its READ flag set.
  905. * Probably should be a macro.
  906. *
  907. */
  908. if ( canread == TRUE ) {
  909. if ( (count = read(fd, buf, n)) == -1 && errno == EINTR )
  910. count = 0;
  911. } else count = 0;
  912. return(count);
  913. } /* End of Read */
  914. /*****************************************************************************/
  915. Write(fd, buf, n)
  916. int fd;
  917. char *buf;
  918. int n;
  919. {
  920. int count;
  921. /*
  922. *
  923. * Used to replace some of the write() calls. Again only needed if we're running
  924. * separate read and write processes. Should only be used to replace write calls
  925. * on ttyo. Always returns n to the caller if the process doesn't have its WRITE
  926. * flag set. Should also probably be a macro.
  927. *
  928. */
  929. if ( canwrite == TRUE ) {
  930. if ( (count = write(fd, buf, n)) == -1 && errno == EINTR )
  931. count = n;
  932. } else count = n;
  933. return(count);
  934. } /* End of Write */
  935. /*****************************************************************************/