[comp.protocols.appletalk] New version of page-reversal/job-banner enhancements to papif.c

dplatt@coherent.com (Dave Platt) (02/23/88)

This posting contains an updated version of the page-reversal and
job-banner enhancements to the papif print-filter that I posted a
couple of weeks ago.  The major change in this version is that it will
permit papif to work properly with print spoolers other than lpd if the
"pstext" feature is enabled.

Up until now, papif has been unable to perform its filtering "magic"
successfully if papif's standard input was assigned to a pipe... papif
would attempt to "rewind" the pipe after reading the first few bytes,
and the bytes that papif had read would never be printed.  This problem
is visible on Masscomp systems, which use a proprietary print-spooling
package that passes input to papif via a pipe;   it may also be visible
on other systems.

The problem is avoided (I won't say "fixed") by adding some smarts to
the pstext() procedure.  If it sees that the lseek() call has returned
a -1 ("seek failed"), it forks off a child process that interposes itself
between the parent papif and the original input pipe;  the child simply
reads the input pipe, prepends the 11-byte magic string, and writes
the reconstructed data into a pipe to the parent.  This is definitely
a kluge, if not a crock... but it does work.

If your system uses the BSD-standard "lpd", you probably won't need to
install this modification... the version I posted on the 12th should
work OK.  You can install this mod if you wish, though... it won't be
harmful, and the buffering-child-process won't be started if it isn't
needed (which it won't be).

These diffs should be applied with "patch", starting with versions of
papif.c and the samples Makefile that have are at the "prerelease
distribution 4 plus patch sets 5, 6, and 7".  Don't try to apply the
patch against sources that have had the 2/12 version of my enhancements
added... patch will probably reject all of the batches.


Dave Platt
  UUCP: ...!{ames,sun,uunet}!coherent!dplatt     DOMAIN: dplatt@coherent.com
    INTERNET: coherent!dplatt@ames.arpa, ...@sun.com, ...@uunet.uu.net


*** Makefile.patch7	Tue Feb  9 17:31:06 1988
--- Makefile	Mon Feb 22 11:08:38 1988
***************
*** 2,8 ****
  #
  # If you have Transcript from Adobe for your laserWriter and want to
  #  print text files, uncomment the next line and set the location properly
! # ADOBEPS="-DPSTEXT=\"/usr/local/lib/ps/pstext\""
  # 
  # Note: for papif, there is another define called "IDLESTUFF" which
  # handles a problem that some sites see in papif seeing a long standing
--- 2,15 ----
  #
  # If you have Transcript from Adobe for your laserWriter and want to
  #  print text files, uncomment the next line and set the location properly
! #ADOBEPS='-DPSTEXT="/usr/local/lib/lw/pstext"'
! 
! # If you have TranScript, have uncommented the ADOBEPS line above, and
! # wish to have conforming PostScript files printed last-page-first
! # so that they stack nicely, uncomment the next line and set the
! # location properly.
! #ADOBEREV='-DPSREVERSE="/usr/local/lib/lw/psrev"'
! 
  # 
  # Note: for papif, there is another define called "IDLESTUFF" which
  # handles a problem that some sites see in papif seeing a long standing
***************
*** 10,15 ****
--- 17,38 ----
  #
  # Define SFLOWQ=1 in CFLAGS if you seem to have problems with your LW dropping
  # pkts
+ #
+ # Define TRAILER in CFLAGS if you want an end-of-job trailer page, which
+ # contains the user's name and gives a time-stamped log of the printing
+ # process (including any printer errors).
+ #
+ # Define BOGON in CFLAGS if you want the bogon (control-D) filter
+ # activated.  This filter is provided for the benefit of PC-NFS, which
+ # insists on adding a control-D at the end of its headers.
+ #
+ # Define NOSTATUS in CFLAGS if you want to suppress the periodic
+ # status-checking code (some users find that it causes the printer
+ # to hang).
+ #
+ # Define SEEKUNRELIABLE in CFLAGS if your system's implementation of
+ # lseek() doesn't return a value of -1 to indicate "seek failed."
+ 
  CFLAGS=-O
  LFLAGS=
  MAC_EFSSRC=/usr/src/local/mac/efs/src/mac
***************
*** 69,75 ****
  	cc ${LFLAGS} -o papif papif.o $(O)  $(CAPLIB)
  
  papif.o: papif.c
! 	cc ${CFLAGS} ${ADOBEPS} -c papif.c
  
  #
  # look, looks, and pinger all have a common source
--- 92,98 ----
  	cc ${LFLAGS} -o papif papif.o $(O)  $(CAPLIB)
  
  papif.o: papif.c
! 	cc ${CFLAGS} ${ADOBEPS} ${ADOBEREV} -c papif.c
  
  #
  # look, looks, and pinger all have a common source
*** papif.c.patch7	Tue Feb  9 17:33:03 1988
--- papif.c	Fri Feb 19 14:10:02 1988
***************
*** 17,22 ****
--- 17,24 ----
   *  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
+  *  Feb      1988    coherent!dplatt, add psrev, trailer page w/job log,
+  *                   control-D filter for PC-NFS.
   *
   */
  
***************
*** 30,35 ****
--- 32,38 ----
  #include <sys/param.h>
  #include <signal.h>
  #include <strings.h>
+ #include <ctype.h>
  
  #include <netat/appletalk.h>		/* include appletalk definitions */
  #include <netat/compat.h>
***************
*** 45,50 ****
--- 48,55 ----
  int cno;
  int ppid;
  int pid;
+ int eof, rlen, rcomp, wcomp, paperr;
+ int spc, epc;
  char host[30],printer[30],user[30];
  #define RFLOWQ 8
  #ifndef SFLOWQ
***************
*** 57,62 ****
--- 62,78 ----
  int xdebug = TRUE;
  char *ptime();
  
+ #ifdef TRAILER;
+ struct status_msg
+ {
+   struct status_msg *flink;
+   char               text[128];
+ };
+ 
+ struct status_msg *status_head, *status_tail;
+ int errors_received;
+ #endif
+ 
  /* quit gets called when we've been killed by lprm via SIGINT */
  quit()
  {
***************
*** 143,150 ****
    char *getlwname();
    char *acctfile;
  
- 
-   int spc, epc;
  #ifdef DEBUG
    int abort();
  #endif
--- 159,164 ----
***************
*** 156,174 ****
--- 170,197 ----
    getargs(argc, argv, printer, user, host, &acctfile);
  
    lwname = getlwname(printer);	/* based on this */
+ 
    if (lwname == NULL) {
      fprintf(stderr,"Cannot map name %s to LaserWriter name\n",printer);
      exit(lpd_REPRINT);
    }
  
+ #ifdef TRAILER
+   status_head = status_tail = (struct status_msg *) NULL;
+   errors_received = 0;
+   log_message("Job started.");
+ #endif
+ 
    /* init cap */
    abInit(xdebug);		/* initialize appletalk driver */
    nbpInit();
    PAPInit();			/* init PAP printer routines */
+   ATPSetResponseTimeout(sectotick(60*4)); /* set to 4 minutes */
  
    /* log message */
    fprintf(stderr,"PAPIF: Starting job for %s@%s at %s on printer %s\n",
  	  user,host,ptime(),lwname);
+   fflush(stderr);
  
  #ifdef DEBUG
    signal(SIGEMT, abort);
***************
*** 184,189 ****
--- 207,216 ----
  
    cno = openlw(lwname);
  
+ #ifdef TRAILER
+   log_message("Printer open.");
+ #endif;
+ 
  #ifndef NOSTATUS
    /* base runs status process */
    if ((pid = fork()) < 0) {
***************
*** 237,243 ****
--- 264,272 ----
    }
    do {
      abSleep(16, TRUE);
+ #ifndef NOSTATUS
      pstatus(status.StatusStr);
+ #endif
    } while (ocomp  > 0);
    return(cno);
  }
***************
*** 249,294 ****
  */
  pstext()
  {
!   char magic[2];
!   int fdpipe[2];
!   int fpid;
  
!   if (read(fileno(stdin),magic,2) != 2) {
      perror("pstext setup: read");
      quit();
    }
!   rewind(stdin);
!   lseek(fileno(stdin), 0L, 0);
!   if (strncmp(magic,"%!",2) == 0) /* postscript file? */
!     return;			/* yes, no more to do */
!   /* else assume a text file and pipe thru pstext */
    if (pipe(fdpipe) != 0) {
!     perror("pstext setup: pipe");
      quit();
    }
    fpid = vfork();
    switch (fpid) {
    case 0:			/* child */
      if (dup2(fdpipe[1], fileno(stdout)) == -1) {
!       perror("pstext setup: child dup2");
        quit();
      }
      close(fdpipe[1]);		/* ignore errs */
      close(fdpipe[0]);
!     execl(PSTEXT, "pstext", 0);
      /* if we are here again, then... */
!     perror("pstext setup: child exec");
      kill(ppid, SIGINT);
      quit();
      break;
    case -1:
!     perror("pstext setup: fork");
      quit();
      break;
    default:			/* parent continues */
      /* set up stdin to be pipe */
      if (dup2(fdpipe[0],fileno(stdin)) == -1) {
!       perror("pstext setup: parent dup2");
        quit();
      }
      close(fdpipe[1]);		/* ignore errs */
--- 278,422 ----
  */
  pstext()
  {
!   char magic[11];
  
!   if (read(fileno(stdin),magic,11) != 11) {
      perror("pstext setup: read");
      quit();
    }
! #ifdef SEEKUNRELIABLE
!   fork_buffer_process(magic, 11);
! #else
!   if (lseek(fileno(stdin), 0L, 0) == -1)
!     {
!       fork_buffer_process(magic, 11);
!     }
! #endif
!   if (strncmp(magic, "%!", 2) == 0)
!     { /* It's PostScript;  no need to filter through pstext */
! #ifdef PSREVERSE
!       if (strncmp(magic, "%!PS-Adobe-", 11) != 0)
! 	{
! 	  return; /* Nonconforming PostScript;  send as-received */
! 	}
! #else
!       return;
! #endif
!     }
!   else
!     {
!       fork_filter(PSTEXT);   /* Not PostScript;  filter it */
! #ifdef TRAILER
!       log_message("Text-to-PostScript filter open.");
! #endif
!     }
! #ifdef PSREVERSE
!   fork_filter(PSREVERSE);
! #ifdef TRAILER
!  log_message("Page-reversal filter open.");
! #endif
! #endif
! }
! 
! /*
!   If the input to papif is a pipe, rather than the spool file itself,
!   the lseek() call to "rewind" the pipe and reposition in front of the
!   11-byte "magic" string will fail.  We must fork off a child that will
!   sit between papif and whatever is piping data to it;  the child's
!   sole duty is to read the pipe and write it to papif, prepending the
!   magic string.  Ugh.
! */
! fork_buffer_process(magic, magiclength)
! char *magic;
! int magiclength;
! {
!   int fdpipe[2];
!   int fpid, err;
    if (pipe(fdpipe) != 0) {
!     perror("buffer-child setup: pipe");
      quit();
    }
+   fpid = fork();
+   switch (fpid) {
+   case 0:			/* child */
+     if (dup2(fdpipe[1], fileno(stdout)) == -1) {
+       perror("buffer-child setup: child dup2");
+       quit();
+     }
+     close(fdpipe[1]);		/* ignore errs */
+     close(fdpipe[0]);
+     write(fileno(stdout), magic, magiclength);
+     do
+       {
+ 	err = read(fileno(stdin), buf, SBUFMAX);
+ 	if (err > 0)
+ 	  write(fileno(stdout), buf, err);
+       } while (err > 0);
+     if (err < 0)
+       {
+ 	sprintf(buf, "Read error %d\n", err);
+ 	write(fileno(stdout), buf, strlen(buf));
+ 	fprintf(stderr, buf);
+ 	fflush(stderr);
+       }
+     close(fileno(stdout));
+     exit(lpd_OK);
+     break;
+   case -1:
+     perror("buffer-child setup: fork");
+     quit();
+     break;
+   default:			/* parent continues */
+     /* set up stdin to be pipe */
+     if (dup2(fdpipe[0],fileno(stdin)) == -1) {
+       perror("buffer-child setup: parent dup2");
+       quit();
+     }
+     close(fdpipe[1]);		/* ignore errs */
+     close(fdpipe[0]);
+     log_message("Pipe-buffer process started.");
+     return;
+   }
+ }
+ 
+ /*
+   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(whatever)
+ char *whatever;
+ {
+   int fdpipe[2];
+   int fpid;
+ 
+   if (pipe(fdpipe) != 0) {
+     perror("filter setup: pipe");
+     quit();
+   }
    fpid = vfork();
    switch (fpid) {
    case 0:			/* child */
      if (dup2(fdpipe[1], fileno(stdout)) == -1) {
!       perror("filter setup: child dup2");
        quit();
      }
      close(fdpipe[1]);		/* ignore errs */
      close(fdpipe[0]);
!     execl(whatever, "psfilter", 0);
      /* if we are here again, then... */
!     perror("filter setup: child exec");
      kill(ppid, SIGINT);
      quit();
      break;
    case -1:
!     perror("filter setup: fork");
      quit();
      break;
    default:			/* parent continues */
      /* set up stdin to be pipe */
      if (dup2(fdpipe[0],fileno(stdin)) == -1) {
!       perror("filter setup: parent dup2");
        quit();
      }
      close(fdpipe[1]);		/* ignore errs */
***************
*** 304,317 ****
  sendfile(cno)
  int cno;
  {
!   int eof, rlen, rcomp, wcomp, paperr, err, fd;
  
    fd = fileno(stdin);
    /* post initial read from LW */
    if ((paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp)) < 0) {
  	fprintf(stderr,"PAPRead error %d\n",paperr);
    }
!     
    wcomp = 0;
    /* this is the main read/write loop */
    do {
--- 432,462 ----
  sendfile(cno)
  int cno;
  {
!   int err, fd;
  
+ #ifdef TRAILER
+   static char trailercode[] = "\ngsave initgraphics /Helvetica findfont 36\
+  scalefont setfont /gl 0 def 700 -36 400 {200 exch moveto gl setgray (%s)\
+  show /gl gl .12 add def} for /Helvetica findfont 8 scalefont setfont 0\
+  setgray\n";
+   static char logcode[] = "50 %d moveto (%s) show\n";
+   char trailercmd[256];
+   enum {sendbody, sendEOF1, sendident, sendlog, sendEOF2}
+    trailer_phase = sendbody;
+   int log_offset = 350;
+ #endif  
+ 
    fd = fileno(stdin);
    /* post initial read from LW */
    if ((paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp)) < 0) {
  	fprintf(stderr,"PAPRead error %d\n",paperr);
+ 	fflush(stderr);
    }
!   fprintf(stderr,"Sending file");
!   fflush(stderr);
! #ifdef TRAILER
!   log_message("Printing starts.");
! #endif  
    wcomp = 0;
    /* this is the main read/write loop */
    do {
***************
*** 318,331 ****
--- 463,483 ----
      if (rcomp <= 0) {
        if (rcomp != noErr) {
  	fprintf(stderr,"PAPRead error %d\n",rcomp);
+ 	fflush(stderr);
  	break;
        } else if (rlen > 0) {
  	rbuf[rlen] = '\0';
  	fprintf(stderr,"%s",rbuf);
+ 	fflush(stderr);
+ #ifdef TRAILER
+ 	errors_received++;
+ 	log_message(rbuf);
+ #endif
        }
        paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp);
        if (paperr < 0) {
  	fprintf(stderr,"PAPRead error %d\n",paperr);
+ 	fflush(stderr);
  	break;
        }
      }
***************
*** 332,344 ****
      if (wcomp <= 0) {
        if (wcomp != noErr) {
  	fprintf(stderr,"PAPWrite error %d\n",wcomp);
  	kill(ppid, SIGINT);	/* signal abort to parent */
        }
        else {
! 	err = read(fd, buf, SBUFMAX);
! 	if (err > 0) 
! 	  if ((paperr = PAPWrite(cno, buf, err, FALSE, &wcomp)) < 0)
  	    break;
        }
      }
      abSleep(4, TRUE);		/* wait a bit */
--- 484,576 ----
      if (wcomp <= 0) {
        if (wcomp != noErr) {
  	fprintf(stderr,"PAPWrite error %d\n",wcomp);
+ 	fflush(stderr);
  	kill(ppid, SIGINT);	/* signal abort to parent */
        }
        else {
! #ifdef TRAILER
! 	switch (trailer_phase)
! 	  {
! 	  case sendbody:
! 	    err = read(fd, buf, SBUFMAX);
! 	    if (err != 0) break;
! 	    trailer_phase = sendEOF1;
! 	  case sendEOF1:
! 	    sendPAPeof();
! 	    epc = getpagecount(cno);
! 	    if (spc != -1 && epc != -1)
! 	      {
! 	        if (epc - spc == 1)
! 	          {
! 	            log_message("End-of-file; 1 page printed.");
! 	          }
! 	        else
! 	          {
! 		    sprintf(buf, "End-of-file; %d pages printed.", epc-spc);
! 		    log_message(buf);
! 		  }
! 	      }
! 	    else
! 	      {
! 		log_message("End-of-file.");
! 	      }
! 	    fprintf(stderr, " EOF... ");
! 	    fflush(stderr);
! 	    trailer_phase = sendident;
! 	  case sendident:
! 	    fprintf(stderr, " trailer");
! 	    fflush(stderr);
! 	    log_message("Job completed.");
! 	    if (strlen(user) > 0)
! 	      {
! 		sprintf(buf, trailercode, user);
! 	      }
! 	    else
! 	      {
! 		sprintf(buf, trailercode, "no name");
! 	      }
! 	    err = strlen(buf);
! 	    trailer_phase = sendlog;
  	    break;
+ 	  case sendlog:
+ 	    if (status_head != (struct status_msg *) NULL)
+ 	      {
+ 		sprintf(buf, logcode, log_offset, status_head->text);
+ 		log_offset -= 9;
+ 		status_head = status_head->flink;
+ 	      }
+ 	    else
+ 	      {
+ 		strcpy(buf, "showpage grestore\n");
+ 		trailer_phase = sendEOF2;
+ 	      }
+ 	    err = strlen(buf);
+ 	    break;
+ 	  case sendEOF2:
+ 	    err = 0;
+ 	    break;
+ 	  }
+ #else
+ 	err = read(fd, buf, SBUFMAX);
+ #endif
+ 	if (err > 0)
+ 	  {
+ #ifdef BOGON
+ 	    register int bogon_scan;
+ 	    for (bogon_scan = 0; bogon_scan < err; bogon_scan++)
+ 	      {
+ 		if (buf[bogon_scan] == '\004')
+ 		  {
+ 		    buf[bogon_scan] = '\n';
+ 		  }
+ 	      }
+ #endif
+ 	    if ((paperr = PAPWrite(cno, buf, err, FALSE, &wcomp)) < 0)
+ 	      break;
+ 	    fprintf(stderr, ".");
+ 	    fflush(stderr);
+ 	  }
+ 	
        }
      }
      abSleep(4, TRUE);		/* wait a bit */
***************
*** 346,370 ****
  
    if (err < 0)			/* this is a little overloaded */
      perror("read");
!   paperr = PAPWrite(cno, NULL, 0, TRUE, &wcomp); /* send eof */
!   while (!eof) {		/* wait for completion */
!     if (rcomp <= 0) {
!       if (rcomp != noErr)  {
! 	fprintf(stderr,"PAPRead error %d\n",rcomp);
! 	break;
!       } else if (rlen > 0) {
! 	rbuf[rlen] = '\0';
! 	fprintf(stderr,"%s",rbuf);
!       }
!       if (eof) break;
!       PAPRead(cno, rbuf, &rlen, &eof, &rcomp);
!     }
!     abSleep(4,TRUE);
!   } 
!   if (paperr != noErr)
!     fprintf(stderr,"PAPWrite error %d\n",paperr);
!   else
!     do { abSleep(4, TRUE); } while (wcomp > 0);
  }
  
  /*
--- 578,588 ----
  
    if (err < 0)			/* this is a little overloaded */
      perror("read");
!   fprintf(stderr, " EOF... ");
!   fflush(stderr);
!   sendPAPeof();
!   fprintf(stderr, " fini.\n");
!   fflush(stderr);
  }
  
  /*
***************
*** 527,533 ****
    AddrBlock addr;
    int hangup();
    PAPStatusRec status;
!   
    PAPHalfClose(cno);
    signal(SIGCHLD, hangup);
    addr.net = 0;		/* sufficient */
--- 745,754 ----
    AddrBlock addr;
    int hangup();
    PAPStatusRec status;
! #ifdef IDLESTUFF
!   int count = 0;
! #endif IDLESTUFF
! 
    PAPHalfClose(cno);
    signal(SIGCHLD, hangup);
    addr.net = 0;		/* sufficient */
***************
*** 542,557 ****
        cpyp2cstr(tmpbuf, status.StatusStr);
        if (strncmp("status: idle", tmpbuf, 12) == 0
  	  || access("/tmp/papifidletest", F_OK) == 0) {
! 	fprintf(stderr,"PAPIF:  status: idle bug; restarting\n");
! 	fflush(stderr);
! 	unlink("/tmp/papifidletest");
! 	for (i = 0 ; i < NSIG ; i++)
! 	  signal(i, SIG_IGN);
! 	sprintf(retry,
! 		"(sleep 2;/etc/lpc abort %s;sleep 2;/etc/lpc start %s)&", 
! 		printer, printer);
! 	system(retry);
! 	exit(lpd_REPRINT);
        }
      }
  #endif IDLESTUFF
--- 763,783 ----
        cpyp2cstr(tmpbuf, status.StatusStr);
        if (strncmp("status: idle", tmpbuf, 12) == 0
  	  || access("/tmp/papifidletest", F_OK) == 0) {
! 	count++;
! 	if (count >= 3) {	/* Idle 30 seconds?  Could be made shorter. */
! 	  fprintf(stderr,"PAPIF:  status: idle bug; restarting\n");
! 	  fflush(stderr);
! 	  unlink("/tmp/papifidletest");
! 	  for (i = 0 ; i < NSIG ; i++)
! 	    signal(i, SIG_IGN);
! 	  sprintf(retry,
! 		  "(sleep 2;/etc/lpc abort %s;sleep 2;/etc/lpc start %s)&", 
! 		  printer, printer);
! 	  system(retry);
! 	  exit(lpd_REPRINT);
! 	}
!       } else {
!         count = 0;	/* once it goes non-idle; restart count */
        }
      }
  #endif IDLESTUFF
***************
*** 558,561 ****
--- 784,867 ----
      pstatus(status.StatusStr);
      sleep(10);			/* update every 10 seconds */
    } while (1);
