Xref: utzoo comp.protocols.tcp-ip:4132 comp.protocols.tcp-ip.ibmpc:663 Path: utzoo!attcan!uunet!husc6!rutgers!njin!princeton!phoenix!asjoshi From: asjoshi@phoenix.Princeton.EDU (Amit S. Joshi) Newsgroups: comp.protocols.tcp-ip,comp.protocols.tcp-ip.ibmpc Subject: Ethernet Printer driver using KA9Q code Keywords: TCP/IP Printers ethernet Message-ID: <3289@phoenix.Princeton.EDU> Date: 17 Jul 88 18:32:56 GMT Organization: Princeton University, NJ Lines: 1613 Hi, Some people had requested a small printer driver that I had written using the KA9Q code as the interface to the ethernet. Here it is. Unshar it and compile. I only know that it compiles using Turbo C v1.5 It also need the KA9Q TCP/IP code. ( I know that it works with my port to Turbo C of Ver 870829.6). Have fun Amit Joshi BITNET | Q3696@PUCC.BITNET USENET | {seismo, rutgers}\!princeton\!phoenix\!asjoshi "There's a pleasure in being mad... which none but madmen know!" - St.Dryden #! /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 EPRINT.C <<'END_OF_EPRINT.C' X/* eprint.c - a simple print utility Author: Amit Joshi */ X X/** X X This is a sample demo program to show how the use of X the generic connections simplifies connecting to the X net. You need Phil Karn's KA9Q TCP/IP package, and the X 4 files "generic.c", "generic.h", "tick.c" and "tick.h" X which have the interface functions. X X January 1988 X Amit Joshi X**/ X X/** X Revision History: X v1.1 March 1988 - added query on printfile option. X - no longer have to perform netservice X - ^C works properly now. X v2.0 July 1988 - added wildcards support. X - added recursive directory printing support. X**/ X X#define VERSION "2.00" X X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#define PAGEFEED '\f' X X#define NULLCHAR (char)NULL X#define YES 1 X#define NO 0 X#define MAXNAME 80 X Xextern int errno; X Xchar printfailed[] = "Couldn't connect to printer\r\n"; Xint done = 0,closed =1,opened = 0; Xstatic char **files; Xstatic int nfiles = 0; Xstruct generic *gn, *open_generic(); Xchar next = '\0'; Xint opt[] = {0,0,0,0 }; Xint skip = 0; Xchar *default_printer = "otto"; X Xvoid prt_dir(); Xvoid prt_file(); Xvoid print_file(); X Xvoid Xpout(gn,bp,cnt) Xstruct generic *gn; Xstruct mbuf *bp; Xint16 cnt; X{ X int c; X FILE *fd; X char *cp; X X cp = bp->data; X X if ((fd = (FILE *)gn->user) == NULLFILE) { X if (closed == YES) exit(1); X else return; X } X X while (cnt != 0) { X /* while there is buffer space get characters */ X if ((c = getc(fd)) == EOF) { X /* done current file - flush page */ X *cp++ = PAGEFEED; X bp->cnt++; X cnt--; X /* check for more files */ X done = 1; X gn->user = (int *)NULLFILE; X return; X } else { X *cp++ = c; X bp->cnt++; X cnt--; X } X } X} X Xpsend() { X char *buf[BUFSIZ]; X struct mbuf *bp, *qdata(); X FILE *fd; X int cnt; X X if ((fd = (FILE *)gn->user) == NULLFILE) { X if (closed == YES) exit(1); X else return; X } X X if ((cnt = sizeof(char)*fread(buf,sizeof(char),BUFSIZ,fd)) != 0) { X bp = qdata(buf,(int16)cnt); X send_tcp((struct tcb *)gn->tcb,bp); X } X X} X X/* State change upcall routine */ Xvoid Xpstate(gn,new) Xstruct generic *gn; Xchar new; X{ X /* Only interested if connection has been closed */ X switch (new) { X#ifdef DEBUG X case LISTEN: printf("\nlistening\n");break; X case SYN_SENT: printf("\nsyn sent\n"); break; X case SYN_RECEIVED: printf("\nsyn received\n"); break; X#endif DEBUG X case ESTABLISHED: X printf("connected\n"); X opened = YES; X break; X case CLOSED: X closed = 1; X#ifdef DEBUG X printf("Debug: closed\n"); X#endif DEBUG X break; X default: X#ifdef DEBUG X printf("pstate, default : new = %d\n",new); X#endif DEBUG X break; X } X} X Xpopen(char *prtname) { X /* open a connection to the printer */ X gn = open_generic(NULLPORT,PRINTER_PORT,prtname,NULLVFP,pout,pstate); X if (gn == NULLGN) { X printf("Couldn't connect to printer %s\n", prtname); X exit(1); X } X printf("Opening connection to printer %s ...",prtname); X fflush(stdout); X opened = NO; X closed = NO; X while(opened == NO && closed == NO) ; X if (closed == YES) exit(1); X X} X Xpclose() { X X if (closed == YES) return; X X close_generic(gn); X X printf("Done: Waiting to close connection to printer\n"); X fflush(stdout); X X while(closed == NO) ; X X} X Xvoid Xprint_file(char *f){ X X char dr[MAXDRIVE],dir[MAXDIR],fn[MAXFILE],fe[MAXEXT],dum[MAXPATH+5]; X int done; X struct ffblk ffblk; X X fnsplit(f,dr,dir,fn,fe); X X#define FERR "ERROR: Path or file name = %s not found\n" X X done = findfirst(f,&ffblk,FA_DIREC); X if (done && (errno == ENOENT) && (!opt[1])) { X printf(FERR,f); X } X X while (!done) { X fnsplit(ffblk.ff_name,dum,dum,fn,fe); X fnmerge(dum,dr,dir,fn,fe); X if (ffblk.ff_attrib & FA_DIREC) { X prt_dir(dum,ffblk.ff_name); X } else { X prt_file(dum); X } X done = findnext(&ffblk); X } X} X Xvoid Xprt_file(char *fn) X{ X char c; X FILE *file; X X /* Make sure we have the printer */ X if (closed == YES) popen(default_printer); X X if (fn == NULL) { X file = stdin; X } else { X /* Do we want to print this file */ X if (opt[1]) { X printf("print %s (y/n) ?",fn); fflush(stdout); X while (((c=getch()) != 'y') && (c != 'n')); X printf("%c\n",c); fflush(stdout); X } else c = 'y'; X if (c != 'y') return; /* Don't print this file */ X if ((file = fopen(fn,"r")) == NULL) { X if (!opt[3]) { X printf("Couldn't open %s\n",fn); X exit(1); X } X } X } X X gn->user = (int *)file; X if (fn != NULL) printf("Printing file: %s ...",fn); X fflush(stdout); X psend(); X while (!done) ; X done = 0; X if (fn != NULL) { X printf(" \n"); X fclose(file); X } X} X Xvoid Xprt_dir(char *f, char *fn){ X X int c; X char *temp; X void print_file(); X X if (!strcmp(fn,".")) return; X if (!strcmp(fn,"..")) return; X X if (!opt[2]) { X if (!opt[3]) { X printf("%s is a directory, print anyway (y/n) ?",f); X while (((c=getch()) != 'y') && (c != 'n')); X printf("%c\n",c); X } else c = 'y'; X } else c = 'y'; X if (c == 'y') { X c = strlen(f); X strcat(f,"\\*.*"); X print_file(f); X f[c] = NULLCHAR; X } X X} X Xparseopt(char *f) { X X if ((*f == '-') && (!skip)) { X if (*(++f) == NULLCHAR) { X skip = 1; X } else while (*f != NULLCHAR) { X switch(*f) { X case '?': usage('?'); X case 'p': X if (*++f == NULLCHAR) { X next = 'p'; X } else { X if (closed == NO) pclose(); X popen(f); X } X return; X case 'i':opt[0] = 1;opt[1] = 1;break; X case 'r':opt[0] = 2;opt[2] = 1;break; X case 'f':opt[0] = 3;opt[3] = 1;break; X case '-':opt[opt[0]] = 0;opt[0] = 0;break; X default: if (!opt[1]) usage(*f); X } X f++; X } X } else { X if (next) { X switch(next) { X case 'p': X if (closed == NO) pclose(); X popen(f); X default: X } X next = 0; X } else { X nfiles++; X print_file(f); X } X } X} X Xusage(char c) { X if (c != '?') printf("Unknown option : \"%c\n\"",c); X printf("Usage:eprint [-?] [-p] [-i[-]] [-] \n"); X printf("\t-?\tPrint this message\n"); X printf("\t-i[-]\tQuery every file\n"); X printf("\t-r[-]\tRecursively print contents of directories\n"); X/* printf("\t-f[-]\tPrint quietly\n");*/ X printf("\t-\tUse next argument as a file name\n"); X printf("\t-p \t Printer to use\n"); X printf("\t\t\t\t Default printer is %s\n",default_printer); X printf("\t\t files to print. No files => use stdin\n"); X printf("\nv%s (c) Princeton LCA\n",VERSION); X exit(0); X} X Xmain (int argc, char *argv[]) { X X char *f,*prtopt; X int i; X X (void)remove_cbrk(NULLVFP); /* NEVER use ^C with the net BAD */ X prtopt = getenv("PRTOPT"); X for (f = strtok(prtopt," ");f != NULL; f = strtok(NULL," ")) { X parseopt(f); X } X X for (i = 1; i < argc; i++) { X f = argv[i]; X parseopt(f); X } X if (!nfiles) { X parseopt("-i-"); /* turn off interactive mode */ X prt_file(NULL); X } X pclose(); X} X END_OF_EPRINT.C echo shar: Missing newline added to \"EPRINT.C\" if test 6794 -ne `wc -c EPRINT.DOC <<'END_OF_EPRINT.DOC' XNAME X eprint - print files over the ethernet. X XVERSION X 2.0 July 1988 X XSYNOPSIS X eprint [-?] [-ri[-]] [-pprintername] X XDESCIRPTION X Eprint prints named files or directories. Unless the '-r' option is X specified it asks before printing directories. Options can be stacked X and can turned off and on for selective portions of the command line X using the turn on/off feature X X Eprint depends links in all the TCP/IP routines from the KA9Q package X that it needs and is standalone. It however requires a file called X "system.rc" to be located in the root directory in order to resolve X host names and certain other TCP/IP setup information. The contents X of the "system.rc" file are similar to the "autoexec.net" file X in the KA9Q TCP/IP package. X X An evironment variable lookup is available. Set the environment X variable "PRTOPT" from DOS to your favourite settings.The DOS command: X set PRTOPT=arguments X will set the environment string to the arguments, which are exactly X like those by the command itself (including filenames). First the X environment options are evaluated, filenames given there are printed X and then the usual command string is parsed. A typical use would be X to put the following command in the autoexec.bat file X X set PRTOPT=-i X X A generic option line would look like: X X eprint options files option- files option2- files option files X X The options apply to files following the options and can be turned X off by using '-' after the option (see examples). X X X -? Give a short usage note. X X -r[-] Print directories recursively. If a directory is encountered X it is printed and all its contents (including subdirectories) X are printed. '-r' turns the option on and '-r-' turns the X option off. X X -i[-] Print interactively. Queries before printing every file. X '-i' turns the option on and '-i-' turns the option off. X X -[-] Use the next argument as a filename. This is to allow use X of file names which begin with a '-'. '-' turns the option X on and '--' turns the option off (can't think of any use of X '--' since it is a noop!). X X Names of files to be printed. Wild cards are allowed. For X names begining with '-' see option '-'. Directories are X not deleted unless '-r' option is specified. NOTE: If no X file name is specified the standard input is used. However X since it does not about EOF's from the keyboard (^D or ^Z) X this is useful only for pipes and redirected output. X XEXAMPLES X All examples assume that PRTOPT is not set to anything. X X eprint a *.opt X will print the file "a" and all files "*.opt". Directories will not X be printed. X X eprint -r dirname X will print "dirname" even if it is a directory. X X eprint -r dirs1 -r- dirs2 X will print "dirs1" even if it is a directory but will not print X "dirs2" if it is a directory. The option '-r-' is read as follows X '-' + 'r-' => option symbol + don't print directories X X eprint - -r X will print a file "-r". Note here because of the '-' option "-r" X is used as a filename rather than as an option. X XNOTES X The option on/off feature is not available in Unix(tm). X The environment option is not available in Unix(tm). X This version fixes a bug in v1.00 : X When a nonexistent single file was asked to be deleted X the program correctly found there was no file but then X tried to delete the file as if it were read-only. X This version fixes a bug in v1.10 : X When a multiple directory deletion was attempted the X entire disk was wiped out. X XBUGS X 1. ^C does not work. It may abort the program but will not X reset interrupt vectors and so should not be used. It is X an inherent problem with the KA9Q TCP/IP package. X 2. When the amount of data to be printed is a lot and the X printer buffer is full then sometimes the program will X hang. This is because typically when the printer is X processing data it does not reply to queries and causes X it's own TCP/IP to time out while the eprint program X does not know this and so it keeps tyring (since it started X the whole thing). One way around is not to use the collated X mode. This bug has been seen on the Imagen 2308 printer using X v3.2 TCP/IP software. X Notify all bugs to Q3696@PUCC.BITNET or X {seismo, rutgers}\!princeton\!phoenix\!asjoshi. X XACKNOWLEDGEMENTS X The program was written using Turbo C v1.5. The command is based on X the idea of "iprint", a very basic ethernet printer driver for X imagen printers available in binary with the MIT TCP/IP package. X This version has been written using the KA9Q TCP/IP package. X The program and this manual page are completly written by me. X Thanks go to Phil Karn for the great TCP/IP package and very X readable code. X Thanks also to Borland for the inexpensive, fast C compiler. X Thanks to Dennis Linse for pointing some serious shortcomings X in v1.1. END_OF_EPRINT.DOC if test 4833 -ne `wc -c EPRINT.SH <<'END_OF_EPRINT.SH' END_OF_EPRINT.SH if test 0 -ne `wc -c GENERIC.C <<'END_OF_GENERIC.C' X/* Main network program - provides both client and server functions */ X#ifdef TURBOC /* The length of the command line causes problems */ X#include "tcdefs.h" X#endif X#ifndef CLEAN X#define NSESSIONS 10 /* Maximum interactive client sessions */ X#endif X#ifndef STARTUP X#define STARTUP "/system.rc" /* File to read startup commands from */ X#endif X X#ifdef TURBOC X#include X#include X#endif X X#include X#include "generic.h" X Xextern struct interface *ifaces; Xextern char major_rev[], minor_rev[]; Xextern struct mbuf *loopq; X Xint mode; XFILE *logfp; Xchar badhost[] = "Unknown host %s\r\n"; Xint32 gateway; X X#ifdef TRACE X#include "trace.h" Xint32 trace; X#endif X X/* Command lookup and branch table */ Xint doipaddr(),doexit(),dohostname(), X dowindow(),doroute(), X domss(),doattach(), X dottl(),go(), X dogateway(); X Xstatic struct cmds cmds[] = { X /* The "go" command must be first */ X "", go, 0, NULLCHAR, NULLCHAR, X "attach", doattach, 2, X "attach ", NULLCHAR, X "exit", doexit, 0, NULLCHAR, NULLCHAR, X "gateway", dogateway, 0, NULLCHAR, NULLCHAR, X "hostname", dohostname, 0, NULLCHAR, NULLCHAR, X "ipaddr", doipaddr, 0, NULLCHAR, NULLCHAR, X "mss", domss, 0, NULLCHAR, NULLCHAR, X "route", doroute, 0, NULLCHAR, NULLCHAR, X "ttl", dottl, 0, NULLCHAR, NULLCHAR, X "window", dowindow, 0, NULLCHAR, NULLCHAR, X NULLCHAR, NULLFP, 0, X "Unknown command; type \"?\" for list", NULLCHAR, X}; X Xchar hostname[HOSTNAMELEN]; Xint32 resolve(); Xint16 lportavail = 1001; Xchar net_inited = 0; Xchar prompt[] = "net> "; Xchar nospace[] = "No space!!\r\n"; /* Generic malloc fail message */ Xchar notval[] = "Not a valid TCB\r\n"; Xchar finished = 0,wait_to_close = 0; Xstruct generic *session; X Xstruct generic * Xopen_generic(lport,fport,fhost,in,out,state) Xint16 lport,fport; /* local and foreign ports */ Xchar *fhost; /* foreign host name */ Xvoid (*in)(); /* called when ready to process data */ Xvoid (*out)(); /* called when ready to send data */ Xvoid (*state)(); /* called when state changes */ X{ X void g_state(),grcv_char(),gsend_char(); X char *inet_ntoa(); X#ifdef TURBOC X void *calloc(),*malloc(); X#else X char *calloc(),*malloc(); X#endif X int32 resolve(); X struct generic *gn; X struct tcb *tcb; X struct socket lsocket,fsocket; X X if (!net_inited) X if (net_init()) X return NULLGN; X X lsocket.address = ip_addr; X if (lport == NULLPORT) lsocket.port = lportavail++; X else lsocket.port = lport; X if ((fsocket.address = resolve(fhost)) == 0) { X printf(badhost,fhost); X return NULLGN; X } X X if (fport == NULLPORT) { X printf("badport %d",fport); X return NULLGN; X } else fsocket.port = fport; X X /* Allocate a session descriptor */ X if((gn = (struct generic *)calloc(1,sizeof(struct generic))) == NULLGN){ X printf(nospace); X return NULLGN; X } X gn->in = in; X gn->out = out; X gn->state = state; X X tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,0, X grcv_char,gsend_char,g_state,0,(int *)gn); X gn->tcb = tcb; X session = gn; X return gn; X} X Xint Xclose_generic(gn) Xstruct generic *gn; X{ X if (gn != NULLGN) X close_tcp(gn->tcb); X} X Xint Xsend_generic(gn,bp) Xstruct generic *gn; Xstruct mbuf *bp; X{ X if (gn != NULLGN) X if (gn->tcb != NULLTCB) X send_tcp(gn->tcb,bp); X} X X/* Generic receiver upcall routine */ Xvoid Xgrcv_char(tcb,cnt) Xregister struct tcb *tcb; Xint16 cnt; X{ X struct mbuf *bp; X struct generic *gn; X X if((gn = (struct generic *)tcb->user) == NULLGN){ X /* Unknown connection; ignore it */ X return; X } X X if(recv_tcp(tcb,&bp,cnt) > 0) X if (gn->in != NULLVFP) gn->in(gn,bp,cnt); X} X X/* State change upcall routine */ Xvoid Xg_state(tcb,old,new) Xregister struct tcb *tcb; Xchar old,new; X{ X struct generic *gn; X char notify = 0; X extern char *tcpstates[]; X extern char *reasons[]; X extern char *unreach[]; X extern char *exceed[]; X void (*state)(); X X /* Can't add a check for unknown connection here, it would loop X * on a close upcall! We're just careful later on. X */ X gn = (struct generic *)tcb->user; X if ((state = gn->state) != NULLVFP) notify = 1; X X switch(new){ X case CLOSE_WAIT: X close_tcp(tcb); X break; X case CLOSED: /* court adjourned */ X if(tcb->reason == NETWORK){ X printf("%s (%s",tcpstates[new],reasons[tcb->reason]); X switch(tcb->type){ X case DEST_UNREACH: X printf(": %s unreachable",unreach[tcb->code]); X break; X case TIME_EXCEED: X printf(": %s time exceeded",exceed[tcb->code]); X break; X } X printf(")\r\n"); X fflush(stdout); X } X del_tcp(tcb); X if(gn != NULLGN) X free_generic(gn); X break; X default: X break; X } X if(notify) state(gn,new); X} X X/* Delete generic structure */ Xstatic Xfree_generic(gn) Xstruct generic *gn; X{ X if(gn != NULLGN) { X free((char *)gn); X } X} X Xvoid Xgsend_char(tcb,cnt) Xstruct tcb *tcb; Xint16 cnt; X{ X struct generic *gn; X struct mbuf *bp; X char *cp; X int c; X X if ((gn = (struct generic *)tcb->user) == NULLGN) { X /* Unknown connection */ X return; X } X X if ((bp = alloc_mbuf(cnt)) == NULLBUF) { X /* just don't do a thing here */ X return; X } X X if (gn->out != NULLVFP) X gn->out(gn,bp,cnt); X X if(bp->cnt != 0) X send_tcp(tcb,bp); X else X free_p(bp); X} X Xint Xnet_init() X{ X static char inbuf[BUFSIZ]; /* keep it off the stack */ X int cmdparse(); X FILE *fp; X struct mbuf *bp; X X#ifdef TURBOC X#define INITERR "Can't initialize network: %s\n" X void c_break(); /* clean up on system abort */ X void net_exit(); /* clean up on exit */ X void net_service(); /* net background service routine */ X X#if (INSTALL_TIMER != 0) X if (install_timer(net_service)) { X printf(INITERR, "can't install timer"); X return 1; X } X#endif INSTALL_TIMER X if (install_cbrk(c_break)) { X printf(INITERR,"can't install ctrl brk function"); X return 1; X } X if (atexit(net_exit)) { X printf(INITERR,"too many exit functions\n"); X return 1; X } X#endif TURBOC X ioinit(); X if((fp = fopen(STARTUP,"r")) != NULLFILE){ X while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){ X cmdparse(cmds,inbuf); X } X fclose(fp); X } X net_inited = 1; X return 0; X} X X/* Clean up everthing before exiting or else pay the price */ Xvoid Xnet_exit() { X net_inited = 0; /* I'm pessimistic */ X iostop(0); X} X Xvoid Xnet_service() { X static int n_ticks = 0; X struct mbuf *bp; X struct interface *ifp; X X#if (INSTALL_TIMER != 0) X /* Service net every INSTALL_TIMER ticks to avoid */ X /* DOS stack failiures */ X if (n_ticks++ <= INSTALL_TIMER) return; else n_ticks = 0; X#endif INSTALL_TIMER X X /* Service the loopback queue */ X while((bp = dequeue(&loopq)) != NULLBUF) X ip_recv(bp,0); X X /* Service the interfaces */ X for(ifp = ifaces; ifp != NULLIF; ifp = ifp->next){ X if(ifp->recv != NULLFP) X (*ifp->recv)(ifp); X } X X grcv_char(session->tcb,0); /* get any pending input */ X X /* Service the clock if it has ticked */ X check_time(); X} X Xgo(){} /* This needed by the command parser routine! */ X X#ifdef TURBOC Xvoid Xc_break(void) X{ X fprintf(stderr,"Aborting : wait for cleanup ...\n"); X net_inited = 0; X iostop(1); /* clean up dos only */ X} X#endif X X Xstatic Xdogateway(argc,argv) Xint argc; Xchar *argv[]; X{ X char *inet_ntoa(); X int32 n; X X if(argc < 2){ X printf("%s\r\n",inet_ntoa(gateway)); X } else if((n = resolve(argv[1])) == 0){ X printf(badhost,argv[1]); X return 1; X } else X gateway = n; X return 0; X} Xstatic Xdoexit(argc,argv) Xint argc; Xchar *argv[]; X{ X iostop(); X exit(0); X} Xstatic Xdohostname(argc,argv) Xint argc; Xchar *argv[]; X{ X char *strncpy(); X X if(argc < 2) X printf("%s\r\n",hostname); X else X strncpy(hostname,argv[1],HOSTNAMELEN); X return 0; X} X/* List of supported hardware devices */ Xint ec_attach(); X Xstruct cmds attab[] = { X#ifdef PC_EC X /* 3-Com Ethernet interface */ X "3c500", ec_attach, 7, X "attach 3c500
arpa