Newsgroups: comp.sources.unix From: sir-alan!ispin!lbartz@iuvax.cs.indiana.edu (Larry Bartz) Subject: v25i118: Indianapolis Standard Printer Interface for Networked printers, Part07/15 Sender: sources-moderator@pa.dec.com Approved: vixie@pa.dec.com Submitted-By: sir-alan!ispin!lbartz@iuvax.cs.indiana.edu (Larry Bartz) Posting-Number: Volume 25, Issue 118 Archive-Name: ispin/part07 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'ISPIN/src/ISPIN.c.aa' <<'END_OF_FILE' X/****************************************************************************/ X/* ISPIN */ X/* */ X/* Indianapolis Standard Printer Interface (for Network printers) */ X/****************************************************************************/ X/* */ X/* Copyright (C) 1991 */ X/* Larry Bartz */ X/* Internal Revenue Service */ X/* Indianapolis District Office */ X/* */ X/* This program is free software; you can redistribute it and/or modify */ X/* it under the terms of the GNU General Public License as published by */ X/* the Free Software Foundation, version 1. */ X/* */ X/* This program is distributed in the hope that it will be useful, */ X/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ X/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ X/* GNU General Public License for more details. */ X/* */ X/* You should have received a copy of the GNU General Public License */ X/* along with this program; if not, write to the Free Software */ X/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ X/* */ X/****************************************************************************/ X/* */ X/* COMMENTS */ X/* */ X/* See common.h and ispin.h */ X/* */ X/****************************************************************************/ X/* */ X/* DEFINES */ X/* */ X/* #define DEBUG */ X/* */ X/****************************************************************************/ X/* */ X/* INCLUDES */ X/* */ X#include "../h/common.h" X#include "../h/ispin.h" X/* */ X/* */ X/* */ X/****************************************************************************/ X/* */ X/* DATA TYPES, VARIABLES, ETC */ X/* */ X/* See common.h and ispin.h */ X/* */ X/****************************************************************************/ X/* */ X/* */ X/* */ Xmain(argc,argv) Xint argc; Xchar *argv[]; X X{ X X#ifdef DEBUG X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X strcpy(errmsg,"ISPIN: getting started. time:\n"); X strcat(errmsg," "); X strcat(errmsg,time_str); X strcat(errmsg,"\n"); X if((logfile = fopen(LOGFILE,"a+")) != NULL) X { X fprintf(logfile,errmsg); X fclose(logfile); X } X#endif X X X X/* take care of the signals */ X signal(SIGINT, SIG_IGN); /* ignore "interrupt" signal */ X signal(SIGHUP, SIG_IGN); /* ignore "hang up" signal */ X /*************************************/ X /* Ignore SIGHUP until setuptty(). */ X /* We don't want some unknown tty to */ X /* HUP us. When we get to setuptty(),*/ X /* we'll do a setpgrp() to make this */ X /* proc the grp leader. The next tty */ X /* we open will become the control */ X /* terminal. Then we'll re-enable */ X /* SIGHUP with action my_error(). */ X /* This will enable us to intelli- */ X /* gently loop if the printer HUPs. */ X /* We're sure no other process group */ X /* will have the tty as its control */ X /* terminal because IQUEUER won't let*/ X /* us have it until the previous */ X /* ISPIN is finished with it. */ X /* This little procedure is the */ X /* reason it's SO CRITICAL that none */ X /* of the ttys which may be used by */ X /* ISPIN may be made known to the na-*/ X /* tive queuer via lpadmin (LP) or */ X /* /usr/spool/queuer/config (NQ). If */ X /* either dqueuer (NQ) or lp (LP) */ X /* knew about the tty we want to use,*/ X /* they would assume it as their own */ X /* control terminal, thus possibly */ X /* ruining any chance we might have */ X /* had to choose our own. */ X /*************************************/ X signal(SIGQUIT, SIG_IGN); /* ignore "quit" signal */ X signal(SIGALRM, SIG_IGN); X signal(SIGTERM, my_error); X#ifdef NQ X signal(SIGRES, SIG_IGN); /* ignore "restart" signal */ X signal(SIGBACK, SIG_IGN); /* ignore "back-up" signal */ X signal(SIGSTOP, my_error); /* trap "stop" signal */ X#endif X X /* Assign an initial value to GO_dev string, so log messages which use */ X /* it make sense. Also so we don't dump core trying to strcat from a */ X /* memory address which is undefined. 05/09/90 LSB */ X sprintf(GO_dev,"undefined"); X X /* format a default error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X strcpy(errmsg,"ISPIN: undefined error. time:\n"); X strcat(errmsg," "); X strcat(errmsg,time_str); X strcat(errmsg,"\n"); X X/* Parse the arguments. */ X/* */ X/* If NQ, dqueuer will invoke the backend with file descriptors and options. */ X/* See backend(M) of Zeus 3.21 Reference Manual. */ X/* */ X/* If LP, lp will invoke the backend with printer name, id, user, title, */ X/* copies, options, file(s). The backend's standard input is /dev/null. */ X/* Standard out and standard error are both directed to the printer. */ X/* See discussion of lp interface programs in your System V UNIX box's */ X/* administration manual (such as pages LP4-15 through LP4-18 of AT&T */ X/* 3B2 Administration Manual). */ X/* */ X/* The two parse functions will each fill the same global variables, so that */ X/* we may consider the program somewhat homogenous after this point. */ X/* */ X#ifdef NQ X parse_NQ(argc,argv); X if(no_input) X { X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X X sprintf(errmsg,"ISPIN: printer %s no input file to print.\n",dest); X sprintf(errmsg2," USER: %s\n",from); X strcat(errmsg,errmsg2); X sprintf(errmsg2," TIME: %s\n",time_str); X strcat(errmsg,errmsg2); X X /* call the error routine, never come back */ X my_error(MYERR); X } X#else X parse_LP(argc,argv); X#endif X X#ifdef DEBUG X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X sprintf(errmsg,"ISPIN: printer %s passed parse_LP. time:\n",dest); X strcat(errmsg," "); X strcat(errmsg,time_str); X strcat(errmsg,"\n"); X if((logfile = fopen(LOGFILE,"a+")) != NULL) X { X fprintf(logfile,errmsg); X fclose(logfile); X } X#endif X X X/* read the table to get network connection info */ X status = read_tbl(); X if(status == 1) X { X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X X sprintf(errmsg,"ISPIN: printer %s not in rtab.\n",dest); X sprintf(errmsg2," USER: %s\n",from); X strcat(errmsg,errmsg2); X if(usr_addr) X { X sprintf(errmsg2," ADDR: %s\n",usr_strng); X strcat(errmsg,errmsg2); X } X#ifdef NQ X sprintf(errmsg2," FILE: %s\n",fyle); X#else X sprintf(errmsg2," FILE: %s\n",fyles[0]); X#endif X strcat(errmsg,errmsg2); X sprintf(errmsg2," TIME: %s\n",time_str); X strcat(errmsg,errmsg2); X X /* call the error routine, never come back */ X my_error(MYERR); X } X X#ifdef DEBUG X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X sprintf(errmsg,"ISPIN: printer %s passed read_tbl. time:\n",dest); X strcat(errmsg," "); X strcat(errmsg,time_str); X strcat(errmsg,"\n"); X if((logfile = fopen(LOGFILE,"a+")) != NULL) X { X fprintf(logfile,errmsg); X fclose(logfile); X } X#endif X X X /* otherwise, okay. proceed */ X X/* open the communication fifos */ X open_fifos(); X X#ifdef DEBUG X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X sprintf(errmsg,"ISPIN: printer %s passed open_fifos. time:\n",dest); X strcat(errmsg," "); X strcat(errmsg,time_str); X strcat(errmsg,"\n"); X if((logfile = fopen(LOGFILE,"a+")) != NULL) X { X fprintf(logfile,errmsg); X fclose(logfile); X } X#endif X X X/* Notify the secondary queuer of our existence. Wait for goahead. */ X notify(HERE); X X#ifdef DEBUG X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X sprintf(errmsg,"ISPIN: printer %s passed notify. time:\n",dest); X strcat(errmsg," "); X strcat(errmsg,time_str); X strcat(errmsg,"\n"); X if((logfile = fopen(LOGFILE,"a+")) != NULL) X { X fprintf(logfile,errmsg); X fclose(logfile); X } X#endif X X/* This is where we re-commence if we have to loop. */ X/* Changed to setjmp/longjmp style. No! It is NOT the same as a GOTO! */ X/* The longjmp back to here is in the again() function. */ X/* 07/27/90 LSB */ XSETJMP(agayn); X X/* Secure and set up the output device. */ X setuptty(); X X X#ifdef DEBUG X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X sprintf(errmsg,"ISPIN: printer %s passed setuptty. time:\n",dest); X strcat(errmsg," "); X strcat(errmsg,time_str); X strcat(errmsg,"\n"); X strcat(errmsg,GO_dev); X strcat(errmsg,"\n"); X if((logfile = fopen(LOGFILE,"a+")) != NULL) X { X fprintf(logfile,errmsg); X fclose(logfile); X } X#endif X X/* Negotiate the network connection. If we can't get through, error out */ X/* down there. */ X negotiate(); X X X#ifdef DEBUG X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X sprintf(errmsg,"ISPIN: printer %s passed negotiate. time:\n",dest); X strcat(errmsg," "); X strcat(errmsg,time_str); X strcat(errmsg,"\n"); X strcat(errmsg,GO_dev); X strcat(errmsg,"\n"); X if((logfile = fopen(LOGFILE,"a+")) != NULL) X { X fprintf(logfile,errmsg); X fclose(logfile); X } X#endif X X/* Do the job. Send from the input file to the output file. */ X/* do a loop here in the LP case for each output file */ X#ifdef NQ X i_stream = fdopen(RFFD,"r"); X if(i_stream != NULL) X { X do_it(); X fclose(i_stream); X close(RFFD); X } X else X { X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X X sprintf(errmsg,"ISPIN: printer: %s file %s: %s.\n",dest,fyle,sys_errlist[errno]); X sprintf(errmsg2," USER: %s\n",from); X strcat(errmsg,errmsg2); X if(usr_addr) X { X sprintf(errmsg2," ADDR: %s\n",usr_strng); X strcat(errmsg,errmsg2); X } X sprintf(errmsg2," FILE: %s\n",fyle); X strcat(errmsg,errmsg2); X if(port_open == 1) X { X sprintf(errmsg2," DEV: %s\n",GO_dev); X strcat(errmsg,errmsg2); X } X sprintf(errmsg2," TIME: %s\n",time_str); X strcat(errmsg,errmsg2); X X /* call the error routine, never come back */ X my_error(SYSERR); X } X#else X X while(numfiles > 0) X { X X --numfiles; X X X if((i_stream = fopen(fyles[numfiles],"r")) != NULL) X { X X X do_it(); X fclose(i_stream); X } X else X { X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X X sprintf(errmsg,"ISPIN: printer: %s file %s: %s.\n",dest,fyles[numfiles],sys_errlist[errno]); X sprintf(errmsg2," USER: %s\n",from); X strcat(errmsg,errmsg2); X if(usr_addr) X { X sprintf(errmsg2," ADDR: %s\n",usr_strng); X strcat(errmsg,errmsg2); X } X sprintf(errmsg2," FILE: %s\n",fyles[numfiles]); X strcat(errmsg,errmsg2); X if(port_open == 1) X { X sprintf(errmsg2," DEV: %s\n",GO_dev); X strcat(errmsg,errmsg2); X } X sprintf(errmsg2," TIME: %s\n",time_str); X strcat(errmsg,errmsg2); X X /* Call the error routine, but come back. */ X /* We don't want to bomb off the whole thing just because */ X /* one of possibly many files might not exist. */ X X /* However, we'll save errno to use later when we exit, */ X /* so that lpsched will know we encountered a problem. */ X /* This will permit the "-m" flag of lp to work properly, */ X /* notifying the user that his request was not entirely */ X /* successful. 05/09/90 LSB */ X /* We have to make sure savd_errno won't have the same */ X /* value as other "reason"s we pass to my_exit(). */ X savd_errno = (errno + 255); X X ret_val = my_error(NO_EXIT); X } X } X X#endif X X#ifdef DEBUG X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X sprintf(errmsg,"ISPIN: printer %s passed do_it. time:\n",dest); X strcat(errmsg," "); X strcat(errmsg,time_str); X strcat(errmsg,"\n"); X strcat(errmsg,GO_dev); X strcat(errmsg,"\n"); X if((logfile = fopen(LOGFILE,"a+")) != NULL) X { X fprintf(logfile,errmsg); X fclose(logfile); X } X#endif X X X/* all done */ X if(port_open == 1) X { X stayt = DISCONNECTING; X ret_val = quit_net(); X } X my_exit(0); X X} X X X X#ifdef NQ X/****************************************************************************/ X/* parse_NQ author = loftin, hacker = bartz 073088 */ X/****************************************************************************/ X/* */ X/* subroutine to get options passed to spooler backend */ X/* */ X/****************************************************************************/ X/* */ X/* description - */ X/* This subroutine scans and stores-off the arguments passed to the */ X/* backend (i.e., argv[]) by dqueuer(M). */ X/* */ X/* how invoked - parse_NQ(argc, argv) */ X/* int argc; */ X/* char *argv[]; */ X/* */ X/* inputs - none */ X/* */ X/* outputs - none */ X/* */ X/* subroutines (in addition to standard C) - none */ X/* */ X/* comments - */ X/* Freely given by Mark Loftin, this routine was originally named */ X/* "getoptns". Adapted for use with ISPIN by Larry Bartz. */ X/* */ X/* This backend recognizes one flag and argument in addition */ X/* to those passed to it by dqueuer. The "-P" flag and its */ X/* argument (que:dev) MUST be included in the config file, as */ X/* shown below. */ X/* */ X/* Queue descriptor */ X/* Comment */ X/* Device descriptor */ X/* Comment */ X/* Device descriptor */ X/* */ X/* */ X/* */ X/* Qremote,R,N,F */ X/* # GAR-1, Gary, IN POD, Qume 11/55 */ X/* DGAR-1,R,/dev/tty15,/usr/spool/queuer/ISPI/ISPIN -P GAR-1 */ X/* # LAF-1, Lafayette, IN POD, Centronics LW855 */ X/* DLAF-1,R,/dev/tty15,/usr/spool/queuer/ISPI/ISPIN -P LAF-1 */ X/* */ X/* */ X/* */ X/* See Zeus Reference Manual "backend(M)", and Zilog Systems */ X/* Administrator Manual (for ZEUS 3.21) 7.2.3, "Configuration */ X/* File Description." */ X/* */ X/* */ X/* */ X/****************************************************************************/ X Xparse_NQ(argc, argv) Xint argc; Xchar *argv[]; X X{ X register int x; /* general purpose index register */ X extern short optind; /* \ used to get */ X extern char *optarg; /* > options and */ X short opt; /* / arguments */ X X X while ((opt=getopt(argc, argv, "Bf:c:P:F:s:t:d:b")) != EOF) X { X switch(opt) X { X case 'B': X no_input = 1; X break; X case 'f': X fyle = optarg; X /* we will only get one file from dqueuer */ X break; X case 'c': X sscanf(optarg, "%d", &prtimes); X break; X case 'P': X dest = optarg; X break; X case 'F': X from = optarg; X break; X case 's': X sptime = optarg; X break; X case 't': X title = optarg; X break; X case 'd': X usr_strng = optarg; X ++usr_addr; X break; X case 'b': X banner = 1; X break; X case '?': X default: X break; X } X } X X X /* Since we're choosing our output file from the specifications given */ X /* in rtab, we don't want the file descriptor dqueuer passed to us. */ X X close(SFFD); X X /* Since we have chosen not to update the status file, close it. */ X X close(STATUSFD); X X return(0); X} X#endif X X X X X X#ifdef LP X/****************************************************************************/ X/* parse_LP author = bartz 073088 */ X/****************************************************************************/ X/* */ X/* subroutine to parse options passed to spooler backend */ X/* */ X/****************************************************************************/ X/* */ X/* description - */ X/* This subroutine scans and stores-off the arguments passed to the */ X/* backend (i.e., argv[]) by lp spooler. */ X/* */ X/* how invoked - parse_LP(argc, argv) */ X/* int argc; */ X/* char *argv[]; */ X/* */ X/* inputs - none */ X/* */ X/* outputs - none */ X/* */ X/* subroutines (in addition to standard C) - none */ X/* */ X/* */ X/* */ X/* */ X/* */ X/****************************************************************************/ X Xparse_LP(argc, argv) Xint argc; Xchar *argv[]; X X{ X X X X /* X the number of arguments is: argc X the 0th argument is: exec path to this file X the 1st argument is: spool member name passed by ispintrfce program X the 2nd argument is: request id returned by lp X the 3rd argument is: logname of user who made request X the 4th argument is: title, if specified by user X the 5th argument is: number of copies requested by user X the 6th argument is: optional string specified by user on X the command line as the argument to X the "-o" flag X the 7th thru nth of arguments is: full path name of file to print X */ X X /* dest is used as the search key for table lookup. */ X /* It is the queue member's (printer's) name */ X X X dest = &argv[1][0]; X X from = &argv[3][0]; X X title = &argv[4][0]; X X X if(strlen(title) > 0) X { X /* if user bothers to specify title, assume he wants a banner */ X banner = 1; X } X X X prtimes = atoi(argv[5]); X X X usr_strng = &argv[6][0]; X X if(strlen(usr_strng) > 0) X { X /* if user bothers to specify arg to "-o" flag */ X ++usr_addr; X } X X X fyles = &argv[7]; X X X X /* how many args for file names to print ? */ X num_ofile = numfiles = argc - 7; X X X X /* Since we're sending all of our messages to LOGFILE, */ X /* close the standard error file we were given. */ X fclose(stderr); X X X X /* LP gives us stdin assigned to /dev/null. We'll just */ X /* close it in the interest of conservation. */ X fclose(stdin); X X X X /* Since we're choosing our own output file from specs */ X /* given in rtab, also close the stdout LP gave us. */ X /* See installation notes and notes on control terminal. */ X fclose(stdout); X X X X /* We'll assign our generic output output file pointer */ X /* after the daemon tells us where we're going. */ X X X X} X#endif X X X/****************************************************************************/ X/* pid = read_tbl author = lbartz 22nov88 */ X/****************************************************************************/ X/* */ X/* subroutine to read and parse the table of remote printer info */ X/* */ X/****************************************************************************/ X/* */ X/* description - */ X/* This subroutine is invoked to collect info necessary to make */ X/* contact with the remote printer. */ X/* */ X/* how invoked - read_tbl() */ X/* */ X/* inputs - none */ X/* */ X/* outputs - none */ X/* */ X/* subroutines (in addition to standard C) - none */ X/* */ X/* comments - The success of this subroutine depends on the existence */ X/* of the external file which is #defined as RTAB: */ X Xread_tbl() X{ X ushort not_yet = 1; /* we haven't found the entry we're looking for yet */ X X int buf_ctr = 0; X X int c; X X X X if((rtab = fopen(RTAB,"r")) == NULL) X { X /* format an error message */ X time(&tloc); X nowtime = (struct tm *)localtime(&tloc); X time_str = asctime(nowtime); X X sprintf(errmsg,"ISPIN: printer %s: open %s: %s.\n",dest,RTAB,sys_errlist[errno]); X sprintf(errmsg2," USER: %s\n",from); X strcat(errmsg,errmsg2); X if(usr_addr) X { X sprintf(errmsg2," ADDR: %s\n",usr_strng); X strcat(errmsg,errmsg2); X } X#ifdef NQ X sprintf(errmsg2," FILE: %s\n",fyle); X#else X sprintf(errmsg2," FILE: %s\n",fyles[0]); X#endif X strcat(errmsg,errmsg2); X sprintf(errmsg2," TIME: %s\n",time_str); X strcat(errmsg,errmsg2); X X /* call the error routine, never come back */ X my_error(SYSERR); X X } X X X X while(not_yet) X { X while(((c = getc(rtab)) == ' ') || (c == '\t')); /* ignore spaces */ X X while((c != EOF) && (not_yet)) X { X buf_ctr = 0; X switch(c) X { X case '#': X ignore_line(); /* if line begins with a # */ X break; X default: X line_buf[buf_ctr] = c; X ++buf_ctr; X while(((c = getc(rtab)) != '\n') && (c != EOF)) X { X line_buf[buf_ctr] = c; X ++buf_ctr; X } X line_buf[buf_ctr] = '\0'; X /* check here to see if we got the correct line */ X status = 0; X status = parse_tokens(); X X X if(status == 0) X { X /* we found what we were looking for */ X not_yet = 0; X return(0); X } X break; X } X if((c != EOF)&&(not_yet)) X while(((c = getc(rtab)) == ' ') || (c == '\t')); X } X if((c == EOF)&&(not_yet)) X { X /* we got all the way through the file but didn't find the printer */ X return(1); X } X } X return(0); X} X X Xignore_line() X{ X int c; X X /* ignore everything after # char as a comment */ X while(((c = getc(rtab)) != '\n') && (c != EOF)); X} X X X X X Xparse_tokens() X{ X char *tokptr, *movptr, *lastptr ; X char *strptr = line_buf; X int ctr1 = 0; /* a counter, which token is this? */ X int toggle = 0; /* a toggle switch for EXPECT/SEND */ X int n = 0; X X X X if((tokptr = strtok(strptr, ";")) != NULLCHARPTR) X { X X X strptr = NULLCHARPTR; /* so we can proceed if we want to */ X if(strcmp(tokptr,dest) == 0) X { X /* This is the line from RTAB we are looking for. */ X X X X /* First, set up the heads of our lists. */ X X busy_head = (struct busy_list *) calloc(1,sizeof(struct busy_list)); X busy_head->next = NULL; X busy_list = busy_curr = busy_head; X X dead_head = (struct dead_list *) calloc(1,sizeof(struct dead_list)); X dead_head->next = NULL; X dead_list = dead_curr = dead_head; X X quit_head = (struct quit_list *) calloc(1,sizeof(struct quit_list)); X quit_head->next = NULL; X quit_list = quit_curr = quit_head; X X disc_head = (struct disc_list *) calloc(1,sizeof(struct disc_list)); X disc_head->next = NULL; X disc_list = disc_curr = disc_head; X X expt_head = (struct expt_list *) calloc(1,sizeof(struct expt_list)); X expt_head->prev = NULL; X expt_head->next = NULL; X expt_list = expt_curr = expt_head; X X send_head = (struct send_list *) calloc(1,sizeof(struct send_list)); X send_head->prev = NULL; X send_head->next = NULL; X send_list = send_curr = send_head; X X /* Now we'll get the info we need to make the connection. */ X X while((tokptr = strtok(strptr, ";")) != NULLCHARPTR) X { X strptr = NULLCHARPTR; /* so we can proceed if we want to */ X ++ctr1; X X X switch(ctr1) X { X case 1: X /* strtok can only parse one string at a time,*/ X /* so we have to process the DEVICES field */ X /* ourselves. */ X /* PROCESS DEVICES FIELD HERE */ X /**********************************************/ X if((movptr = strchr(tokptr,',')) != NULLCHARPTR) X { X /* we have more than one device */ X X /* save current position */ X ++movptr; X lastptr = movptr; X X /* get the first one */ X /* how many characters before the first comma ? */ X n = strcspn(tokptr,","); X strncpy(dev_ray[0].name,tokptr,n); X dev_ray[0].name[n] = '\0'; X X ret_val = 0; X X while((movptr = strchr(lastptr,',')) != NULLCHARPTR) X { X ++ret_val; X if(ret_val <= 10) X { X /* get the next one */ X /* how many characters before the next comma ? */ X n = strcspn(lastptr,","); X strncpy(dev_ray[ret_val].name,lastptr,n); X dev_ray[ret_val].name[n] = '\0'; X } X ++movptr; X lastptr = movptr; X } X X /* get the last one */ X /* how many characters before the end ? */ X n = strlen(lastptr); X ++ret_val; X if(ret_val <= 10) X { X strcpy(dev_ray[ret_val].name,lastptr); X } X X for(++ret_val; ret_val <= 10; ++ret_val) X { X strcpy(dev_ray[ret_val].name,"NONE"); X } X } X else X { X /* only one device has been specified */ X strcpy(dev_ray[0].name,tokptr); X X X for(ret_val = 1; ret_val <= 10; ++ret_val) X { X strcpy(dev_ray[ret_val].name,"NONE"); X } X } X /**********************************************/ X break; X case 2: X speed = atoi(tokptr); X break; X default: X switch(tokptr[0]) X { X case '-': X switch(tokptr[1]) X { X case 'B': X /* BUSY */ X while(busy_curr->next != NULL) /* until end o'list */ X { X /* walk down the list */ X busy_curr = busy_curr->next; X } X X /* don't really use the head for data storage */ X /* allocate a new one */ X busy_list = (struct busy_list *) calloc(1,sizeof(struct busy_list)); X X /* link it */ X busy_curr->next = busy_list; X X /* make the new list member the current list member */ X busy_curr = busy_list; X X /* Put our new-found data in the newly allocated storage */ X /* after first aiming the pointer at more new storage. */ X /* string begins at tokptr[2] */ X busy_curr->busy_strg = (char *) calloc(1, ((strlen(&tokptr[2]))+1)); X strcpy(busy_curr->busy_strg,&tokptr[2]); X X X X X X /* terminate the list */ X busy_curr->next = NULL; X break; X case 'I': X /* DEAD */ X while(dead_curr->next != NULL) /* until end o'list */ X { X /* walk down the list */ X dead_curr = dead_curr->next; X } X X /* don't really use the head for data storage */ X /* allocate a new one */ X dead_list = (struct dead_list *) calloc(1,sizeof(struct dead_list)); X X /* link it */ X dead_curr->next = dead_list; X X /* make the new list member the current list member */ X dead_curr = dead_list; END_OF_FILE if test 38857 -ne `wc -c <'ISPIN/src/ISPIN.c.aa'`; then echo shar: \"'ISPIN/src/ISPIN.c.aa'\" unpacked with wrong size! fi # end of 'ISPIN/src/ISPIN.c.aa' fi if test -f 'ISPIN/doc/TECH.doc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ISPIN/doc/TECH.doc'\" else echo shar: Extracting \"'ISPIN/doc/TECH.doc'\" \(7256 characters\) sed "s/^X//" >'ISPIN/doc/TECH.doc' <<'END_OF_FILE' X X X Indianapolis Standard Printer Interface for Networked printers X X ISPIN X X ISPIN was developed by the Internal Revenue Service at Indianapolis. It X supports printing to networked printers through the native print spooler. X The author has secured permission through appropriate channels to release X ISPIN into the public domain. X X X /* WHY? */ X X Why would you want to use ISPIN? X X - You've got more printers than you like hardwired to your cpu(s) X already and your users want more. You'd like to be able to X support more printers while using fewer ttys than you do now. X X - You've got users at far-flung remote sites. Their printers are X "slaved" to a few terminals. This limits the access of those X printers to those users who are using those particular terminals. X Everybody has their own "personal" printer - wasteful. X X - You have several cpus at your site, each with their own hardwired X printers. This limits your flexibility in balancing workload X across multiple systems. If you move an application to another X system, you've got to make sure the printers go with it. X X - You're bored. You need a new software toy. X X - If all users on all of your systems could access all of your X printers through all of your applications, life would be so X sweet. You'd do a lot less wiring/re-wiring and spend much X less time adding/deleting queue members and maintaining printer X selection menus. X X - You have already tried uux'ing print jobs from one system to X another to overcome the limitations imposed by hardwired printers. X But your users gripe about the delays, and you are sick of the X maintenance. X X - Your system(s) support(s) System V's lp/lpsched or X (the old, Old, OLD) Zilog ZEUS 3.21 nq/xq/dqueuer. X (ISPIN does not, at present, support the Berzerkeley lpr.) X X - You're bored. You need a new software toy. Not just something X to diddle around with and clear the screen when the boss walks X by. Something that will save money, increase productivity, and X make you a systems administration/datacomm hero in your shop X (or in your own mind, like me). X X X /* HOW? */ X X How could you implement ISPIN? X X 1. Disconnect all/some printers from their hardwired-to-cpu X connections. X X 2. Connect all/some of your printers to a network, like: X X - statistical switching multiplexor, such as Tellabs, Equinox, X David, Gandolph, Mitron, whatever. (examples, NOT ENDORSEMENT!) X X or X X - X.25 with asynchronous pads X X 3. Connect one or more ttys per cpu to the network. How many depends X only upon how many simultaneous print jobs you want to support. X In any case, it'll be far fewer ports than you use to support X hardwired printers. X X 4. Install and configure ISPIN, the easiest and least time-consuming X step. X X 5. Use your native print spooling software to add/delete, enable/ X disable queue members. Also use native commands to issue and X cancel print requests. X X X /* HUH? */ X X How does ISPIN work? X X - Your native spooler/queuer subsystem is fine for hardwired X printers. It accepts enqueueing and cancellation requests, X reports on its status, and allows you to add and remove queue X members (printers). When the spooler/queuer daemon determines X that it is time for a user's request to be satisfied, it X passes pertinent information to an executable or a shell script X which sends the print job out the designated tty to the printer. X This last process is known as the backend, or interface. X X - We substitute a call to the ISPIN executable for the backend. X X - The ISPIN process reads an entry for itself from a table X called "rtab". The rtab is intentionally similar to the uucp X L.sys file (or Systems file under HoneyDanBer uucp). There X is one rtab entry per virtual printer (any given physical printer X may be known or treated as several virtual printers). X X - The rtab entry contains the chat data (EXPECT/SEND) necessary X for the ISPIN process to negotiate through the network to the X printer. This data may also include printer initialization and X configuration commands which can be passed both before and after X the print job. X X - The rtab entry also contains a field which defines which tty(s) X (from one to eleven possible) this printer may be called through. X X - The ISPIN process passes a formatted message to a daemon process X known as IQUEUER. IQUEUER determines which of the requested X ttys is available, and issues a message back to the ISPIN, telling X it to proceed, using a particular tty. IQUEUER also maintains X lock files, thereby avoiding conflicts with uucp, cu, uugetty, X etc. X X - The ISPIN process, after receipt of its marching orders, sets X up the tty, negotiates the network, and sends the job to the X printer. X X - The rtab entry also contains flags and arguments which indicate X network and printer busy and fault conditions. The ISPIN process X watches for these indications to determine whether it should X terminate and loop or quit. X X - All the while, the ISPIN process is under the complete control X of the native spooler/queuer. Its status (active or waiting) is X known to the native spooler/queuer daemon. It may be cancelled, X rescheduled, whatever by the native commands. X X - While the ISPIN is "active" with respect to the native queuer, X the IQUEUER daemon is notified by the ISPIN process of its X current phase of execution (startup, negotiating the network, X printing, disconnecting, looping). The current status of all X currently executing ISPIN processes may be queryied via another X executable, known as "iq". X X - This all sounds very busy. But is is quite efficient and is X gratifyingly robust. If the printer is offline or busy, or X the network is unable to make the connection, the ISPIN will X loop and resubmit itself to the IQUEUER. If other jobs are X waiting for network access through the tty(s), the looper is X made to wait while other potentially successful jobs go ahead. X If a Greyhound takes out a telephone pole between the cpu and X the printer (or other mid-job disconnection), ISPIN detects X the fault and loops as above. Upon successful re-connection, X the job will be printed in its entirety. X X - ISPIN is not experimental software. It has been in production X use since June 1989 and is currently (Nov 1991) operational at X more than forty multiple-cpu sites. END_OF_FILE if test 7256 -ne `wc -c <'ISPIN/doc/TECH.doc'`; then echo shar: \"'ISPIN/doc/TECH.doc'\" unpacked with wrong size! fi # end of 'ISPIN/doc/TECH.doc' fi if test -f 'ISPIN/doc/OLD-DOCS/README.beta.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ISPIN/doc/OLD-DOCS/README.beta.1'\" else echo shar: Extracting \"'ISPIN/doc/OLD-DOCS/README.beta.1'\" \(3950 characters\) sed "s/^X//" >'ISPIN/doc/OLD-DOCS/README.beta.1' <<'END_OF_FILE' X X X X date: February 1, 1989 X X to: ISPIN Beta Test Site Volunteer X X from: Chief, Operations Branch, Information Systems Division, 35:IS X X subject: Indianapolis Standard Printer Interface (for Network printers) X X X X Thank you for accepting my invitation to help me test ISPIN. X I chose you and your site for extension of my invitation because I X trust your judgement, your technical expertise, and your potential X for helping me "market" this thing, should our testing prove X successful. I am also counting on your impartiality to help me X me discover and address whatever shortcomings may become X apparent during the course of testing. X X This test is a test of the application, not of installation procedures, X nor of documentation, so please accept what is included here under X that premise for now. I promise to "pretty it up" for the general X release. X X ISPIN is designed so that it may be installed anywhere on your system. X The decision about where files and directories will reside is made at X compile time. I have, however, defined default locations in the code X and in the install scripts. For purposes of our testing, please just X use the default locations. X X The tape upon which I have transmitted the application was created X in cpio format. cd to a directory in which you want this release to X be written, then cpio -iudmvB < your_nine_track_drive X X The result of this cpio will be the creation of a directory named X ISPIN.beta. ISPIN.beta will contain two subdirectories, namely X ISPI and ISPIN. X X ISPIN contains the source code, install documents, and install X scripts for the new ISPIN application. Of course, the ISPIN X application serves as a BACKEND for either of two native X queuer/spoolers, lp or dqueuer. X X The ISPI directory contains the freshest version of our ISPI X application. ISPI is an application which serves as a FRONTEND X for the two queuer/spoolers. The ISPI application is fully self- X contained, with its own source code, documentation, etc. Use it X if you like, or don't use it. I sent ISPI along because we have X found it to be an extremely useful tool to support the native X queuer/spoolers. X X ISPIN and ISPI are TWO SEPARATE AND DISTINCT APPLICATIONS. X Neither depends upon the other for functionality. You may use ISPIN X without ISPI. You may use ISPI without ISPIN. X X The documentation of the formal type is sparse, but the code is X overflowing with comments. Read comments in the files common.h X and rtab to get a general idea of what is going on in ISPIN and X its related entities. X X If you are installing this application under ZEUS 3.21 and its X nq/xq/dqueuer queuer family, read NQinstall.doc. X X If you are installing this application under System V UNIX and X the lp spooler, read LPinstall.doc. X X For now, install on Zilogs only, please. I haven't yet had an X opportunity to test in any other environment. I expect, however, X to have the code running on a Sequent Balance B8 soon. X X Here's a late news flash: ISPIN also compiles and runs just fine X on the AT&T UNIX PC (mine is a 3B1). I have included an install X script called 3B1install.csh, which is identical to LPinstall.csh X except for deletion of the "-f" flag from the compile lines. X X I'll also say that after my testing, my confidence level is high. X Indianapolis is currently proceeding with full scale implementation X of ISPIN. X X Feedback is what I need. Please communicate whatever observations X you may have soon and often. X X X X Larry Bartz X X END_OF_FILE if test 3950 -ne `wc -c <'ISPIN/doc/OLD-DOCS/README.beta.1'`; then echo shar: \"'ISPIN/doc/OLD-DOCS/README.beta.1'\" unpacked with wrong size! fi # end of 'ISPIN/doc/OLD-DOCS/README.beta.1' fi if test -f 'ISPIN/doc/OLD-DOCS/rel_1.2.fixes' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ISPIN/doc/OLD-DOCS/rel_1.2.fixes'\" else echo shar: Extracting \"'ISPIN/doc/OLD-DOCS/rel_1.2.fixes'\" \(2255 characters\) sed "s/^X//" >'ISPIN/doc/OLD-DOCS/rel_1.2.fixes' <<'END_OF_FILE' X X X ISPIN RELEASE 1.2 CHANGES X X ISPIN release 1.2 includes updates to correct a problem which existed X in release 1.1. The method by which waiting jobs are queued has X been modified. Formerly, waiting print jobs were assigned to a device X (port) and remained in queue for that device, even if another device which X the job could use became available. Now under release 1.2, all waiting X jobs are put in one queue and are not assigned a device until they are X actually ready to be printed. This results in more efficient scheduling X of print jobs. The files which are affected by this change are iqueuer.h, X IQUEUER.c, iqueuer, IQ.c, and iq. X X Release 1.2 also corrects a problem with ISPIN flow control on Sequent Balance X systems which are connected directly to CDN. The DYNIX SVAE Summary (Section X A.2.16) states that the AT&T UNIX System V general terminal interface is X not fully supported under the Sequent System V Applications Environment. X Since the Sequent Balance is NOT a TRUE System V machine, a portion of the X ISPIN application was not properly working. The program ISPIN.c has been X modified to correct this problem. The rtab entries on Sequent Balance machines X connected directly to CDN should also be modified to fix this problem. At X least two disconnect arguments - "-Dpad>" and "-De" should be included in X each rtab entry. Otherwise, a disconnection because of a printer being X turned off may not be recognized. See rtab for an example of a Sequent X Balance rtab entry. X X Also, the example rtab entries for "CPU to CDN to PRINTER" connections X have been enhanced to add features which make printing more reliable. X In most cases, the old examples work fine. However, the new rtab entries X are more flexible in case of a failure to negotiate successfully to a X printer. The examples include entries to try again if no characters are X received from CDN. Also, the disconnect sequence has been changed to use X a CDN disconnect command other than the break character. The break X character causes parity errors to occur on some printers (i.e. Qume 11/55). X For more details, see the example files in the directory X "ISPIN.rel_1.2/ISPIN/install/lib_rtab/CPU_CDN_PTR". X END_OF_FILE if test 2255 -ne `wc -c <'ISPIN/doc/OLD-DOCS/rel_1.2.fixes'`; then echo shar: \"'ISPIN/doc/OLD-DOCS/rel_1.2.fixes'\" unpacked with wrong size! fi # end of 'ISPIN/doc/OLD-DOCS/rel_1.2.fixes' fi if test -f 'ISPIN/misc/ISPIT.dr/ispit.sh' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ISPIN/misc/ISPIT.dr/ispit.sh'\" else echo shar: Extracting \"'ISPIN/misc/ISPIT.dr/ispit.sh'\" \(395 characters\) sed "s/^X//" >'ISPIN/misc/ISPIT.dr/ispit.sh' <<'END_OF_FILE' X#!/bin/sh X# ispit.sh 080791 LSB X# written to run as the user ispit's default shell (last field in passwd entry) X# stty call is of "att" type, so must be run under att on dual universe boxes Xecho "OUTPUT_FILE?" Xread OF Xecho "OK" Xstty raw ixoff -echo hupcl brkint Xecho "ISPIT START" >> ispit.log Xdate >> ispit.log Xecho $OF >> ispit.log X./eot_trap > $OF Xdate >> ispit.log Xstty sane Xexit END_OF_FILE if test 395 -ne `wc -c <'ISPIN/misc/ISPIT.dr/ispit.sh'`; then echo shar: \"'ISPIN/misc/ISPIT.dr/ispit.sh'\" unpacked with wrong size! fi # end of 'ISPIN/misc/ISPIT.dr/ispit.sh' fi echo shar: End of archive 7 \(of 15\). cp /dev/null ark7isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 15 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0