1: /*
   2:  * Copyright (c) 1983 Regents of the University of California.
   3:  * All rights reserved.  The Berkeley software License Agreement
   4:  * specifies the terms and conditions for redistribution.
   5:  */
   6: 
   7: #if !defined(lint) && defined(DOSCCS)
   8: static char sccsid[] = "@(#)printjob.c	5.2.3 (2.11BSD GTE) 1996/12/23";
   9: #endif
  10: 
  11: /*
  12:  * printjob -- print jobs in the queue.
  13:  *
  14:  *	NOTE: the lock file is used to pass information to lpq and lprm.
  15:  *	it does not need to be removed because file locks are dynamic.
  16:  */
  17: 
  18: #include "lp.h"
  19: #include <sys/time.h>
  20: 
  21: #define DORETURN    0   /* absorb fork error */
  22: #define DOABORT     1   /* abort if dofork fails */
  23: 
  24: /*
  25:  * Error tokens
  26:  */
  27: #define REPRINT     -2
  28: #define ERROR       -1
  29: #define OK      0
  30: #define FATALERR    1
  31: #define NOACCT      2
  32: #define FILTERERR   3
  33: #define ACCESS      4
  34: 
  35: char    title[80];      /* ``pr'' title */
  36: FILE    *cfp;           /* control file */
  37: int pfd;            /* printer file descriptor */
  38: int ofd;            /* output filter file descriptor */
  39: int lfd;            /* lock file descriptor */
  40: int pid;            /* pid of lpd process */
  41: int prchild;        /* id of pr process */
  42: int child;          /* id of any filters */
  43: int ofilter;        /* id of output filter, if any */
  44: int tof;            /* true if at top of form */
  45: int remote;         /* true if sending files to remote */
  46: dev_t   fdev;           /* device of file pointed to by symlink */
  47: ino_t   fino;           /* inode of file pointed to by symlink */
  48: 
  49: char    fromhost[64];       /* user's host machine */
  50: char    logname[32];        /* user's login name */
  51: char    jobname[100];       /* job or file name */
  52: char    class[32];      /* classification field */
  53: char    width[10] = "-w";   /* page width in characters */
  54: char    length[10] = "-l";  /* page length in lines */
  55: char    pxwidth[10] = "-x"; /* page width in pixels */
  56: char    pxlength[10] = "-y";    /* page length in pixels */
  57: char    indent[10] = "-i0"; /* indentation size in characters */
  58: char    tmpfile[] = "errsXXXXXX"; /* file name for filter output */
  59: 
  60: printjob()
  61: {
  62:     struct stat stb;
  63:     register struct queue *q, **qp;
  64:     struct queue **queue;
  65:     register int i, nitems;
  66:     long pidoff;
  67:     int count = 0;
  68:     extern int abortpr();
  69: 
  70:     init();                 /* set up capabilities */
  71:     (void) write(1, "", 1);         /* ack that daemon is started */
  72:     (void) close(2);            /* set up log file */
  73:     if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
  74:         syslog(LOG_ERR, "%s: %m", LF);
  75:         (void) open("/dev/null", O_WRONLY);
  76:     }
  77:     setgid(getegid());
  78:     pid = getpid();             /* for use with lprm */
  79:     setpgrp(0, pid);
  80:     signal(SIGHUP, abortpr);
  81:     signal(SIGINT, abortpr);
  82:     signal(SIGQUIT, abortpr);
  83:     signal(SIGTERM, abortpr);
  84: 
  85:     (void) mktemp(tmpfile);
  86: 
  87:     /*
  88: 	 * uses short form file names
  89: 	 */
  90:     if (chdir(SD) < 0) {
  91:         syslog(LOG_ERR, "%s: %m", SD);
  92:         exit(1);
  93:     }
  94:     if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
  95:         exit(0);        /* printing disabled */
  96:     lfd = open(LO, O_WRONLY|O_CREAT, 0644);
  97:     if (lfd < 0) {
  98:         syslog(LOG_ERR, "%s: %s: %m", printer, LO);
  99:         exit(1);
 100:     }
 101:     if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
 102:         if (errno == EWOULDBLOCK)   /* active deamon present */
 103:             exit(0);
 104:         syslog(LOG_ERR, "%s: %s: %m", printer, LO);
 105:         exit(1);
 106:     }
 107:     ftruncate(lfd, 0L);
 108:     /*
 109: 	 * write process id for others to know
 110: 	 */
 111:     sprintf(line, "%u\n", pid);
 112:     pidoff = i = strlen(line);
 113:     if (write(lfd, line, i) != i) {
 114:         syslog(LOG_ERR, "%s: %s: %m", printer, LO);
 115:         exit(1);
 116:     }
 117:     /*
 118: 	 * search the spool directory for work and sort by queue order.
 119: 	 */
 120:     if ((nitems = getq(&queue)) < 0) {
 121:         syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
 122:         exit(1);
 123:     }
 124:     if (nitems == 0)        /* no work to do */
 125:         exit(0);
 126:     if (stb.st_mode & 01) {     /* reset queue flag */
 127:         if (fchmod(lfd, stb.st_mode & 0776) < 0)
 128:             syslog(LOG_ERR, "%s: %s: %m", printer, LO);
 129:     }
 130:     openpr();           /* open printer or remote */
 131: again:
 132:     /*
 133: 	 * we found something to do now do it --
 134: 	 *    write the name of the current control file into the lock file
 135: 	 *    so the spool queue program can tell what we're working on
 136: 	 */
 137:     for (qp = queue; nitems--; free((char *) q)) {
 138:         q = *qp++;
 139:         if (stat(q->q_name, &stb) < 0)
 140:             continue;
 141:     restart:
 142:         (void) lseek(lfd, (off_t)pidoff, 0);
 143:         (void) sprintf(line, "%s\n", q->q_name);
 144:         i = strlen(line);
 145:         if (write(lfd, line, i) != i)
 146:             syslog(LOG_ERR, "%s: %s: %m", printer, LO);
 147:         if (!remote)
 148:             i = printit(q->q_name);
 149:         else
 150:             i = sendit(q->q_name);
 151:         /*
 152: 		 * Check to see if we are supposed to stop printing or
 153: 		 * if we are to rebuild the queue.
 154: 		 */
 155:         if (fstat(lfd, &stb) == 0) {
 156:             /* stop printing before starting next job? */
 157:             if (stb.st_mode & 0100)
 158:                 goto done;
 159:             /* rebuild queue (after lpc topq) */
 160:             if (stb.st_mode & 01) {
 161:                 for (free((char *) q); nitems--; free((char *) q))
 162:                     q = *qp++;
 163:                 if (fchmod(lfd, stb.st_mode & 0776) < 0)
 164:                     syslog(LOG_WARNING, "%s: %s: %m",
 165:                         printer, LO);
 166:                 break;
 167:             }
 168:         }
 169:         if (i == OK)        /* file ok and printed */
 170:             count++;
 171:         else if (i == REPRINT) { /* try reprinting the job */
 172:             syslog(LOG_INFO, "restarting %s", printer);
 173:             if (ofilter > 0) {
 174:                 kill(ofilter, SIGCONT); /* to be sure */
 175:                 (void) close(ofd);
 176:                 while ((i = wait(0)) > 0 && i != ofilter)
 177:                     ;
 178:                 ofilter = 0;
 179:             }
 180:             (void) close(pfd);  /* close printer */
 181:             if (ftruncate(lfd, (off_t)pidoff) < 0)
 182:                 syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
 183:             openpr();       /* try to reopen printer */
 184:             goto restart;
 185:         }
 186:     }
 187:     free((char *) queue);
 188:     /*
 189: 	 * search the spool directory for more work.
 190: 	 */
 191:     if ((nitems = getq(&queue)) < 0) {
 192:         syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
 193:         exit(1);
 194:     }
 195:     if (nitems == 0) {      /* no more work to do */
 196:     done:
 197:         if (count > 0) {    /* Files actually printed */
 198:             if (!SF && !tof)
 199:                 (void) write(ofd, FF, strlen(FF));
 200:             if (TR != NULL)     /* output trailer */
 201:                 (void) write(ofd, TR, strlen(TR));
 202:         }
 203:         (void) unlink(tmpfile);
 204:         exit(0);
 205:     }
 206:     goto again;
 207: }
 208: 
 209: char    fonts[4][50];   /* fonts for troff */
 210: 
 211: char *ifonts[4] = {
 212:     "/usr/share/vfont/R",
 213:     "/usr/share/vfont/I",
 214:     "/usr/share/vfont/B",
 215:     "/usr/share/vfont/S"
 216: };
 217: 
 218: /*
 219:  * The remaining part is the reading of the control file (cf)
 220:  * and performing the various actions.
 221:  */
 222: printit(file)
 223:     char *file;
 224: {
 225:     register int i;
 226:     char *cp;
 227:     int bombed = OK;
 228: 
 229:     /*
 230: 	 * open control file; ignore if no longer there.
 231: 	 */
 232:     if ((cfp = fopen(file, "r")) == NULL) {
 233:         syslog(LOG_INFO, "%s: %s: %m", printer, file);
 234:         return(OK);
 235:     }
 236:     /*
 237: 	 * Reset troff fonts.
 238: 	 */
 239:     for (i = 0; i < 4; i++)
 240:         strcpy(fonts[i], ifonts[i]);
 241:     sprintf(width+2, "%d", PW);
 242:     strcpy(indent+2, "0");
 243: 
 244:     /*
 245: 	 *      read the control file for work to do
 246: 	 *
 247: 	 *      file format -- first character in the line is a command
 248: 	 *      rest of the line is the argument.
 249: 	 *      valid commands are:
 250: 	 *
 251: 	 *		S -- "stat info" for symbolic link protection
 252: 	 *		J -- "job name" on banner page
 253: 	 *		C -- "class name" on banner page
 254: 	 *              L -- "literal" user's name to print on banner
 255: 	 *		T -- "title" for pr
 256: 	 *		H -- "host name" of machine where lpr was done
 257: 	 *              P -- "person" user's login name
 258: 	 *              I -- "indent" amount to indent output
 259: 	 *              f -- "file name" name of text file to print
 260: 	 *		l -- "file name" text file with control chars
 261: 	 *		p -- "file name" text file to print with pr(1)
 262: 	 *		t -- "file name" troff(1) file to print
 263: 	 *		n -- "file name" ditroff(1) file to print
 264: 	 *		d -- "file name" dvi file to print
 265: 	 *		g -- "file name" plot(1G) file to print
 266: 	 *		v -- "file name" plain raster file to print
 267: 	 *		c -- "file name" cifplot file to print
 268: 	 *		1 -- "R font file" for troff
 269: 	 *		2 -- "I font file" for troff
 270: 	 *		3 -- "B font file" for troff
 271: 	 *		4 -- "S font file" for troff
 272: 	 *		N -- "name" of file (used by lpq)
 273: 	 *              U -- "unlink" name of file to remove
 274: 	 *                    (after we print it. (Pass 2 only)).
 275: 	 *		M -- "mail" to user when done printing
 276: 	 *
 277: 	 *      getline reads a line and expands tabs to blanks
 278: 	 */
 279: 
 280:     /* pass 1 */
 281: 
 282:     while (getline(cfp))
 283:         switch (line[0]) {
 284:         case 'H':
 285:             strcpy(fromhost, line+1);
 286:             if (class[0] == '\0')
 287:                 strncpy(class, line+1, sizeof(class)-1);
 288:             continue;
 289: 
 290:         case 'P':
 291:             strncpy(logname, line+1, sizeof(logname)-1);
 292:             if (RS) {           /* restricted */
 293:                 if (getpwnam(logname) == (struct passwd *)0) {
 294:                     bombed = NOACCT;
 295:                     sendmail(line+1, bombed);
 296:                     goto pass2;
 297:                 }
 298:             }
 299:             continue;
 300: 
 301:         case 'S':
 302:             cp = line+1;
 303:             i = 0;
 304:             while (*cp >= '0' && *cp <= '9')
 305:                 i = i * 10 + (*cp++ - '0');
 306:             fdev = i;
 307:             cp++;
 308:             i = 0;
 309:             while (*cp >= '0' && *cp <= '9')
 310:                 i = i * 10 + (*cp++ - '0');
 311:             fino = i;
 312:             continue;
 313: 
 314:         case 'J':
 315:             if (line[1] != '\0')
 316:                 strncpy(jobname, line+1, sizeof(jobname)-1);
 317:             else
 318:                 strcpy(jobname, " ");
 319:             continue;
 320: 
 321:         case 'C':
 322:             if (line[1] != '\0')
 323:                 strncpy(class, line+1, sizeof(class)-1);
 324:             else if (class[0] == '\0')
 325:                 gethostname(class, sizeof(class));
 326:             continue;
 327: 
 328:         case 'T':   /* header title for pr */
 329:             strncpy(title, line+1, sizeof(title)-1);
 330:             continue;
 331: 
 332:         case 'L':   /* identification line */
 333:             if (!SH && !HL)
 334:                 banner(line+1, jobname);
 335:             continue;
 336: 
 337:         case '1':   /* troff fonts */
 338:         case '2':
 339:         case '3':
 340:         case '4':
 341:             if (line[1] != '\0')
 342:                 strcpy(fonts[line[0]-'1'], line+1);
 343:             continue;
 344: 
 345:         case 'W':   /* page width */
 346:             strncpy(width+2, line+1, sizeof(width)-3);
 347:             continue;
 348: 
 349:         case 'I':   /* indent amount */
 350:             strncpy(indent+2, line+1, sizeof(indent)-3);
 351:             continue;
 352: 
 353:         default:    /* some file to print */
 354:             switch (i = print(line[0], line+1)) {
 355:             case ERROR:
 356:                 if (bombed == OK)
 357:                     bombed = FATALERR;
 358:                 break;
 359:             case REPRINT:
 360:                 (void) fclose(cfp);
 361:                 return(REPRINT);
 362:             case FILTERERR:
 363:             case ACCESS:
 364:                 bombed = i;
 365:                 sendmail(logname, bombed);
 366:             }
 367:             title[0] = '\0';
 368:             continue;
 369: 
 370:         case 'N':
 371:         case 'U':
 372:         case 'M':
 373:             continue;
 374:         }
 375: 
 376:     /* pass 2 */
 377: 
 378: pass2:
 379:     fseek(cfp, 0L, 0);
 380:     while (getline(cfp))
 381:         switch (line[0]) {
 382:         case 'L':   /* identification line */
 383:             if (!SH && HL)
 384:                 banner(line+1, jobname);
 385:             continue;
 386: 
 387:         case 'M':
 388:             if (bombed < NOACCT)    /* already sent if >= NOACCT */
 389:                 sendmail(line+1, bombed);
 390:             continue;
 391: 
 392:         case 'U':
 393:             (void) unlink(line+1);
 394:         }
 395:     /*
 396: 	 * clean-up in case another control file exists
 397: 	 */
 398:     (void) fclose(cfp);
 399:     (void) unlink(file);
 400:     return(bombed == OK ? OK : ERROR);
 401: }
 402: 
 403: /*
 404:  * Print a file.
 405:  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
 406:  * Return -1 if a non-recoverable error occured,
 407:  * 2 if the filter detected some errors (but printed the job anyway),
 408:  * 1 if we should try to reprint this job and
 409:  * 0 if all is well.
 410:  * Note: all filters take stdin as the file, stdout as the printer,
 411:  * stderr as the log file, and must not ignore SIGINT.
 412:  */
 413: print(format, file)
 414:     int format;
 415:     char *file;
 416: {
 417:     register int n;
 418:     register char *prog;
 419:     int fi, fo;
 420:     char *av[15], buf[BUFSIZ];
 421:     int pid, p[2], stopped = 0;
 422:     union wait status;
 423:     struct stat stb;
 424: 
 425:     if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
 426:         return(ERROR);
 427:     /*
 428: 	 * Check to see if data file is a symbolic link. If so, it should
 429: 	 * still point to the same file or someone is trying to print
 430: 	 * something he shouldn't.
 431: 	 */
 432:     if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
 433:         (stb.st_dev != fdev || stb.st_ino != fino))
 434:         return(ACCESS);
 435:     if (!SF && !tof) {      /* start on a fresh page */
 436:         (void) write(ofd, FF, strlen(FF));
 437:         tof = 1;
 438:     }
 439:     if (IF == NULL && (format == 'f' || format == 'l')) {
 440:         tof = 0;
 441:         while ((n = read(fi, buf, BUFSIZ)) > 0)
 442:             if (write(ofd, buf, n) != n) {
 443:                 (void) close(fi);
 444:                 return(REPRINT);
 445:             }
 446:         (void) close(fi);
 447:         return(OK);
 448:     }
 449:     switch (format) {
 450:     case 'p':   /* print file using 'pr' */
 451:         if (IF == NULL) {   /* use output filter */
 452:             prog = PR;
 453:             av[0] = "pr";
 454:             av[1] = width;
 455:             av[2] = length;
 456:             av[3] = "-h";
 457:             av[4] = *title ? title : " ";
 458:             av[5] = 0;
 459:             fo = ofd;
 460:             goto start;
 461:         }
 462:         pipe(p);
 463:         if ((prchild = dofork(DORETURN)) == 0) {    /* child */
 464:             dup2(fi, 0);        /* file is stdin */
 465:             dup2(p[1], 1);      /* pipe is stdout */
 466:             for (n = 3; n < NOFILE; n++)
 467:                 (void) close(n);
 468:             execl(PR, "pr", width, length, "-h", *title ? title : " ", 0);
 469:             syslog(LOG_ERR, "cannot execl %s", PR);
 470:             exit(2);
 471:         }
 472:         (void) close(p[1]);     /* close output side */
 473:         (void) close(fi);
 474:         if (prchild < 0) {
 475:             prchild = 0;
 476:             (void) close(p[0]);
 477:             return(ERROR);
 478:         }
 479:         fi = p[0];          /* use pipe for input */
 480:     case 'f':   /* print plain text file */
 481:         prog = IF;
 482:         av[1] = width;
 483:         av[2] = length;
 484:         av[3] = indent;
 485:         n = 4;
 486:         break;
 487:     case 'l':   /* like 'f' but pass control characters */
 488:         prog = IF;
 489:         av[1] = "-c";
 490:         av[2] = width;
 491:         av[3] = length;
 492:         av[4] = indent;
 493:         n = 5;
 494:         break;
 495:     case 'r':   /* print a fortran text file */
 496:         prog = RF;
 497:         av[1] = width;
 498:         av[2] = length;
 499:         n = 3;
 500:         break;
 501:     case 't':   /* print troff output */
 502:     case 'n':   /* print ditroff output */
 503:     case 'd':   /* print tex output */
 504:         (void) unlink(".railmag");
 505:         if ((fo = creat(".railmag", FILMOD)) < 0) {
 506:             syslog(LOG_ERR, "%s: cannot create .railmag", printer);
 507:             (void) unlink(".railmag");
 508:         } else {
 509:             for (n = 0; n < 4; n++) {
 510:                 if (fonts[n][0] != '/')
 511:                     (void)write(fo, "/usr/share/vfont/",17);
 512:                 (void) write(fo, fonts[n], strlen(fonts[n]));
 513:                 (void) write(fo, "\n", 1);
 514:             }
 515:             (void) close(fo);
 516:         }
 517:         prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
 518:         av[1] = pxwidth;
 519:         av[2] = pxlength;
 520:         n = 3;
 521:         break;
 522:     case 'c':   /* print cifplot output */
 523:         prog = CF;
 524:         av[1] = pxwidth;
 525:         av[2] = pxlength;
 526:         n = 3;
 527:         break;
 528:     case 'g':   /* print plot(1G) output */
 529:         prog = GF;
 530:         av[1] = pxwidth;
 531:         av[2] = pxlength;
 532:         n = 3;
 533:         break;
 534:     case 'v':   /* print raster output */
 535:         prog = VF;
 536:         av[1] = pxwidth;
 537:         av[2] = pxlength;
 538:         n = 3;
 539:         break;
 540:     default:
 541:         (void) close(fi);
 542:         syslog(LOG_ERR, "%s: illegal format character '%c'",
 543:             printer, format);
 544:         return(ERROR);
 545:     }
 546:     if ((av[0] = rindex(prog, '/')) != NULL)
 547:         av[0]++;
 548:     else
 549:         av[0] = prog;
 550:     av[n++] = "-n";
 551:     av[n++] = logname;
 552:     av[n++] = "-h";
 553:     av[n++] = fromhost;
 554:     av[n++] = AF;
 555:     av[n] = 0;
 556:     fo = pfd;
 557:     if (ofilter > 0) {      /* stop output filter */
 558:         write(ofd, "\031\1", 2);
 559:         while ((pid = wait3(&status, WUNTRACED, 0)) > 0 && pid != ofilter)
 560:             ;
 561:         if (status.w_stopval != WSTOPPED) {
 562:             (void) close(fi);
 563:             syslog(LOG_WARNING, "%s: output filter died (%d)",
 564:                 printer, status.w_retcode);
 565:             return(REPRINT);
 566:         }
 567:         stopped++;
 568:     }
 569: start:
 570:     if ((child = dofork(DORETURN)) == 0) {  /* child */
 571:         dup2(fi, 0);
 572:         dup2(fo, 1);
 573:         n = open(tmpfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
 574:         if (n >= 0)
 575:             dup2(n, 2);
 576:         for (n = 3; n < NOFILE; n++)
 577:             (void) close(n);
 578:         execv(prog, av);
 579:         syslog(LOG_ERR, "cannot execv %s", prog);
 580:         exit(2);
 581:     }
 582:     (void) close(fi);
 583:     if (child < 0)
 584:         status.w_retcode = 100;
 585:     else
 586:         while ((pid = wait(&status)) > 0 && pid != child)
 587:             ;
 588:     child = 0;
 589:     prchild = 0;
 590:     if (stopped) {      /* restart output filter */
 591:         if (kill(ofilter, SIGCONT) < 0) {
 592:             syslog(LOG_ERR, "cannot restart output filter");
 593:             exit(1);
 594:         }
 595:     }
 596:     tof = 0;
 597:     if (!WIFEXITED(status)) {
 598:         syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
 599:             printer, format, status.w_termsig);
 600:         return(ERROR);
 601:     }
 602:     switch (status.w_retcode) {
 603:     case 0:
 604:         tof = 1;
 605:         return(OK);
 606:     case 1:
 607:         return(REPRINT);
 608:     default:
 609:         syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
 610:             printer, format, status.w_retcode);
 611:     case 2:
 612:         return(ERROR);
 613:     }
 614: }
 615: 
 616: /*
 617:  * Send the daemon control file (cf) and any data files.
 618:  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
 619:  * 0 if all is well.
 620:  */
 621: sendit(file)
 622:     char *file;
 623: {
 624:     register int i, err = OK;
 625:     char *cp, last[BUFSIZ];
 626: 
 627:     /*
 628: 	 * open control file
 629: 	 */
 630:     if ((cfp = fopen(file, "r")) == NULL)
 631:         return(OK);
 632:     /*
 633: 	 *      read the control file for work to do
 634: 	 *
 635: 	 *      file format -- first character in the line is a command
 636: 	 *      rest of the line is the argument.
 637: 	 *      commands of interest are:
 638: 	 *
 639: 	 *            a-z -- "file name" name of file to print
 640: 	 *              U -- "unlink" name of file to remove
 641: 	 *                    (after we print it. (Pass 2 only)).
 642: 	 */
 643: 
 644:     /*
 645: 	 * pass 1
 646: 	 */
 647:     while (getline(cfp)) {
 648:     again:
 649:         if (line[0] == 'S') {
 650:             cp = line+1;
 651:             i = 0;
 652:             while (*cp >= '0' && *cp <= '9')
 653:                 i = i * 10 + (*cp++ - '0');
 654:             fdev = i;
 655:             cp++;
 656:             i = 0;
 657:             while (*cp >= '0' && *cp <= '9')
 658:                 i = i * 10 + (*cp++ - '0');
 659:             fino = i;
 660:             continue;
 661:         }
 662:         if (line[0] >= 'a' && line[0] <= 'z') {
 663:             strcpy(last, line);
 664:             while (i = getline(cfp))
 665:                 if (strcmp(last, line))
 666:                     break;
 667:             switch (sendfile('\3', last+1)) {
 668:             case OK:
 669:                 if (i)
 670:                     goto again;
 671:                 break;
 672:             case REPRINT:
 673:                 (void) fclose(cfp);
 674:                 return(REPRINT);
 675:             case ACCESS:
 676:                 sendmail(logname, ACCESS);
 677:             case ERROR:
 678:                 err = ERROR;
 679:             }
 680:             break;
 681:         }
 682:     }
 683:     if (err == OK && sendfile('\2', file) > 0) {
 684:         (void) fclose(cfp);
 685:         return(REPRINT);
 686:     }
 687:     /*
 688: 	 * pass 2
 689: 	 */
 690:     fseek(cfp, 0L, 0);
 691:     while (getline(cfp))
 692:         if (line[0] == 'U')
 693:             (void) unlink(line+1);
 694:     /*
 695: 	 * clean-up in case another control file exists
 696: 	 */
 697:     (void) fclose(cfp);
 698:     (void) unlink(file);
 699:     return(err);
 700: }
 701: 
 702: /*
 703:  * Send a data file to the remote machine and spool it.
 704:  * Return positive if we should try resending.
 705:  */
 706: sendfile(type, file)
 707:     char type, *file;
 708: {
 709:     register int f, amt;
 710:     long i;
 711:     struct stat stb;
 712:     char buf[BUFSIZ];
 713:     int sizerr, resp;
 714: 
 715:     if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
 716:         return(ERROR);
 717:     /*
 718: 	 * Check to see if data file is a symbolic link. If so, it should
 719: 	 * still point to the same file or someone is trying to print something
 720: 	 * he shouldn't.
 721: 	 */
 722:     if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
 723:         (stb.st_dev != fdev || stb.st_ino != fino))
 724:         return(ACCESS);
 725:     (void) sprintf(buf, "%c%ld %s\n", type, stb.st_size, file);
 726:     amt = strlen(buf);
 727:     for (i = 0;  ; i++) {
 728:         if (write(pfd, buf, amt) != amt ||
 729:             (resp = response()) < 0 || resp == '\1') {
 730:             (void) close(f);
 731:             return(REPRINT);
 732:         } else if (resp == '\0')
 733:             break;
 734:         if (i == 0)
 735:             status("no space on remote; waiting for queue to drain");
 736:         if (i == 10)
 737:             syslog(LOG_ALERT, "%s: can't send to %s; queue full",
 738:                 printer, RM);
 739:         sleep(5 * 60);
 740:     }
 741:     if (i)
 742:         status("sending to %s", RM);
 743:     sizerr = 0;
 744:     for (i = 0; i < stb.st_size; i += BUFSIZ) {
 745:         amt = BUFSIZ;
 746:         if (i + amt > stb.st_size)
 747:             amt = stb.st_size - i;
 748:         if (sizerr == 0 && read(f, buf, amt) != amt)
 749:             sizerr = 1;
 750:         if (write(pfd, buf, amt) != amt) {
 751:             (void) close(f);
 752:             return(REPRINT);
 753:         }
 754:     }
 755:     (void) close(f);
 756:     if (sizerr) {
 757:         syslog(LOG_INFO, "%s: %s: changed size", printer, file);
 758:         /* tell recvjob to ignore this file */
 759:         (void) write(pfd, "\1", 1);
 760:         return(ERROR);
 761:     }
 762:     if (write(pfd, "", 1) != 1 || response())
 763:         return(REPRINT);
 764:     return(OK);
 765: }
 766: 
 767: /*
 768:  * Check to make sure there have been no errors and that both programs
 769:  * are in sync with eachother.
 770:  * Return non-zero if the connection was lost.
 771:  */
 772: response()
 773: {
 774:     char resp;
 775: 
 776:     if (read(pfd, &resp, 1) != 1) {
 777:         syslog(LOG_INFO, "%s: lost connection", printer);
 778:         return(-1);
 779:     }
 780:     return(resp);
 781: }
 782: 
 783: /*
 784:  * Banner printing stuff
 785:  */
 786: banner(name1, name2)
 787:     char *name1, *name2;
 788: {
 789:     time_t tvec;
 790: 
 791:     time(&tvec);
 792:     if (!SF && !tof)
 793:         (void) write(ofd, FF, strlen(FF));
 794:     if (SB) {   /* short banner only */
 795:         if (class[0]) {
 796:             (void) write(ofd, class, strlen(class));
 797:             (void) write(ofd, ":", 1);
 798:         }
 799:         (void) write(ofd, name1, strlen(name1));
 800:         (void) write(ofd, "  Job: ", 7);
 801:         (void) write(ofd, name2, strlen(name2));
 802:         (void) write(ofd, "  Date: ", 8);
 803:         (void) write(ofd, ctime(&tvec), 24);
 804:         (void) write(ofd, "\n", 1);
 805:     } else {    /* normal banner */
 806:         (void) write(ofd, "\n\n\n", 3);
 807:         scan_out(ofd, name1, '\0');
 808:         (void) write(ofd, "\n\n", 2);
 809:         scan_out(ofd, name2, '\0');
 810:         if (class[0]) {
 811:             (void) write(ofd,"\n\n\n",3);
 812:             scan_out(ofd, class, '\0');
 813:         }
 814:         (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
 815:         (void) write(ofd, name2, strlen(name2));
 816:         (void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
 817:         (void) write(ofd, ctime(&tvec), 24);
 818:         (void) write(ofd, "\n", 1);
 819:     }
 820:     if (!SF)
 821:         (void) write(ofd, FF, strlen(FF));
 822:     tof = 1;
 823: }
 824: 
 825: char *
 826: scnline(key, p, c)
 827:     register char key, *p;
 828:     char c;
 829: {
 830:     register scnwidth;
 831: 
 832:     for (scnwidth = WIDTH; --scnwidth;) {
 833:         key <<= 1;
 834:         *p++ = key & 0200 ? c : BACKGND;
 835:     }
 836:     return (p);
 837: }
 838: 
 839: #define TRC(q)  (((q)-' ')&0177)
 840: 
 841: scan_out(scfd, scsp, dlm)
 842:     int scfd;
 843:     char *scsp, dlm;
 844: {
 845:     register char *strp;
 846:     register nchrs, j;
 847:     char outbuf[LINELEN+1], *sp, c, cc;
 848:     int d, scnhgt;
 849:     extern char scnkey[][HEIGHT];   /* in lpdchar.c */
 850: 
 851:     for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
 852:         strp = &outbuf[0];
 853:         sp = scsp;
 854:         for (nchrs = 0; ; ) {
 855:             d = dropit(c = TRC(cc = *sp++));
 856:             if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
 857:                 for (j = WIDTH; --j;)
 858:                     *strp++ = BACKGND;
 859:             else
 860:                 strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
 861:             if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
 862:                 break;
 863:             *strp++ = BACKGND;
 864:             *strp++ = BACKGND;
 865:         }
 866:         while (*--strp == BACKGND && strp >= outbuf)
 867:             ;
 868:         strp++;
 869:         *strp++ = '\n';
 870:         (void) write(scfd, outbuf, strp-outbuf);
 871:     }
 872: }
 873: 
 874: dropit(c)
 875:     char c;
 876: {
 877:     switch(c) {
 878: 
 879:     case TRC('_'):
 880:     case TRC(';'):
 881:     case TRC(','):
 882:     case TRC('g'):
 883:     case TRC('j'):
 884:     case TRC('p'):
 885:     case TRC('q'):
 886:     case TRC('y'):
 887:         return (DROP);
 888: 
 889:     default:
 890:         return (0);
 891:     }
 892: }
 893: 
 894: /*
 895:  * sendmail ---
 896:  *   tell people about job completion
 897:  */
 898: sendmail(user, bombed)
 899:     char *user;
 900:     int bombed;
 901: {
 902:     register int i;
 903:     int p[2], s;
 904:     register char *cp;
 905:     char buf[100];
 906:     struct stat stb;
 907:     FILE *fp;
 908: 
 909:     pipe(p);
 910:     if ((s = dofork(DORETURN)) == 0) {      /* child */
 911:         dup2(p[0], 0);
 912:         for (i = 3; i < NOFILE; i++)
 913:             (void) close(i);
 914:         if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
 915:             cp++;
 916:         else
 917:             cp = _PATH_SENDMAIL;
 918:         sprintf(buf, "%s@%s", user, fromhost);
 919:         execl(_PATH_SENDMAIL, cp, buf, 0);
 920:         exit(0);
 921:     } else if (s > 0) {             /* parent */
 922:         dup2(p[1], 1);
 923:         printf("To: %s@%s\n", user, fromhost);
 924:         printf("Subject: printer job\n\n");
 925:         printf("Your printer job ");
 926:         if (*jobname)
 927:             printf("(%s) ", jobname);
 928:         switch (bombed) {
 929:         case OK:
 930:             printf("\ncompleted successfully\n");
 931:             break;
 932:         default:
 933:         case FATALERR:
 934:             printf("\ncould not be printed\n");
 935:             break;
 936:         case NOACCT:
 937:             printf("\ncould not be printed without an account on %s\n", host);
 938:             break;
 939:         case FILTERERR:
 940:             if (stat(tmpfile, &stb) < 0 || stb.st_size == 0 ||
 941:                 (fp = fopen(tmpfile, "r")) == NULL) {
 942:                 printf("\nwas printed but had some errors\n");
 943:                 break;
 944:             }
 945:             printf("\nwas printed but had the following errors:\n");
 946:             while ((i = getc(fp)) != EOF)
 947:                 putchar(i);
 948:             (void) fclose(fp);
 949:             break;
 950:         case ACCESS:
 951:             printf("\nwas not printed because it was not linked to the original file\n");
 952:         }
 953:         fflush(stdout);
 954:         (void) close(1);
 955:     }
 956:     (void) close(p[0]);
 957:     (void) close(p[1]);
 958:     wait(&s);
 959: }
 960: 
 961: /*
 962:  * dofork - fork with retries on failure
 963:  */
 964: dofork(action)
 965:     int action;
 966: {
 967:     register int i, pid;
 968: 
 969:     for (i = 0; i < 20; i++) {
 970:         if ((pid = fork()) < 0) {
 971:             sleep((unsigned)(i*i));
 972:             continue;
 973:         }
 974:         /*
 975: 		 * Child should run as daemon instead of root
 976: 		 */
 977:         if (pid == 0)
 978:             setuid(DU);
 979:         return(pid);
 980:     }
 981:     syslog(LOG_ERR, "can't fork");
 982: 
 983:     switch (action) {
 984:     case DORETURN:
 985:         return (-1);
 986:     default:
 987:         syslog(LOG_ERR, "bad action (%d) to dofork", action);
 988:         /*FALL THRU*/
 989:     case DOABORT:
 990:         exit(1);
 991:     }
 992:     /*NOTREACHED*/
 993: }
 994: 
 995: /*
 996:  * Kill child processes to abort current job.
 997:  */
 998: abortpr()
 999: {
1000:     (void) unlink(tmpfile);
1001:     kill(0, SIGINT);
1002:     if (ofilter > 0)
1003:         kill(ofilter, SIGCONT);
1004:     while (wait(0) > 0)
1005:         ;
1006:     exit(0);
1007: }
1008: 
1009: init()
1010: {
1011:     int status;
1012: 
1013:     if ((status = pgetent(line, printer)) < 0) {
1014:         syslog(LOG_ERR, "can't open printer description file");
1015:         exit(1);
1016:     } else if (status == 0) {
1017:         syslog(LOG_ERR, "unknown printer: %s", printer);
1018:         exit(1);
1019:     }
1020:     if ((LP = pgetstr("lp", &bp)) == NULL)
1021:         LP = DEFDEVLP;
1022:     if ((RP = pgetstr("rp", &bp)) == NULL)
1023:         RP = DEFLP;
1024:     if ((LO = pgetstr("lo", &bp)) == NULL)
1025:         LO = DEFLOCK;
1026:     if ((ST = pgetstr("st", &bp)) == NULL)
1027:         ST = DEFSTAT;
1028:     if ((LF = pgetstr("lf", &bp)) == NULL)
1029:         LF = DEFLOGF;
1030:     if ((SD = pgetstr("sd", &bp)) == NULL)
1031:         SD = DEFSPOOL;
1032:     if ((DU = pgetnum("du")) < 0)
1033:         DU = DEFUID;
1034:     if ((FF = pgetstr("ff", &bp)) == NULL)
1035:         FF = DEFFF;
1036:     if ((PW = pgetnum("pw")) < 0)
1037:         PW = DEFWIDTH;
1038:     sprintf(&width[2], "%d", PW);
1039:     if ((PL = pgetnum("pl")) < 0)
1040:         PL = DEFLENGTH;
1041:     sprintf(&length[2], "%d", PL);
1042:     if ((PX = pgetnum("px")) < 0)
1043:         PX = 0;
1044:     sprintf(&pxwidth[2], "%d", PX);
1045:     if ((PY = pgetnum("py")) < 0)
1046:         PY = 0;
1047:     sprintf(&pxlength[2], "%d", PY);
1048:     RM = pgetstr("rm", &bp);
1049:     /*
1050: 	 * Figure out whether the local machine is the same as the remote
1051: 	 * machine entry (if it exists).  If not, then ignore the local
1052: 	 * queue information.
1053: 	 */
1054:      if (RM != (char *) NULL) {
1055:         char name[256];
1056:         struct hostent *hp;
1057: 
1058:         /* get the standard network name of the local host */
1059:         gethostname(name, sizeof(name));
1060:         name[sizeof(name)-1] = '\0';
1061:         hp = gethostbyname(name);
1062:         if (hp == (struct hostent *) NULL) {
1063:             syslog(LOG_ERR,
1064:             "unable to get network name for local machine %s",
1065:             name);
1066:             goto localcheck_done;
1067:         } else strcpy(name, hp->h_name);
1068: 
1069:         /* get the standard network name of RM */
1070:         hp = gethostbyname(RM);
1071:         if (hp == (struct hostent *) NULL) {
1072:             syslog(LOG_ERR,
1073:             "unable to get hostname for remote machine %s", RM);
1074:             goto localcheck_done;
1075:         }
1076: 
1077:         /* if printer is not on local machine, ignore LP */
1078:         if (strcmp(name, hp->h_name) != 0) *LP = '\0';
1079:     }
1080: localcheck_done:
1081: 
1082:     AF = pgetstr("af", &bp);
1083:     OF = pgetstr("of", &bp);
1084:     IF = pgetstr("if", &bp);
1085:     RF = pgetstr("rf", &bp);
1086:     TF = pgetstr("tf", &bp);
1087:     NF = pgetstr("nf", &bp);
1088:     DF = pgetstr("df", &bp);
1089:     GF = pgetstr("gf", &bp);
1090:     VF = pgetstr("vf", &bp);
1091:     CF = pgetstr("cf", &bp);
1092:     TR = pgetstr("tr", &bp);
1093:     RS = pgetflag("rs");
1094:     SF = pgetflag("sf");
1095:     SH = pgetflag("sh");
1096:     SB = pgetflag("sb");
1097:     HL = pgetflag("hl");
1098:     RW = pgetflag("rw");
1099:     BR = pgetnum("br");
1100:     if ((FC = pgetnum("fc")) < 0)
1101:         FC = 0;
1102:     if ((FS = pgetnum("fs")) < 0)
1103:         FS = 0;
1104:     if ((XC = pgetnum("xc")) < 0)
1105:         XC = 0;
1106:     if ((XS = pgetnum("xs")) < 0)
1107:         XS = 0;
1108:     tof = !pgetflag("fo");
1109: }
1110: 
1111: /*
1112:  * Acquire line printer or remote connection.
1113:  */
1114: openpr()
1115: {
1116:     register int i, n;
1117:     int resp;
1118: 
1119:     if (*LP) {
1120:         for (i = 1; ; i = i < 32 ? i << 1 : i) {
1121:             pfd = open(LP, RW ? O_RDWR : O_WRONLY);
1122:             if (pfd >= 0)
1123:                 break;
1124:             if (errno == ENOENT) {
1125:                 syslog(LOG_ERR, "%s: %m", LP);
1126:                 exit(1);
1127:             }
1128:             if (i == 1)
1129:                 status("waiting for %s to become ready (offline ?)", printer);
1130:             sleep(i);
1131:         }
1132:         if (isatty(pfd))
1133:             setty();
1134:         status("%s is ready and printing", printer);
1135:     } else if (RM != NULL) {
1136:         for (i = 1; ; i = i < 256 ? i << 1 : i) {
1137:             resp = -1;
1138:             pfd = getport(RM);
1139:             if (pfd >= 0) {
1140:                 (void) sprintf(line, "\2%s\n", RP);
1141:                 n = strlen(line);
1142:                 if (write(pfd, line, n) == n &&
1143:                     (resp = response()) == '\0')
1144:                     break;
1145:                 (void) close(pfd);
1146:             }
1147:             if (i == 1) {
1148:                 if (resp < 0)
1149:                     status("waiting for %s to come up", RM);
1150:                 else {
1151:                     status("waiting for queue to be enabled on %s", RM);
1152:                     i = 256;
1153:                 }
1154:             }
1155:             sleep(i);
1156:         }
1157:         status("sending to %s", RM);
1158:         remote = 1;
1159:     } else {
1160:         syslog(LOG_ERR, "%s: no line printer device or host name",
1161:             printer);
1162:         exit(1);
1163:     }
1164:     /*
1165: 	 * Start up an output filter, if needed.
1166: 	 */
1167:     if (OF) {
1168:         int p[2];
1169:         char *cp;
1170: 
1171:         pipe(p);
1172:         if ((ofilter = dofork(DOABORT)) == 0) { /* child */
1173:             dup2(p[0], 0);      /* pipe is std in */
1174:             dup2(pfd, 1);       /* printer is std out */
1175:             for (i = 3; i < NOFILE; i++)
1176:                 (void) close(i);
1177:             if ((cp = rindex(OF, '/')) == NULL)
1178:                 cp = OF;
1179:             else
1180:                 cp++;
1181:             execl(OF, cp, width, length, 0);
1182:             syslog(LOG_ERR, "%s: %s: %m", printer, OF);
1183:             exit(1);
1184:         }
1185:         (void) close(p[0]);     /* close input side */
1186:         ofd = p[1];         /* use pipe for output */
1187:     } else {
1188:         ofd = pfd;
1189:         ofilter = 0;
1190:     }
1191: }
1192: 
1193: struct bauds {
1194:     int baud;
1195:     int speed;
1196: } bauds[] = {
1197:     50, B50,
1198:     75, B75,
1199:     110,    B110,
1200:     134,    B134,
1201:     150,    B150,
1202:     200,    B200,
1203:     300,    B300,
1204:     600,    B600,
1205:     1200,   B1200,
1206:     1800,   B1800,
1207:     2400,   B2400,
1208:     4800,   B4800,
1209:     9600,   B9600,
1210:     19200,  EXTA,
1211:     38400,  EXTB,
1212:     0,  0
1213: };
1214: 
1215: /*
1216:  * setup tty lines.
1217:  */
1218: setty()
1219: {
1220:     struct sgttyb ttybuf;
1221:     register struct bauds *bp;
1222: 
1223:     if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1224:         syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
1225:         exit(1);
1226:     }
1227:     if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
1228:         syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
1229:         exit(1);
1230:     }
1231:     if (BR > 0) {
1232:         for (bp = bauds; bp->baud; bp++)
1233:             if (BR == bp->baud)
1234:                 break;
1235:         if (!bp->baud) {
1236:             syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
1237:             exit(1);
1238:         }
1239:         ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1240:     }
1241:     ttybuf.sg_flags &= ~FC;
1242:     ttybuf.sg_flags |= FS;
1243:     if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
1244:         syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
1245:         exit(1);
1246:     }
1247:     if (XC || XS) {
1248:         int ldisc = NTTYDISC;
1249: 
1250:         if (ioctl(pfd, TIOCSETD, &ldisc) < 0) {
1251:             syslog(LOG_ERR, "%s: ioctl(TIOCSETD): %m", printer);
1252:             exit(1);
1253:         }
1254:     }
1255:     if (XC) {
1256:         if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
1257:             syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
1258:             exit(1);
1259:         }
1260:     }
1261:     if (XS) {
1262:         if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
1263:             syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
1264:             exit(1);
1265:         }
1266:     }
1267: }
1268: 
1269: /*VARARGS1*/
1270: status(msg, a1, a2, a3)
1271:     char *msg;
1272: {
1273:     register int fd;
1274:     char buf[BUFSIZ];
1275: 
1276:     umask(0);
1277:     fd = open(ST, O_WRONLY|O_CREAT, 0664);
1278:     if (fd < 0 || flock(fd, LOCK_EX) < 0) {
1279:         syslog(LOG_ERR, "%s: %s: %m", printer, ST);
1280:         exit(1);
1281:     }
1282:     ftruncate(fd, 0L);
1283:     sprintf(buf, msg, a1, a2, a3);
1284:     strcat(buf, "\n");
1285:     (void) write(fd, buf, strlen(buf));
1286:     (void) close(fd);
1287: }

Defined functions

abortpr defined in line 998; used 5 times
banner defined in line 786; used 2 times
dofork defined in line 964; used 4 times
dropit defined in line 874; used 1 times
init defined in line 1009; used 1 times
  • in line 70
openpr defined in line 1114; used 2 times
print defined in line 413; used 1 times
printit defined in line 222; used 1 times
printjob defined in line 60; used 3 times
response defined in line 772; used 3 times
scan_out defined in line 841; used 3 times
scnline defined in line 825; used 1 times
sendfile defined in line 706; used 2 times
sendit defined in line 621; used 1 times
sendmail defined in line 898; used 4 times
setty defined in line 1218; used 1 times
status defined in line 1270; used 20 times

Defined variables

bauds defined in line 1196; used 1 times
child defined in line 42; used 4 times
class defined in line 52; used 13 times
fdev defined in line 46; used 4 times
fino defined in line 47; used 4 times
fonts defined in line 209; used 5 times
fromhost defined in line 49; used 4 times
ifonts defined in line 211; used 1 times
indent defined in line 57; used 5 times
jobname defined in line 51; used 7 times
length defined in line 54; used 7 times
lfd defined in line 39; used 11 times
logname defined in line 50; used 6 times
ofd defined in line 38; used 30 times
ofilter defined in line 43; used 11 times
pfd defined in line 37; used 22 times
pid defined in line 40; used 12 times
prchild defined in line 41; used 4 times
pxlength defined in line 56; used 5 times
pxwidth defined in line 55; used 5 times
remote defined in line 45; used 2 times
sccsid defined in line 8; never used
title defined in line 35; used 7 times
tmpfile defined in line 58; used 6 times
tof defined in line 44; used 9 times
width defined in line 53; used 10 times

Defined struct's

bauds defined in line 1193; used 2 times

Defined macros

ACCESS defined in line 33; used 3 times
DOABORT defined in line 22; used 1 times
DORETURN defined in line 21; used 3 times
ERROR defined in line 28; used 9 times
FATALERR defined in line 30; used 1 times
FILTERERR defined in line 32; never used
NOACCT defined in line 31; used 2 times
OK defined in line 29; used 12 times
REPRINT defined in line 27; used 10 times
TRC defined in line 839; used 9 times
Last modified: 1996-12-23
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 7940
Valid CSS Valid XHTML 1.0 Strict