ftp.nice.ch/pub/next/unix/network/system/cap.5.0.s.tar.gz#/cap_5.0/applications/papif/papif.c

This is papif.c in view mode; [Download] [Up]

static char rcsid[] = "$Author: cck $ $Date: 88/03/30 11:59:56 $";
static char rcsident[] = "$Header: papif.c,v 1.68 88/03/30 11:59:56 cck Rel $";
static char revision[] = "$Revision: 1.68 $";

/*
 * papif - UNIX AppleTalk test program: simple line printer input filter 
 *  for LaserWriter
 *
 * AppleTalk package for UNIX (4.2 BSD).
 *
 * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the
 * City of New York.
 *
 * Edit History:
 *
 *  June 29, 1986    Schilit&CCKim	Created.
 *  July  5, 1986    CCKim		Clean up
 *  Aug  20, 1986    CCKim, Gave up on old version, use lwpr routines instead.
 *  Nov      1986    croft, restart after "status: idle", call pstext
 *
 */

char copyright[] = "Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the City of New York";

#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>
#ifndef _TYPES
# include <sys/types.h>		/* in case param doesn't */
#endif
#include <sys/file.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <signal.h>

#include <netat/appletalk.h>		/* include appletalk definitions */
#include <netat/compat.h>
#ifndef NOWAIT3
# include <sys/wait.h>
#endif
#ifdef USESTRINGDOTH
# include <string.h>
#else
# include <strings.h>
#endif
#ifdef NEEDFCNTLDOTH
# include <fcntl.h>
#endif
#ifdef USEVPRINTF
# include <varargs.h>
#endif

/* Configuration options */

#ifndef CAPPRINTERS
# define CAPPRINTERS "/etc/cap.printers"
#endif

/*
 * status watch time: minimum time to wait before doing next status
 * watch 
*/
#ifndef WATCHTIME
# define WATCHTIME 10		/* status watch every 10 seconds */
#endif

/* PAP defines */
#ifndef RFLOWQ			/* default read flow quantum to use */
# define RFLOWQ atpMaxNum
#endif
#ifndef SFLOWQ			/* default send flow quantum to use */
# define SFLOWQ atpMaxNum
#endif
#define MAX_RFLOWQ atpMaxNum	/* maximum read flow quantum */
#define MAX_SFLOWQ atpMaxNum	/* maximum send flow quantum */

/* buffers sizes for papread/writes - based on maximum flow quantum */
#define R_BUFMAX PAPSegSize*MAX_RFLOWQ
#define S_BUFMAX PAPSegSize*MAX_SFLOWQ

/* default for debugging output or not */
#ifndef DEBUG
# define DEBUG 0		/* no */
#endif

/* are we running with Adobe Document Structuring parsing? */  
/* for now, do we look for %%EOF in input and send an EOF to remote */
/* based on this */
#ifndef NO_STRUCT
# define NO_STRUCT 0		/* yes */
#endif

/* default to doing accounting? */
#ifndef NOACCT
# define NOACCT 0		/* yes, do it */
#endif

/* default to stripping out ^Ds? */
#ifndef STRIPCONTROLD
# define STRIPCONTROLD 0	/* no */
#endif

/* default to mapping ^M to ^J for psrv, etc. */
#ifndef MAPCRTOLF
# define MAPCRTOLF 0		/* no */
#endif

/* default to idle check? */
#ifndef IDLESTUFF
# define IDLESTUFF 0		/* no */
#endif

/* default to charge banner? */
#ifndef CHARGEBANNER
# define CHARGEBANNER 0		/* no */
#endif

#ifndef ATPRESPONSETIMEOUT
# define ATPRESPONSETIMEOUT sectotick(60*2);
#endif

/* Transcript compatibility section */

/* allow psmagic */
/* use pstext? */
#ifndef PSTEXT
# define PSTEXT NULL		/* no */
#endif
/* use psreverse */
#ifndef PSREVERSE
# define PSREVERSE NULL		/* no */
#endif
/* need to check magic */
#ifndef PSMAGIC
# define PSMAGIC 0		/* no */
#endif

/* default to putting banner first? */
#ifndef BANNERFIRST
# define BANNERFIRST 0		/* no */
#endif

/* default to putting banner last? */
#ifndef BANNERLAST
# define BANNERLAST 0		/* no */
#endif

#ifndef BANNER
# define BANNER ".banner"
#endif


/*
 * GLOBAL VARIABLES
*/
int cno;			/* pap connection number */
int pid = -1;			/* top fork's pid */
/* don't know where I got 30 from */
char host[30];			/* host name */
char printer[30];		/* printer name */
char user[30];			/* user name */

char *lwname;			/* entity name */

/* for pap */
int rflowq = RFLOWQ;		/* flow quatum on local reads */
int sflowq = SFLOWQ;		/* flow q on remote reads (local writes) */
int r_bufsiz;			/* flow quantum translated to buffers size */
int s_bufsiz;

int debug = DEBUG;		/* debugging output? */
char *capprinters = CAPPRINTERS; /* location of cap.printers */
char *bannerfile = BANNER;	/* banner file name */
int watchtime = WATCHTIME;	/* status watch time */
int dostruct = !NO_STRUCT;	/* parse adobe structuring? */
int doacct = !NOACCT;		/* do accounting? */
int doidlestuff = IDLESTUFF;	/* handle idle watch? */
int chargebanner = CHARGEBANNER; /* charge user for banner pages? */
u_long atpresponsetimeout = ATPRESPONSETIMEOUT;	/* atp resp. cache timeout */
int strip_control_d = STRIPCONTROLD;
int map_crtolf = MAPCRTOLF;

/* Transcript compatiblity */
int neverreverse = FALSE;	/* never page reverse */
int verboselog = TRUE;		/* default */
char *pstext = PSTEXT;		/* pstext location */
char *psreverse = PSREVERSE;	/* psrv location */
int psmagic = PSMAGIC;		/* will be reset if necessary by initenv */
int bannerfirst = BANNERFIRST;	/* banner comes first? */
int bannerlast = BANNERLAST;	/* banner comes last? */

/* declarations */
char *ptime();

/* Definitions */

/* yes or no depending on the value */
#define boolvalstr(value) ((value) ? "yes" : "no")
/* string value if not null, "none" o.w. */
#define strval(str) (((str) == NULL) ? "none" : (str))

/* logging levels */
#define log_i dolog /* information */
#define log_w dolog /* warning */
#define log_e dolog /* error */
#define log_r dolog /* return from remote */
#define log_d dolog /* log debugging */

/*
 * LPD Error tokens - what to return from exit
 */
#define	lpd_OK		0	/* all is well? */
#define	lpd_REPRINT	1	/* forces a reprint */
#define lpd_ERRORS	2	/* printed with errors */

/* quit gets called when we've been killed by lprm via SIGINT */
quit()
{
  if (cno >= 0)
    PAPClose(cno);
  log_w("papif: Aborted job at %s\n",ptime());
  exit(lpd_OK);
}

/*
 * return true: if s is "y"* or "on"* or s is a non-zero number (string)
 * return false: o.w.
 *
*/
int
getbval(s)
char *s;
{
  if (isdigit(s[0]))
    return(atoi(s));
  if (s[0] == 'y' || (s[0] == 'o' && s[1] == 'n'))
    return(TRUE);
  return(FALSE);
}

/*
 * get environment variables set by Transcript and set on that basis
 * 
*/
initenv()
{
  char *getenv();
  char *v;

  if ((v = getenv("JOBOUTPUT"))) /* Sys V lp Transcript compatibility */
    joboutput(v);
  if ((v = getenv("DOACCT")))	/* papif specific */
    doacct = getbval(v);
  if ((v = getenv("DEBUG")))	/* papif specific */
    debug = getbval(v);
  if ((v = getenv("BANNERFIRST")))
    bannerfirst = atoi(v);
  if ((v = getenv("BANNERLAST")))
    bannerlast = atoi(v);
  if ((v = getenv("BANNER")))	/* papif specific */
    bannerfile = v;
  if ((v = getenv("VERBOSELOG")))
    verboselog = getbval(v);
  if ((v = getenv("ATPRESPONSETIMEOUT"))) /* papif specific  */
    atpresponsetimeout = sectotick(60*2);
  if ((v = getenv("RFLOWQ"))) /* papif specific  */
    if ((rflowq = atoi(v)) <= 0) {
      log_w("papif: environment RFLOWQ too small, setting to 1");
      rflowq = 1;
    }
  if (rflowq > atpMaxNum) {
    rflowq = atpMaxNum;
    log_w("papif: rflowq too large - setting to %d\n", rflowq);
  }
  if ((v = getenv("SFLOWQ"))) /* papif specific  */
    if ((sflowq = atoi(v)) <= 0) {
      log_w("papif: environment SFLOWQ too small, setting to 1");
      sflowq = 1;
    }
  if (sflowq > atpMaxNum) {
    sflowq = atpMaxNum;
    log_w("papif: sflowq too large - setting to %d\n", sflowq);
  }
  if ((v = getenv("CHARGEBANNER"))) /* papif specific  */
    chargebanner = getbval(v);
  if ((v = getenv("WATCHTIME"))) /* papif specific  */
    if ((watchtime = atoi(v)) < 0) {
      watchtime = WATCHTIME;
      log_w("papif: environment watchtime too small, setting to %d\n",
	    watchtime);
    }
  if ((v = getenv("IDLESTUFF"))) /* papif specific  */
    doidlestuff = getbval(v);
  if ((v = getenv("ADOBE_STRUCTURED"))) /* papif specific  */
    dostruct = getbval(v);
  if ((v = getenv("MAPCRTOLF"))) /* papif specific */
    map_crtolf = getbval(v);
  if ((v = getenv("STRIPCONTROLD"))) /* papif specific */
    strip_control_d = getbval(v);
  if ((v = getenv("CAPPRINTERS"))) /* papif specific */
    if (access(v, R_OK)) 
      capprinters =  v;
  else
    log_w("papif: user specified cap.printers: %s not found, ignoring\n",v);
  if ((v = getenv("REVERSE")))
    {
      if (*v == '\0')
      psreverse = NULL;
      else
	{
	  if (access(v, X_OK) == 0)
	    psreverse = v;             /* good enough I guess */
	  else
	    log_w("papif: specified psreverse filter %s not found, ignoring\n",
		  v);
	}
    }
  if ((v = getenv("PSTEXT")))
    if (access(v, X_OK) == 0)
      pstext = v;
    else
      log_w("papif: specified pstext filter %s not found, ignoring\n", v);
  if (psreverse != NULL || pstext != NULL)
    psmagic = 1;
  r_bufsiz = PAPSegSize*rflowq;
  s_bufsiz = PAPSegSize*sflowq;
}

