📄 postio.c
字号:
/* * * postio - RS-232 serial interface for PostScript printers * * A simple program that manages input and output for PostScript printers. Much * has been added and changed from early versions of the program, but the basic * philosophy is still the same. Don't send real data until we're certain we've * connected to a PostScript printer that's in the idle state and try to hold the * connection until the job is completely done. It's more work than you might * expect is necessary, but should provide a reasonably reliable spooler interface * that can return error indications to the caller via the program's exit status. * * I've added code that will let you split the program into separate read/write * processes. Although it's not the default it should be useful if you have a file * that will be returning useful data from the printer. The two process stuff was * laid down on top of the single process code and both methods still work. The * implementation isn't as good as it could be, but didn't require many changes * to the original program (despite the fact that there are now many differences). * * By default the program still runs as a single process. The -R2 option forces * separate read and write processes after the intial connection is made. If you * want that as the default initialize splitme (below) to TRUE. In addition the * -t option that's used to force stuff not recognized as status reports to stdout * also tries to run as two processes (by setting splitme to TRUE). It will only * work if the required code (ie. resetline() in ifdef.c) has been implemented * for your Unix system. I've only tested the System V code. * * Code needed to support interactive mode has also been added, although again it's * not as efficient as it could be. It depends on the system dependent procedures * resetline() and setupstdin() (file ifdef.c) and for now is only guaranteed to * work on System V. Can be requested using the -i option. * * Quiet mode (-q option) is also new, but was needed for some printers connected * to RADIAN. If you're running in quiet mode no status requests will be sent to * the printer while files are being transmitted (ie. in send()). * * The program expects to receive printer status lines that look like, * * %%[ status: idle; source: serial 25 ]%% * %%[ status: waiting; source: serial 25 ]%% * %%[ status: initializing; source: serial 25 ]%% * %%[ status: busy; source: serial 25 ]%% * %%[ status: printing; source: serial 25 ]%% * %%[ status: PrinterError: out of paper; source: serial 25 ]%% * %%[ status: PrinterError: no paper tray; source: serial 25 ]%% * * although this list isn't complete. Sending a '\024' (control T) character forces * the return of a status report. PostScript errors detected on the printer result * in the immediate transmission of special error messages that look like, * * %%[ Error: undefined; OffendingCommand: xxx ]%% * %%[ Flushing: rest of job (to end-of-file) will be ignored ]%% * * although we only use the Error and Flushing keywords. Finally conditions, like * being out of paper, result in other messages being sent back from the printer * over the communications line. Typical PrinterError messages look like, * * %%[ PrinterError: out of paper; source: serial 25 ]%% * %%[ PrinterError: paper jam; source: serial 25 ]%% * * although we only use the PrinterError keyword rather than trying to recognize * all possible printer errors. * * The implications of using one process and only flow controlling data going to * the printer are obvious. Job transmission should be reliable, but there can be * data loss in stuff sent back from the printer. Usually that only caused problems * with jobs designed to run on the printer and return useful data back over the * communications line. If that's the kind of job you're sending call postio with * the -t option. That should force the program to split into separate read and * write processes and everything not bracketed by "%%[ " and " ]%%" strings goes * to stdout. In otherwords the data you're expecting should be separated from the * status stuff that goes to the log file (or stderr). The -R2 option does almost * the same thing (ie. separate read and write processes), but everything that * comes back from the printer goes to the log file (stderr by default) and you'll * have to separate your data from any printer messages. * * A typical command line might be, * * postio -l /dev/tty01 -b 9600 -L log file1 file2 * * where -l selects the line, -b sets the baud rate, and -L selects the printer * log file. Since there's no default line, at least not right now, you'll always * need to use the -l option, and if you don't choose a log file stderr will be * used. If you have a program that will be returning data the command line might * look like, * * postio -t -l/dev/tty01 -b9600 -Llog file >results * * Status stuff goes to file log while the data you're expecting back from the * printer gets put in file results. * */#include <stdio.h>#include <ctype.h>#include <fcntl.h>#include <signal.h>#include <sys/types.h>#include <errno.h>#include "ifdef.h" /* conditional compilation stuff */#include "gen.h" /* general purpose definitions */#include "postio.h" /* some special definitions */char **argv; /* global so everyone can use them */int argc;char *prog_name = ""; /* really just for error messages */int x_stat = 0; /* program exit status */int debug = OFF; /* debug flag */int ignore = OFF; /* what's done for FATAL errors */char *line = NULL; /* printer is on this tty line */short baudrate = BAUDRATE; /* and running at this baud rate */Baud baudtable[] = BAUDTABLE; /* converts strings to termio values */int stopbits = 1; /* number of stop bits */int tostdout = FALSE; /* non-status stuff goes to stdout? */int quiet = FALSE; /* no status queries in send() if TRUE */int interactive = FALSE; /* interactive mode */char *postbegin = POSTBEGIN; /* preceeds all the input files */int useslowsend = FALSE; /* not recommended! */int sendctrlC = TRUE; /* interrupt with ctrl-C when BUSY */int window_size = -1; /* for Datakit - use -w */char *block = NULL; /* input file buffer */int blocksize = BLOCKSIZE; /* and its size in bytes */int head = 0; /* block[head] is the next character */int tail = 0; /* one past the last byte in block[] */int splitme = FALSE; /* into READ and WRITE processes if TRUE */int whatami = READWRITE; /* a READ or WRITE process - or both */int canread = TRUE; /* allow reads */int canwrite = TRUE; /* and writes if TRUE */int otherpid = -1; /* who gets signals if greater than 1 */int joinsig = SIGTRAP; /* reader gets this when writing is done */int writedone = FALSE; /* and then sets this to TRUE */char mesg[MESGSIZE]; /* exactly what came back on ttyi */char sbuf[MESGSIZE]; /* for parsing the message */int next = 0; /* next character goes in mesg[next] */char *mesgptr = NULL; /* printer message starts here in mesg[] */char *endmesg = NULL; /* as far as readline() can go in mesg[] */Status status[] = STATUS; /* for converting status strings */int nostatus = NOSTATUS; /* default getstatus() return value */int currentstate = NOTCONNECTED; /* what's happening START, SEND, or DONE */int ttyi = 0; /* input */int ttyo = 2; /* and output file descriptors */FILE *fp_log = stderr; /* log file for stuff from the printer *//*****************************************************************************/main(agc, agv) int agc; char *agv[];{/* * * A simple program that manages input and output for PostScript printers. Can run * as a single process or as separate read/write processes. What's done depends on * the value assigned to splitme when split() is called. * */ argc = agc; /* other routines may want them */ argv = agv; prog_name = argv[0]; /* really just for error messages */ init_signals(); /* sets up interrupt handling */ options(); /* get command line options */ initialize(); /* must be done after options() */ start(); /* make sure the printer is ready */ split(); /* into read/write processes - maybe */ arguments(); /* then send each input file */ done(); /* wait until the printer is finished */ cleanup(); /* make sure the write process stops */ exit(x_stat); /* everything probably went OK */} /* End of main *//*****************************************************************************/init_signals(){ void interrupt(); /* handles them if we catch signals *//* * * Makes sure we handle interrupts. The proper way to kill the program, if * necessary, is to do a kill -15. That forces a call to interrupt(), which in * turn tries to reset the printer and then exits with a non-zero status. If the * program is running as two processes, sending SIGTERM to either the parent or * child should clean things up. * */ if ( signal(SIGINT, interrupt) == SIG_IGN ) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGHUP, SIG_IGN); } else { signal(SIGHUP, interrupt); signal(SIGQUIT, interrupt); } /* End else */ signal(SIGTERM, interrupt);} /* End of init_sig *//*****************************************************************************/options(){ int ch; /* return value from getopt() */ char *optnames = "b:cil:qs:tw:B:L:P:R:SDI"; extern char *optarg; /* used by getopt() */ extern int optind;/* * * Reads and processes the command line options. The -R2, -t, and -i options all * force separate read and write processes by eventually setting splitme to TRUE * (check initialize()). The -S option is not recommended and should only be used * as a last resort! * */ while ( (ch = getopt(argc, argv, optnames)) != EOF ) { switch ( ch ) { case 'b': /* baud rate string */ baudrate = getbaud(optarg); break; case 'c': /* no ctrl-C's */ sendctrlC = FALSE; break; case 'i': /* interactive mode */ interactive = TRUE; break; case 'l': /* printer line */ line = optarg; break; case 'q': /* no status queries - for RADIAN? */ quiet = TRUE; break; case 's': /* use 2 stop bits - for UNISON? */ if ( (stopbits = atoi(optarg)) < 1 || stopbits > 2 ) stopbits = 1; break; case 't': /* non-status stuff goes to stdout */ tostdout = TRUE; break; case 'w': /* Datakit window size */ window_size = atoi(optarg); break; case 'B': /* set the job buffer size */ if ( (blocksize = atoi(optarg)) <= 0 ) blocksize = BLOCKSIZE; break; case 'L': /* printer log file */ if ( (fp_log = fopen(optarg, "w")) == NULL ) { fp_log = stderr; error(NON_FATAL, "can't open log file %s", optarg); } /* End if */ break; case 'P': /* initial PostScript code */ postbegin = optarg; break; case 'R': /* run as one or two processes */ if ( atoi(optarg) == 2 ) splitme = TRUE; else splitme = FALSE; break; case 'S': /* slow and kludged up version of send */ useslowsend = TRUE; break; case 'D': /* debug flag */ debug = ON; break; case 'I': /* ignore FATAL errors */ ignore = ON; break; case '?': /* don't understand the option */ error(FATAL, ""); break; default: /* don't know what to do for ch */ error(FATAL, "missing case for option %c\n", ch); break; } /* End switch */ } /* End while */ argc -= optind; /* get ready for non-option args */ argv += optind;} /* End of options *//*****************************************************************************/getbaud(rate) char *rate; /* string representing the baud rate */{ int i; /* for looking through baudtable[] *//* * * Called from options() to convert a baud rate string into an appropriate termio * value. *rate is looked up in baudtable[] and if it's found, the corresponding * value is returned to the caller. * */ for ( i = 0; baudtable[i].rate != NULL; i++ ) if ( strcmp(rate, baudtable[i].rate) == 0 ) return(baudtable[i].val); error(FATAL, "don't recognize baud rate %s", rate);} /* End of getbaud *//*****************************************************************************/initialize(){/* * * Initialization, a few checks, and a call to setupline() (file ifdef.c) to open * and configure the communications line. Settings for interactive mode always * take precedence. The setupstdin() call with an argument of 0 saves the current * terminal settings if interactive mode has been requested - otherwise nothing's * done. Unbuffering stdout (via the setbuf() call) isn't really needed on System V * since it's flushed whenever terminal input is requested. It's more efficient if * we buffer the stdout (on System V) but safer (for other versions of Unix) if we * include the setbuf() call. * */ whatami = READWRITE; /* always run start() as one process */ canread = canwrite = TRUE; if ( tostdout == TRUE ) /* force separate read/write processes */ splitme = TRUE; if ( interactive == TRUE ) { /* interactive mode settings always win */ quiet = FALSE; tostdout = FALSE; splitme = TRUE; blocksize = 1; postbegin = NULL; useslowsend = FALSE; nostatus = INTERACTIVE; setbuf(stdout, NULL); } /* End if */ if ( useslowsend == TRUE ) { /* last resort only - not recommended */ quiet = FALSE; splitme = FALSE; if ( blocksize > 1024 ) /* don't send too much all at once */ blocksize = 1024; } /* End if */ if ( tostdout == TRUE && fp_log == stderr ) fp_log = NULL; if ( line == NULL && (interactive == TRUE || tostdout == TRUE) ) error(FATAL, "a printer line must be supplied - use the -l option"); if ( (block = malloc(blocksize)) == NULL ) error(FATAL, "no memory"); endmesg = mesg + sizeof mesg - 2; /* one byte from last position in mesg */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -