1 /* lpr.c 4.17 83/05/18 */ 2 /* 3 * lpr -- off line print 4 * 5 * Allows multiple printers and printers on remote machines by 6 * using information from a printer data base. 7 */ 8 9 #include "lp.h" 10 11 char *tfname; /* tmp copy of cf before linking */ 12 char *cfname; /* daemon control files, linked from tf's */ 13 char *dfname; /* data files */ 14 15 int nact; /* number of jobs to act on */ 16 int tfd; /* control file descriptor */ 17 int mailflg; /* send mail */ 18 int qflag; /* q job, but don't exec daemon */ 19 char format = 'f'; /* format char for printing files */ 20 int rflag; /* remove files upon completion */ 21 int sflag; /* symbolic link flag */ 22 int inchar; /* location to increment char in file names */ 23 int ncopies = 1; /* # of copies to make */ 24 int iflag; /* indentation wanted */ 25 int indent; /* amount to indent */ 26 int hdr = 1; /* print header or not (default is yes) */ 27 int userid; /* user id */ 28 char *person; /* user name */ 29 char *title; /* pr'ing title */ 30 char *fonts[4]; /* troff font names */ 31 char *width; /* width for versatec printing */ 32 char *class = host; /* class title on header page */ 33 char *jobname; /* job name on header page */ 34 35 char *linked(); 36 int cleanup(); 37 38 /*ARGSUSED*/ 39 main(argc, argv) 40 int argc; 41 char *argv[]; 42 { 43 extern struct passwd *getpwuid(); 44 struct passwd *pw; 45 extern char *itoa(); 46 register char *arg, *cp; 47 int i, f; 48 struct stat stb; 49 50 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 51 signal(SIGHUP, cleanup); 52 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 53 signal(SIGINT, cleanup); 54 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 55 signal(SIGQUIT, cleanup); 56 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 57 signal(SIGTERM, cleanup); 58 59 name = argv[0]; 60 gethostname(host, sizeof (host)); 61 62 while (argc > 1 && argv[1][0] == '-') { 63 argc--; 64 arg = *++argv; 65 switch (arg[1]) { 66 67 case 'P': /* specifiy printer name */ 68 printer = &arg[2]; 69 break; 70 71 case 'C': /* classification spec */ 72 hdr++; 73 if (arg[2]) 74 class = &arg[2]; 75 else if (argc > 1) { 76 argc--; 77 class = *++argv; 78 } 79 break; 80 81 case 'J': /* job name */ 82 hdr++; 83 if (arg[2]) 84 jobname = &arg[2]; 85 else if (argc > 1) { 86 argc--; 87 jobname = *++argv; 88 } 89 break; 90 91 case 'T': /* pr's title line */ 92 if (arg[2]) 93 title = &arg[2]; 94 else if (argc > 1) { 95 argc--; 96 title = *++argv; 97 } 98 break; 99 100 case 'l': /* literal output */ 101 case 'p': /* print using ``pr'' */ 102 case 't': /* print troff output (cat files) */ 103 case 'd': /* print tex output (dvi files) */ 104 case 'g': /* print graph(1G) output */ 105 case 'c': /* print cifplot output */ 106 case 'v': /* print vplot output */ 107 format = arg[1]; 108 break; 109 110 case 'f': /* print fortran output */ 111 format = 'r'; 112 break; 113 114 case '4': /* troff fonts */ 115 case '3': 116 case '2': 117 case '1': 118 if (argc > 1) { 119 argc--; 120 fonts[arg[1] - '1'] = *++argv; 121 format = 't'; 122 } 123 break; 124 125 case 'w': /* versatec page width */ 126 width = arg+2; 127 break; 128 129 case 'r': /* remove file when done */ 130 rflag++; 131 break; 132 133 case 'm': /* send mail when done */ 134 mailflg++; 135 break; 136 137 case 'h': /* toggle want of header page */ 138 hdr = !hdr; 139 break; 140 141 case 's': /* try to link files */ 142 sflag++; 143 break; 144 145 case 'q': /* just q job */ 146 qflag++; 147 break; 148 149 case 'i': /* indent output */ 150 iflag++; 151 indent = arg[2] ? atoi(&arg[2]) : 8; 152 break; 153 154 case '#': /* n copies */ 155 if (isdigit(arg[2])) 156 ncopies = atoi(&arg[2]); 157 } 158 } 159 if (printer == NULL && (printer = getenv("PRINTER")) == NULL) 160 printer = DEFLP; 161 chkprinter(printer); 162 /* 163 * Check to make sure queuing is enabled. 164 */ 165 (void) sprintf(line, "%s/%s", SD, LO); 166 if (stat(line, &stb) == 0 && (stb.st_mode & 010)) 167 fatal("Printer queue is disabled"); 168 /* 169 * Get the identity of the person doing the lpr using the same 170 * algorithm as lprm. 171 */ 172 userid = getuid(); 173 if ((pw = getpwuid(userid)) == NULL) 174 fatal("Who are you?"); 175 person = pw->pw_name; 176 /* 177 * Initialize the control file. 178 */ 179 mktemps(); 180 tfd = nfile(tfname); 181 card('H', host); 182 card('P', person); 183 if (hdr) { 184 if (jobname == NULL) { 185 if (argc == 1) 186 jobname = "stdin"; 187 else 188 jobname = (arg = rindex(argv[1], '/')) ? arg+1 : argv[1]; 189 } 190 card('J', jobname); 191 card('C', class); 192 card('L', person); 193 } 194 if (iflag) 195 card('I', itoa(indent)); 196 if (mailflg) 197 card('M', person); 198 if (format == 't' || format == 'd') 199 for (i = 0; i < 4; i++) 200 if (fonts[i] != NULL) 201 card('1'+i, fonts[i]); 202 if (width != NULL) 203 card('W', width); 204 205 /* 206 * Read the files and spool them. 207 */ 208 if (argc == 1) 209 copy(0, " "); 210 else while (--argc) { 211 if ((f = test(arg = *++argv)) < 0) 212 continue; /* file unreasonable */ 213 214 if (sflag && (cp = linked(arg)) != NULL) { 215 if (format == 'p') 216 card('T', title ? title : arg); 217 for (i = 0; i < ncopies; i++) 218 card(format, &dfname[inchar-2]); 219 card('U', &dfname[inchar-2]); 220 if (f) 221 card('U', cp); 222 card('N', arg); 223 dfname[inchar]++; 224 nact++; 225 continue; 226 } 227 if (sflag) 228 printf("%s: %s: not linked, copying instead\n", name, arg); 229 if ((i = open(arg, 0)) < 0) { 230 printf("%s: cannot open %s\n", name, arg); 231 continue; 232 } 233 copy(i, arg); 234 (void) close(i); 235 if (f && unlink(arg) < 0) 236 printf("%s: %s: not removed\n", name, arg); 237 } 238 239 if (nact) { 240 (void) close(tfd); 241 tfname[inchar]--; 242 /* 243 * Touch the control file to fix position in the queue. 244 */ 245 if ((tfd = open(tfname, 2)) >= 0) { 246 char c; 247 248 if (read(tfd, &c, 1) == 1 && lseek(tfd, 0L, 0) == 0 && 249 write(tfd, &c, 1) != 1) { 250 printf("%s: cannot touch %s\n", name, tfname); 251 tfname[inchar]++; 252 cleanup(); 253 } 254 (void) close(tfd); 255 } 256 if (link(tfname, cfname) < 0) { 257 printf("%s: cannot rename %s\n", name, cfname); 258 tfname[inchar]++; 259 cleanup(); 260 } 261 unlink(tfname); 262 if (qflag) /* just q things up */ 263 exit(0); 264 if (!startdaemon()) 265 printf("jobs queued, but cannot start daemon.\n"); 266 exit(0); 267 } 268 cleanup(); 269 /* NOTREACHED */ 270 } 271 272 /* 273 * Create the file n and copy from file descriptor f. 274 */ 275 copy(f, n) 276 int f; 277 char n[]; 278 { 279 register int fd, i, nr, nc; 280 char buf[BUFSIZ]; 281 282 if (format == 'p') 283 card('T', title ? title : n); 284 for (i = 0; i < ncopies; i++) 285 card(format, &dfname[inchar-2]); 286 card('U', &dfname[inchar-2]); 287 card('N', n); 288 fd = nfile(dfname); 289 nr = nc = 0; 290 while ((i = read(f, buf, BUFSIZ)) > 0) { 291 if (write(fd, buf, i) != i) { 292 printf("%s: %s: temp file write error\n", name, n); 293 break; 294 } 295 nc += i; 296 if (nc >= BUFSIZ) { 297 nc -= BUFSIZ; 298 nr++; 299 if (MX > 0 && nr > MX) { 300 printf("%s: %s: copy file is too large\n", name, n); 301 break; 302 } 303 } 304 } 305 (void) close(fd); 306 nact++; 307 } 308 309 /* 310 * Try and link the file to dfname. Return a pointer to the full 311 * path name if successful. 312 */ 313 char * 314 linked(file) 315 register char *file; 316 { 317 register char *cp; 318 static char buf[BUFSIZ]; 319 320 if (*file != '/') { 321 if (getwd(buf) == NULL) 322 return(NULL); 323 while (file[0] == '.') { 324 switch (file[1]) { 325 case '/': 326 file += 2; 327 continue; 328 case '.': 329 if (file[2] == '/') { 330 if ((cp = rindex(buf, '/')) != NULL) 331 *cp = '\0'; 332 file += 3; 333 continue; 334 } 335 } 336 break; 337 } 338 strcat(buf, "/"); 339 strcat(buf, file); 340 file = buf; 341 } 342 return(symlink(file, dfname) ? NULL : file); 343 } 344 345 /* 346 * Put a line into the control file. 347 */ 348 card(c, p2) 349 register char c, *p2; 350 { 351 char buf[BUFSIZ]; 352 register char *p1 = buf; 353 register int len = 2; 354 355 *p1++ = c; 356 while ((c = *p2++) != '\0') { 357 *p1++ = c; 358 len++; 359 } 360 *p1++ = '\n'; 361 write(tfd, buf, len); 362 } 363 364 /* 365 * Create a new file in the spool directory. 366 */ 367 nfile(n) 368 char *n; 369 { 370 register f; 371 int oldumask = umask(0); /* should block signals */ 372 373 f = creat(n, FILMOD); 374 (void) umask(oldumask); 375 if (f < 0) { 376 printf("%s: cannot create %s\n", name, n); 377 cleanup(); 378 } 379 if (++n[inchar] > 'z') { 380 if (++n[inchar-2] == 't') { 381 printf("too many files - break up the job\n"); 382 cleanup(); 383 } 384 n[inchar] = 'A'; 385 } else if (n[inchar] == '[') 386 n[inchar] = 'a'; 387 return(f); 388 } 389 390 /* 391 * Cleanup after interrupts and errors. 392 */ 393 cleanup() 394 { 395 register i; 396 397 signal(SIGHUP, SIG_IGN); 398 signal(SIGINT, SIG_IGN); 399 signal(SIGQUIT, SIG_IGN); 400 signal(SIGTERM, SIG_IGN); 401 i = inchar; 402 if (tfname) 403 do 404 unlink(tfname); 405 while (tfname[i]-- != 'A'); 406 if (cfname) 407 do 408 unlink(cfname); 409 while (cfname[i]-- != 'A'); 410 if (dfname) 411 do { 412 do 413 unlink(dfname); 414 while (dfname[i]-- != 'A'); 415 dfname[i] = 'z'; 416 } while (dfname[i-2]-- != 'd'); 417 exit(1); 418 } 419 420 /* 421 * Test to see if this is a printable file. 422 * Return -1 if it is not, 0 if its printable, and 1 if 423 * we should remove it after printing. 424 */ 425 test(file) 426 char *file; 427 { 428 struct exec execb; 429 struct stat statb; 430 register int fd; 431 register char *cp; 432 433 if (access(file, 4) < 0) { 434 printf("%s: cannot access %s\n", name, file); 435 return(-1); 436 } 437 if (stat(file, &statb) < 0) { 438 printf("%s: cannot stat %s\n", name, file); 439 return(-1); 440 } 441 if ((statb.st_mode & S_IFMT) == S_IFDIR) { 442 printf("%s: %s is a directory\n", name, file); 443 return(-1); 444 } 445 if ((fd = open(file, 0)) < 0) { 446 printf("%s: cannot open %s\n", name, file); 447 return(-1); 448 } 449 if (read(fd, &execb, sizeof(execb)) == sizeof(execb)) 450 switch(execb.a_magic) { 451 case A_MAGIC1: 452 case A_MAGIC2: 453 case A_MAGIC3: 454 #ifdef A_MAGIC4 455 case A_MAGIC4: 456 #endif 457 printf("%s: %s is an executable program", name, file); 458 goto error1; 459 460 case ARMAG: 461 printf("%s: %s is an archive file", name, file); 462 goto error1; 463 } 464 (void) close(fd); 465 if (rflag) { 466 if ((cp = rindex(file, '/')) == NULL) { 467 if (access(".", 2) == 0) 468 return(1); 469 } else { 470 *cp = '\0'; 471 fd = access(file, 2); 472 *cp = '/'; 473 if (fd == 0) 474 return(1); 475 } 476 printf("%s: %s: is not removable by you\n", name, file); 477 } 478 return(0); 479 480 error1: 481 printf(" and is unprintable\n"); 482 (void) close(fd); 483 return(-1); 484 } 485 486 /* 487 * itoa - integer to string conversion 488 */ 489 char * 490 itoa(i) 491 register int i; 492 { 493 static char b[10] = "########"; 494 register char *p; 495 496 p = &b[8]; 497 do 498 *p-- = i%10 + '0'; 499 while (i /= 10); 500 return(++p); 501 } 502 503 /* 504 * Perform lookup for printer name or abbreviation -- 505 */ 506 chkprinter(s) 507 register char *s; 508 { 509 int status; 510 511 if ((status = pgetent(line, s)) < 0) 512 fatal("cannot open printer description file"); 513 else if (status == 0) 514 fatal("unknown printer"); 515 if ((SD = pgetstr("sd", &bp)) == NULL) 516 SD = DEFSPOOL; 517 if ((LO = pgetstr("lo", &bp)) == NULL) 518 LO = DEFLOCK; 519 if ((MX = pgetnum("mx")) < 0) 520 MX = DEFMX; 521 if ((DU = pgetnum("du")) < 0) 522 DU = DEFUID; 523 } 524 525 /* 526 * Make the temp files. 527 */ 528 mktemps() 529 { 530 register int c, len; 531 int n; 532 char buf[BUFSIZ], *mktemp(); 533 FILE *fp; 534 535 (void) sprintf(buf, "%s/.seq", SD); 536 if ((fp = fopen(buf, "r+")) == NULL) { 537 if ((fp = fopen(buf, "w")) == NULL) { 538 printf("%s: cannot create %s\n", name, buf); 539 exit(1); 540 } 541 setbuf(fp, buf); 542 n = 0; 543 } else { 544 setbuf(fp, buf); 545 if (flock(fileno(fp), FEXLOCK)) { 546 printf("%s: cannot lock %s\n", name, buf); 547 exit(1); 548 } 549 n = 0; 550 while ((c = getc(fp)) >= '0' && c <= '9') 551 n = n * 10 + (c - '0'); 552 } 553 len = strlen(SD) + strlen(host) + 8; 554 tfname = mktemp("tf", n, len); 555 cfname = mktemp("cf", n, len); 556 dfname = mktemp("df", n, len); 557 inchar = strlen(SD) + 3; 558 n = (n + 1) % 1000; 559 (void) fseek(fp, 0L, 0); 560 fprintf(fp, "%d\n", n); 561 (void) fclose(fp); 562 } 563 564 /* 565 * Make a temp file name. 566 */ 567 char * 568 mktemp(id, num, len) 569 char *id; 570 int num, len; 571 { 572 register char *s; 573 574 if ((s = malloc(len)) == NULL) 575 fatal("out of memory"); 576 (void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host); 577 return(s); 578 } 579