/*
 * log current environment
 *
*/
logenv()
{
  if (debug) {
    log_d("papif: bannerfirst %s, bannerlast %s, verboselog %s\n",
	  boolvalstr(bannerfirst), boolvalstr(bannerlast),
	  boolvalstr(verboselog));
    log_d("papif: atpresponse timeout = %lu ticks (1/4 second units)\n",
	  atpresponsetimeout);
    if (watchtime)
      log_d("papif: status watch lower time limit %d seconds\n", watchtime);
    else
      log_d("papif: no status watch\n");
    log_d("papif: rflowq = %d packets, sflowq = %d packets\n", rflowq, sflowq);
    log_d("papif: strip control d's %s, map carriage return to line feed %s\n",
	  boolvalstr(strip_control_d), boolvalstr(map_crtolf));
    if (doacct)
      log_d("papif: accounting = %s, chargebanner %s",
	    boolvalstr(doacct), boolvalstr(chargebanner));
    else
      log_d("papif: accounting = %s, ", boolvalstr(doacct));
    log_d(", idlestuff %s, structured %s\n",
	  boolvalstr(doidlestuff), boolvalstr(dostruct));
    log_d("papif: reverse = %s\n", strval(psreverse));
    log_d("papif: pstext = %s\n", strval(pstext));
    log_d("papif: banner file = %s\n", strval(bannerfile));
    log_d("papif: cap.printers = %s\n",strval(capprinters));
  }
}

/*
 * parse arguments
*/
getargs(argc, argv, printer, user, host, acctfile)
int argc;
char **argv;
char *printer, *user, *host, **acctfile;
{
  int i;
  char *pgmname = NULL;
  char *p;

  *printer = *user = *host = '\0'; /* init to nothing */
  *acctfile = NULL;

  for (i=0; i<argc; i++) {
    p = argv[i];
    if (p[0] == '-') {
      switch (p[1]) {
      case 'd':
	dbugarg(p+2);
	break;
      case 'c':			/* specifies "pass control characters" */
				/* (literal mode) */
	break;
      case 'p':			/* program name: pscomm compat */
	pgmname = argv[++i];	/* set program name */
	break;
      case 'P':			/* printer name: pscomm compatible */
	strcpy(printer,argv[++i]);
	break;
      case 'n':			/* user name */
	strcpy(user,argv[++i]);
	break;
      case 'h':			/* host name */
	strcpy(host,argv[++i]);
	break;
      case 'r':
	neverreverse = TRUE;	/* pscomm compat */
	break;
      case 'x':			/* width in pixels */
      case 'y':			/* length in pixels */
      case 'i':			/* indentation size in chars */
      case 'l':			/* length in lines */
      case 'w':			/* width in chars */
	if (p[2] == '\0')	/* any more args for these? */
	  ++i;			/* yes, eat it */
	break;
      default:
	log_e("papif: Unknown argument %c\n",p[1]);
      }
    } else if (doacct)
      *acctfile = argv[i];
  }
  /* Get base of program name if specified with full path */
  if (pgmname == NULL) {
    if ((pgmname = rindex(argv[0], '/')) != NULL)
      pgmname++;			/* else skip over slash */
    else
      pgmname = argv[0];
  }
  /* if program name isn't papif or psif and printer name isn't there */
  /* then use name of program (psif added for Transcript compatibility) */
  if (*printer == '\0') 	/* no printer name? */
    if (strcmp(pgmname, "papif") != 0 && strcmp(pgmname, "psif") != 0)
      strcpy(printer, pgmname);
}


