#include <stdio.h>
#include <stdlib.h>
#include <termio.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <sys/times.h>
#include <string.h>
#include <limits.h>
#include <sys/prnio.h>
#include "lp.h"
#include <locale.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioccom.h>
#include <sys/ioctl.h>
#include <sys/bpp_io.h>
#include <sys/ecppsys.h>
#include <stropts.h>
struct ppc_params_t {
int flags;
int state;
int strobe_w;
int data_setup;
int ack_timeout;
int error_timeout;
int busy_timeout;
};
static void printer_info(char *fmt, ...);
int is_a_parallel_bpp(int);
int bpp_state(int);
int parallel_comm(int, int());
int get_ecpp_status(int fd);
int is_a_prnio(int);
int prnio_state(int);
#define PRINTER_ERROR_PAPER_OUT 1
#define PRINTER_ERROR_OFFLINE 2
#define PRINTER_ERROR_BUSY 3
#define PRINTER_ERROR_ERROR 4
#define PRINTER_ERROR_CABLE_POWER 5
#define PRINTER_ERROR_UNKNOWN 6
#define PRINTER_ERROR_TIMEOUT 7
#define PRINTER_IO_ERROR 129
int
is_a_parallel_bpp(int fd)
{
if (ioctl(fd, BPPIOC_TESTIO) == 0 || errno == EIO)
return (1);
return (0);
}
#if defined(DEBUG) && defined(NOTDEF)
char *
BppState(int state)
{
static char buf[BUFSIZ];
memset(buf, 0, sizeof (buf));
sprintf(buf, "State (0x%.4x) - (%s%s%s%s)\n", state,
((state & BPP_SLCT_ERR) ? "offline " : ""),
((state & BPP_BUSY_ERR) ? "busy " : ""),
((state & BPP_PE_ERR) ? "paper " : ""),
((state & BPP_ERR_ERR) ? "error " : ""));
return (buf);
}
#endif
int
bpp_state(int fd)
{
if (ioctl(fd, BPPIOC_TESTIO)) {
struct bpp_error_status bpp_stat;
int state;
if (ioctl(fd, BPPIOC_GETERR, &bpp_stat) < 0)
exit(PRINTER_IO_ERROR);
state = bpp_stat.pin_status;
#if defined(DEBUG) && defined(NOTDEF)
logit("%s", BppState(state));
#endif
if (state == (BPP_PE_ERR | BPP_ERR_ERR | BPP_SLCT_ERR)) {
return (PRINTER_ERROR_PAPER_OUT);
} else if (state & BPP_BUSY_ERR) {
return (PRINTER_ERROR_BUSY);
} else if (state & BPP_SLCT_ERR) {
return (PRINTER_ERROR_OFFLINE);
} else if (state & BPP_ERR_ERR) {
return (PRINTER_ERROR_ERROR);
} else if (state == BPP_PE_ERR) {
return (PRINTER_ERROR_CABLE_POWER);
} else if (state) {
return (PRINTER_ERROR_UNKNOWN);
} else
return (0);
}
return (0);
}
int
get_ecpp_status(int fd)
{
int state;
struct ecpp_transfer_parms transfer_parms;
if (ioctl(fd, ECPPIOC_GETPARMS, &transfer_parms) == -1) {
return (-1);
}
state = transfer_parms.mode;
if (state != ECPP_CENTRONICS) {
transfer_parms.mode = ECPP_CENTRONICS;
if (ioctl(fd, ECPPIOC_SETPARMS, &transfer_parms) == -1) {
return (-1);
} else {
state = ECPP_CENTRONICS;
}
}
return (state);
}
int
is_a_prnio(int fd)
{
uint_t cap;
if (ioctl(fd, PRNIOC_GET_IFCAP, &cap) == -1) {
return (0);
}
if ((cap & PRN_1284_STATUS) == 0) {
if (cap & PRN_BIDI) {
cap &= ~PRN_BIDI;
(void) ioctl(fd, PRNIOC_SET_IFCAP, &cap);
}
}
return (1);
}
int
prnio_state(int fd)
{
uint_t status;
uchar_t pins;
if ((ioctl(fd, PRNIOC_GET_STATUS, &status) == 0) &&
(status & PRN_READY)) {
return (0);
}
if (ioctl(fd, PRNIOC_GET_1284_STATUS, &pins) != 0) {
return (PRINTER_ERROR_UNKNOWN);
}
if ((pins & ~PRN_1284_BUSY) == PRN_1284_PE) {
return (PRINTER_ERROR_PAPER_OUT);
} else if (pins == (PRN_1284_PE | PRN_1284_SELECT |
PRN_1284_NOFAULT | PRN_1284_BUSY)) {
return (PRINTER_ERROR_CABLE_POWER);
} else if ((pins & PRN_1284_SELECT) == 0) {
return (PRINTER_ERROR_OFFLINE);
} else if ((pins & PRN_1284_NOFAULT) == 0) {
return (PRINTER_ERROR_ERROR);
} else if (pins & PRN_1284_PE) {
return (PRINTER_ERROR_PAPER_OUT);
} else if (pins ^ (PRN_1284_SELECT | PRN_1284_NOFAULT)) {
return (PRINTER_ERROR_UNKNOWN);
}
return (0);
}
static void
ByeByeParallel(int sig)
{
(void) write(1, "\004", 1);
exit(0);
}
static void
printer_info(char *fmt, ...)
{
char mesg[BUFSIZ];
va_list ap;
va_start(ap, fmt);
vsprintf(mesg, fmt, ap);
va_end(ap);
fprintf(stderr, "%s\n", mesg);
fflush(stderr);
fsync(2);
}
static void
printer_error(int error)
{
switch (error) {
case -1:
printer_info("ioctl(): %s", strerror(errno));
break;
case PRINTER_ERROR_PAPER_OUT:
printer_info("out of paper");
break;
case PRINTER_ERROR_OFFLINE:
printer_info("offline");
break;
case PRINTER_ERROR_BUSY:
printer_info("busy");
break;
case PRINTER_ERROR_ERROR:
printer_info("printer error");
break;
case PRINTER_ERROR_CABLE_POWER:
printer_info("printer powered off or disconnected");
break;
case PRINTER_ERROR_UNKNOWN:
printer_info("unknown error");
break;
case PRINTER_ERROR_TIMEOUT:
printer_info("communications timeout");
break;
default:
printer_info("get_status() failed");
}
}
static void
wait_state(int fd, int get_state())
{
int state;
int was_faulted = 0;
while (state = get_state(fd)) {
was_faulted = 1;
printer_error(state);
sleep(15);
}
if (was_faulted) {
fprintf(stderr, "printer ok\n");
fflush(stderr);
fsync(2);
}
}
#define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
#define ISBLK(A) ((A.st_mode & S_IFMT) == S_IFBLK)
#define ISCHR(A) ((A.st_mode & S_IFMT) == S_IFCHR)
#define E_SUCCESS 0
#define E_BAD_INPUT 1
#define E_BAD_OUTPUT 2
#define E_BAD_TERM 3
#define E_IDENTICAL 4
#define E_WRITE_FAILED 5
#define E_TIMEOUT 6
#define E_HANGUP 7
#define E_INTERRUPT 8
#define SAFETY_FACTOR 2.0
#define R(F) (int)((F) + .5)
#define DELAY(N, D) R(SAFETY_FACTOR * ((N) / (double)(D)))
char buffer[BUFSIZ];
void sighup(),
sigint(),
sigquit(),
sigpipe(),
sigalrm(),
sigterm();
#if defined(baudrate)
#undef baudrate
#endif
int baudrate();
int
nop(int fd)
{
return (0);
}
int bpp_state(int);
int
main(int argc, char *argv[])
{
int nin, nout, effective_rate, max_delay = 0, n;
int report_rate;
short print_rate;
struct stat in, out;
struct tms tms;
long epoch_start, epoch_end;
char *TERM;
int (*func)(int fd);
signal(SIGTERM, sigterm);
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigint);
signal(SIGPIPE, sigpipe);
if (argc > 1 && STREQU(argv[1], "-r")) {
report_rate = 1;
argc--;
argv++;
} else
report_rate = 0;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
if (fstat(1, &out) < 0) {
signal(SIGTERM, SIG_IGN);
fprintf(stderr, gettext("Can't stat output "
"(%s);\nincorrect use of lp.cat!\n"), PERROR);
exit(E_BAD_OUTPUT);
}
if (fstat(0, &in) < 0) {
signal(SIGTERM, SIG_IGN);
fprintf(stderr, gettext("Can't stat input "
"(%s);\nincorrect use of lp.cat!\n"), PERROR);
exit(E_BAD_INPUT);
}
if (is_a_prnio(1)) {
func = prnio_state;
} else if (is_a_parallel_bpp(1) ||
(get_ecpp_status(1) == ECPP_CENTRONICS)) {
func = bpp_state;
} else if (isatty(1)) {
func = nop;
} else {
func = nop;
}
if (!ISCHR(out) && !ISBLK(out) && IDENTICAL(out, in)) {
signal(SIGTERM, SIG_IGN);
fprintf(stderr, gettext("Input and output are identical; "
"incorrect use of lp.cat!\n"));
exit(E_IDENTICAL);
}
if (!(TERM = getenv("TERM")) || !*TERM) {
signal(SIGTERM, SIG_IGN);
fprintf(stderr, gettext("No TERM variable defined! "
"Trouble with the Spooler!\n"));
exit(E_BAD_TERM);
}
if (!STREQU(TERM, NAME_UNKNOWN) &&
tidbit(TERM, "cps", &print_rate) == -1) {
signal(SIGTERM, SIG_IGN);
fprintf(stderr, gettext("Trouble identifying printer "
"type \"%s\"; check the Terminfo database.\n"), TERM);
exit(E_BAD_TERM);
}
if (STREQU(TERM, NAME_UNKNOWN))
print_rate = -1;
effective_rate = baudrate() / 10;
if (print_rate != -1 && print_rate < effective_rate)
effective_rate = print_rate;
if (argc > 1 && (n = atoi(argv[1])) >= 0 && n < effective_rate)
effective_rate = n;
if (effective_rate)
max_delay = DELAY(BUFSIZ, effective_rate);
if (max_delay)
signal(SIGALRM, sigalrm);
while ((nin = read(0, buffer, BUFSIZ)) > 0) {
char *ptr = buffer;
#if PIPE_BUF < BUFSIZ || (PIPE_MAX % BUFSIZ)
this_wont_compile;
#endif
if (report_rate)
epoch_start = times(&tms);
do {
wait_state(1, func);
if (max_delay)
alarm(max_delay);
nout = write(1, ptr, nin);
alarm(0);
if (nout < 0) {
fprintf(stderr, gettext("Write failed "
"(%s);\nperhaps the printer has gone "
"off-line.\n"), PERROR);
fflush(stderr);
if (errno != EINTR)
exit(PRINTER_IO_ERROR);
else
sleep(15);
} else {
nin -= nout;
ptr += nout;
}
} while (nin > 0);
if (max_delay)
alarm(0);
else if (report_rate) {
epoch_end = times(&tms);
if (epoch_end - epoch_start > 0)
fprintf(stderr, "%d CPS\n",
R((100 * BUFSIZ) /
(double)(epoch_end - epoch_start)));
}
}
return (E_SUCCESS);
}
void
sighup()
{
signal(SIGTERM, SIG_IGN);
signal(SIGHUP, SIG_IGN);
fprintf(stderr, gettext(HANGUP_FAULT_LPCAT));
exit(E_HANGUP);
}
void
sigint()
{
signal(SIGTERM, SIG_IGN);
signal(SIGINT, SIG_IGN);
fprintf(stderr, gettext(INTERRUPT_FAULT));
exit(E_INTERRUPT);
}
void
sigpipe()
{
signal(SIGTERM, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
fprintf(stderr, gettext(PIPE_FAULT));
exit(E_INTERRUPT);
}
void
sigalrm()
{
signal(SIGTERM, SIG_IGN);
fprintf(stderr, gettext("Excessive write delay; "
"perhaps the printer has gone off-line.\n"));
exit(E_TIMEOUT);
}
void
sigterm()
{
signal(SIGTERM, SIG_IGN);
ioctl(1, I_FLUSH, FLUSHW);
exit(E_SUCCESS);
}
static int baud_convert[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
1800, 2400, 4800, 9600, 19200, 38400, 57600,
76800, 115200, 153600, 230400, 307200, 460800, 921600,
1000000, 1152000, 1500000, 2000000, 2500000, 3000000,
3500000, 4000000
};
int
baudrate()
{
struct termio tm;
struct termios tms;
int speed;
if (ioctl(1, TCGETS, &tms) < 0) {
if (ioctl(1, TCGETA, &tm) < 0) {
return (1200);
} else {
speed = tm.c_cflag&CBAUD;
}
} else {
speed = cfgetospeed(&tms);
}
return (speed ? baud_convert[speed] : 1200);
}