/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
* A simple program that can be used to filter jobs for PostScript
* printers. It's a cleaned up version of usg_iox, that I assume was
* written by Richard Flood. The most important addition includes some
* simple processing of printer status reports, usually obtained when
* \024 is sent to the printer. The returned status lines look like:
*
*
* %%[ status: idle; source serial 25 ]%%
* %%[ status: waiting; source serial 25 ]%%
* %%[ status: initializing; source serial 25 ]%%
* %%[ status: busy; source serial 25 ]%%
* %%[ status: printing; source serial 25 ]%%
* %%[ status: PrinterError: out of paper; source serial 25 ]%%
* %%[ status: PrinterError: no paper tray; source serial 25 ]%%
*
*
* although the list isn't meant to be complete.
*
* Other changes to the original program include the addition of
* options that let you select the tty line, baud rate, and printer
* log file. The program seems to work reasonably well, at least for
* our QMS PS-800 printer, but could still use some work.
*
* There were a couple of serious mistakes in the first few versions of
* postcomm. Both were made in setting up flow control in routine
* initialize(). Setting the IXANY flag in c_iflag was wrong, and
* often caused problems when the printer transmitted a spontaneous
* status report, which always happens when the paper runs out.
* Things were kludged up to get around the problems, but they were
* never exactly right, and probably could never be guaranteed to work
* 100%.
*
* The other mistake was setting the IXOFF flag, again in c_iflag.
* Although I never saw deadlock in the original versions of postcomm,
* it could happen. Apparently the IXANY and IXOFF flags combined to
* make that an unlikely event. Anyway both flags should normally be
* turned off to ensure reliable transmission of jobs.
*
* The implications of only setting IXON are obvious. Job transmission
* should be reliable, but data returned by the printer over the tty
* line may get lost. That won't cause problems in postcomm, but there
* may be occasions when you want to run a job and recover data
* generated by the printer. The -t option sets the IXOFF, IXANY, and
* IXON flags in c_iflag and causes send() to be far more careful about
* when data is sent to the printer. In addition anything not
* recognized as a status report is written on stdout. It seems to
* work reasonably well, but it's slow and may hang or have flow
* control problems. Only use the -t option when it's absolutely
* necessary. A small block size, like 512, should also help.
*
* Using two processes, one for reads and the other for writes, may
* eventually be needed. For now postcomm seems to do a good job
* transmitting data, and the -t option is probably acceptable for
* those few jobs that need to recover data from the printer.
*
* A typical command line might be:
*
* postcomm -L log -t <file1 > device
*
* where -L selects the printer log file and -t sends data from the
* printer out to the printer. If you don't choose a log file stderr
* will be used and the information mailed or written to you.
*
*/
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
# define OFF 0
# define FALSE 0
# define NON_FATAL 0
#include "postcomm.h" /* some special definitions */
static void filter(void);
static int getstatus(int);
static void initialize(void);
static void options(int, char *[]);
static int readblock(int);
static int readline(void);
static void reset(void);
static int writeblock(void);
void
{
/*
*
* Simple routine that's used to write a message to the log file.
*
*/
{
}
} /* End of logit */
void
{
/*
*
* Called when we've run into some kind of program error. First *mesg is
* printed using the control string arguments a?. Then if kind is FATAL
* and we're not ignoring errors the program will be terminated.
*
* If mesg is NULL or *mesg is the NULL string nothing will be printed.
*
*/
} /* End if */
exit(1);
} /* End if */
} /* End of error */
int
{
/*
*
* A simple program that manages input and output for PostScript
* printers. If you're sending a PostScript program that will be
* returning useful information add the -ot option to the lp(1) command
* line. Everything not recognized as a printer status report will go
* to stdout. The -ot option should only be used when needed! It's slow
* and doesn't use flow control properly, but it's probably the best
* that can be done using a single process for reading and writing.
*/
initialize(); /* Set printer up for printing */
filter();
reset(); /* wait 'til it's finished & reset it*/
return (0); /* everything probably went OK */
} /* End of main */
static void
{
extern char *optarg; /* used by getopt() */
/*
*
* Reads and processes the command line options. The -t option should
* only be used when absolutely necessary. It's slow and doesn't do
* flow control properly. Selecting a small block size (eg. 512 or
* less) with with the -B option may help when you need the -t option.
*
*/
{
switch ( ch )
{
case 't': /* non-status stuff goes to stdout */
break;
case 'B': /* set the job buffer size */
break;
case 'L': /* printer log file */
{
optarg);
} /* End if */
break;
case 'P': /* initial PostScript program */
break;
case 'D': /* debug flag */
break;
case 'I': /* ignore FATAL errors */
break;
case '?': /* don't understand the option */
break;
default: /* don't know what to do for ch */
break;
} /* End switch */
} /* End while */
} /* End of options */
static void
initialize(void)
{
/*
*
* Makes sure the printer is in the
* IDLE state before any real data is sent.
*
*/
logit("printer startup\n");
while ( 1 )
switch (getstatus(1))
{
case IDLE:
else
return;
case WAITING:
case BUSY:
case ERROR:
sleep(1);
break;
case FLUSHING:
sleep(1);
break;
case PRINTERERROR:
case INITIALIZING:
sleep(15);
break;
case DISCONNECT:
/* talk to spooler w/S_FAULT_ALERT */
break;
default:
sleep(1);
break;
} /* End switch */
} /* End of initialize */
static void
filter(void)
{
/*
*
* Responsible for sending the next file to the printer.
* Most of the hard stuff is done in getstatus() and readline().
* All this routine really does is control what happens for the
* different printer states.
*
*/
logit("sending file\n");
curfile++;
switch (getstatus(0))
{
case WAITING:
writeblock();
wflag = 1;
break;
case BUSY:
case PRINTING:
case PRINTERERROR:
{
writeblock();
wflag = 1;
}
else
sleep(1);
break;
case UNKNOWN:
{
writeblock();
wflag = 1;
}
break;
case NOSTATUS:
{
if (wflag)
writeblock();
}
else
sleep(1);
break;
case IDLE:
if (wflag)
break;
case ERROR:
break;
case FLUSHING:
break;
case INITIALIZING:
break;
case DISCONNECT:
break;
} /* End switch */
} /* End of print */
static int
/* current input file */
{
/*
*
* Fills the input buffer with the next block, provided we're all done
* with the last one. Blocks from fd_in are stored in array block[].
* Head is the index of the next byte in block[] that's supposed to go
* to the printer. tail points one past the last byte in the current
* block. head is adjusted in writeblock() after each successful
* write, while head and tail are reset here each time a new block is
* read. Returns the number of bytes left in the current block. Read
* errors cause the program to abort.
*
*/
{ /* done with the last block */
head = 0;
}
} /* End of readblock */
static int
writeblock(void)
{
/*
*
* Called from send() when it's OK to send the next block to the
* printer. head is adjusted after the write, and the number of bytes
* that were successfully written is returned to the caller.
*
*/
else
if (count == 0)
return(count);
} /* End of writeblock */
static int
getstatus(int t)
/* sleep time after sending '\024' */
{
int i; /* index of new state in status[] */
/* last state we found out about */
/*
*
* Sends a status request to the printer and tries to read the response.
* If an entire line is available readline() returns TRUE and the
* string in sbuf[] is parsed and converted into an integer code that
* represents the printer's state. If readline() returns FALSE,
* meaning an entire line wasn't available, NOSTATUS is returned.
*
*/
{
{
}
break;
} /* End if */
if ( t > 0 )
sleep(t);
return(NOSTATUS);
} /* End of getstatus */
static void
reset(void)
{
/*
*
* We're all done sending the input files, so we'll send an EOF to the
* printer and wait until it tells us it's done.
*
*/
logit("waiting for end of job\n");
while (1)
{
switch (getstatus(2))
{
case WAITING:
sleeptime = 15;
break;
case ENDOFJOB:
{
logit("job complete\n");
return;
}
sleeptime = 15;
break;
case BUSY:
case PRINTING:
sleeptime = 15;
sleep(1);
break;
case PRINTERERROR:
break;
case ERROR:
return;
case FLUSHING:
return;
case IDLE:
return;
case INITIALIZING:
return;
case DISCONNECT:
return;
default:
sleep(1);
break;
} /* End switch */
if (sleeptime > 60)
sleeptime = 60;
} /* End while */
} /* End of reset */
static int
readline(void)
{
int n; /* read() return value */
/*
*
* Reads the printer's tty line up to a newline (or EOF) or until no
* more characters are available. As characters are read they're
* converted to lower case and put in sbuf[next] until a newline (or
* EOF) are found. The string is then terminated with '\0', next is
* reset to zero, and TRUE is returned.
*
*/
{
if (n < 0)
{
if (ch == '\004')
next = 0;
return(TRUE);
} /* End if */
} /* End while */
return(FALSE);
} /* End of readline */