/*
 * our "input" filter - send input to laserwriter
 *
 * stdin - input
 * stdout - points to devices, empty here
 * stderr - points to log file on BSD4.2, Ultrix 1.0 through Ultrix 1.2
 *        - points to "err" tmp file on BSD 4.3
 *
 * Exit codes are listed above
 *
*/
main(argc,argv)
int argc;
char **argv;
{
  int pstatus();
  char *getlwname();
  char *acctfile = NULL;
  int spc, epc;

  /* Initialize base vars */
  cno = -1;
  pid = getpid();		/* mark as not there for now */

  getargs(argc, argv, printer, user, host, &acctfile);
  initenv();			/* Transcript compatibility */

  lwname = getlwname(printer);	/* based on this */
  if (lwname == NULL) {
    log_e("papif: Cannot map name %s to LaserWriter name\n",printer);
    exit(lpd_REPRINT);
  }

  /* init cap */
  abInit(FALSE);		/* don't printout -- messes up with <stdin> */
  nbpInit();
  PAPInit();			/* init PAP printer routines */
  ATPSetResponseTimeout(atpresponsetimeout); /* set to 2 minutes */

  /* log message */
  log_i("papif: Starting job for %s@%s at %s on printer %s\n",
       user,host,ptime(),lwname);
  logenv();
  signal(SIGEMT, SIG_IGN);	/* used by psrev, etc to signal */
				/* they are "ready" */
  signal(SIGINT,quit);		/* this is what lprm sends us */
  signal(SIGTERM,quit);		/* this is what disable sends us */

  cno = openlw(lwname);
  if (doacct && chargebanner)
    spc = getpagecount(cno);	/* get the page count */
  if (bannerfirst)
    sendbanner(cno, host, user);
  if (doacct && !chargebanner)
    spc = getpagecount(cno);	/* get the page count */

  if (psmagic)
    dopsmagic(cno);
  sendfile(cno,fileno(stdin),host,user);/* send file to laserwriter */

  if (doacct && !chargebanner)
    epc = getpagecount(cno);	/* get the page count */
  if (bannerlast)
    sendbanner(cno, host, user);
  if (doacct && chargebanner)
    epc = getpagecount(cno);	/* get the page count */

  PAPClose(cno);

  if (doacct && acctfile != NULL)
    doaccounting(spc, epc, acctfile, user, host);
  log_i("papif: Finished job at %s\n", ptime ());
  exit(lpd_OK);			/* exit okay */
}

/*
 * send banner page: unlink afterwards so bannerfirst+bannerlast
 * won't result in two banner pages
 *
*/
sendbanner(cno, host, user)
int cno;
char *host, *user;
{
  int bannerfd;

  if ((bannerfd = open(bannerfile, 0)) >= 0) {
    sendfile(cno, bannerfd, host, user);
    close(bannerfd);
    unlink(bannerfile);
  }
}

/*
 * open laserwriter connection
 * log errors every 5 minutes to stderr
 *
*/
int
openlw(lwname)
char *lwname;
{
  int i, cno, ocomp, err;
  PAPStatusRec status;

  i = 0;
  /* Keep trying to open */
  while ((err = PAPOpen(&cno, lwname, rflowq, &status, &ocomp) ) != noErr) {
    if (err != -1)		/* should be can't find lw.. */
      log_e("papif: PAPOpen returns %d\n",err);
    else {
      if ((i % 10) == 0) {	/* waited 5 minutes? */
	log_e("papif: Problems finding %s\n",lwname);
	i = 1;
      } else i++;
    }
    sleep(30);			/* wait N seconds */
  }
  do {
    abSleep(16, TRUE);
    if (watchtime != 0)		/* a little bogus.... */
      pstatus(status.StatusStr);
  } while (ocomp > 0);
  return(cno);
}

/*
 * reaper - deal with death of children
 *
*/
reaper()
{
#ifdef NOWAIT3
  int status;
#else /* have WAIT3 */
  union wait status;
#endif /* NOWAIT3 */
  register int i;
 
#ifdef NOWAIT3
  if ((i = wait(&status)) == 0) {
    log_w("papif: SIGCHLD but nothing from wait3\n");
    return;
  }
#else
  if ((i = wait3(&status, WNOHANG, 0)) == 0) {
    log_w("papif: SIGCHLD but nothing from wait3\n");
    return;
  }
#endif

  if (WIFSTOPPED(status)) {
    log_e("papif: Child %d stopped!\n", i);
    exit(lpd_REPRINT);
  }
 
  if (WIFSIGNALED(status)) {
    i = W_TERMSIG(status);
    if (i != SIGINT && i != SIGTERM) {
      log_e("papif: Child killed by signal %d\n", i);
      exit(lpd_REPRINT);
    }
  } else {
    if (i = W_RETCODE(status)) {
      log_w("papif: Finished job at %s with status %d\n", ptime(),i);
      /* probably psrev complaining */
      exit(lpd_ERRORS);
    }
    /* nothing to do with zero exit code */
  }
}

#define MAGICSIZE 11
#define MAXFILTERS 2
private char *filters[MAXFILTERS];
private int numfilters = 0;