+ }
+ 
+ #ifdef TRAILER
+ 
+ int
+ log_message(what)
+      char *what;
+ {
+   struct status_msg *msg;
+   char text[512], *next;
+ 
+   msg = (struct status_msg *) malloc(sizeof(struct status_msg));
+   msg->flink = (struct status_msg *) NULL;
+ 
+   if (status_tail)
+     {
+       status_tail->flink = msg;
+     }
+   else
+     {
+       status_head = msg;
+     }
+   status_tail = msg;
+ 
+   strcpy(text, ptime());
+   strcat(text, "    ");
+ 
+   next = text+strlen(text);
+   
+   while (*what)
+     {
+       if (*what >= ' ')
+ 	{
+ 	  if (!isspace(*what) && !isalnum(*what)) /* Overkill, but WTF */
+ 	    {
+ 	      *next++ = '\\';
+ 	    }
+ 	  *next++ = *what;
+ 	}
+       what++;
+     }
+ 
+   *next = '\0';
+ 
+   strncpy(msg->text, text, sizeof msg->text);
+ }
+ #endif
+ 
+ int
+ sendPAPeof()
+ {
+ 
+   paperr = PAPWrite(cno, NULL, 0, TRUE, &wcomp); /* send eof */
+   while (!eof) {		/* wait for completion */
+     if (rcomp <= 0) {
+       if (rcomp != noErr)  {
+ 	fprintf(stderr,"PAPRead error %d\n",rcomp);
+ 	fflush(stderr);
+ 	break;
+       } else if (rlen > 0) {
+ 	rbuf[rlen] = '\0';
+ 	fprintf(stderr,"%s",rbuf);
+ 	fflush(stderr);
+ #ifdef TRAILER
+ 	log_message(rbuf);
+ 	errors_received++;
+ #endif
+       }
+       if (eof) break;
+       PAPRead(cno, rbuf, &rlen, &eof, &rcomp);
+     }
+     abSleep(4,TRUE);
+   } 
+   if (paperr != noErr)
+     {
+       fprintf(stderr,"PAPWrite error %d\n",paperr);
+       fflush(stderr);
+     }
+   else
+     do { abSleep(4, TRUE); } while (wcomp > 0);
  }

-- 

Dave Platt
  UUCP: ...!{ames,sun,uunet}!coherent!dplatt     DOMAIN: dplatt@coherent.com
    INTERNET: coherent!dplatt@ames.arpa, ...@sun.com, ...@uunet.uu.net