2 |
- |
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 |
|
|
|
94 |
#include <stdio.h>
|
|
|
95 |
#include <ctype.h>
|
|
|
96 |
#include <fcntl.h>
|
|
|
97 |
#include <signal.h>
|
|
|
98 |
#include <sys/types.h>
|
|
|
99 |
#include <errno.h>
|
|
|
100 |
|
|
|
101 |
#include "ifdef.h" /* conditional compilation stuff */
|
|
|
102 |
#include "gen.h" /* general purpose definitions */
|
|
|
103 |
#include "postio.h" /* some special definitions */
|
|
|
104 |
|
|
|
105 |
char **argv; /* global so everyone can use them */
|
|
|
106 |
int argc;
|
|
|
107 |
|
|
|
108 |
char *prog_name = ""; /* really just for error messages */
|
|
|
109 |
int x_stat = 0; /* program exit status */
|
|
|
110 |
int debug = OFF; /* debug flag */
|
|
|
111 |
int ignore = OFF; /* what's done for FATAL errors */
|
|
|
112 |
|
|
|
113 |
char *line = NULL; /* printer is on this tty line */
|
|
|
114 |
short baudrate = BAUDRATE; /* and running at this baud rate */
|
|
|
115 |
Baud baudtable[] = BAUDTABLE; /* converts strings to termio values */
|
|
|
116 |
|
|
|
117 |
int stopbits = 1; /* number of stop bits */
|
|
|
118 |
int tostdout = FALSE; /* non-status stuff goes to stdout? */
|
|
|
119 |
int quiet = FALSE; /* no status queries in send() if TRUE */
|
|
|
120 |
int interactive = FALSE; /* interactive mode */
|
|
|
121 |
char *postbegin = POSTBEGIN; /* preceeds all the input files */
|
|
|
122 |
int useslowsend = FALSE; /* not recommended! */
|
|
|
123 |
int sendctrlC = TRUE; /* interrupt with ctrl-C when BUSY */
|
|
|
124 |
int window_size = -1; /* for Datakit - use -w */
|
|
|
125 |
|
|
|
126 |
char *block = NULL; /* input file buffer */
|
|
|
127 |
int blocksize = BLOCKSIZE; /* and its size in bytes */
|
|
|
128 |
int head = 0; /* block[head] is the next character */
|
|
|
129 |
int tail = 0; /* one past the last byte in block[] */
|
|
|
130 |
|
|
|
131 |
int splitme = FALSE; /* into READ and WRITE processes if TRUE */
|
|
|
132 |
int whatami = READWRITE; /* a READ or WRITE process - or both */
|
|
|
133 |
int canread = TRUE; /* allow reads */
|
|
|
134 |
int canwrite = TRUE; /* and writes if TRUE */
|
|
|
135 |
int otherpid = -1; /* who gets signals if greater than 1 */
|
|
|
136 |
int joinsig = SIGTRAP; /* reader gets this when writing is done */
|
|
|
137 |
int writedone = FALSE; /* and then sets this to TRUE */
|
|
|
138 |
|
|
|
139 |
char mesg[MESGSIZE]; /* exactly what came back on ttyi */
|
|
|
140 |
char sbuf[MESGSIZE]; /* for parsing the message */
|
|
|
141 |
int next = 0; /* next character goes in mesg[next] */
|
|
|
142 |
char *mesgptr = NULL; /* printer message starts here in mesg[] */
|
|
|
143 |
char *endmesg = NULL; /* as far as readline() can go in mesg[] */
|
|
|
144 |
|
|
|
145 |
Status status[] = STATUS; /* for converting status strings */
|
|
|
146 |
int nostatus = NOSTATUS; /* default getstatus() return value */
|
|
|
147 |
|
|
|
148 |
int currentstate = NOTCONNECTED; /* what's happening START, SEND, or DONE */
|
|
|
149 |
|
|
|
150 |
int ttyi = 0; /* input */
|
|
|
151 |
int ttyo = 2; /* and output file descriptors */
|
|
|
152 |
|
|
|
153 |
FILE *fp_log = stderr; /* log file for stuff from the printer */
|
|
|
154 |
|
|
|
155 |
/*****************************************************************************/
|
|
|
156 |
|
|
|
157 |
main(agc, agv)
|
|
|
158 |
|
|
|
159 |
int agc;
|
|
|
160 |
char *agv[];
|
|
|
161 |
|
|
|
162 |
{
|
|
|
163 |
|
|
|
164 |
/*
|
|
|
165 |
*
|
|
|
166 |
* A simple program that manages input and output for PostScript printers. Can run
|
|
|
167 |
* as a single process or as separate read/write processes. What's done depends on
|
|
|
168 |
* the value assigned to splitme when split() is called.
|
|
|
169 |
*
|
|
|
170 |
*/
|
|
|
171 |
|
|
|
172 |
argc = agc; /* other routines may want them */
|
|
|
173 |
argv = agv;
|
|
|
174 |
|
|
|
175 |
prog_name = argv[0]; /* really just for error messages */
|
|
|
176 |
|
|
|
177 |
init_signals(); /* sets up interrupt handling */
|
|
|
178 |
options(); /* get command line options */
|
|
|
179 |
initialize(); /* must be done after options() */
|
|
|
180 |
start(); /* make sure the printer is ready */
|
|
|
181 |
split(); /* into read/write processes - maybe */
|
|
|
182 |
arguments(); /* then send each input file */
|
|
|
183 |
done(); /* wait until the printer is finished */
|
|
|
184 |
cleanup(); /* make sure the write process stops */
|
|
|
185 |
|
|
|
186 |
exit(x_stat); /* everything probably went OK */
|
|
|
187 |
|
|
|
188 |
} /* End of main */
|
|
|
189 |
|
|
|
190 |
/*****************************************************************************/
|
|
|
191 |
|
|
|
192 |
init_signals()
|
|
|
193 |
|
|
|
194 |
{
|
|
|
195 |
|
|
|
196 |
void interrupt(); /* handles them if we catch signals */
|
|
|
197 |
|
|
|
198 |
/*
|
|
|
199 |
*
|
|
|
200 |
* Makes sure we handle interrupts. The proper way to kill the program, if
|
|
|
201 |
* necessary, is to do a kill -15. That forces a call to interrupt(), which in
|
|
|
202 |
* turn tries to reset the printer and then exits with a non-zero status. If the
|
|
|
203 |
* program is running as two processes, sending SIGTERM to either the parent or
|
|
|
204 |
* child should clean things up.
|
|
|
205 |
*
|
|
|
206 |
*/
|
|
|
207 |
|
|
|
208 |
if ( signal(SIGINT, interrupt) == SIG_IGN ) {
|
|
|
209 |
signal(SIGINT, SIG_IGN);
|
|
|
210 |
signal(SIGQUIT, SIG_IGN);
|
|
|
211 |
signal(SIGHUP, SIG_IGN);
|
|
|
212 |
} else {
|
|
|
213 |
signal(SIGHUP, interrupt);
|
|
|
214 |
signal(SIGQUIT, interrupt);
|
|
|
215 |
} /* End else */
|
|
|
216 |
|
|
|
217 |
signal(SIGTERM, interrupt);
|
|
|
218 |
|
|
|
219 |
} /* End of init_sig */
|
|
|
220 |
|
|
|
221 |
/*****************************************************************************/
|
|
|
222 |
|
|
|
223 |
options()
|
|
|
224 |
|
|
|
225 |
{
|
|
|
226 |
|
|
|
227 |
int ch; /* return value from getopt() */
|
|
|
228 |
char *optnames = "b:cil:qs:tw:B:L:P:R:SDI";
|
|
|
229 |
|
|
|
230 |
extern char *optarg; /* used by getopt() */
|
|
|
231 |
extern int optind;
|
|
|
232 |
|
|
|
233 |
/*
|
|
|
234 |
*
|
|
|
235 |
* Reads and processes the command line options. The -R2, -t, and -i options all
|
|
|
236 |
* force separate read and write processes by eventually setting splitme to TRUE
|
|
|
237 |
* (check initialize()). The -S option is not recommended and should only be used
|
|
|
238 |
* as a last resort!
|
|
|
239 |
*
|
|
|
240 |
*/
|
|
|
241 |
|
|
|
242 |
while ( (ch = getopt(argc, argv, optnames)) != EOF ) {
|
|
|
243 |
switch ( ch ) {
|
|
|
244 |
case 'b': /* baud rate string */
|
|
|
245 |
baudrate = getbaud(optarg);
|
|
|
246 |
break;
|
|
|
247 |
|
|
|
248 |
case 'c': /* no ctrl-C's */
|
|
|
249 |
sendctrlC = FALSE;
|
|
|
250 |
break;
|
|
|
251 |
|
|
|
252 |
case 'i': /* interactive mode */
|
|
|
253 |
interactive = TRUE;
|
|
|
254 |
break;
|
|
|
255 |
|
|
|
256 |
case 'l': /* printer line */
|
|
|
257 |
line = optarg;
|
|
|
258 |
break;
|
|
|
259 |
|
|
|
260 |
case 'q': /* no status queries - for RADIAN? */
|
|
|
261 |
quiet = TRUE;
|
|
|
262 |
break;
|
|
|
263 |
|
|
|
264 |
case 's': /* use 2 stop bits - for UNISON? */
|
|
|
265 |
if ( (stopbits = atoi(optarg)) < 1 || stopbits > 2 )
|
|
|
266 |
stopbits = 1;
|
|
|
267 |
break;
|
|
|
268 |
|
|
|
269 |
case 't': /* non-status stuff goes to stdout */
|
|
|
270 |
tostdout = TRUE;
|
|
|
271 |
break;
|
|
|
272 |
|
|
|
273 |
case 'w': /* Datakit window size */
|
|
|
274 |
window_size = atoi(optarg);
|
|
|
275 |
break;
|
|
|
276 |
|
|
|
277 |
case 'B': /* set the job buffer size */
|
|
|
278 |
if ( (blocksize = atoi(optarg)) <= 0 )
|
|
|
279 |
blocksize = BLOCKSIZE;
|
|
|
280 |
break;
|
|
|
281 |
|
|
|
282 |
case 'L': /* printer log file */
|
|
|
283 |
if ( (fp_log = fopen(optarg, "w")) == NULL ) {
|
|
|
284 |
fp_log = stderr;
|
|
|
285 |
error(NON_FATAL, "can't open log file %s", optarg);
|
|
|
286 |
} /* End if */
|
|
|
287 |
break;
|
|
|
288 |
|
|
|
289 |
case 'P': /* initial PostScript code */
|
|
|
290 |
postbegin = optarg;
|
|
|
291 |
break;
|
|
|
292 |
|
|
|
293 |
case 'R': /* run as one or two processes */
|
|
|
294 |
if ( atoi(optarg) == 2 )
|
|
|
295 |
splitme = TRUE;
|
|
|
296 |
else splitme = FALSE;
|
|
|
297 |
break;
|
|
|
298 |
|
|
|
299 |
case 'S': /* slow and kludged up version of send */
|
|
|
300 |
useslowsend = TRUE;
|
|
|
301 |
break;
|
|
|
302 |
|
|
|
303 |
case 'D': /* debug flag */
|
|
|
304 |
debug = ON;
|
|
|
305 |
break;
|
|
|
306 |
|
|
|
307 |
case 'I': /* ignore FATAL errors */
|
|
|
308 |
ignore = ON;
|
|
|
309 |
break;
|
|
|
310 |
|
|
|
311 |
case '?': /* don't understand the option */
|
|
|
312 |
error(FATAL, "");
|
|
|
313 |
break;
|
|
|
314 |
|
|
|
315 |
default: /* don't know what to do for ch */
|
|
|
316 |
error(FATAL, "missing case for option %c\n", ch);
|
|
|
317 |
break;
|
|
|
318 |
} /* End switch */
|
|
|
319 |
} /* End while */
|
|
|
320 |
|
|
|
321 |
argc -= optind; /* get ready for non-option args */
|
|
|
322 |
argv += optind;
|
|
|
323 |
|
|
|
324 |
} /* End of options */
|
|
|
325 |
|
|
|
326 |
/*****************************************************************************/
|
|
|
327 |
|
|
|
328 |
getbaud(rate)
|
|
|
329 |
|
|
|
330 |
char *rate; /* string representing the baud rate */
|
|
|
331 |
|
|
|
332 |
{
|
|
|
333 |
|
|
|
334 |
int i; /* for looking through baudtable[] */
|
|
|
335 |
|
|
|
336 |
/*
|
|
|
337 |
*
|
|
|
338 |
* Called from options() to convert a baud rate string into an appropriate termio
|
|
|
339 |
* value. *rate is looked up in baudtable[] and if it's found, the corresponding
|
|
|
340 |
* value is returned to the caller.
|
|
|
341 |
*
|
|
|
342 |
*/
|
|
|
343 |
|
|
|
344 |
for ( i = 0; baudtable[i].rate != NULL; i++ )
|
|
|
345 |
if ( strcmp(rate, baudtable[i].rate) == 0 )
|
|
|
346 |
return(baudtable[i].val);
|
|
|
347 |
|
|
|
348 |
error(FATAL, "don't recognize baud rate %s", rate);
|
|
|
349 |
|
|
|
350 |
} /* End of getbaud */
|
|
|
351 |
|
|
|
352 |
/*****************************************************************************/
|
|
|
353 |
|
|
|
354 |
initialize()
|
|
|
355 |
|
|
|
356 |
{
|
|
|
357 |
|
|
|
358 |
/*
|
|
|
359 |
*
|
|
|
360 |
* Initialization, a few checks, and a call to setupline() (file ifdef.c) to open
|
|
|
361 |
* and configure the communications line. Settings for interactive mode always
|
|
|
362 |
* take precedence. The setupstdin() call with an argument of 0 saves the current
|
|
|
363 |
* terminal settings if interactive mode has been requested - otherwise nothing's
|
|
|
364 |
* done. Unbuffering stdout (via the setbuf() call) isn't really needed on System V
|
|
|
365 |
* since it's flushed whenever terminal input is requested. It's more efficient if
|
|
|
366 |
* we buffer the stdout (on System V) but safer (for other versions of Unix) if we
|
|
|
367 |
* include the setbuf() call.
|
|
|
368 |
*
|
|
|
369 |
*/
|
|
|
370 |
|
|
|
371 |
whatami = READWRITE; /* always run start() as one process */
|
|
|
372 |
canread = canwrite = TRUE;
|
|
|
373 |
|
|
|
374 |
if ( tostdout == TRUE ) /* force separate read/write processes */
|
|
|
375 |
splitme = TRUE;
|
|
|
376 |
|
|
|
377 |
if ( interactive == TRUE ) { /* interactive mode settings always win */
|
|
|
378 |
quiet = FALSE;
|
|
|
379 |
tostdout = FALSE;
|
|
|
380 |
splitme = TRUE;
|
|
|
381 |
blocksize = 1;
|
|
|
382 |
postbegin = NULL;
|
|
|
383 |
useslowsend = FALSE;
|
|
|
384 |
nostatus = INTERACTIVE;
|
|
|
385 |
setbuf(stdout, NULL);
|
|
|
386 |
} /* End if */
|
|
|
387 |
|
|
|
388 |
if ( useslowsend == TRUE ) { /* last resort only - not recommended */
|
|
|
389 |
quiet = FALSE;
|
|
|
390 |
splitme = FALSE;
|
|
|
391 |
if ( blocksize > 1024 ) /* don't send too much all at once */
|
|
|
392 |
blocksize = 1024;
|
|
|
393 |
} /* End if */
|
|
|
394 |
|
|
|
395 |
if ( tostdout == TRUE && fp_log == stderr )
|
|
|
396 |
fp_log = NULL;
|
|
|
397 |
|
|
|
398 |
if ( line == NULL && (interactive == TRUE || tostdout == TRUE) )
|
|
|
399 |
error(FATAL, "a printer line must be supplied - use the -l option");
|
|
|
400 |
|
|
|
401 |
if ( (block = malloc(blocksize)) == NULL )
|
|
|
402 |
error(FATAL, "no memory");
|
|
|
403 |
|
|
|
404 |
endmesg = mesg + sizeof mesg - 2; /* one byte from last position in mesg */
|
|
|
405 |
|
|
|
406 |
setupline(); /* configure the communications line */
|
|
|
407 |
setupstdin(0); /* save current stdin terminal settings */
|
|
|
408 |
|
|
|
409 |
} /* End of initialize */
|
|
|
410 |
|
|
|
411 |
/*****************************************************************************/
|
|
|
412 |
|
|
|
413 |
start()
|
|
|
414 |
|
|
|
415 |
{
|
|
|
416 |
|
|
|
417 |
/*
|
|
|
418 |
*
|
|
|
419 |
* Tries to put the printer in the IDLE state before anything important is sent.
|
|
|
420 |
* Run as a single process no matter what has been assigned to splitme. Separate
|
|
|
421 |
* read and write processes, if requested, will be created after we're done here.
|
|
|
422 |
*
|
|
|
423 |
*/
|
|
|
424 |
|
|
|
425 |
logit("printer startup\n");
|
|
|
426 |
|
|
|
427 |
currentstate = START;
|
|
|
428 |
clearline();
|
|
|
429 |
|
|
|
430 |
while ( 1 )
|
|
|
431 |
switch ( getstatus(1) ) {
|
|
|
432 |
case IDLE:
|
|
|
433 |
case INTERACTIVE:
|
|
|
434 |
if ( postbegin != NULL && *postbegin != '\0' )
|
|
|
435 |
Write(ttyo, postbegin, strlen(postbegin));
|
|
|
436 |
clearline();
|
|
|
437 |
return;
|
|
|
438 |
|
|
|
439 |
case BUSY:
|
|
|
440 |
if ( sendctrlC == TRUE ) {
|
|
|
441 |
Write(ttyo, "\003", 1);
|
|
|
442 |
Rest(1);
|
|
|
443 |
} /* End if */
|
|
|
444 |
break;
|
|
|
445 |
|
|
|
446 |
case WAITING:
|
|
|
447 |
case ERROR:
|
|
|
448 |
case FLUSHING:
|
|
|
449 |
Write(ttyo, "\004", 1);
|
|
|
450 |
Rest(1);
|
|
|
451 |
break;
|
|
|
452 |
|
|
|
453 |
case PRINTERERROR:
|
|
|
454 |
Rest(15);
|
|
|
455 |
break;
|
|
|
456 |
|
|
|
457 |
case DISCONNECT:
|
|
|
458 |
error(FATAL, "Disconnected - printer may be offline");
|
|
|
459 |
break;
|
|
|
460 |
|
|
|
461 |
case ENDOFJOB:
|
|
|
462 |
case UNKNOWN:
|
|
|
463 |
clearline();
|
|
|
464 |
break;
|
|
|
465 |
|
|
|
466 |
default:
|
|
|
467 |
Rest(1);
|
|
|
468 |
break;
|
|
|
469 |
} /* End switch */
|
|
|
470 |
|
|
|
471 |
} /* End of start */
|
|
|
472 |
|
|
|
473 |
/*****************************************************************************/
|
|
|
474 |
|
|
|
475 |
split()
|
|
|
476 |
|
|
|
477 |
{
|
|
|
478 |
|
|
|
479 |
int pid;
|
|
|
480 |
void interrupt();
|
|
|
481 |
|
|
|
482 |
/*
|
|
|
483 |
*
|
|
|
484 |
* If splitme is TRUE we fork a process, make the parent handle reading, and let
|
|
|
485 |
* the child take care of writing. resetline() (file ifdef.c) contains all the
|
|
|
486 |
* system dependent code needed to reset the communications line for separate
|
|
|
487 |
* read and write processes. For now it's expected to return TRUE or FALSE and
|
|
|
488 |
* that value controls whether we try the fork. I've only tested the two process
|
|
|
489 |
* stuff for System V. Other versions of resetline() may just be dummy procedures
|
|
|
490 |
* that always return FALSE. If the fork() failed previous versions continued as
|
|
|
491 |
* a single process, although the implementation wasn't quite right, but I've now
|
|
|
492 |
* decided to quit. The main reason is a Datakit channel may be configured to
|
|
|
493 |
* flow control data in both directions, and if we run postio over that channel
|
|
|
494 |
* as a single process we likely will end up in deadlock.
|
|
|
495 |
*
|
|
|
496 |
*/
|
|
|
497 |
|
|
|
498 |
if ( splitme == TRUE )
|
|
|
499 |
if ( resetline() == TRUE ) {
|
|
|
500 |
pid = getpid();
|
|
|
501 |
signal(joinsig, interrupt);
|
|
|
502 |
if ( (otherpid = fork()) == -1 )
|
|
|
503 |
error(FATAL, "can't fork");
|
|
|
504 |
else if ( otherpid == 0 ) {
|
|
|
505 |
whatami = WRITE;
|
|
|
506 |
nostatus = WRITEPROCESS;
|
|
|
507 |
otherpid = pid;
|
|
|
508 |
setupstdin(1);
|
|
|
509 |
} else whatami = READ;
|
|
|
510 |
} else if ( interactive == TRUE || tostdout == TRUE )
|
|
|
511 |
error(FATAL, "can't create two process - check resetline()");
|
|
|
512 |
else error(NON_FATAL, "running as a single process - check resetline()");
|
|
|
513 |
|
|
|
514 |
canread = (whatami & READ) ? TRUE : FALSE;
|
|
|
515 |
canwrite = (whatami & WRITE) ? TRUE : FALSE;
|
|
|
516 |
|
|
|
517 |
} /* End of split */
|
|
|
518 |
|
|
|
519 |
/*****************************************************************************/
|
|
|
520 |
|
|
|
521 |
arguments()
|
|
|
522 |
|
|
|
523 |
{
|
|
|
524 |
|
|
|
525 |
int fd_in; /* next input file */
|
|
|
526 |
|
|
|
527 |
/*
|
|
|
528 |
*
|
|
|
529 |
* Makes sure all the non-option command line arguments are processed. If there
|
|
|
530 |
* aren't any arguments left when we get here we'll send stdin. Input files are
|
|
|
531 |
* only read and sent to the printer if canwrite is TRUE. Checking it here means
|
|
|
532 |
* we won't have to do it in send(). If interactive mode is TRUE we'll stay here
|
|
|
533 |
* forever sending stdin when we run out of files - exit with a break. Actually
|
|
|
534 |
* the loop is bogus and used at most once when we're in interactive mode because
|
|
|
535 |
* stdin is in a pseudo raw mode and the read() in readblock() should never see
|
|
|
536 |
* the end of file.
|
|
|
537 |
*
|
|
|
538 |
*/
|
|
|
539 |
|
|
|
540 |
if ( canwrite == TRUE )
|
|
|
541 |
do /* loop is for interactive mode */
|
|
|
542 |
if ( argc < 1 )
|
|
|
543 |
send(fileno(stdin), "pipe.end");
|
|
|
544 |
else {
|
|
|
545 |
while ( argc > 0 ) {
|
|
|
546 |
if ( (fd_in = open(*argv, O_RDONLY)) == -1 )
|
|
|
547 |
error(FATAL, "can't open %s", *argv);
|
|
|
548 |
send(fd_in, *argv);
|
|
|
549 |
close(fd_in);
|
|
|
550 |
argc--;
|
|
|
551 |
argv++;
|
|
|
552 |
} /* End while */
|
|
|
553 |
} /* End else */
|
|
|
554 |
while ( interactive == TRUE );
|
|
|
555 |
|
|
|
556 |
} /* End of arguments */
|
|
|
557 |
|
|
|
558 |
/*****************************************************************************/
|
|
|
559 |
|
|
|
560 |
send(fd_in, name)
|
|
|
561 |
|
|
|
562 |
int fd_in; /* next input file */
|
|
|
563 |
char *name; /* and it's pathname */
|
|
|
564 |
|
|
|
565 |
{
|
|
|
566 |
|
|
|
567 |
/*
|
|
|
568 |
*
|
|
|
569 |
* Sends file *name to the printer. There's nothing left here that depends on
|
|
|
570 |
* sending and receiving status reports, although it can be reassuring to know
|
|
|
571 |
* the printer is responding and processing our job. Only the writer gets here
|
|
|
572 |
* in the two process implementation, and in that case split() has reset nostatus
|
|
|
573 |
* to WRITEPROCESS and that's what getstatus() always returns. For now we accept
|
|
|
574 |
* the IDLE state and ENDOFJOB as legitimate and ignore the INITIALIZING state.
|
|
|
575 |
*
|
|
|
576 |
*/
|
|
|
577 |
|
|
|
578 |
if ( interactive == FALSE )
|
|
|
579 |
logit("sending file %s\n", name);
|
|
|
580 |
|
|
|
581 |
currentstate = SEND;
|
|
|
582 |
|
|
|
583 |
if ( useslowsend == TRUE ) {
|
|
|
584 |
slowsend(fd_in);
|
|
|
585 |
return;
|
|
|
586 |
} /* End if */
|
|
|
587 |
|
|
|
588 |
while ( readblock(fd_in) )
|
|
|
589 |
switch ( getstatus(0) ) {
|
|
|
590 |
case IDLE:
|
|
|
591 |
case BUSY:
|
|
|
592 |
case WAITING:
|
|
|
593 |
case PRINTING:
|
|
|
594 |
case ENDOFJOB:
|
|
|
595 |
case PRINTERERROR:
|
|
|
596 |
case UNKNOWN:
|
|
|
597 |
case NOSTATUS:
|
|
|
598 |
case WRITEPROCESS:
|
|
|
599 |
case INTERACTIVE:
|
|
|
600 |
writeblock();
|
|
|
601 |
break;
|
|
|
602 |
|
|
|
603 |
case ERROR:
|
|
|
604 |
fprintf(stderr, "%s", mesg); /* for csw */
|
|
|
605 |
error(USER_FATAL, "PostScript Error");
|
|
|
606 |
break;
|
|
|
607 |
|
|
|
608 |
case FLUSHING:
|
|
|
609 |
error(USER_FATAL, "Flushing Job");
|
|
|
610 |
break;
|
|
|
611 |
|
|
|
612 |
case DISCONNECT:
|
|
|
613 |
error(FATAL, "Disconnected - printer may be offline");
|
|
|
614 |
break;
|
|
|
615 |
} /* End switch */
|
|
|
616 |
|
|
|
617 |
} /* End of send */
|
|
|
618 |
|
|
|
619 |
/*****************************************************************************/
|
|
|
620 |
|
|
|
621 |
done()
|
|
|
622 |
|
|
|
623 |
{
|
|
|
624 |
|
|
|
625 |
int sleeptime = 15; /* for 'out of paper' etc. */
|
|
|
626 |
|
|
|
627 |
/*
|
|
|
628 |
*
|
|
|
629 |
* Tries to stay connected to the printer until we're reasonably sure the job is
|
|
|
630 |
* complete. It's the only way we can recover error messages or data generated by
|
|
|
631 |
* the PostScript program and returned over the communication line. Actually doing
|
|
|
632 |
* it correctly for all possible PostScript jobs is more difficult that it might
|
|
|
633 |
* seem. For example if we've sent several jobs, each with their own EOF mark, then
|
|
|
634 |
* waiting for ENDOFJOB won't guarantee all the jobs have completed. Even waiting
|
|
|
635 |
* for IDLE isn't good enough. Checking for the WAITING state after all the files
|
|
|
636 |
* have been sent and then sending an EOF may be the best approach, but even that
|
|
|
637 |
* won't work all the time - we could miss it or might not get there. Even sending
|
|
|
638 |
* our own special PostScript job after all the input files has it's own different
|
|
|
639 |
* set of problems, but probably could work (perhaps by printing a fake status
|
|
|
640 |
* message or just not timing out). Anyway it's probably not worth the trouble so
|
|
|
641 |
* for now we'll quit if writedone is TRUE and we get ENDOFJOB or IDLE.
|
|
|
642 |
*
|
|
|
643 |
* If we're running separate read and write processes the reader gets here after
|
|
|
644 |
* after split() while the writer goes to send() and only gets here after all the
|
|
|
645 |
* input files have been transmitted. When they're both here the writer sends the
|
|
|
646 |
* reader signal joinsig and that forces writedone to TRUE in the reader. At that
|
|
|
647 |
* point the reader can begin looking for an indication of the end of the job.
|
|
|
648 |
* The writer hangs around until the reader kills it (usually in cleanup()) sending
|
|
|
649 |
* occasional status requests.
|
|
|
650 |
*
|
|
|
651 |
*/
|
|
|
652 |
|
|
|
653 |
if ( canwrite == TRUE )
|
|
|
654 |
logit("waiting for end of job\n");
|
|
|
655 |
|
|
|
656 |
currentstate = DONE;
|
|
|
657 |
writedone = (whatami == READWRITE) ? TRUE : FALSE;
|
|
|
658 |
|
|
|
659 |
while ( 1 ) {
|
|
|
660 |
switch ( getstatus(1) ) {
|
|
|
661 |
|
|
|
662 |
case WRITEPROCESS:
|
|
|
663 |
if ( writedone == FALSE ) {
|
|
|
664 |
sendsignal(joinsig);
|
|
|
665 |
Write(ttyo, "\004", 1);
|
|
|
666 |
writedone = TRUE;
|
|
|
667 |
sleeptime = 1;
|
|
|
668 |
} /* End if */
|
|
|
669 |
Rest(sleeptime++);
|
|
|
670 |
break;
|
|
|
671 |
|
|
|
672 |
case WAITING:
|
|
|
673 |
Write(ttyo, "\004", 1);
|
|
|
674 |
Rest(1);
|
|
|
675 |
sleeptime = 15;
|
|
|
676 |
break;
|
|
|
677 |
|
|
|
678 |
case IDLE:
|
|
|
679 |
case ENDOFJOB:
|
|
|
680 |
if ( writedone == TRUE ) {
|
|
|
681 |
logit("job complete\n");
|
|
|
682 |
return;
|
|
|
683 |
} /* End if */
|
|
|
684 |
break;
|
|
|
685 |
|
|
|
686 |
case BUSY:
|
|
|
687 |
case PRINTING:
|
|
|
688 |
case INTERACTIVE:
|
|
|
689 |
sleeptime = 15;
|
|
|
690 |
break;
|
|
|
691 |
|
|
|
692 |
case PRINTERERROR:
|
|
|
693 |
Rest(sleeptime++);
|
|
|
694 |
break;
|
|
|
695 |
|
|
|
696 |
case ERROR:
|
|
|
697 |
fprintf(stderr, "%s", mesg); /* for csw */
|
|
|
698 |
error(USER_FATAL, "PostScript Error");
|
|
|
699 |
return;
|
|
|
700 |
|
|
|
701 |
case FLUSHING:
|
|
|
702 |
error(USER_FATAL, "Flushing Job");
|
|
|
703 |
return;
|
|
|
704 |
|
|
|
705 |
case DISCONNECT:
|
|
|
706 |
error(FATAL, "Disconnected - printer may be offline");
|
|
|
707 |
return;
|
|
|
708 |
|
|
|
709 |
default:
|
|
|
710 |
Rest(1);
|
|
|
711 |
break;
|
|
|
712 |
} /* End switch */
|
|
|
713 |
|
|
|
714 |
if ( sleeptime > 60 )
|
|
|
715 |
sleeptime = 60;
|
|
|
716 |
} /* End while */
|
|
|
717 |
|
|
|
718 |
} /* End of done */
|
|
|
719 |
|
|
|
720 |
/*****************************************************************************/
|
|
|
721 |
|
|
|
722 |
cleanup()
|
|
|
723 |
|
|
|
724 |
{
|
|
|
725 |
|
|
|
726 |
int w;
|
|
|
727 |
|
|
|
728 |
/*
|
|
|
729 |
*
|
|
|
730 |
* Only needed if we're running separate read and write processes. Makes sure the
|
|
|
731 |
* write process is killed after the read process has successfully finished with
|
|
|
732 |
* all the jobs. sendsignal() returns a -1 if there's nobody to signal so things
|
|
|
733 |
* work when we're running a single process.
|
|
|
734 |
*
|
|
|
735 |
*/
|
|
|
736 |
|
|
|
737 |
while ( sendsignal(SIGKILL) != -1 && (w = wait((int *)0)) != otherpid && w != -1 ) ;
|
|
|
738 |
|
|
|
739 |
} /* End of cleanup */
|
|
|
740 |
|
|
|
741 |
/*****************************************************************************/
|
|
|
742 |
|
|
|
743 |
readblock(fd_in)
|
|
|
744 |
|
|
|
745 |
int fd_in; /* current input file */
|
|
|
746 |
|
|
|
747 |
{
|
|
|
748 |
|
|
|
749 |
static long blocknum = 1;
|
|
|
750 |
|
|
|
751 |
/*
|
|
|
752 |
*
|
|
|
753 |
* Fills the input buffer with the next block, provided we're all done with the
|
|
|
754 |
* last one. Blocks from fd_in are stored in array block[]. head is the index
|
|
|
755 |
* of the next byte in block[] that's supposed to go to the printer. tail points
|
|
|
756 |
* one past the last byte in the current block. head is adjusted in writeblock()
|
|
|
757 |
* after each successful write, while head and tail are reset here each time
|
|
|
758 |
* a new block is read. Returns the number of bytes left in the current block.
|
|
|
759 |
* Read errors cause the program to abort. The fake status message that's put out
|
|
|
760 |
* in quiet mode is only so you can look at the log file and know something's
|
|
|
761 |
* happening - take it out if you want.
|
|
|
762 |
*
|
|
|
763 |
*/
|
|
|
764 |
|
|
|
765 |
if ( head >= tail ) { /* done with the last block */
|
|
|
766 |
if ( (tail = read(fd_in, block, blocksize)) == -1 )
|
|
|
767 |
error(FATAL, "error reading input file");
|
|
|
768 |
if ( quiet == TRUE && tail > 0 ) /* put out a fake message? */
|
|
|
769 |
logit("%%%%[ status: busy; block: %d ]%%%%\n", blocknum++);
|
|
|
770 |
head = 0;
|
|
|
771 |
} /* End if */
|
|
|
772 |
|
|
|
773 |
return(tail - head);
|
|
|
774 |
|
|
|
775 |
} /* End of readblock */
|
|
|
776 |
|
|
|
777 |
/*****************************************************************************/
|
|
|
778 |
|
|
|
779 |
writeblock()
|
|
|
780 |
|
|
|
781 |
{
|
|
|
782 |
|
|
|
783 |
int count; /* bytes successfully written */
|
|
|
784 |
|
|
|
785 |
/*
|
|
|
786 |
*
|
|
|
787 |
* Called from send() when it's OK to send the next block to the printer. head
|
|
|
788 |
* is adjusted after the write, and the number of bytes that were successfully
|
|
|
789 |
* written is returned to the caller.
|
|
|
790 |
*
|
|
|
791 |
*/
|
|
|
792 |
|
|
|
793 |
if ( (count = write(ttyo, &block[head], tail - head)) == -1 )
|
|
|
794 |
error(FATAL, "error writing to %s", line);
|
|
|
795 |
else if ( count == 0 )
|
|
|
796 |
error(FATAL, "printer appears to be offline");
|
|
|
797 |
|
|
|
798 |
head += count;
|
|
|
799 |
return(count);
|
|
|
800 |
|
|
|
801 |
} /* End of writeblock */
|
|
|
802 |
|
|
|
803 |
/*****************************************************************************/
|
|
|
804 |
|
|
|
805 |
getstatus(t)
|
|
|
806 |
|
|
|
807 |
int t; /* sleep time after sending '\024' */
|
|
|
808 |
|
|
|
809 |
{
|
|
|
810 |
|
|
|
811 |
int gotline = FALSE; /* value returned by readline() */
|
|
|
812 |
int state = nostatus; /* representation of the current state */
|
|
|
813 |
int mesgch; /* to restore mesg[] when tostdout == TRUE */
|
|
|
814 |
|
|
|
815 |
static int laststate = NOSTATUS; /* last state recognized */
|
|
|
816 |
|
|
|
817 |
/*
|
|
|
818 |
*
|
|
|
819 |
* Looks for things coming back from the printer on the communications line, parses
|
|
|
820 |
* complete lines retrieved by readline(), and returns an integer representation
|
|
|
821 |
* of the current printer status to the caller. If nothing was available a status
|
|
|
822 |
* request (control T) is sent to the printer and nostatus is returned to the
|
|
|
823 |
* caller (provided quiet isn't TRUE). Interactive mode either never returns from
|
|
|
824 |
* readline() or returns FALSE.
|
|
|
825 |
*
|
|
|
826 |
*/
|
|
|
827 |
|
|
|
828 |
if ( canread == TRUE && (gotline = readline()) == TRUE ) {
|
|
|
829 |
state = parsemesg();
|
|
|
830 |
if ( state != laststate || state == UNKNOWN || mesgptr != mesg || debug == ON )
|
|
|
831 |
logit("%s", mesg);
|
|
|
832 |
|
|
|
833 |
if ( tostdout == TRUE && currentstate != START ) {
|
|
|
834 |
mesgch = *mesgptr;
|
|
|
835 |
*mesgptr = '\0';
|
|
|
836 |
fprintf(stdout, "%s", mesg);
|
|
|
837 |
fflush(stdout);
|
|
|
838 |
*mesgptr = mesgch; /* for ERROR in send() and done() */
|
|
|
839 |
} /* End if */
|
|
|
840 |
return(laststate = state);
|
|
|
841 |
} /* End if */
|
|
|
842 |
|
|
|
843 |
if ( (quiet == FALSE || currentstate != SEND) &&
|
|
|
844 |
(tostdout == FALSE || currentstate == START) && interactive == FALSE ) {
|
|
|
845 |
if ( Write(ttyo, "\024", 1) != 1 )
|
|
|
846 |
error(FATAL, "printer appears to be offline");
|
|
|
847 |
if ( t > 0 ) Rest(t);
|
|
|
848 |
} /* End if */
|
|
|
849 |
|
|
|
850 |
return(nostatus);
|
|
|
851 |
|
|
|
852 |
} /* End of getstatus */
|
|
|
853 |
|
|
|
854 |
/*****************************************************************************/
|
|
|
855 |
|
|
|
856 |
parsemesg()
|
|
|
857 |
|
|
|
858 |
{
|
|
|
859 |
|
|
|
860 |
char *e; /* end of printer message in mesg[] */
|
|
|
861 |
char *key, *val; /* keyword/value strings in sbuf[] */
|
|
|
862 |
char *p; /* for converting to lower case etc. */
|
|
|
863 |
int i; /* where *key was found in status[] */
|
|
|
864 |
|
|
|
865 |
/*
|
|
|
866 |
*
|
|
|
867 |
* Parsing the lines that readline() stores in mesg[] is messy, and what's done
|
|
|
868 |
* here isn't completely correct nor as fast as it could be. The general format
|
|
|
869 |
* of lines that come back from the printer (assuming no data loss) is:
|
|
|
870 |
*
|
|
|
871 |
* str%%[ key: val; key: val; key: val ]%%\n
|
|
|
872 |
*
|
|
|
873 |
* where str can be most anything not containing a newline and printer reports
|
|
|
874 |
* (eg. status or error messages) are bracketed by "%%[ " and " ]%%" strings and
|
|
|
875 |
* end with a newline. Usually we'll have the string or printer report but not
|
|
|
876 |
* both. For most jobs the leading string will be empty, but could be anything
|
|
|
877 |
* generated on a printer and returned over the communications line using the
|
|
|
878 |
* PostScript print operator. I'll assume PostScript jobs are well behaved and
|
|
|
879 |
* never bracket their messages with "%%[ " and " ]%%" strings that delimit status
|
|
|
880 |
* or error messages.
|
|
|
881 |
*
|
|
|
882 |
* Printer reports consist of one or more key/val pairs, and what we're interested
|
|
|
883 |
* in (status or error indications) may not be the first pair in the list. In
|
|
|
884 |
* addition we'll sometimes want the value associated with a keyword (eg. when
|
|
|
885 |
* key = status) and other times we'll want the keyword (eg. when key = Error or
|
|
|
886 |
* Flushing). The last pair isn't terminated by a semicolon and a value string
|
|
|
887 |
* often contains many space separated words and it can even include colons in
|
|
|
888 |
* meaningful places. I've also decided to continue converting things to lower
|
|
|
889 |
* case before doing the lookup in status[]. The isupper() test is for Berkeley
|
|
|
890 |
* systems.
|
|
|
891 |
*
|
|
|
892 |
*/
|
|
|
893 |
|
|
|
894 |
if ( *(mesgptr = find("%%[ ", mesg)) != '\0' && *(e = find(" ]%%", mesgptr+4)) != '\0' ) {
|
|
|
895 |
strcpy(sbuf, mesgptr+4); /* don't change mesg[] */
|
|
|
896 |
sbuf[e-mesgptr-4] = '\0'; /* ignore the trailing " ]%%" */
|
|
|
897 |
|
|
|
898 |
for ( key = strtok(sbuf, " :"); key != NULL; key = strtok(NULL, " :") ) {
|
|
|
899 |
if ( (val = strtok(NULL, ";")) != NULL && strcmp(key, "status") == 0 )
|
|
|
900 |
key = val;
|
|
|
901 |
|
|
|
902 |
for ( ; *key == ' '; key++ ) ; /* skip any leading spaces */
|
|
|
903 |
for ( p = key; *p; p++ ) /* convert to lower case */
|
|
|
904 |
if ( *p == ':' ) {
|
|
|
905 |
*p = '\0';
|
|
|
906 |
break;
|
|
|
907 |
} else if ( isupper(*p) ) *p = tolower(*p);
|
|
|
908 |
|
|
|
909 |
for ( i = 0; status[i].state != NULL; i++ )
|
|
|
910 |
if ( strcmp(status[i].state, key) == 0 )
|
|
|
911 |
return(status[i].val);
|
|
|
912 |
} /* End for */
|
|
|
913 |
} else if ( strcmp(mesg, "CONVERSATION ENDED.\n") == 0 )
|
|
|
914 |
return(DISCONNECT);
|
|
|
915 |
|
|
|
916 |
return(mesgptr == '\0' ? nostatus : UNKNOWN);
|
|
|
917 |
|
|
|
918 |
} /* End of parsemesg */
|
|
|
919 |
|
|
|
920 |
/*****************************************************************************/
|
|
|
921 |
|
|
|
922 |
char *find(str1, str2)
|
|
|
923 |
|
|
|
924 |
char *str1; /* look for this string */
|
|
|
925 |
char *str2; /* in this one */
|
|
|
926 |
|
|
|
927 |
{
|
|
|
928 |
|
|
|
929 |
char *s1, *s2; /* can't change str1 or str2 too fast */
|
|
|
930 |
|
|
|
931 |
/*
|
|
|
932 |
*
|
|
|
933 |
* Looks for *str1 in string *str2. Returns a pointer to the start of the substring
|
|
|
934 |
* if it's found or to the end of string str2 otherwise.
|
|
|
935 |
*
|
|
|
936 |
*/
|
|
|
937 |
|
|
|
938 |
for ( ; *str2 != '\0'; str2++ ) {
|
|
|
939 |
for ( s1 = str1, s2 = str2; *s1 != '\0' && *s1 == *s2; s1++, s2++ ) ;
|
|
|
940 |
if ( *s1 == '\0' )
|
|
|
941 |
break;
|
|
|
942 |
} /* End for */
|
|
|
943 |
|
|
|
944 |
return(str2);
|
|
|
945 |
|
|
|
946 |
} /* End of find */
|
|
|
947 |
|
|
|
948 |
/*****************************************************************************/
|
|
|
949 |
|
|
|
950 |
clearline()
|
|
|
951 |
|
|
|
952 |
{
|
|
|
953 |
|
|
|
954 |
/*
|
|
|
955 |
*
|
|
|
956 |
* Reads characters from the input line until nothing's left. Don't do anything if
|
|
|
957 |
* we're currently running separate read and write processes.
|
|
|
958 |
*
|
|
|
959 |
*/
|
|
|
960 |
|
|
|
961 |
if ( whatami == READWRITE )
|
|
|
962 |
while ( readline() != FALSE ) ;
|
|
|
963 |
|
|
|
964 |
} /* End of clearline */
|
|
|
965 |
|
|
|
966 |
/*****************************************************************************/
|
|
|
967 |
|
|
|
968 |
sendsignal(sig)
|
|
|
969 |
|
|
|
970 |
int sig; /* this goes to the other process */
|
|
|
971 |
|
|
|
972 |
{
|
|
|
973 |
|
|
|
974 |
/*
|
|
|
975 |
*
|
|
|
976 |
* Sends signal sig to the other process if we're running as separate read and
|
|
|
977 |
* write processes. Returns the result of the kill if there's someone else to
|
|
|
978 |
* signal or -1 if we're running alone.
|
|
|
979 |
*
|
|
|
980 |
*/
|
|
|
981 |
|
|
|
982 |
if ( whatami != READWRITE && otherpid > 1 )
|
|
|
983 |
return(kill(otherpid, sig));
|
|
|
984 |
|
|
|
985 |
return(-1);
|
|
|
986 |
|
|
|
987 |
} /* End of sendsignal */
|
|
|
988 |
|
|
|
989 |
/*****************************************************************************/
|
|
|
990 |
|
|
|
991 |
void interrupt(sig)
|
|
|
992 |
|
|
|
993 |
int sig; /* signal that we caught */
|
|
|
994 |
|
|
|
995 |
{
|
|
|
996 |
|
|
|
997 |
/*
|
|
|
998 |
*
|
|
|
999 |
* Caught a signal - all except joinsig cause the program to quit. joinsig is the
|
|
|
1000 |
* signal sent by the writer to the reader after all the jobs have been transmitted.
|
|
|
1001 |
* Used to tell the read process when it can start looking for the end of the job.
|
|
|
1002 |
*
|
|
|
1003 |
*/
|
|
|
1004 |
|
|
|
1005 |
signal(sig, SIG_IGN);
|
|
|
1006 |
|
|
|
1007 |
if ( sig != joinsig ) {
|
|
|
1008 |
x_stat |= FATAL;
|
|
|
1009 |
if ( canread == TRUE )
|
|
|
1010 |
if ( interactive == FALSE )
|
|
|
1011 |
error(NON_FATAL, "signal %d abort", sig);
|
|
|
1012 |
else error(NON_FATAL, "quitting");
|
|
|
1013 |
quit(sig);
|
|
|
1014 |
} /* End if */
|
|
|
1015 |
|
|
|
1016 |
writedone = TRUE;
|
|
|
1017 |
signal(joinsig, interrupt);
|
|
|
1018 |
|
|
|
1019 |
} /* End of interrupt */
|
|
|
1020 |
|
|
|
1021 |
/*****************************************************************************/
|
|
|
1022 |
|
|
|
1023 |
logit(mesg, a1, a2, a3)
|
|
|
1024 |
|
|
|
1025 |
char *mesg; /* control string */
|
|
|
1026 |
unsigned a1, a2, a3; /* and possible arguments */
|
|
|
1027 |
|
|
|
1028 |
{
|
|
|
1029 |
|
|
|
1030 |
/*
|
|
|
1031 |
*
|
|
|
1032 |
* Simple routine that's used to write a message to the log file.
|
|
|
1033 |
*
|
|
|
1034 |
*/
|
|
|
1035 |
|
|
|
1036 |
if ( mesg != NULL && fp_log != NULL ) {
|
|
|
1037 |
fprintf(fp_log, mesg, a1, a2, a3);
|
|
|
1038 |
fflush(fp_log);
|
|
|
1039 |
} /* End if */
|
|
|
1040 |
|
|
|
1041 |
} /* End of logit */
|
|
|
1042 |
|
|
|
1043 |
/*****************************************************************************/
|
|
|
1044 |
|
|
|
1045 |
error(kind, mesg, a1, a2, a3)
|
|
|
1046 |
|
|
|
1047 |
int kind; /* FATAL or NON_FATAL error */
|
|
|
1048 |
char *mesg; /* error message control string */
|
|
|
1049 |
unsigned a1, a2, a3; /* control string arguments */
|
|
|
1050 |
|
|
|
1051 |
{
|
|
|
1052 |
|
|
|
1053 |
FILE *fp_err;
|
|
|
1054 |
|
|
|
1055 |
/*
|
|
|
1056 |
*
|
|
|
1057 |
* Called when we've run into some kind of program error. First *mesg is printed
|
|
|
1058 |
* using the control string arguments a?. If kind is FATAL and we're not ignoring
|
|
|
1059 |
* errors the program will be terminated. If mesg is NULL or *mesg is the NULL
|
|
|
1060 |
* string nothing will be printed.
|
|
|
1061 |
*
|
|
|
1062 |
*/
|
|
|
1063 |
|
|
|
1064 |
fp_err = (fp_log != NULL) ? fp_log : stderr;
|
|
|
1065 |
|
|
|
1066 |
if ( mesg != NULL && *mesg != '\0' ) {
|
|
|
1067 |
fprintf(fp_err, "%s: ", prog_name);
|
|
|
1068 |
fprintf(fp_err, mesg, a1, a2, a3);
|
|
|
1069 |
putc('\n', fp_err);
|
|
|
1070 |
} /* End if */
|
|
|
1071 |
|
|
|
1072 |
x_stat |= kind;
|
|
|
1073 |
|
|
|
1074 |
if ( kind != NON_FATAL && ignore == OFF )
|
|
|
1075 |
quit(SIGTERM);
|
|
|
1076 |
|
|
|
1077 |
} /* End of error */
|
|
|
1078 |
|
|
|
1079 |
/*****************************************************************************/
|
|
|
1080 |
|
|
|
1081 |
quit(sig)
|
|
|
1082 |
|
|
|
1083 |
int sig;
|
|
|
1084 |
|
|
|
1085 |
{
|
|
|
1086 |
|
|
|
1087 |
int w;
|
|
|
1088 |
|
|
|
1089 |
/*
|
|
|
1090 |
*
|
|
|
1091 |
* Makes sure everything is properly cleaned up if there's a signal or FATAL error
|
|
|
1092 |
* that should cause the program to terminate. The sleep by the write process is
|
|
|
1093 |
* to help give the reset sequence a chance to reach the printer before we break
|
|
|
1094 |
* the connection - primarily for printers connected to Datakit. There's a very
|
|
|
1095 |
* slight chance the reset sequence that's sent to the printer could get us stuck
|
|
|
1096 |
* here. Simplest solution is don't bother to send it - everything works without it.
|
|
|
1097 |
* Flushing ttyo would be better, but means yet another system dependent procedure
|
|
|
1098 |
* in ifdef.c! I'll leave things be for now.
|
|
|
1099 |
*
|
|
|
1100 |
* Obscure problem on PS-810 turbos says wait a bit after sending an interrupt.
|
|
|
1101 |
* Seem to remember the printer getting into a bad state immediately after the
|
|
|
1102 |
* top was opened when the toner light was on. A sleep after sending the ctrl-C
|
|
|
1103 |
* seemed to fix things.
|
|
|
1104 |
*
|
|
|
1105 |
*/
|
|
|
1106 |
|
|
|
1107 |
signal(sig, SIG_IGN);
|
|
|
1108 |
ignore = ON;
|
|
|
1109 |
|
|
|
1110 |
while ( sendsignal(sig) != -1 && (w = wait((int *)0)) != otherpid && w != -1 ) ;
|
|
|
1111 |
|
|
|
1112 |
setupstdin(2);
|
|
|
1113 |
|
|
|
1114 |
if ( currentstate != NOTCONNECTED ) {
|
|
|
1115 |
if ( sendctrlC == TRUE ) {
|
|
|
1116 |
Write(ttyo, "\003", 1);
|
|
|
1117 |
Rest(1); /* PS-810 turbo problem?? */
|
|
|
1118 |
} /* End if */
|
|
|
1119 |
Write(ttyo, "\004", 1);
|
|
|
1120 |
} /* End if */
|
|
|
1121 |
|
|
|
1122 |
alarm(0); /* prevents sleep() loop on V9 systems */
|
|
|
1123 |
Rest(2);
|
|
|
1124 |
|
|
|
1125 |
exit(x_stat);
|
|
|
1126 |
|
|
|
1127 |
} /* End of quit */
|
|
|
1128 |
|
|
|
1129 |
/*****************************************************************************/
|
|
|
1130 |
|
|
|
1131 |
Rest(t)
|
|
|
1132 |
|
|
|
1133 |
int t;
|
|
|
1134 |
|
|
|
1135 |
{
|
|
|
1136 |
|
|
|
1137 |
/*
|
|
|
1138 |
*
|
|
|
1139 |
* Used to replace sleep() calls. Only needed if we're running the program as
|
|
|
1140 |
* a read and write process and don't want to have the read process sleep. Most
|
|
|
1141 |
* sleeps are in the code because of the non-blocking read used by the single
|
|
|
1142 |
* process implementation. Probably should be a macro.
|
|
|
1143 |
*
|
|
|
1144 |
*/
|
|
|
1145 |
|
|
|
1146 |
if ( t > 0 && canwrite == TRUE )
|
|
|
1147 |
sleep(t);
|
|
|
1148 |
|
|
|
1149 |
} /* End of Rest */
|
|
|
1150 |
|
|
|
1151 |
/*****************************************************************************/
|
|
|
1152 |
|
|
|
1153 |
Read(fd, buf, n)
|
|
|
1154 |
|
|
|
1155 |
int fd;
|
|
|
1156 |
char *buf;
|
|
|
1157 |
int n;
|
|
|
1158 |
|
|
|
1159 |
{
|
|
|
1160 |
|
|
|
1161 |
int count;
|
|
|
1162 |
|
|
|
1163 |
/*
|
|
|
1164 |
*
|
|
|
1165 |
* Used to replace some of the read() calls. Only needed if we're running separate
|
|
|
1166 |
* read and write processes. Should only be used to replace read calls on ttyi.
|
|
|
1167 |
* Always returns 0 to the caller if the process doesn't have its READ flag set.
|
|
|
1168 |
* Probably should be a macro.
|
|
|
1169 |
*
|
|
|
1170 |
*/
|
|
|
1171 |
|
|
|
1172 |
if ( canread == TRUE ) {
|
|
|
1173 |
if ( (count = read(fd, buf, n)) == -1 && errno == EINTR )
|
|
|
1174 |
count = 0;
|
|
|
1175 |
} else count = 0;
|
|
|
1176 |
|
|
|
1177 |
return(count);
|
|
|
1178 |
|
|
|
1179 |
} /* End of Read */
|
|
|
1180 |
|
|
|
1181 |
/*****************************************************************************/
|
|
|
1182 |
|
|
|
1183 |
Write(fd, buf, n)
|
|
|
1184 |
|
|
|
1185 |
int fd;
|
|
|
1186 |
char *buf;
|
|
|
1187 |
int n;
|
|
|
1188 |
|
|
|
1189 |
{
|
|
|
1190 |
|
|
|
1191 |
int count;
|
|
|
1192 |
|
|
|
1193 |
/*
|
|
|
1194 |
*
|
|
|
1195 |
* Used to replace some of the write() calls. Again only needed if we're running
|
|
|
1196 |
* separate read and write processes. Should only be used to replace write calls
|
|
|
1197 |
* on ttyo. Always returns n to the caller if the process doesn't have its WRITE
|
|
|
1198 |
* flag set. Should also probably be a macro.
|
|
|
1199 |
*
|
|
|
1200 |
*/
|
|
|
1201 |
|
|
|
1202 |
if ( canwrite == TRUE ) {
|
|
|
1203 |
if ( (count = write(fd, buf, n)) == -1 && errno == EINTR )
|
|
|
1204 |
count = n;
|
|
|
1205 |
} else count = n;
|
|
|
1206 |
|
|
|
1207 |
return(count);
|
|
|
1208 |
|
|
|
1209 |
} /* End of Write */
|
|
|
1210 |
|
|
|
1211 |
/*****************************************************************************/
|
|
|
1212 |
|