/*
 * check input stream for adobe magic
 *
*/
dopsmagic()
{
  char magic[MAGICSIZE];
  int cnt, i;
  struct stat buf;
  int diskfile = FALSE;
  int retval;
  int in_front = TRUE;		/* fork filters in front of current fork */
  extern int errno;

  if (fstat(fileno(stdin), &buf) < 0) {
    perror("psmagic setup: fstat");
    diskfile = FALSE;
  } else {
    /* is it a regular file? */
    diskfile = S_ISREG(buf.st_mode);
  }
  if (!diskfile) {
    retval = fork_filter("<stdin>", TRUE);
    if (retval < 0)
      quit();
    if (retval != 0)
      return;
    in_front = FALSE;
  }
  if ((cnt = read(fileno(stdin), magic, MAGICSIZE)) < 0) {
    perror("psmagic setup: read");
    if (diskfile)
      quit();
    else
      exit(lpd_ERRORS);
  }
  if (diskfile) {
    rewind(stdin);
    lseek(fileno(stdin), 0L, 0); /* paranoia */
  }
  if (cnt < 2) {		/* nothing to do */
    log_w("papif: psmagic: only read %d, can't check magic\n", cnt);
    if (diskfile)
      return;
    else
      passalong(magic, cnt, MAGICSIZE);
  }

  if (strncmp(magic, "%!", 2) != 0) {
    if (pstext) {
      filters[numfilters++] = pstext;
      if (psreverse && !neverreverse)
	filters[numfilters++] = psreverse;
    }
  }
  /* check to see if follows Doc structuring 1.0 or better */
  /* note mutually exclusive of pstext */
  if (!neverreverse && psreverse &&
      cnt >= MAGICSIZE && strncmp(magic, "%!PS-Adobe-", MAGICSIZE) == 0)
    filters[numfilters++] = psreverse;

  if (in_front) {
    for (i = 0 ; i < numfilters; i++)
      fork_filter(filters[i], in_front);
  } else { /* must do in revse order */
    while (numfilters--)
      fork_filter(filters[numfilters], in_front);
  }
  if (diskfile)
    return;
  passalong(magic, cnt, MAGICSIZE);
}

/*
 * duplicate stdin with prefixed buffer
 *
*/
passalong(prebuf, precnt, wantcnt)
char *prebuf;
int precnt;
int wantcnt;
{
  register int cnt;
  register char *p;
  register int i;
  char buf[BUFSIZ];


  if (precnt < 0)
    exit(lpd_ERRORS);
  if (map_crtolf) {
    for (i = 0; i < precnt; i++)
      if (prebuf[i] == '\r')
	prebuf[i] = '\n';
  }
  if (write(fileno(stdout), prebuf, precnt) < 0)
    exit(lpd_ERRORS);
  if (precnt < wantcnt)
    exit(lpd_OK);
  while ((cnt = read(fileno(stdin), buf, sizeof(char) * BUFSIZ)) > 0) {
    /* dangerous */
    if (map_crtolf) {
      for (i = 0, p = buf; i < cnt; i++, p++)
	if (*p  == '\r')
	  *p = '\n';
    }
    if (write(fileno(stdout), buf, cnt) < 0)
      exit(lpd_ERRORS);
  }
  exit(cnt < 0 ? lpd_ERRORS : lpd_OK);
}


char *current_fork = "papif";

/*
 * Run a filter program in a child process;  diddle the descriptors so that
 * the filter eats the parent process's former stdin, and pipes its output
 * into the parent's new stdin.
 *
*/
fork_filter(fp, in_front)
char *fp;
{
  int fdpipe[2];
  int fpid;
  char *fn;
  int not_stdin_fork;
  int mask;

  not_stdin_fork = (fp != NULL) && (strcmp(fp, "<stdin>") != 0);
  if (debug) {
    log_d("papif: Forking %s %s current fork %s\n",
	  not_stdin_fork ?  fp : "<stdin>",
	  in_front ? "in front of" : "behind",
	  current_fork);
  }
  if (pipe(fdpipe) != 0) {
    perror("filter setup: pipe");
    quit();
  }

  if (fp) {
    fn = rindex(fp, '/');
    if (fn == NULL || fn[1] == '\0')
      fn = fp;
    else
      fn++;
  } else fn = NULL;
  if (!not_stdin_fork)
    fn = "<stdin>";

  /* interlock */
  mask = sigblock(sigmask(SIGCHLD)|sigmask(SIGINT)|sigmask(SIGTERM));
#ifdef NOVFORK
  fpid = fork();
#else
  fpid = not_stdin_fork ? vfork() : fork();
#endif
  switch (fpid) {
  case 0:			/* child */
    if (in_front) {
      if (dup2(fdpipe[1], fileno(stdout)) == -1) {
	perror("filter setup: child dup2");
	exit(lpd_ERRORS);
      }
    } else {
      if (dup2(fdpipe[0], fileno(stdin)) == -1) {
	perror("filter setup: child dup2");
	exit(lpd_ERRORS);
      }
    }
    close(fdpipe[1]);		/* ignore errs */
    close(fdpipe[0]);
    if (not_stdin_fork) {
      sigsetmask(mask);
      execl(fp, fn, 0);
      /* if we are here again, then... */
      perror("filter setup: child exec");
      exit(1);
    } else {
      current_fork = fn;
      signal(SIGINT, SIG_DFL);
      signal(SIGTERM, SIG_DFL);
      sigsetmask(mask);
      PAPShutdown(cno);		/* make sure child doesn't have the */
				/* fd's, etc. */
      return(0);
    }
    break;
  case -1:
    perror("filter setup: fork");
    return(-1);
    break;
  default:			/* parent continues */
    /* set up stdin to be pipe */
    signal(SIGCHLD, reaper);
    sigsetmask(mask);
    if (in_front) {
      if (dup2(fdpipe[0],fileno(stdin)) == -1) {
	perror("filter setup: parent dup2");
	return(-1);
      }
    } else {
      if (dup2(fdpipe[1],fileno(stdout)) == -1) {
	perror("filter setup: parent dup2");
	return(-1);
      }
    }
    close(fdpipe[1]);		/* ignore errs */
    close(fdpipe[0]);
    return(fpid);
  }
  return(0);
}

