pdp11v/usr/src/cmd/lp/lp.c
/* lp -- print files on a line printer */
#include "lp.h"
SCCSID("@(#)lp.c 3.3")
char work[FILEMAX]; /* Global error message string */
struct stat stbuf; /* Global stat buffer */
char title[TITLEMAX + 1] = NULL; /* User-supplied title for output */
int copies = 1; /* number of copies of output */
int optlen = 0; /* size of option string for interface program */
char opts[OPTMAX] = NULL; /* options for interface program */
int silent = 0; /* don't run off at the mouth */
short mail = FALSE; /* TRUE ==> user wants mail, FALSE ==> no mail */
short wrt = FALSE; /* TRUE ==> user wants notification on tty via write,
FALSE ==> don't write */
short copy = FALSE; /* TRUE ==> copy files, FALSE ==> don't */
char curdir[FILEMAX+1]; /* working directory at time of request */
int nfiles = 0; /* number of files on cmd line (excluding "-") */
int fileargs = 0; /* total number of file args */
int stdinp = 0; /* indicates how many times to print std input
-1 ==> standard input empty */
short slocked = FALSE; /* TRUE ==> sequence file is locked, FALSE ==> isn't */
char tname[RNAMEMAX] = NULL; /* name of temp request file */
FILE *tfile = NULL; /* stream for temp request file */
char rname[RNAMEMAX] = NULL; /* name of actual request file */
char stdbase[NAMEMAX]; /* basename of copy of standard input */
char reqid[IDSIZE + 1]; /* request id to be supplied to user */
struct outq o = { /* output request to be appended to output queue */
NULL, /* destination */
NULL, /* logname */
0, /* sequence # */
0L, /* size of request */
NULL, /* device where printing */
0L, /* date of request */
0 /* not printing, not deleted */
};
main(argc, argv)
int argc;
char *argv[];
{
struct qstat q; /* acceptance status */
startup(argv[0]);
options(argc, argv); /* process command line options */
if(chdir(SPOOL) == -1)
fatal("spool directory non-existent", 1);
defaults(); /* establish default parameters */
if(getqdest(&q, o.o_dest) == EOF) { /* get acceptance status */
sprintf(work,
"acceptance status of destination \"%s\" unknown", o.o_dest);
fatal(work, 1);
}
endqent();
if(! q.q_accept) { /* accepting requests ? */
sprintf(work,
"can't accept requests for destination \"%s\" -\n\t%s",
o.o_dest, q.q_reason);
fatal(work, 1);
}
openreq(); /* create and init request file */
putrent(R_TITLE, title, tfile);
sprintf(work, "%d", copies);
putrent(R_COPIES, work, tfile);
putrent(R_OPTIONS, opts, tfile);
if(stdinp > 0)
savestd(); /* save standard input */
files(argc, argv); /* process command line file arguments */
closereq(); /* complete and then close request file */
time(&o.o_date);
o.o_size *= copies;
signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
addoent(&o); /* enter request in output queue */
endoent();
sprintf(work, "%s %d %s", o.o_dest, o.o_seqno, o.o_logname);
enqueue(F_REQUEST, work); /* notify scheduler of queued output */
qmesg(); /* issue request id message */
exit(0);
}
/* catch -- catch signals */
catch()
{
int cleanup();
signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
cleanup();
exit(1);
}
/* cleanup -- called by catch() after interrupts or by fatal() after errors */
cleanup()
{
/* Unlink request and data files (if any) */
rmreq(o.o_dest, o.o_seqno);
/* Unlock lock files and temp lock file */
tunlock();
if(slocked)
unlock(SEQLOCK);
endoent();
endqent();
}
/* closereq -- complete and close request file */
closereq()
{
if(fileargs == 0)
putrent(R_FILE, stdbase, tfile);
if(mail)
putrent(R_MAIL, o.o_logname, tfile);
if(wrt)
putrent(R_WRITE, o.o_logname, tfile);
/* Rename temporary request file to real request file name */
fclose(tfile);
if(link(tname, rname) == -1) {
sprintf(work, "can't create request file %s", rname);
fatal(work, 1);
}
unlink(tname);
}
/*
* copyfile(stream, name) -- copy stream to file "name"
* name is actually just the basename of a file
* the file is created under REQUEST/o.o_dest/
*/
copyfile(stream, name)
FILE *stream;
char *name;
{
FILE *ostream;
int i;
char buf[BUFSIZ];
sprintf(work, "%s/%s/%s", REQUEST, o.o_dest, name);
if((ostream = fopen(work, "w")) == NULL) {
sprintf(work, "cannot create temp file %s", name);
fatal(work, 1);
}
chmod(work, 0444);
while((i = fread(buf, sizeof(char), BUFSIZ, stream)) > 0)
fwrite(buf, sizeof(char), (unsigned) i, ostream);
fclose(ostream);
}
/* defaults -- establish default destination if not set on command line */
defaults()
{
char *d, *getenv(), *c, *strcpy();
FILE *dflt; /* stream for reading default destination file */
if(o.o_dest[0] == '\0') { /* avoid optimiser bug */
if((d = getenv(LPDEST)) != NULL && *d != '\0') {
if(strlen(d) > DESTMAX) {
sprintf(work,
"%s destination \"%s\" illegal",
LPDEST, d);
fatal(work, 1);
}
strcpy(o.o_dest, d);
}
else {
if((dflt = fopen(DEFAULT, "r")) == NULL)
fatal("can't open default destination file",1);
if(fgets(o.o_dest, DESTMAX+1, dflt) == NULL) {
fatal("no system default destination", 1);
}
fclose(dflt);
if(*(c = o.o_dest + strlen(o.o_dest) -1) == '\n')
*c = '\0';
}
if(! isdest(o.o_dest)) {
sprintf(work,
"default destination \"%s\" non-existent",
o.o_dest);
fatal(work, 1);
}
}
}
/* files -- process command line file arguments */
files(argc, argv)
int argc;
char *argv[];
{
int i;
char *fullpath(), *file, *dname, *newname(), *full;
FILE *f;
for(i = 1; i < argc; i++) {
file = argv[i];
if(file == NULL)
continue;
if(strcmp(file, "-") == 0) {
if(stdinp > 0)
putrent(R_FILE, stdbase, tfile);
}
else {
if((full = fullpath(file, curdir)) == NULL)
fatal("can't read current directory", 1);
dname = newname();
if(copy) {
if((f=fopen(full,"r")) != NULL) {
copyfile(f, dname);
fclose(f);
}
else {
sprintf(work, "can't open file %s",
full);
fatal(work, 1);
}
putrent(R_FILE, dname, tfile);
}
else {
sprintf(work, "%s/%s/%s", REQUEST,
o.o_dest, dname);
if(link(file, work) < 0)
putrent(R_FILE, full, tfile);
else
putrent(R_FILE, dname, tfile);
}
}
}
}
/* getseq(snum) -- get next sequence number */
getseq(snum)
int *snum;
{
FILE *fp;
if (trylock(SEQLOCK, LOCKTIME, LOCKTRIES, LOCKSLEEP) == -1)
fatal("can't lock sequence number file", 1);
slocked = TRUE;
if ((fp = fopen(SEQFILE, "r")) != NULL) {
/* read sequence number file */
fscanf(fp, "%d\n", snum);
fp = freopen(SEQFILE, "w", fp);
}
else {
/* can not read file - create a new one */
if ((fp = fopen(SEQFILE, "w")) == NULL)
fatal("can't create new sequence number file", 1);
*snum = 0;
}
chmod(SEQFILE, 0644);
if(++(*snum) == SEQMAX)
*snum = 1;
fprintf(fp, "%d\n", *snum);
fclose(fp);
unlock(SEQLOCK);
slocked = FALSE;
}
/* newname() -- create new name for data file
returns the new name, which is just the base name for the file.
The file will ultimately be created under SPOOL/REQUEST/o.o_dest/
*/
char *
newname()
{
static int n = 0;
static char name[NAMEMAX];
sprintf(name, "d%d-%d", n++, o.o_seqno);
return(name);
}
/* openreq -- open and initialize request file */
openreq()
{
while(tfile == NULL) {
getseq(&o.o_seqno);
sprintf(tname, "%s/%s/t-%d", REQUEST, o.o_dest, o.o_seqno);
sprintf(rname, "%s/%s/r-%d", REQUEST, o.o_dest, o.o_seqno);
if(eaccess(tname, 0) == -1 && eaccess(rname, 0) == -1 &&
(tfile = fopen(tname, "w")) != NULL)
chmod(tname, 0444);
}
sprintf(reqid, "%s-%d", o.o_dest, o.o_seqno);
}
/* options -- process command line options */
#define OPTMSG "too many options for interface program"
options(argc, argv)
int argc;
char *argv[];
{
int i;
char letter; /* current keyletter */
char *value; /* value of current keyletter (or NULL) */
char *strcat(), *strncpy(), *file;
for(i = 1; i < argc; i++) {
if(argv[i][0] == '-' && (letter = argv[i][1]) != '\0') {
if(! isalpha(letter)) {
sprintf(work, "unknown keyletter \"%c\"",
letter);
fatal(work, 1);
}
letter = tolower(letter);
value = &argv[i][2];
switch(letter) {
case 'c': /* copy files */
copy = TRUE;
break;
case 'd': /* destination */
if(! isdest(value)) {
sprintf(work,
"destination \"%s\" non-existent",
value);
fatal(work, 1);
}
strncpy(o.o_dest, value, DESTMAX);
o.o_dest[DESTMAX] = NULL;
break;
case 'm': /* mail */
mail = TRUE;
break;
case 'n': /* # of copies */
if(*value == '\0' || (copies=atoi(value)) <= 0)
copies = 1;
break;
case 'o': /* option for interface program */
if(*value != '\0') {
if(optlen == 0)
optlen = strlen(value);
else
optlen = strlen(value) + 1;
if(optlen >= OPTMAX)
fatal(OPTMSG, 1);
if(*opts != '\0')
strcat(opts, " ");
strcat(opts, value);
}
break;
case 's': /* silent */
silent = 1;
break;
case 't': /* title */
strncpy(title, value, TITLEMAX);
title[TITLEMAX] = '\0';
break;
case 'w': /* write */
wrt = TRUE;
break;
default:
sprintf(work, "unknown keyletter \"-%c\"",
letter);
fatal(work, 1);
break;
}
argv[i] = NULL;
}
else { /* file name or - */
fileargs++;
file = argv[i];
if(strcmp(file, "-") == 0)
stdinp++;
else {
if(stat(file, &stbuf) == -1) {
sprintf(work,
"can't access file \"%s\"", file);
goto badf;
}
else if((stbuf.st_mode & S_IFMT) == S_IFDIR) {
sprintf(work,
"\"%s\" is a directory", file);
goto badf;
}
else if(stbuf.st_size == 0) {
sprintf(work,
"file \"%s\" is empty", file);
goto badf;
}
else if(eaccess(argv[i], ACC_R) == -1) {
sprintf(work,
"can't access file \"%s\"", file);
goto badf;
}
else {
nfiles++;
o.o_size += stbuf.st_size;
}
}
}
continue;
badf: fatal(work, 0);
argv[i] = NULL;
}
if(fileargs == 0)
stdinp = 1;
else if(nfiles == 0 && stdinp == 0)
fatal("request not accepted", 1);
}
/* qmesg -- issue request id message */
qmesg()
{
if(silent) return; /* not so elegant but 'twill do */
printf("request id is %s (", reqid);
if(nfiles > 0) {
printf("%d file", nfiles);
if(nfiles > 1)
printf("s");
}
if(stdinp > 0) {
if(nfiles > 0)
printf(" and ");
printf("standard input");
}
printf(")\n");
}
/* savestd -- save standard input */
savestd()
{
char *newname();
strcpy(stdbase, newname());
copyfile(stdin, stdbase);
sprintf(work, "%s/%s/%s", REQUEST, o.o_dest, stdbase);
stat(work, &stbuf);
if(stbuf.st_size == 0) {
fatal("standard input is empty", 0);
unlink(work);
if(nfiles == 0) /* no files to queue */
fatal("request not accepted", 1);
else /* inhibit printing of std input */
stdinp = -1;
}
else
o.o_size += (stdinp * stbuf.st_size);
}
/* startup -- initialization routine */
startup(name)
char *name;
{
int catch(), cleanup();
extern char *f_name;
extern int (*f_clean)();
if(signal(SIGHUP, SIG_IGN) != SIG_IGN)
signal(SIGHUP, catch);
if(signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, catch);
if(signal(SIGQUIT, SIG_IGN) != SIG_IGN)
signal(SIGQUIT, catch);
if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
signal(SIGTERM, catch);
umask(0000);
f_name = name;
f_clean = cleanup;
gwd(curdir); /* get current directory */
strcpy(o.o_logname, getname());
strcpy(o.o_dev, "-");
}