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