#ifdef NODUP2
#ifndef NOFILE
YOU MUST SET THIS TO THE MAXIMUM NUMBER OF FILE DESCRIPTORS AVAILABLE ON YOUR SYSTEM
#endif

/* emulate dup2 (hopefully) */
dup2(fdcur, fdwant)
int fdcur;
int fdwant;
{
  int fdarr[NOFILE];
  int i,j;
  int fd;

  close(fdwant);
  for (i = 0 ; i < NOFILE; i++) {
    fd = dup(fdcur);		/* duplicate */
    if (fd == fdwant)
      break;
    fdarr[i] = fd;		/* remember so we can close off */
  }
  if (i == NOFILE)
    return(-1);
  for (j = 0; j < i; j++)
    close(fdarr[j]);
  return(fdwant);
}
#endif

private char ps_buf[S_BUFMAX+10];
private char pr_buf[R_BUFMAX+10];

/*
 * Send a file to the specified connection
 */
sendfile(cno,fd,host,user)
int cno;
int fd;
char *host;
char *user;
{
  int eof, wcomp, paperr, err, doeof = FALSE;
  char *bp = ps_buf;
  wcomp = 0;


  strcpy(ps_buf, "/statusdict where {pop statusdict /jobname (");
  strcat(ps_buf, host);
  strcat(ps_buf, ":");
  strcat(ps_buf, user);
  strcat(ps_buf, ") put} if\n");
  if ((paperr=PAPWrite(cno, ps_buf,strlen(ps_buf), FALSE, &wcomp)) < 0) {
    log_e("papif: sendfile: 1st PAPWrite: call error %d\n", paperr);
    PAPClose(cno);
    exit(lpd_REPRINT);
  }
  /* post initial read from LW */
  err = 1;
  /* this is the main read/write loop */
  inithandleread();		/* initialze handleread */
  initgetbuf();			/* initialize getbuf */
  do {
    if ((eof = handleread(cno)))
      break;
    if (wcomp <= 0) {
      if (wcomp != noErr) {
	log_e("papif: sendfile: PAPWrite completion error %d\n",wcomp);
	PAPClose(cno);		/* sigh... */
	exit(lpd_REPRINT);
      } else {
	err = getbuf(fd, ps_buf, s_bufsiz, &bp, &doeof);
	/* err == 0 and no doeof means that we didn't see input */
	/* within a second or so, so we should try going ahead first */
	if (err || doeof) {
	  if ((paperr=PAPWrite(cno, bp, ((err<0)?0 :err), doeof, &wcomp)) < 0)
	    break;
	} else err = 1;
      }
    }

    statuswatch();
    abSleep(4, TRUE);		/* wait a bit */
  } while (err > 0 );

  if (err < 0)			/* this is a little overloaded */
    perror("read");
  if (paperr != noErr) {
    log_e("papif: sendfile: PAPWrite call error %d\n",paperr);
    wcomp = 0;
  }
  while (!eof || wcomp > 0) {		/* wait for completion */
    statuswatch();
    abSleep(4,TRUE);
    if (!eof)
      eof = handleread(cno);
  }
}

/*
 * return page count from a laserwriter
 *
*/
int
getpagecount(cno)
int cno;
{
  static char gpcstr[] = "statusdict begin pagecount (*) print == ";
  char buf[100];		/* enough for page count! */
  char *p;
  int err, wcomp, rlen, eof, rcomp, started;

  err = PAPWrite(cno, gpcstr, sizeof(gpcstr), TRUE, &wcomp);
  if (err != noErr)
    return(-1);
  err = PAPRead(cno, pr_buf, &rlen, &eof, &rcomp);
  if (err != noErr)
    return(-1);
  started = 0;
  do {
    if (rcomp <= 0) {
      if (rcomp == noErr && rlen > 0) {
	pr_buf[rlen] = '\0';	/* tie off string */
	if (!started) {
	  started = 1;
	  p = index(pr_buf, '*'); /* look for marker */
	  if (p != NULL) 
	    strcpy(buf, p+1);
	} else
	  strcat(buf, p);
      }
      if (eof)
	break;
      err = PAPRead(cno, pr_buf, &rlen, &eof, &rcomp);
      if (err != noErr)
	return(-1);
    }
    statuswatch();
    abSleep(4, TRUE);
  } while (!eof);
  return(atoi(buf));
}

/*
 * write out accounting information
 *
*/
doaccounting(spc, epc, acctfile, user, host)
int spc, epc;
char *acctfile, *user, *host;
{
  FILE *afd;

  if (epc >= spc && epc > 0 && spc > 0)  {
    if (user[0] != '\0' && acctfile && access(acctfile, W_OK) >= 0 &&
	(afd = fopen(acctfile, "a")) != NULL)
      fprintf(afd,"%7.2f\t%s:%s\n", (float)(epc-spc), host, user);
  } else
    log_w("papif: Problems getting pagecount: start %d, end %d\n",spc,epc);
}

