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

unix.superglobalmegacorp.com

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