xref: /netbsd-src/external/bsd/ekermit/dist/unixio.c (revision a704a1616e7ddaeafbe5387b074cf5a12f1e6545)
1 /* Sample system-dependent communications i/o routines for embedded Kermit. */
2 
3 /*
4   Author: Frank da Cruz.
5   Copyright (C) 1995, 2011.
6   Trustees of Columbia University in the City of New York.
7   All rights reserved.
8   See kermit.c for license.
9 */
10 
11 /*
12   The sample i/o routines for UNIX that provide packet i/o
13   functions on the console (login) device.
14   Copy this file, rename it appropriately, and replace the contents
15   of each routine appropriately for your platform.
16 
17   Device i/o:
18 
19     int devopen()    Communications device - open
20     int pktmode()    Communications device - enter/exit packet mode
21     int readpkt()    Communications device - read a packet
22     int tx_data()    Communications device - send data
23     int devclose()   Communications device - close
24     int inchk()      Communications device - check if bytes are ready to read
25 
26   File i/o:
27 
28     int openfile()   File - open for input or output
29     ULONG fileinfo() Get input file modtime and size
30     int readfile()   Input file - read data
31     int writefile()  Output file - write data
32     int closefile()  Input or output file - close
33 
34   Full definitions below, prototypes in kermit.h.
35 
36   These routines must handle speed setting, parity, flow control, file i/o,
37   and similar items without the kermit() routine knowing anything about it.
38   If parity is in effect, these routines must add it to outbound characters
39   and strip it from inbound characters.
40 */
41 #include <stdio.h>
42 #include <sys/stat.h>
43 #include <time.h>
44 #include <errno.h>
45 #ifndef O_WRONLY
46 #include <sys/file.h>
47 #ifdef X_OK
48 #undef X_OK
49 #endif /* X_OK */
50 #endif /* O_WRONLY */
51 
52 #include "cdefs.h"
53 #include "debug.h"
54 #include "platform.h"
55 #include "kermit.h"
56 
57 UCHAR o_buf[OBUFLEN+8];			/* File output buffer */
58 UCHAR i_buf[IBUFLEN+8];			/* File output buffer */
59 
60 /*
61   In this example, the output file is unbuffered to ensure that every
62   output byte is commited.  The input file, however, is buffered for speed.
63   This is just one of many possible implmentation choices, invisible to the
64   Kermit protocol module.
65 */
66 static int ttyfd, ofile = -1;		/* File descriptors */
67 static FILE * ifile = (FILE *)0;	/* and pointers */
68 
69 /* Debugging */
70 
71 #ifdef DEBUG
72 static FILE * dp = (FILE *)0;		/* Debug log */
73 static int xdebug = 0;			/* Debugging on/off */
74 
75 void
dodebug(int fc,UCHAR * label,UCHAR * sval,long nval)76 dodebug(int fc, UCHAR * label, UCHAR * sval, long nval) {
77 
78     if (fc != DB_OPN && !xdebug)
79       return;
80     if (!label)
81       label = "";
82 
83     switch (fc) {			/* Function code */
84       case DB_OPN:			/* Open debug log */
85 	if (dp) fclose(dp);
86 	if (!*label) label = "debug.log";
87 	dp = fopen(label,"w");
88 	if (!dp) {
89 	    dp = stderr;
90 	} else {
91 	    setbuf(dp,(char *)0);
92 	}
93 	xdebug = 1;
94 	fprintf(dp,"DEBUG LOG OPEN\n");
95 	return;
96       case DB_MSG:			/* Write a message */
97 	if (dp) fprintf(dp,"%s\n",label);
98 	return;
99       case DB_CHR:			/* Write label and character */
100 	if (dp) fprintf(dp,"%s=[%c]\n",label,(char)nval);
101 	return;
102       case DB_PKT:			/* Log a packet */
103 	/* (fill in later, fall thru for now...) */
104       case DB_LOG:			/* Write label and string or number */
105 	if (sval && dp)
106 	  fprintf(dp,"%s[%s]\n",label,sval);
107 	else
108 	  fprintf(dp,"%s=%ld\n",label,nval);
109 	return;
110       case DB_CLS:			/* Close debug log */
111 	if (dp) {
112 	    fclose(dp);
113 	    dp = (FILE *)0;
114 	}
115 	xdebug = 0;
116     }
117 }
118 #endif /* DEBUG */
119 
120 /*  D E V O P E N  --  Open communications device  */
121 /*
122 
123   Call with: string pointer to device name.  This routine should get the
124   current device settings and save them so devclose() can restore them.
125   It should open the device.  If the device is a serial port, devopen()
126   set the speed, stop bits, flow control, etc.
127   Returns: 0 on failure, 1 on success.
128 */
129 int
devopen(char * device)130 devopen(char *device) {
131     ttyfd = 0;
132     return(1);
133 }
134 
135 /*  P K T M O D E  --  Put communications device into or out of packet mode  */
136 /*
137   Call with: 0 to put in normal (cooked) mode, 1 to put in packet (raw) mode.
138   For a "dumb i/o device" like an i/o port that does not have a login attached
139   to it, this routine can usually be a no-op.
140   Returns: 0 on failure, 1 on success.
141 */
142 int
pktmode(short on)143 pktmode(short on) {
144     if (ttyfd < 0)                      /* Device must be open */
145       return(0);
146     system(on ? "stty raw -echo" : "stty sane"); /* Crude but effective */
147     return(1);
148 }
149 
150 
151 /*  D E V S E T T I N G S  */
152 
153 int
devsettings(char * s)154 devsettings(char * s) {
155     /* Get current device settings, save them for devrestore() */
156     /* Parse string s, do whatever it says, e.g. "9600;8N1" */
157     if (!pktmode(ON))			/* And put device in packet mode */
158       return(0);
159     return(1);
160 }
161 
162 /*  D E V R E S T O R E  */
163 
164 int
devrestore(void)165 devrestore(void) {
166     /* Put device back as we found it */
167     pktmode(OFF);
168     return(1);
169 }
170 
171 
172 /*  D E V C L O S E  --  Closes the current open communications device  */
173 /*
174   Call with: nothing
175   Closes the device and puts it back the way it was found by devopen().
176   Returns: 0 on failure, 1 on success.
177 */
178 int
devclose(void)179 devclose(void) {
180     ttyfd = -1;
181     return(1);
182 }
183 
184 /* I N C H K  --  Check if input waiting */
185 
186 /*
187   Check if input is waiting to be read, needed for sliding windows.  This
188   sample version simply looks in the stdin buffer (which is not portable
189   even among different Unixes).  If your platform does not provide a way to
190   look at the device input buffer without blocking and without actually
191   reading from it, make this routine return -1.  On success, returns the
192   numbers of characters waiting to be read, i.e. that can be safely read
193   without blocking.
194 */
195 int
inchk(struct k_data * k)196 inchk(struct k_data * k) {
197 #ifdef _IO_file_flags			/* Linux */
198     if (ttyfd < 0)                      /* Device must be open */
199       return(0);
200     return((int) ((stdin->_IO_read_end) - (stdin->_IO_read_ptr)));
201 #else
202 #ifdef AIX				/* AIX */
203     if (ttyfd < 0)
204       return(0);
205     return(stdin->_cnt);
206 #else
207 #ifdef SunOS				/* Solaris and SunOS */
208     if (ttyfd < 0)
209       return(0);
210     return(stdin->_cnt);
211 #else
212 #ifdef HPUX				/* HPUX */
213     if (ttyfd < 0)
214       return(0);
215     return(stdin->__cnt);
216 #else
217     return(-1);
218 #endif /* HPUX */
219 #endif /* SunOS */
220 #endif /* AIX */
221 #endif /* _IO_file_flags */
222 }
223 
224 /*  R E A D P K T  --  Read a Kermit packet from the communications device  */
225 /*
226   Call with:
227     k   - Kermit struct pointer
228     p   - pointer to read buffer
229     len - length of read buffer
230 
231   When reading a packet, this function looks for start of Kermit packet
232   (k->r_soh), then reads everything between it and the end of the packet
233   (k->r_eom) into the indicated buffer.  Returns the number of bytes read, or:
234      0   - timeout or other possibly correctable error;
235     -1   - fatal error, such as loss of connection, or no buffer to read into.
236 */
237 
238 int
readpkt(struct k_data * k,UCHAR * p,int len,int fc)239 readpkt(struct k_data * k, UCHAR *p, int len, int fc) {
240     int x, n, max;
241     short flag;
242     UCHAR c;
243 /*
244   Timeout not implemented in this sample.
245   It should not be needed.  All non-embedded Kermits that are capable of
246   making connections are also capable of timing out, and only one Kermit
247   needs to time out.  NOTE: This simple example waits for SOH and then
248   reads everything up to the negotiated packet terminator.  A more robust
249   version might be driven by the value of the packet-length field.
250 */
251 #ifdef DEBUG
252     char * p2;
253 #endif	/* DEBUG */
254 
255 #ifdef F_CTRLC
256     short ccn;
257     ccn = 0;
258 #endif /* F_CTRLC */
259 
260     if (ttyfd < 0 || !p) {		/* Device not open or no buffer */
261 	debug(DB_MSG,"readpkt FAIL",0,0);
262 	return(-1);
263     }
264     flag = n = 0;                       /* Init local variables */
265 
266 #ifdef DEBUG
267     p2 = p;
268 #endif	/* DEBUG */
269 
270     while (1) {
271         x = getchar();                  /* Replace this with real i/o */
272         c = (k->parity) ? x & 0x7f : x & 0xff; /* Strip parity */
273 
274 #ifdef F_CTRLC
275 	/* In remote mode only: three consecutive ^C's to quit */
276         if (k->remote && c == (UCHAR) 3) {
277             if (++ccn > 2) {
278 		debug(DB_MSG,"readpkt ^C^C^C",0,0);
279 		return(-1);
280 	    }
281         } else {
282 	    ccn = 0;
283 	}
284 #endif /* F_CTRLC */
285 
286         if (!flag && c != k->r_soh)	/* No start of packet yet */
287           continue;                     /* so discard these bytes. */
288         if (c == k->r_soh) {		/* Start of packet */
289             flag = 1;                   /* Remember */
290             continue;                   /* But discard. */
291         } else if (c == k->r_eom	/* Packet terminator */
292 		   || c == '\012'	/* 1.3: For HyperTerminal */
293 		   ) {
294 #ifdef DEBUG
295             *p = NUL;                   /* Terminate for printing */
296 	    debug(DB_PKT,"RPKT",p2,n);
297 #endif /* DEBUG */
298             return(n);
299         } else {                        /* Contents of packet */
300             if (n++ > k->r_maxlen)	/* Check length */
301               return(0);
302             else
303               *p++ = x & 0xff;
304         }
305     }
306     debug(DB_MSG,"READPKT FAIL (end)",0,0);
307     return(-1);
308 }
309 
310 /*  T X _ D A T A  --  Writes n bytes of data to communication device.  */
311 /*
312   Call with:
313     k = pointer to Kermit struct.
314     p = pointer to data to transmit.
315     n = length.
316   Returns:
317     X_OK on success.
318     X_ERROR on failure to write - i/o error.
319 */
320 int
tx_data(struct k_data * k,UCHAR * p,int n)321 tx_data(struct k_data * k, UCHAR *p, int n) {
322     int x;
323     int max;
324 
325     max = 10;                           /* Loop breaker */
326 
327     while (n > 0) {                     /* Keep trying till done */
328         x = write(ttyfd,p,n);
329         debug(DB_MSG,"tx_data write",0,x);
330         if (x < 0 || --max < 1)         /* Errors are fatal */
331           return(X_ERROR);
332         n -= x;
333 	p += x;
334     }
335     return(X_OK);                       /* Success */
336 }
337 
338 /*  O P E N F I L E  --  Open output file  */
339 /*
340   Call with:
341     Pointer to filename.
342     Size in bytes.
343     Creation date in format yyyymmdd hh:mm:ss, e.g. 19950208 14:00:00
344     Mode: 1 = read, 2 = create, 3 = append.
345   Returns:
346     X_OK on success.
347     X_ERROR on failure, including rejection based on name, size, or date.
348 */
349 int
openfile(struct k_data * k,UCHAR * s,int mode)350 openfile(struct k_data * k, UCHAR * s, int mode) {
351 
352     switch (mode) {
353       case 1:				/* Read */
354 	if (!(ifile = fopen(s,"r"))) {
355 	    debug(DB_LOG,"openfile read error",s,0);
356 	    return(X_ERROR);
357 	}
358 	k->s_first   = 1;		/* Set up for getkpt */
359 	k->zinbuf[0] = '\0';		/* Initialize buffer */
360 	k->zinptr    = k->zinbuf;	/* Set up buffer pointer */
361 	k->zincnt    = 0;		/* and count */
362 	debug(DB_LOG,"openfile read ok",s,0);
363 	return(X_OK);
364 
365       case 2:				/* Write (create) */
366         ofile = creat(s,0644);
367 	if (ofile < 0) {
368 	    debug(DB_LOG,"openfile write error",s,0);
369 	    return(X_ERROR);
370 	}
371 	debug(DB_LOG,"openfile write ok",s,0);
372 	return(X_OK);
373 
374 #ifdef COMMENT
375       case 3:				/* Append (not used) */
376         ofile = open(s,O_WRONLY|O_APPEND);
377 	if (ofile < 0) {
378 	    debug(DB_LOG,"openfile append error",s,0);
379 	    return(X_ERROR);
380 	}
381 	    debug(DB_LOG,"openfile append ok",s,0);
382 	return(X_OK);
383 #endif /* COMMENT */
384 
385       default:
386         return(X_ERROR);
387     }
388 }
389 
390 /*  F I L E I N F O  --  Get info about existing file  */
391 /*
392   Call with:
393     Pointer to filename
394     Pointer to buffer for date-time string
395     Length of date-time string buffer (must be at least 18 bytes)
396     Pointer to int file type:
397        0: Prevailing type is text.
398        1: Prevailing type is binary.
399     Transfer mode (0 = auto, 1 = manual):
400        0: Figure out whether file is text or binary and return type.
401        1: (nonzero) Don't try to figure out file type.
402   Returns:
403     X_ERROR on failure.
404     0L or greater on success == file length.
405     Date-time string set to yyyymmdd hh:mm:ss modtime of file.
406     If date can't be determined, first byte of buffer is set to NUL.
407     Type set to 0 (text) or 1 (binary) if mode == 0.
408 */
409 #ifdef F_SCAN
410 #define SCANBUF 1024
411 #define SCANSIZ 49152
412 #endif /* F_SCAN */
413 
414 ULONG
fileinfo(struct k_data * k,UCHAR * filename,UCHAR * buf,int buflen,short * type,short mode)415 fileinfo(struct k_data * k,
416 	 UCHAR * filename, UCHAR * buf, int buflen, short * type, short mode) {
417     struct stat statbuf;
418     struct tm * timestamp, * localtime();
419 
420 #ifdef F_SCAN
421     FILE * fp;				/* File scan pointer */
422     char inbuf[SCANBUF];		/* and buffer */
423 #endif /* F_SCAN */
424 
425     if (!buf)
426       return(X_ERROR);
427     buf[0] = '\0';
428     if (buflen < 18)
429       return(X_ERROR);
430     if (stat(filename,&statbuf) < 0)
431       return(X_ERROR);
432     timestamp = localtime(&(statbuf.st_mtime));
433     sprintf(buf,"%04d%02d%02d %02d:%02d:%02d",
434 	    timestamp->tm_year + 1900,
435             timestamp->tm_mon + 1,
436             timestamp->tm_mday,
437             timestamp->tm_hour,
438             timestamp->tm_min,
439             timestamp->tm_sec
440 	    );
441 #ifdef F_SCAN
442 /*
443   Here we determine if the file is text or binary if the transfer mode is
444   not forced.  This is an extremely crude sample, which diagnoses any file
445   that contains a control character other than HT, LF, FF, or CR as binary.
446   A more thorough content analysis can be done that accounts for various
447   character sets as well as various forms of Unicode (UTF-8, UTF-16, etc).
448   Or the diagnosis could be based wholly or in part on the filename.
449   etc etc.  Or the implementation could skip this entirely by not defining
450   F_SCAN and/or by always calling this routine with type set to -1.
451 */
452     if (!mode) {			/* File type determination requested */
453 	int isbinary = 1;
454 	fp = fopen(filename,"r");	/* Open the file for scanning */
455 	if (fp) {
456 	    int n = 0, count = 0;
457 	    char c, * p;
458 
459 	    debug(DB_LOG,"fileinfo scan ",filename,0);
460 
461 	    isbinary = 0;
462 	    while (count < SCANSIZ && !isbinary) { /* Scan this much */
463 		n = fread(inbuf,1,SCANBUF,fp);
464 		if (n == EOF || n == 0)
465 		  break;
466 		count += n;
467 		p = inbuf;
468 		while (n--) {
469 		    c = *p++;
470 		    if (c < 32 || c == 127) {
471 			if (c !=  9 &&	/* Tab */
472 			    c != 10 &&	/* LF */
473 			    c != 12 &&	/* FF */
474 			    c != 13) {	/* CR */
475 			    isbinary = 1;
476 			    debug(DB_MSG,"fileinfo BINARY",0,0);
477 			    break;
478 			}
479 		    }
480 		}
481 	    }
482 	    fclose(fp);
483 	    *type = isbinary;
484 	}
485     }
486 #endif /* F_SCAN */
487 
488     return((ULONG)(statbuf.st_size));
489 }
490 
491 
492 /*  R E A D F I L E  --  Read data from a file  */
493 
494 int
readfile(struct k_data * k)495 readfile(struct k_data * k) {
496     if (!k->zinptr) {
497 #ifdef DEBUG
498 	fprintf(dp,"readfile ZINPTR NOT SET\n");
499 #endif /* DEBUG */
500 	return(X_ERROR);
501     }
502     if (k->zincnt < 1) {		/* Nothing in buffer - must refill */
503 	if (k->binary) {		/* Binary - just read raw buffers */
504 	    k->dummy = 0;
505 	    k->zincnt = fread(k->zinbuf, 1, k->zinlen, ifile);
506 	    debug(DB_LOG,"readfile binary ok zincnt",0,k->zincnt);
507 
508 	} else {			/* Text mode needs LF/CRLF handling */
509 	    int c;			/* Current character */
510 	    for (k->zincnt = 0; (k->zincnt < (k->zinlen - 2)); (k->zincnt)++) {
511 		if ((c = getc(ifile)) == EOF)
512 		  break;
513 		if (c == '\n')		/* Have newline? */
514 		  k->zinbuf[(k->zincnt)++] = '\r'; /* Insert CR */
515 		k->zinbuf[k->zincnt] = c;
516 	    }
517 #ifdef DEBUG
518 	    k->zinbuf[k->zincnt] = '\0';
519 	    debug(DB_LOG,"readfile text ok zincnt",0,k->zincnt);
520 #endif /* DEBUG */
521 	}
522 	k->zinbuf[k->zincnt] = '\0';	/* Terminate. */
523 	if (k->zincnt == 0)		/* Check for EOF */
524 	  return(-1);
525 	k->zinptr = k->zinbuf;		/* Not EOF - reset pointer */
526     }
527     (k->zincnt)--;			/* Return first byte. */
528 
529     debug(DB_LOG,"readfile exit zincnt",0,k->zincnt);
530     debug(DB_LOG,"readfile exit zinptr",0,k->zinptr);
531     return(*(k->zinptr)++ & 0xff);
532 }
533 
534 
535 /*  W R I T E F I L E  --  Write data to file  */
536 /*
537   Call with:
538     Kermit struct
539     String pointer
540     Length
541   Returns:
542     X_OK on success
543     X_ERROR on failure, such as i/o error, space used up, etc
544 */
545 int
writefile(struct k_data * k,UCHAR * s,int n)546 writefile(struct k_data * k, UCHAR * s, int n) {
547     int rc;
548     rc = X_OK;
549 
550     debug(DB_LOG,"writefile binary",0,k->binary);
551 
552     if (k->binary) {			/* Binary mode, just write it */
553 	if (write(ofile,s,n) != n)
554 	  rc = X_ERROR;
555     } else {				/* Text mode, skip CRs */
556 	UCHAR * p, * q;
557 	int i;
558 	q = s;
559 
560 	while (1) {
561 	    for (p = q, i = 0; ((*p) && (*p != (UCHAR)13)); p++, i++) ;
562 	    if (i > 0)
563 	      if (write(ofile,q,i) != i)
564 		rc = X_ERROR;
565 	    if (!*p) break;
566 	    q = p+1;
567 	}
568     }
569     return(rc);
570 }
571 
572 /*  C L O S E F I L E  --  Close output file  */
573 /*
574   Mode = 1 for input file, mode = 2 or 3 for output file.
575 
576   For output files, the character c is the character (if any) from the Z
577   packet data field.  If it is D, it means the file transfer was canceled
578   in midstream by the sender, and the file is therefore incomplete.  This
579   routine should check for that and decide what to do.  It should be
580   harmless to call this routine for a file that that is not open.
581 */
582 int
closefile(struct k_data * k,UCHAR c,int mode)583 closefile(struct k_data * k, UCHAR c, int mode) {
584     int rc = X_OK;			/* Return code */
585 
586     switch (mode) {
587       case 1:				/* Closing input file */
588 	if (!ifile)			/* If not not open */
589 	  break;			/* do nothing but succeed */
590 	debug(DB_LOG,"closefile (input)",k->filename,0);
591 	if (fclose(ifile) < 0)
592 	  rc = X_ERROR;
593 	break;
594       case 2:				/* Closing output file */
595       case 3:
596 	if (ofile < 0)			/* If not open */
597 	  break;			/* do nothing but succeed */
598 	debug(DB_LOG,"closefile (output) name",k->filename,0);
599 	debug(DB_LOG,"closefile (output) keep",0,k->ikeep);
600 	if (close(ofile) < 0) {		/* Try to close */
601 	    rc = X_ERROR;
602 	} else if ((k->ikeep == 0) &&	/* Don't keep incomplete files */
603 		   (c == 'D')) {	/* This file was incomplete */
604 	    if (k->filename) {
605 		debug(DB_LOG,"deleting incomplete",k->filename,0);
606 		unlink(k->filename);	/* Delete it. */
607 	    }
608 	}
609 	break;
610       default:
611 	rc = X_ERROR;
612     }
613     return(rc);
614 }
615 
616 #ifdef DEBUG
xerror()617 int xerror() {
618     unsigned int x;
619     extern int errorrate;		/* Fix this - NO EXTERNS */
620     if (!errorrate)
621       return(0);
622     x = rand() % 100;			/* Fix this - NO C LIBRARY */
623     debug(DB_LOG,"RANDOM",0,x);
624     debug(DB_LOG,"ERROR",0,(x < errorrate));
625     return(x < errorrate);
626 }
627 #endif /* DEBUG */
628