/*
 * output status message to disk so lpq et al. can show it
 * Note: input string is a pascal string
 * Keeps around status until we have written out the status file again
 * so looks for status aren't there are minimized
 *
*/
pstatus(s)
char *s;
{
  int fd;
  int okay = TRUE;

  unlink("newstatus");
  if ((fd = open("newstatus",O_CREAT|O_WRONLY,0664)) < 0)
    return;
  if (write(fd, s+1, *s) < 0)
    okay = FALSE;
  write(fd, "\n", 1);
  close(fd);
  if (okay) {
    unlink("status");
    link("newstatus","status");
    unlink("newstatus");
  }
}

/*
 * returns nicely formated time string
*/
char *
ptime()
{
  long clock;
  char *timestr;
  char *p;

  clock = time(0);
  timestr = (char *)asctime(localtime(&clock));
  /* truncate after first linefeed */
  if ((p = index(timestr, '\n')))
    *p = '\0';
  return(timestr);
}

/*
 * get the laserwriter name of the unix spooled printer
 *
 * returns NULL if nothing found
 * returns 'LaserWriter Plus' if printer is null
*/
char *
getlwname(printer)
char *printer;
{
  FILE *fd;
  static char buf[1024];
  char *ep;
  char *getenv();

  if (printer[0] == '\0') {	/* no printer */
    /* try last resort */
    if ((printer = getenv("PRINTER")) == NULL) {
      return("LaserWriter Plus:LaserWriter@*");	/* default */
    }
  }

  if ((fd = fopen(capprinters,"r")) == NULL) {
    perror(capprinters);
    return(NULL);
  }
  do {
    if (fgets(buf, 256, fd) == NULL)
      break;
    buf[strlen(buf)-1] = '\0';	/* get rid of the lf */
    if (buf[0] == '#' || buf[0] == '\0')
      continue;
    if ((ep=index(buf,'=')) == NULL) /* find first = */
      continue;			/* no = in string */
    *ep = '\0';			/* set = to null now */
    if (strcmp(buf,printer) == 0) {
      if (strlen(ep+1) == 0)	/* no name */
	continue;
      fclose(fd);
      return(ep+1);		/* return pointer to value */
    }
  } while (1);
  fclose(fd);
  return(NULL);
}

/* MODULE: statuswatch EXPORTS(statuswatch) IMPORTS(lwname, pstatus) */

private time_t oldtime = -1;
private time_t newtime;
/*
 * Listens for status messages from lw
 *
*/
statuswatch()
{
  AddrBlock addr;
  PAPStatusRec status;
  int i;
  char retry[255];
  char tmpbuf[255];
  static int idletime = 0;
  OSErr err;

  if (watchtime == 0)		/* no status watch */
    return;
  if (oldtime == -1)
    time(&oldtime);
  else {
    time(&newtime);
    if ((newtime - oldtime) < watchtime) /* delay this much */
      return;
  }
  addr.net = 0;		/* sufficient */
  err = PAPStatus(lwname, &status, &addr);
  if (err < 0) {
    if (err != reqFailed)	/* no status message */
      return;
  } else
    oldtime = newtime;		/* reset */
  if (doidlestuff) {
    cpyp2cstr(tmpbuf, status.StatusStr);
    /* idletimeout if 40 seconds of "status: idle" or if papifidletest is */
    /* set.  Note: I have seen cases when the open reply pkt was lost */
    /* and never got back into open state, but only when running without */
    /* xo on the opens (a bug) */
    if (idletime == 0)
      time(&idletime);
    if ((strncmp("status: idle", tmpbuf, 12) == 0 && (time(0)-idletime)>40) || 
	access("/tmp/papifidletest", F_OK) == 0) {
      log_e("papif: status: idle bug; restarting\n");
      fflush(stderr);
      unlink("/tmp/papifidletest");
      for (i = 0 ; i < NSIG ; i++)
	signal(i, SIG_IGN);
      sprintf(retry, "(/usr/bin/sleep 2;/etc/lpc abort %s;/usr/bin/sleep 2;/etc/lpc start %s)&", 
	      printer, printer);
      system(retry);
      exit(lpd_REPRINT);
    }
  }
  pstatus(status.StatusStr);
}
/* END MODULE */


/* MODULE: getbuf EXPORTS(initgetbuf, getbuf) IMPORTS() */
/*
 * handle reads from file/pipe
 *
*/

static int gb_bc = 0, gb_stage = 0;
static char *gb_bp;
static char *gb_eofstr = "%%EOF\n";

initgetbuf()
{
  gb_bc = 0;
  gb_stage = 0;
}

