Annotation of researchv10no/cmd/post.src/postio/postio.c, revision 1.1.1.1

1.1       root        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: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.