/*
 * do a read, but don't let it hangup protocol if the read is blocking
 *
*/
private
dosread(fd, buf, length, nodata)
int fd;
char *buf;
int length;
int *nodata;
{
  int rdy;
  struct timeval t;
  gfd_set aset;
  int j;

  t.tv_sec = 0;
  t.tv_usec = 250;		/* 1/4 second? */
  FD_ZERO(&aset);
  FD_SET(fd, &aset);
  rdy = -1, j = 0;
  *nodata = FALSE;
  while (rdy <= 0 && j < 4) {
    FD_SET(fd, &aset);
    rdy = select(fd+1, &aset, 0, 0, &t);
    if (rdy > 0)
      break;
    abSleep(1, TRUE);
    j++;
  }
  if (rdy <= 0) {
    *nodata = TRUE;
    return(0);
  }
  return(read(fd, buf, length));
}

/*
 * Get a buffer looking for "%%EOF" at the start of a line if dostruct is set
 * otherwise just return a buffer
 *
*/
getbuf(fd, buf, maxbufsiz, bstart, doeof)
int fd;
char *buf;
int maxbufsiz;
char **bstart;
int *doeof;
{
  char *p;
  int i, j, nodata;

  if (!dostruct) {
    i = dosread(fd, buf, maxbufsiz, &nodata);
    if (nodata)
      return(i);
    if (i <= 0)
      *doeof = TRUE;
    if (strip_control_d) {
      /* dangerous, but may be necessary in some cases */
      for (p = buf, j = 0 ; j < i; j++)
	if (*p == '\004')
	  *p = '\n';
    }
    return(i);
  }
  if (gb_bc <= 0) {
    /* feed in previously read here: maybe backup this way? */
    if ((gb_bc = dosread(fd, buf, maxbufsiz, &nodata)) <= 0) {
      if (nodata)
	return(gb_bc);
      *doeof = TRUE;
      return(gb_bc);
    }
    gb_bp = buf;
  }
  /* scan gb_stage */
  *doeof = FALSE;
  for (p = gb_bp, i = 0; i < gb_bc; i++, p++) {
    if (strip_control_d) {
      if (*p == '\004')
	*p = '\n';
    }    
    if (gb_stage == 0)  {
      if (*p == '\r' || *p == '\n')
	gb_stage++;
    } else {
      if (*p == '\r')
	*p = '\n';
      if (*p == gb_eofstr[gb_stage-1]) {
	gb_stage++;
	if (gb_eofstr[gb_stage] == '\0') {
	  p++;
	  *doeof = TRUE;
	  gb_stage = 0;
	  break;
	}
      } else gb_stage = 0;
    }
  }
  *bstart = gb_bp;
  if (i != gb_bc)
    i++;			/* okay, have offset to convert to count */
  gb_bp = p;
  gb_bc -= i;			/* decrement by count */
  return(i);			/* return count */
}
/* END MODULE: getbuf */

/* MODULE: handleread EXPORTS(inithandleread,handleread) */
/* IMPORTS() */
private int hr_rcomp = noErr;
private int hr_rlen = 0;
private char hr_rbuf[R_BUFMAX+10];
private int hr_eof = 0;

inithandleread()
{
  hr_rcomp = noErr;
  hr_rlen = 0;
  hr_eof = 0;
}

/*
 * handle the papread
 *  return: -1 paperr
 *           0 ok
 *           1 eof
*/
handleread(cno)
{
  int paperr;

  if (hr_rcomp > 0)
    return(0);
  switch (hr_rcomp) {
  case noErr:
    break;
  default:
    log_e("papif: handleread: PAPRead error %d\n", hr_rcomp);
    return(-1);
  }
  hr_rbuf[hr_rlen] = '\0';
  if (hr_rlen) {
    log_r("%s", hr_rbuf);
  }
  if (hr_eof) {
    return(1);
  }
  paperr = PAPRead(cno, hr_rbuf, &hr_rlen, &hr_eof, &hr_rcomp);
  switch (paperr) {
  case noErr:
    break;
  default:
    log_e("papif: handleread: PAPRead error %d\n", paperr);
    return(-1);
  }
  return(0);
}

/* END MODULE: handleread */

/* BEGIN MODULE: log EXPORTS(log,joboutput) */

/*
 * Setup this so we can be smarter about errors in future
 * logging level are setup as: i - information, w - warning
 * e - error, r - return from laserwriter, and d - for debugging
 *
*/

private FILE *jobout;

#ifndef USEVPRINTF
/* Bletch - gotta do it because pyramids don't work the other way */
/* (using _doprnt and &args) and don't have vprintf */
/* of course, there will be something that is just one arg larger :-) */
/* VARARGS1 */
dolog(fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af)
char *fmt;
#else
dolog(va_alist)
va_dcl
#endif
{
#ifdef USEVPRINTF
  register char *fmt;
  va_list args;

  va_start(args);
  fmt = va_arg(args, char *);
  if (jobout)
    vfprintf(jobout, fmt, args);
  vfprintf(stderr, fmt, args);
  va_end(args);
#else
  if (jobout)
    fprintf(jobout, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af);
      fprintf(stderr, fmt, a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae,af);
#endif
}

/*
 * Open the jobout file if possible - used for lp model
 *
*/
joboutput(filename)
char *filename;
{
  if ((jobout = fopen(filename, "w"))) {
    /* put a return at the start */
    putc('\n', jobout);
  }
}
/* END MODULE: log */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.