|
|
1.1 root 1: /*
2: * K e r m i t File Transfer Utility
3: *
4: * UNIX Kermit, Columbia University, 1981, 1982, 1983
5: * Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell
6: *
7: * Also: Jim Guyton, Rand Corporation
8: * Walter Underwood, Ford Aerospace
9: * Lauren Weinstein
10: *
11: * usage: kermit c|C [lbe line baud escapechar] to connect
12: * kermit s [d..iflbht line baud] file ... to send files
13: * kermit r [d..iflbht line baud] to receive files
14: *
15: * where c|C=connect, s=send, r=receive,
16: * d=debug, i=image mode, f=no filename conversion, l=tty line,
17: * b=baud rate, e=escape char, h=host (server) mode, t=tymnet mode.
18: *
19: * For remote Kermit, format is either:
20: * kermit r to receive files
21: * or kermit s file ... to send files
22: *
23: */
24:
25: /*
26: * Modification History:
27: *
28: * Oct. 17 Included fixes from Alan Crosswell (CUCCA) for IBM_UTS:
29: * - Changed MYEOL character from \n to \r.
30: * - Change char to int in bufill so getc would return -1 on
31: * EOF instead of 255 (-1 truncated to 8 bits)
32: * - Added read() in rpack to eat the EOL character
33: * - Added fflush() call in printmsg to force the output
34: * NOTE: The last three changes are not conditionally compiled
35: * since they should work equally well on any system.
36: *
37: * Changed Berkeley 4.x conditional compilation flag from
38: * UNIX4X to UCB4X.
39: * Added support for error packets and cleaned up the printing
40: * routines.
41: * Nov. 5, 1983 (Lauren Weinstein)
42: * Changed "write(0..." to "write(1..." in escape char code.
43: * Changed escape char processing to send the escape char to
44: * the remote system if entered twice in a row.
45: * Added support for the "Coherent" OS.
46: * Changed the "UCB4X" conditional to "UNIXL" to more correctly
47: * indicate its broader meaning.
48: * Added explicit "host" (server) flag ("-h") option.
49: * Misc. code cleanups.
50: * Jan. 21, 1984 (Lauren Weinstein)
51: * Added "suspend" (no hangup) command (^s) for Coherent.
52: * Added default tty line/speed settings for Coherent.
53: * Added Tymnet mode option ("-t").
54: * Apr. 6, 1984 (Lauren Weinstein)
55: * Misc. code cleanups.
56: * June 4, 1984 (Lauren Weinstein)
57: * Added ability to pass the first line of data to send over the
58: * connection via the command line (after the "-C" option flag).
59: */
60:
61: #include <stdio.h> /* Standard UNIX definitions */
62:
63: /* Conditional compilation for different machines/operating systems */
64: /* One and only one of the following lines should be 1 */
65:
66: #define UNIXL 1 /* Various UNIX and UNIX look-alikes */
67: #define TOPS_20 0 /* TOPS-20 */
68: #define IBM_UTS 0 /* Amdahl UTS on IBM systems */
69: #define VAX_VMS 0 /* VAX/VMS (not yet implemented) */
70:
71: /* Conditional compilation for the different Unix and Unix-like variants */
72: /* 0 means don't compile it, nonzero means do */
73:
74: #if UNIXL
75: #define V6_LIBS 0 /* Use retrofit libraries */
76: #define NO_FIONREAD 1 /* Don't use ioctl for flushinput() */
77: #define NO_TANDEM 0 /* Don't use TANDEM line discipline (xon/xoff) */
78: #define MULTIREAD 1 /*++1++*/
79: /* True for multiple byte read/write in -c mode */
80: #define COHERENT 1 /* True for Coherent OS */
81: #define LOCK_LINE 1 /* Lock the line when in local mode */
82: #endif
83:
84: #if IBM_UTS
85: #define V6_LIBS 0 /* Use retrofit libraries */
86: #define NO_FIONREAD 1 /* Don't use ioctl for flushinput() */
87: #define NO_TANDEM 1 /* Don't use TANDEM line discipline (xon/xoff) */
88: #define MULTIREAD 0 /* True for multiple byte read/write in -c mode */
89: #endif
90:
91: #if V6_LIBS
92: #include <retrofit/sgtty.h>
93: #include <retrofit/signal.h>
94: #include <retrofit/setjmp.h>
95: #else
96: #include <sgtty.h>
97: #include <signal.h>
98: #include <setjmp.h>
99: #endif
100:
101: #if NO_TANDEM
102: #define TANDEM 0 /* define it to be nothing if it's unsupported */
103: #endif
104:
105: #if COHERENT
106: #define DEFLINE "/dev/modem" /* default tty comm line */
107: #define DEFSPEED 9600 /* default tty speed */
108: #endif
109:
110: /* Symbol Definitions */
111:
112: #define MAXPACKSIZ 94 /* Maximum packet size */
113: #define SOH 1 /* Start of header */
114: #define CR 13 /* ASCII Carriage Return */
115: #define SP 32 /* ASCII space */
116: #define DEL 127 /* Delete (rubout) */
117: #define ESCCHR '^' /* Default escape character for CONNECT */
118:
119: #define MAXTRY 20 /* Times to retry a packet */
120: #define MYQUOTE '#' /* Quote character I will use */
121: #define MYPAD 0 /* Number of padding characters I will need */
122: #define MYPCHAR 0 /* Padding character I need (NUL) */
123: #define NUL '\0'
124: #if IBM_UTS
125: #define MYEOL '\r' /* End-Of-Line character for UTS systems */
126: #else
127: #define MYEOL '\n' /* End-Of-Line character I need */
128: #endif
129:
130: #define MYTIME 10 /* (10) Seconds after which I should be timed out */
131: #define MAXTIM 60 /* (60) Maximum timeout interval */
132: #define MINTIM 2 /* (2) Minumum timeout interval */
133:
134: #define TRUE -1 /* Boolean constants */
135: #define FALSE 0
136:
137:
138: /* Macro Definitions */
139:
140: /*
141: * tochar: converts a control character to a printable one by adding a space.
142: *
143: * unchar: undoes tochar.
144: *
145: * ctl: converts between control characters and printable characters by
146: * toggling the control bit (ie. ^A becomes A and A becomes ^A).
147: */
148: #define tochar(ch) ((ch) + ' ')
149: #define unchar(ch) ((ch) - ' ')
150: #define ctl(ch) ((ch) ^ 64 )
151:
152:
153: /* Global Variables */
154:
155: int size, /* Size of present data */
156: rpsiz, /* Maximum receive packet size */
157: spsiz, /* Maximum send packet size */
158: pad, /* How much padding to send */
159: timint, /* Timeout for foreign host on sends */
160: n, /* Packet number */
161: numtry, /* Times this packet retried */
162: oldtry, /* Times previous packet retried */
163: ttyfd, /* File descriptor of tty for I/O, 0 if remote */
164: remote, /* -1 means we're a remote kermit */
165: image, /* -1 means 8-bit mode */
166: debug, /* indicates level of debugging output (0=none) */
167: filnamcnv, /* -1 means do file name case conversions */
168: filecount, /* Number of files left to send */
169: tflg; /* Flag for Tymnet mode */
170: logflg; /* Log connect transactions */
171: loglastch; /* last log character for cr elim */
172:
173: char state, /* Present state of the automaton */
174: padchar, /* Padding character to send */
175: eol, /* End-Of-Line character to send */
176: escchr, /* Connect command escape character */
177: quote, /* Quote character in incoming data */
178: **filelist, /* List of files to be sent */
179: *filnam, /* Current file name */
180: *ttyline, /* Pointer to tty line */
181: ttynbuff[128]; /* Name buffer for tty line */
182: inbuff[128]; /* remote data input buffer */
183: recpkt[MAXPACKSIZ], /* Receive packet buffer */
184: packet[MAXPACKSIZ]; /* Packet buffer */
185: ldata[1024]; /* First line of data to send over connection */
186:
187: FILE *fp, /* File pointer for current disk file */
188: *log; /* File pointer for Logfile */
189: int logfd; /* File descriptor for Logfile */
190:
191: jmp_buf env; /* Environment ptr for timeout longjump */
192:
193:
194: /*
195: * m a i n
196: *
197: * Main routine - parse command and options, set up the
198: * tty lines, and dispatch to the appropriate routine.
199: */
200:
201: main(argc,argv)
202: int argc; /* Character pointers to and count of */
203: char **argv; /* command line arguments */
204: {
205: char *cp; /* char pointer */
206: int speed, /* speed of assigned tty, */
207: cflg, rflg, sflg; /* flags for CONNECT, RECEIVE, SEND */
208: int hflg; /* flag for HOST (server) mode */
209: struct sgttyb
210: rawmode, /* Controlling tty raw mode */
211: cookedmode, /* Controlling tty cooked mode */
212: ttymode; /* mode of tty line in LINE option */
213:
214: if (argc < 2) usage(); /* Make sure there's a command line */
215:
216: cp = *++argv; argv++; argc -= 2; /* Set up pointers to args */
217:
218: /* Initialize these values and hope the first packet will get across OK */
219:
220: eol = CR; /* EOL for outgoing packets */
221: quote = '#'; /* Standard control-quote char "#" */
222: pad = 0; /* No padding */
223: padchar = NUL; /* Use null if any padding wanted */
224:
225: speed = hflg = cflg = logflg = sflg = 0; /* Turn off all parse flags */
226: loglastch = -1;
227: rflg = tflg = 0;
228: ttyline = 0; /* Default is remote mode */
229:
230: #if UNIXL /* Default to 7-bit masking, CRLF */
231: image = FALSE; /* translation and filename case */
232: filnamcnv = TRUE; /* conversion for UNIX systems */
233: #if COHERENT
234: ttyline = DEFLINE; /* set default tty line */
235: speed = DEFSPEED; /* set default tty speed */
236: #endif
237: #else
238: image = TRUE; /* Default to no processing for */
239: filnamcnv = FALSE; /* non-UNIX-type systems */
240: #endif
241:
242: escchr = ESCCHR; /* Default escape character */
243:
244: while ((*cp) != NUL) /* Parse characters in first arg */
245: switch (*cp++)
246: {
247: case 'c': /* c/C = Connect command */
248: case 'C':
249: cflg++;
250: if (*(cp-1) == 'C' && argc--) /* "C" command? */
251: strcpy(ldata, *argv++); /* get starting data line */
252: break;
253: case 's': sflg++; break; /* S = Send command */
254: case 'r': rflg++; break; /* R = Receive command */
255: case 'h': hflg++; break; /* H = Host (server) mode */
256: case 't': tflg++; break; /* T = Tymnet mode */
257: case 'd': /* D = Increment debug mode count */
258: debug++; break;
259:
260: case 'f':
261: filnamcnv = FALSE; /* F = don't do case conversion */
262: break; /* on filenames */
263:
264: case 'i': /* I = Image (8-bit) mode */
265: image = TRUE; break; /* (this is default for non-UNIX) */
266:
267: case 'l': /* L = specify tty line to use */
268: if (argc--) ttyline = *argv++;
269: else usage();
270: if (debug) printf("Line to remote host is %s\n", ttyline);
271: break;
272:
273: case 'L': /* write to log file */
274: logflg++; break;
275:
276: case 'e': /* E = specify escape char */
277: if (argc--) escchr = **argv++;
278: else usage();
279: if (debug) printf("Escape char is \"%c\"\n",escchr);
280: break;
281:
282: case 'b': /* B = specify baud rate */
283: #if UNIXL
284: if (argc--) speed = atoi(*argv++);
285: else usage();
286: if (debug) printf("Line speed to remote host is %d\n",speed);
287: break;
288: #else
289: printmsg("Speed setting not implemented.");
290: exit(1);
291: #endif
292: }
293:
294: /* Done parsing */
295:
296: if ((cflg+sflg+rflg) != 1) /* Only one command allowed */
297: usage();
298:
299: if (!hflg) /* local mode if "hflg" is FALSE */
300: {
301: ttyfd = open(ttyline,2); /* Open the tty line */
302: if (ttyfd < 0) {
303: printmsg("Cannot open %s", ttyline);
304: exit(1);
305: }
306: #if LOCK_LINE /* Set exclusive-use mode on line */
307: if (ioctl(ttyfd,TIOCEXCL,0) != 0)
308: {
309: printmsg("Cannot lock %s", ttyline);
310: exit(1);
311: }
312: #endif
313: remote = FALSE; /* Indicate we're in local mode */
314: }
315: else /* No LINE specified so we operate */
316: { /* in remote mode (ie. controlling */
317: ttyfd = 0; /* tty is the communications line) */
318: remote = TRUE;
319: }
320:
321:
322: /* Put the proper tty into the correct mode */
323:
324: if (remote) /* If remote, use controlling tty */
325: {
326: gtty(0,&cookedmode); /* Save current mode so we can */
327: gtty(0,&rawmode); /* restore it later */
328: rawmode.sg_flags |= (RAW|TANDEM);
329: rawmode.sg_flags &= ~(ECHO|CRMOD);
330: stty(0,&rawmode); /* Put tty in raw mode */
331: }
332: else /* Local, use assigned line */
333: {
334: gtty(ttyfd,&ttymode);
335: ttymode.sg_flags |= (RAW|TANDEM);
336: ttymode.sg_flags &= ~(ECHO|CRMOD);
337:
338: #if UNIXL /* Speed changing for UNIX only */
339: if (speed) /* User specified a speed? */
340: {
341: switch(speed) /* Get internal system code */
342: {
343: case 110: speed = B110; break;
344: case 150: speed = B150; break;
345: case 300: speed = B300; break;
346: case 1200: speed = B1200; break;
347: case 2400: speed = B2400; break;
348: case 4800: speed = B4800; break;
349: case 9600: speed = B9600; break;
350: case 19200: speed = B19200; break;
351:
352: default:
353: printmsg("Bad line speed.");
354: exit(1);
355: }
356: ttymode.sg_ispeed = speed;
357: ttymode.sg_ospeed = speed;
358: }
359: #endif /* UNIXL */
360:
361: stty(ttyfd,&ttymode); /* Put asg'd tty in raw mode */
362: }
363:
364:
365: /* All set up, now execute the command that was given. */
366:
367: if (debug)
368: {
369: printf("Debugging level = %d\n\n",debug);
370:
371: if (cflg) printf("Connect command\n\n");
372: if (sflg) printf("Send command\n\n");
373: if (rflg) printf("Receive command\n\n");
374: if (logflg) printf("Logging connect transactions\n\n");
375: }
376:
377: if (cflg) connect(); /* Connect command */
378:
379: if (sflg) /* Send command */
380: {
381: if (argc--) filnam = *argv++; /* Get file to send */
382: else
383: { if (remote)
384: stty(0,&cookedmode); /* Restore controlling tty's modes */
385: usage(); /* and give error */
386: }
387: fp = NULL; /* Indicate no file open yet */
388: filelist = argv; /* Set up the rest of the file list */
389: filecount = argc; /* Number of files left to send */
390: if (sendsw() == FALSE) /* Send the file(s) */
391: printmsg("Send failed."); /* Report failure */
392: else /* or */
393: printmsg("done."); /* success */
394: }
395:
396: if (rflg) /* Receive command */
397: {
398: if (recsw() == FALSE) /* Receive the file(s) */
399: printmsg("Receive failed.");
400: else /* Report failure */
401: printmsg("done."); /* or success */
402: }
403:
404: if (remote) stty(0,&cookedmode); /* Restore controlling tty's modes */
405: exit(0);
406: }
407:
408:
409: /*
410: * s e n d s w
411: *
412: * Sendsw is the state table switcher for sending files. It loops until
413: * either it finishes, or an error is encountered. The routines called
414: * by sendsw are responsible for changing the state.
415: *
416: */
417:
418: sendsw()
419: {
420: char sinit(), sfile(), sdata(), seof(), sbreak();
421:
422: state = 'S'; /* Send initiate is the start state */
423: n = 0; /* Initialize message number */
424: numtry = 0; /* Say no tries yet */
425: while(TRUE) /* Do this as long as necessary */
426: {
427: if (debug) printf("sendsw state: %c\n",state);
428: switch(state)
429: {
430: case 'S': state = sinit(); break; /* Send-Init */
431: case 'F': state = sfile(); break; /* Send-File */
432: case 'D': state = sdata(); break; /* Send-Data */
433: case 'Z': state = seof(); break; /* Send-End-of-File */
434: case 'B': state = sbreak(); break; /* Send-Break */
435: case 'C': return (TRUE); /* Complete */
436: case 'A': return (FALSE); /* "Abort" */
437: default: return (FALSE); /* Unknown, fail */
438: }
439: }
440: }
441:
442:
443: /*
444: * s i n i t
445: *
446: * Send Initiate: send this host's parameters and get other side's back.
447: */
448:
449: char sinit()
450: {
451: int num, len; /* Packet number, length */
452:
453: if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
454: spar(packet); /* Fill up init info packet */
455:
456: flushinput(); /* Flush pending input */
457:
458: spack('S',n,6,packet); /* Send an S packet */
459: switch(rpack(&len,&num,recpkt)) /* What was the reply? */
460: {
461: case 'N': return(state); /* NAK, try it again */
462:
463: case 'Y': /* ACK */
464: if (n != num) /* If wrong ACK, stay in S state */
465: return(state); /* and try again */
466: rpar(recpkt); /* Get other side's init info */
467:
468: if (eol == 0) eol = '\n'; /* Check and set defaults */
469: if (quote == 0) quote = '#';
470:
471: numtry = 0; /* Reset try counter */
472: n = (n+1)%64; /* Bump packet count */
473: return('F'); /* OK, switch state to F */
474:
475: case 'E': /* Error packet received */
476: prerrpkt(recpkt); /* Print it out and */
477: return('A'); /* abort */
478:
479: case FALSE: return(state); /* Receive failure, try again */
480:
481: default: return('A'); /* Anything else, just "abort" */
482: }
483: }
484:
485:
486: /*
487: * s f i l e
488: *
489: * Send File Header.
490: */
491:
492: char sfile()
493: {
494: int num, len; /* Packet number, length */
495: char filnam1[50], /* Converted file name */
496: *newfilnam, /* Pointer to file name to send */
497: *cp; /* char pointer */
498:
499: if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
500:
501: if (fp == NULL) /* If not already open, */
502: { if (debug) printf(" Opening %s for sending.\n",filnam);
503: fp = fopen(filnam,"r"); /* open the file to be sent */
504: if (fp == NULL) /* If bad file pointer, give up */
505: {
506: error("Cannot open file %s",filnam);
507: return('A');
508: }
509: }
510:
511: strcpy(filnam1, filnam); /* Copy file name */
512: newfilnam = cp = filnam1;
513: while (*cp != '\0') /* Strip off all leading directory */
514: if (*cp++ == '/') /* names (ie. up to the last /). */
515: newfilnam = cp;
516:
517: if (filnamcnv) /* Convert lower case to upper */
518: for (cp = newfilnam; *cp != '\0'; cp++)
519: if (*cp >= 'a' && *cp <= 'z')
520: *cp ^= 040;
521:
522: len = cp - newfilnam; /* Compute length of new filename */
523:
524: printmsg("Sending %s as %s",filnam,newfilnam);
525:
526: spack('F',n,len,newfilnam); /* Send an F packet */
527: switch(rpack(&len,&num,recpkt)) /* What was the reply? */
528: {
529: case 'N': /* NAK, just stay in this state, */
530: num = (--num<0 ? 63:num); /* unless it's NAK for next packet */
531: if (n != num) /* which is just like an ACK for */
532: return(state); /* this packet so fall thru to... */
533:
534: case 'Y': /* ACK */
535: if (n != num) return(state); /* If wrong ACK, stay in F state */
536: numtry = 0; /* Reset try counter */
537: n = (n+1)%64; /* Bump packet count */
538: size = bufill(packet); /* Get first data from file */
539: return('D'); /* Switch state to D */
540:
541: case 'E': /* Error packet received */
542: prerrpkt(recpkt); /* Print it out and */
543: return('A'); /* abort */
544:
545: case FALSE: return(state); /* Receive failure, stay in F state */
546:
547: default: return('A'); /* Something else, just "abort" */
548: }
549: }
550:
551:
552: /*
553: * s d a t a
554: *
555: * Send File Data
556: */
557:
558: char sdata()
559: {
560: int num, len; /* Packet number, length */
561:
562: if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
563: spack('D',n,size,packet); /* Send a D packet */
564: switch(rpack(&len,&num,recpkt)) /* What was the reply? */
565: {
566: case 'N': /* NAK, just stay in this state, */
567: num = (--num<0 ? 63:num); /* unless it's NAK for next packet */
568: if (n != num) /* which is just like an ACK for */
569: return(state); /* this packet so fall thru to... */
570:
571: case 'Y': /* ACK */
572: if (n != num) return(state); /* If wrong ACK, fail */
573: numtry = 0; /* Reset try counter */
574: n = (n+1)%64; /* Bump packet count */
575: if ((size = bufill(packet)) == EOF) /* Get data from file */
576: return('Z'); /* If EOF set state to that */
577: return('D'); /* Got data, stay in state D */
578:
579: case 'E': /* Error packet received */
580: prerrpkt(recpkt); /* Print it out and */
581: return('A'); /* abort */
582:
583: case FALSE: return(state); /* Receive failure, stay in D */
584:
585: default: return('A'); /* Anything else, "abort" */
586: }
587: }
588:
589:
590: /*
591: * s e o f
592: *
593: * Send End-Of-File.
594: */
595:
596: char seof()
597: {
598: int num, len; /* Packet number, length */
599: if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
600:
601: spack('Z',n,0,packet); /* Send a 'Z' packet */
602: switch(rpack(&len,&num,recpkt)) /* What was the reply? */
603: {
604: case 'N': /* NAK, just stay in this state, */
605: num = (--num<0 ? 63:num); /* unless it's NAK for next packet, */
606: if (n != num) /* which is just like an ACK for */
607: return(state); /* this packet so fall thru to... */
608:
609: case 'Y': /* ACK */
610: if (n != num) return(state); /* If wrong ACK, hold out */
611: numtry = 0; /* Reset try counter */
612: n = (n+1)%64; /* and bump packet count */
613: if (debug) printf(" Closing input file %s, ",filnam);
614: fclose(fp); /* Close the input file */
615: fp = NULL; /* Set flag indicating no file open */
616:
617: if (debug) printf("looking for next file...\n");
618: if (gnxtfl() == FALSE) /* No more files go? */
619: return('B'); /* if not, break, EOT, all done */
620: if (debug) printf(" New file is %s\n",filnam);
621: return('F'); /* More files, switch state to F */
622:
623: case 'E': /* Error packet received */
624: prerrpkt(recpkt); /* Print it out and */
625: return('A'); /* abort */
626:
627: case FALSE: return(state); /* Receive failure, stay in Z */
628:
629: default: return('A'); /* Something else, "abort" */
630: }
631: }
632:
633:
634: /*
635: * s b r e a k
636: *
637: * Send Break (EOT)
638: */
639:
640: char sbreak()
641: {
642: int num, len; /* Packet number, length */
643: if (numtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
644:
645: spack('B',n,0,packet); /* Send a B packet */
646: switch (rpack(&len,&num,recpkt)) /* What was the reply? */
647: {
648: case 'N': /* NAK, just stay in this state, */
649: num = (--num<0 ? 63:num); /* unless NAK for previous packet, */
650: if (n != num) /* which is just like an ACK for */
651: return(state); /* this packet so fall thru to... */
652:
653: case 'Y': /* ACK */
654: if (n != num) return(state); /* If wrong ACK, fail */
655: numtry = 0; /* Reset try counter */
656: n = (n+1)%64; /* and bump packet count */
657: return('C'); /* Switch state to Complete */
658:
659: case 'E': /* Error packet received */
660: prerrpkt(recpkt); /* Print it out and */
661: return('A'); /* abort */
662:
663: case FALSE: return(state); /* Receive failure, stay in B */
664:
665: default: return ('A'); /* Other, "abort" */
666: }
667: }
668:
669:
670: /*
671: * r e c s w
672: *
673: * This is the state table switcher for receiving files.
674: */
675:
676: recsw()
677: {
678: char rinit(), rfile(), rdata(); /* Use these procedures */
679:
680: state = 'R'; /* Receive-Init is the start state */
681: n = 0; /* Initialize message number */
682: numtry = 0; /* Say no tries yet */
683:
684: while(TRUE)
685: {
686: if (debug) printf(" recsw state: %c\n",state);
687: switch(state) /* Do until done */
688: {
689: case 'R': state = rinit(); break; /* Receive-Init */
690: case 'F': state = rfile(); break; /* Receive-File */
691: case 'D': state = rdata(); break; /* Receive-Data */
692: case 'C': return(TRUE); /* Complete state */
693: case 'A': return(FALSE); /* "Abort" state */
694: }
695: }
696: }
697:
698:
699: /*
700: * r i n i t
701: *
702: * Receive Initialization
703: */
704:
705: char rinit()
706: {
707: int len, num; /* Packet length, number */
708:
709: if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
710:
711: switch(rpack(&len,&num,packet)) /* Get a packet */
712: {
713: case 'S': /* Send-Init */
714: rpar(packet); /* Get the other side's init data */
715: spar(packet); /* Fill up packet with my init info */
716: spack('Y',n,6,packet); /* ACK with my parameters */
717: oldtry = numtry; /* Save old try count */
718: numtry = 0; /* Start a new counter */
719: n = (n+1)%64; /* Bump packet number, mod 64 */
720: return('F'); /* Enter File-Receive state */
721:
722: case 'E': /* Error packet received */
723: prerrpkt(recpkt); /* Print it out and */
724: return('A'); /* abort */
725:
726: case FALSE: /* Didn't get packet */
727: spack('N',n,0,0); /* Return a NAK */
728: return(state); /* Keep trying */
729:
730: default: return('A'); /* Some other packet type, "abort" */
731: }
732: }
733:
734:
735: /*
736: * r f i l e
737: *
738: * Receive File Header
739: */
740:
741: char rfile()
742: {
743: int num, len; /* Packet number, length */
744: char filnam1[50]; /* Holds the converted file name */
745:
746: if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */
747:
748: switch(rpack(&len,&num,packet)) /* Get a packet */
749: {
750: case 'S': /* Send-Init, maybe our ACK lost */
751: if (oldtry++ > MAXTRY) return('A'); /* If too many tries abort */
752: if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
753: { /* Yes, ACK it again with */
754: spar(packet); /* our Send-Init parameters */
755: spack('Y',num,6,packet);
756: numtry = 0; /* Reset try counter */
757: return(state); /* Stay in this state */
758: }
759: else return('A'); /* Not previous packet, "abort" */
760:
761: case 'Z': /* End-Of-File */
762: if (oldtry++ > MAXTRY) return('A');
763: if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
764: { /* Yes, ACK it again. */
765: spack('Y',num,0,0);
766: numtry = 0;
767: return(state); /* Stay in this state */
768: }
769: else return('A'); /* Not previous packet, "abort" */
770:
771: case 'F': /* File Header (just what we want) */
772: if (num != n) return('A'); /* The packet number must be right */
773: strcpy(filnam1, packet); /* Copy the file name */
774:
775: if (filnamcnv) /* Convert upper case to lower */
776: for (filnam=filnam1; *filnam != '\0'; filnam++)
777: if (*filnam >= 'A' && *filnam <= 'Z')
778: *filnam |= 040;
779:
780: if ((fp=fopen(filnam1,"w"))==NULL) /* Try to open a new file */
781: {
782: error("Cannot create %s",filnam1); /* Give up if can't */
783: return('A');
784: }
785: else /* OK, give message */
786: printmsg("Receiving %s as %s",packet,filnam1);
787:
788: spack('Y',n,0,0); /* Acknowledge the file header */
789: oldtry = numtry; /* Reset try counters */
790: numtry = 0; /* ... */
791: n = (n+1)%64; /* Bump packet number, mod 64 */
792: return('D'); /* Switch to Data state */
793:
794: case 'B': /* Break transmission (EOT) */
795: if (num != n) return ('A'); /* Need right packet number here */
796: spack('Y',n,0,0); /* Say OK */
797: return('C'); /* Go to complete state */
798:
799: case 'E': /* Error packet received */
800: prerrpkt(recpkt); /* Print it out and */
801: return('A'); /* abort */
802:
803: case FALSE: /* Didn't get packet */
804: spack('N',n,0,0); /* Return a NAK */
805: return(state); /* Keep trying */
806:
807: default: return ('A'); /* Some other packet, "abort" */
808: }
809: }
810:
811:
812: /*
813: * r d a t a
814: *
815: * Receive Data
816: */
817:
818: char rdata()
819: {
820: int num, len; /* Packet number, length */
821: if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */
822:
823: if (tflg)
824: sleep(1); /* Delay for Tymnet */
825:
826: switch(rpack(&len,&num,packet)) /* Get packet */
827: {
828: case 'D': /* Got Data packet */
829: if (num != n) /* Right packet? */
830: { /* No */
831: if (oldtry++ > MAXTRY)
832: return('A'); /* If too many tries, abort */
833: if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
834: { /* Previous packet again? */
835: spack('Y',num,6,packet); /* Yes, re-ACK it */
836: numtry = 0; /* Reset try counter */
837: return(state); /* Don't write out data! */
838: }
839: else
840: return('A'); /* sorry, wrong number */
841: }
842: /* Got data with right packet number */
843: bufemp(packet,len); /* Write the data to the file */
844: spack('Y',n,0,0); /* Acknowledge the packet */
845: oldtry = numtry; /* Reset the try counters */
846: numtry = 0; /* ... */
847: n = (n+1)%64; /* Bump packet number, mod 64 */
848: return('D'); /* Remain in data state */
849:
850: case 'F': /* Got a File Header */
851: if (oldtry++ > MAXTRY)
852: return('A'); /* If too many tries, "abort" */
853: if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
854: { /* It was the previous one */
855: spack('Y',num,0,0); /* ACK it again */
856: numtry = 0; /* Reset try counter */
857: return(state); /* Stay in Data state */
858: }
859: else
860: return('A'); /* Not previous packet, "abort" */
861:
862: case 'Z': /* End-Of-File */
863: if (num != n) return('A'); /* Must have right packet number */
864: spack('Y',n,0,0); /* OK, ACK it. */
865: fclose(fp); /* Close the file */
866: n = (n+1)%64; /* Bump packet number */
867: return('F'); /* Go back to Receive File state */
868:
869: case 'E': /* Error packet received */
870: prerrpkt(recpkt); /* Print it out and */
871: return('A'); /* abort */
872:
873: case FALSE: /* Didn't get packet */
874: spack('N',n,0,0); /* Return a NAK */
875: return(state); /* Keep trying */
876:
877: default:
878: return('A'); /* Some other packet, "abort" */
879:
880: }
881: }
882:
883: /*
884: * c o n n e c t
885: *
886: * Establish a virtual terminal connection with the remote host, over an
887: * assigned tty line.
888: */
889:
890: connect()
891: {
892: int pid, /* Holds process id of child */
893: connected; /* TRUE if connection open */
894: int closemode = FALSE; /* TRUE if we closed connection */
895: register char *p;
896: register int count;
897: char filetoread [25];
898: char * ofp;
899: int rcount;
900: char bel = '\07',
901: c;
902: static char *heremsg = "\r\nYes, I'm still here... ^r version\r\n";
903: struct sgttyb
904: rawmode, /* Controlling tty raw mode */
905: cookedmode; /* Controlling tty cooked mode */
906:
907: if (remote) /* Nothing to connect to in remote */
908: { /* mode, so just return */
909: printmsg("No line specified for connection.");
910: return;
911: }
912:
913: gtty(0,&cookedmode); /* Save current mode so we can */
914: gtty(0,&rawmode); /* restore it later */
915: rawmode.sg_flags |= (RAW|TANDEM);
916: rawmode.sg_flags &= ~(ECHO|CRMOD);
917: stty(0,&rawmode); /* Put tty in raw mode */
918:
919: if (logflg) {
920: if ((logfd = open ("Log", 2)) == -1) {
921: if ((logfd = creat ("Log", 0644)) == -1) {
922: printmsg ("Cannot open log file\n");
923: stty(0, &cookedmode); /* Restore tty mode */
924: return(-1);
925: }
926: }
927: if ((lseek (logfd, 0L, 2) == -1)) {
928: printmsg ("Cannot seek to end of log file\n");
929: stty(0, &cookedmode); /* Restore tty mode */
930: return (-1);
931: }
932: }
933:
934: pid = fork(); /* Start fork to get typeout from remote host */
935:
936: if (pid) /* Parent: send type-in to remote host */
937: {
938: printmsg("connected...\r");
939: connected = TRUE; /* Put us in "connect mode" */
940:
941: if (*ldata) /* initial data line to send? */
942: { write(ttyfd, ldata, strlen(ldata));
943: write(ttyfd, "\r", 1); /* ending <CR> */
944: }
945:
946: while (connected)
947: {
948: read(0,&c,1); /* Get a character */
949: if ((c&0177) == escchr) /* Check for escape character */
950: {
951: read(0,&c,1);
952: if ((c&0177) == escchr)
953: write(ttyfd,&c,1);
954: else
955: switch (c&0177)
956: {
957: case 'c': /* close connection */
958: case 'C':
959: closemode = TRUE; /* we're closing connection */
960: #if COHERENT
961: ioctl(ttyfd, TIOCHPCL); /* set hangup on modem */
962:
963: case 's': /* suspend connection */
964: case 'S':
965: #endif
966: connected = FALSE;
967: write(1,"\r\n",2);
968: break;
969:
970: case 'r': /* read a file and send it */
971: ofp = filetoread;
972: write (0, "File to send: ", strlen("File to send: "));
973: read(0, &c, 1);
974: while (c != '\r') {
975: write(0, &c, 1);
976: *ofp++ = c;
977: read(0, &c, 1);
978: }
979: *ofp++ = '\0';
980: write(0,"\r\n",2);
981: fp = fopen (filetoread,"r");
982:
983: if (fp == NULL) {
984: error("Cannot open file %s\r\n",filetoread);
985: } else {
986: while((c = getc(fp)) != EOF) {
987: if (c == '\n')
988: c = '\r';
989: write(ttyfd, &c, 1);
990: }
991: fclose(fp);
992: }
993: fp = NULL;
994: break;
995:
996: case 'h':
997: case 'H':
998: write(1,heremsg, strlen(heremsg));
999: break;
1000:
1001: default:
1002: write(1,"\7",1); /* bell */
1003: break;
1004: }
1005: }
1006: else
1007: { /* If not escape charater, */
1008: write(ttyfd,&c,1); /* write it out */
1009: c = NUL; /* Nullify it (why?) */
1010: }
1011: }
1012: kill(pid,9); /* Done, kill the child */
1013: wait(0); /* and bury him */
1014: stty(0,&cookedmode); /* Restore tty mode */
1015: printmsg(closemode ? "disconnected." : "suspended.");
1016: return; /* Done */
1017: }
1018: else /* Child does the reading from the remote host */
1019: {
1020: for(;;) /* Do this forever */
1021: {
1022: #if MULTIREAD
1023: count = rcount = read(ttyfd, inbuff, 128); /* read chars */
1024: #else
1025: count = read(ttyfd, inbuff, 1); /* read one char */
1026: #endif
1027: #if COHERENT
1028: for (p = inbuff; count--; p++)
1029: *p &= 0177; /* strip parity for IBM display */
1030: #endif
1031: #if MULTIREAD
1032: write(1, inbuff, rcount); /* write chars */
1033: if (logflg) {
1034: p = inbuff;
1035: for (count = rcount; count--; p++) {
1036: if (*p != '\r')
1037: write (logfd, p, 1);
1038: }
1039: }
1040: #else
1041: write(1, inbuff, 1); /* write char */
1042:
1043: if (logflg)
1044: if (inbuff [0] != '\r');
1045: write (logfd, inbuff, 1); /* log chars */
1046: #endif
1047: }
1048: }
1049: }
1050:
1051: /*
1052: * KERMIT utilities.
1053: */
1054:
1055: clkint() /* Timer interrupt handler */
1056: {
1057: longjmp(env, TRUE); /* Tell rpack to give up */
1058: }
1059:
1060:
1061: /*
1062: * s p a c k
1063: *
1064: * Send a Packet
1065: */
1066:
1067: spack(type,num,len,data)
1068: char type, *data;
1069: int num, len;
1070: {
1071: int i; /* Character loop counter */
1072: char chksum, buffer[100]; /* Checksum, packet buffer */
1073: register char *bufp; /* Buffer pointer */
1074:
1075: if (debug>1) /* Display outgoing packet */
1076: {
1077: if (data != NULL)
1078: data[len] = '\0'; /* Null-terminate data to print it */
1079: printf(" spack type: %c\n",type);
1080: printf(" num: %d\n",num);
1081: printf(" len: %d\n",len);
1082: if (data != NULL)
1083: printf(" data: \"%s\"\n",data);
1084: }
1085:
1086: bufp = buffer; /* Set up buffer pointer */
1087: for (i=1; i<=pad; i++) write(ttyfd,&padchar,1); /* Issue any padding */
1088:
1089: *bufp++ = SOH; /* Packet marker, ASCII 1 (SOH) */
1090: *bufp++ = tochar(len+3); /* Send the character count */
1091: chksum = tochar(len+3); /* Initialize the checksum */
1092: *bufp++ = tochar(num); /* Packet number */
1093: chksum += tochar(num); /* Update checksum */
1094: *bufp++ = type; /* Packet type */
1095: chksum += type; /* Update checksum */
1096:
1097: for (i=0; i<len; i++) /* Loop for all data characters */
1098: {
1099: *bufp++ = data[i]; /* Get a character */
1100: chksum += data[i]; /* Update checksum */
1101: }
1102: chksum = (((chksum&0300) >> 6)+chksum)&077; /* Compute final checksum */
1103: *bufp++ = tochar(chksum); /* Put it in the packet */
1104: *bufp = eol; /* Extra-packet line terminator */
1105: write(ttyfd, buffer,bufp-buffer+1); /* Send the packet */
1106: }
1107:
1108: /*
1109: * r p a c k
1110: *
1111: * Read a Packet
1112: */
1113:
1114: rpack(len,num,data)
1115: int *len, *num; /* Packet length, number */
1116: char *data; /* Packet data */
1117: {
1118: int i, done; /* Data character number, loop exit */
1119: char t, /* Current input character */
1120: type, /* Packet type */
1121: cchksum, /* Our (computed) checksum */
1122: rchksum; /* Checksum received from other host */
1123:
1124: #if UNIXL /* TOPS-20 can't handle timeouts... */
1125: if (setjmp(env)) return FALSE; /* Timed out, fail */
1126: signal(SIGALRM,clkint); /* Setup the timeout */
1127: if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
1128: alarm(timint);
1129: #endif /* UNIXL */
1130:
1131: while (t != SOH) /* Wait for packet header */
1132: {
1133: read(ttyfd,&t,1);
1134: t &= 0177; /* Handle parity */
1135: }
1136:
1137: done = FALSE; /* Got SOH, init loop */
1138: while (!done) /* Loop to get a packet */
1139: {
1140: read(ttyfd,&t,1); /* Get character */
1141: if (!image) t &= 0177; /* Handle parity */
1142: if (t == SOH) continue; /* Resynchronize if SOH */
1143: cchksum = t; /* Start the checksum */
1144: *len = unchar(t)-3; /* Character count */
1145:
1146: read(ttyfd,&t,1); /* Get character */
1147: if (!image) t &= 0177; /* Handle parity */
1148: if (t == SOH) continue; /* Resynchronize if SOH */
1149: cchksum = cchksum + t; /* Update checksum */
1150: *num = unchar(t); /* Packet number */
1151:
1152: read(ttyfd,&t,1); /* Get character */
1153: if (!image) t &= 0177; /* Handle parity */
1154: if (t == SOH) continue; /* Resynchronize if SOH */
1155: cchksum = cchksum + t; /* Update checksum */
1156: type = t; /* Packet type */
1157:
1158: for (i=0; i<*len; i++) /* The data itself, if any */
1159: { /* Loop for character count */
1160: read(ttyfd,&t,1); /* Get character */
1161: if (!image) t &= 0177; /* Handle parity */
1162: if (t == SOH) continue; /* Resynch if SOH */
1163: cchksum = cchksum + t; /* Update checksum */
1164: data[i] = t; /* Put it in the data buffer */
1165: }
1166: data[*len] = 0; /* Mark the end of the data */
1167:
1168: read(ttyfd,&t,1); /* Get last character (checksum) */
1169: rchksum = unchar(t); /* Convert to numeric */
1170: read(ttyfd,&t,1); /* get EOL character and toss it */
1171: if (!image) t &= 0177; /* Handle parity */
1172: if (t == SOH) continue; /* Resynchronize if SOH */
1173: done = TRUE; /* Got checksum, done */
1174: }
1175:
1176: #if UNIXL
1177: alarm(0); /* Disable the timer interrupt */
1178: #endif
1179:
1180: if (debug>1) /* Display incoming packet */
1181: {
1182: if (data != NULL)
1183: data[*len] = '\0'; /* Null-terminate data to print it */
1184: printf(" rpack type: %c\n",type);
1185: printf(" num: %d\n",*num);
1186: printf(" len: %d\n",*len);
1187: if (data != NULL)
1188: printf(" data: \"%s\"\n",data);
1189: }
1190: /* Fold in bits 7,8 to compute */
1191: cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */
1192:
1193: if (cchksum != rchksum) return(FALSE);
1194:
1195: return(type); /* All OK, return packet type */
1196: }
1197:
1198:
1199: /*
1200: * b u f i l l
1201: *
1202: * Get a bufferful of data from the file that's being sent.
1203: * Only control-quoting is done; 8-bit & repeat count prefixes are
1204: * not handled.
1205: */
1206:
1207: bufill(buffer)
1208: char buffer[]; /* Buffer */
1209: {
1210: int i, /* Loop index */
1211: t; /* Char read from file */
1212: char t7; /* 7-bit version of above */
1213:
1214: i = 0; /* Init data buffer pointer */
1215: while((t = getc(fp)) != EOF) /* Get the next character */
1216: {
1217: t7 = t & 0177; /* Get low order 7 bits */
1218:
1219: if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */
1220: { /* special handling? */
1221: if (t=='\n' && !image)
1222: { /* Do LF->CRLF mapping if !image */
1223: buffer[i++] = quote;
1224: buffer[i++] = ctl('\r');
1225: }
1226: buffer[i++] = quote; /* Quote the character */
1227: if (t7 != quote)
1228: {
1229: t = ctl(t); /* and uncontrolify */
1230: t7 = ctl(t7);
1231: }
1232: }
1233: if (image)
1234: buffer[i++] = t; /* Deposit the character itself */
1235: else
1236: buffer[i++] = t7;
1237:
1238: if (i >= spsiz-8) return(i); /* Check length */
1239: }
1240: if (i==0) return(EOF); /* Wind up here only on EOF */
1241: return(i); /* Handle partial buffer */
1242: }
1243:
1244:
1245: /*
1246: * b u f e m p
1247: *
1248: * Put data from an incoming packet into a file.
1249: */
1250:
1251: bufemp(buffer,len)
1252: char buffer[]; /* Buffer */
1253: int len; /* Length */
1254: {
1255: int i; /* Counter */
1256: char t; /* Character holder */
1257:
1258: for (i=0; i<len; i++) /* Loop thru the data field */
1259: {
1260: t = buffer[i]; /* Get character */
1261: if (t == MYQUOTE) /* Control quote? */
1262: { /* Yes */
1263: t = buffer[++i]; /* Get the quoted character */
1264: if ((t & 0177) != MYQUOTE) /* Low order bits match quote char? */
1265: t = ctl(t); /* No, uncontrollify it */
1266: }
1267: if (t==CR && !image) /* Don't pass CR if in image mode */
1268: continue;
1269:
1270: putc(t,fp);
1271: }
1272: }
1273:
1274:
1275: /*
1276: * g n x t f l
1277: *
1278: * Get next file in a file group
1279: */
1280:
1281: gnxtfl()
1282: {
1283: if (debug) printf(" gnxtfl: filelist = \"%s\"\n",*filelist);
1284: filnam = *(filelist++);
1285: if (filecount-- == 0) return FALSE; /* If no more, fail */
1286: else return TRUE; /* else succeed */
1287: }
1288:
1289:
1290: /*
1291: * s p a r
1292: *
1293: * Fill the data array with my send-init parameters
1294: *
1295: */
1296:
1297: spar(data)
1298: char data[];
1299: {
1300: data[0] = tochar(MAXPACKSIZ); /* Biggest packet I can receive */
1301: data[1] = tochar(MYTIME); /* When I want to be timed out */
1302: data[2] = tochar(MYPAD); /* How much padding I need */
1303: data[3] = ctl(MYPCHAR); /* Padding character I want */
1304: data[4] = tochar(MYEOL); /* End-Of-Line character I want */
1305: data[5] = MYQUOTE; /* Control-Quote character I send */
1306: }
1307:
1308:
1309: /* r p a r
1310: *
1311: * Get the other host's send-init parameters
1312: *
1313: */
1314:
1315: rpar(data)
1316: char data[];
1317: {
1318: spsiz = unchar(data[0]); /* Maximum send packet size */
1319: timint = unchar(data[1]); /* When I should time out */
1320: pad = unchar(data[2]); /* Number of pads to send */
1321: padchar = ctl(data[3]); /* Padding character to send */
1322: eol = unchar(data[4]); /* EOL character I must send */
1323: quote = data[5]; /* Incoming data quote character */
1324: }
1325:
1326:
1327: /*
1328: * f l u s h i n p u t
1329: *
1330: * Dump all pending input to clear stacked up NACK's.
1331: * (Implemented only for Berkeley Unix at this time).
1332: */
1333:
1334: #if UNIXL&(~NO_FIONREAD)
1335: flushinput()
1336: {
1337: #if COHERENT
1338: int count; /* Number of bytes ready to read */
1339: #else
1340: long int count; /* Number of bytes ready to read */
1341: #endif
1342: long int i; /* Number of bytes to read in loop */
1343:
1344: #if COHERENT
1345: ioctl(ttyfd, TIOCQUERY, &count); /* See how many bytes pending read */
1346: #else
1347: ioctl(ttyfd, FIONREAD, &count); /* See how many bytes pending read */
1348: #endif
1349:
1350: if (!count) return; /* If zero, then no input to flush */
1351:
1352: while (count) /* Loop till all are flushed */
1353: {
1354: i = (count<sizeof(recpkt)) ? /* Read min of count and size of */
1355: count : sizeof(recpkt); /* the read buffer */
1356: read(ttyfd, recpkt, i); /* Read a bunch */
1357: count -= i; /* Subtract from amount to read */
1358: }
1359: }
1360: #else
1361: flushinput() /* Null version */
1362: {}
1363: #endif /* UNIXL&(~FIONREAD) */
1364:
1365:
1366: /*
1367: * Kermit printing routines:
1368: *
1369: * usage - print command line options showing proper syntax
1370: * printmsg - like printf with "Kermit: " prepended
1371: * error - like printmsg if local kermit; sends a error packet if remote
1372: * prerrpkt - print contents of error packet received from remote host
1373: */
1374:
1375: /*
1376: * u s a g e
1377: *
1378: * Print summary of usage info and quit
1379: */
1380:
1381: usage()
1382: {
1383: #if UNIXL
1384: printf("Usage: kermit c|C [lbe line baud esc.char] (connect mode)\n");
1385: printf("or: kermit s[diflbht line baud] file ... (send mode)\n");
1386: printf("or: kermit r[diflbht line baud] (receive mode)\n");
1387: #else
1388: printf("Usage: kermit c|C [le line esc.char] (connect mode)\n");
1389: printf("or: kermit s[diflht line] file ... (send mode)\n");
1390: printf("or: kermit r[diflht line] (receive mode)\n");
1391: #endif
1392: exit(1);
1393: }
1394:
1395: /*
1396: * p r i n t m s g
1397: *
1398: * Print message on standard output if not remote.
1399: */
1400:
1401: /*VARARGS1*/
1402: printmsg(fmt, a1, a2, a3, a4, a5)
1403: char *fmt;
1404: {
1405: if (!remote)
1406: {
1407: printf("kermit: ");
1408: printf(fmt,a1,a2,a3,a4,a5);
1409: printf("\n");
1410: fflush(stdout); /* force output (UTS needs it) */
1411: }
1412: }
1413:
1414: /*
1415: * e r r o r
1416: *
1417: * Print error message.
1418: *
1419: * If local, print error message with printmsg.
1420: * If remote, send an error packet with the message.
1421: */
1422:
1423: /*VARARGS1*/
1424: error(fmt, a1, a2, a3, a4, a5)
1425: char *fmt;
1426: {
1427: char msg[80];
1428: int len;
1429:
1430: if (remote)
1431: {
1432: sprintf(msg,fmt,a1,a2,a3,a4,a5); /* Make it a string */
1433: len = strlen(msg);
1434: spack('E',n,len,msg); /* Send the error packet */
1435: }
1436: else
1437: printmsg(fmt, a1, a2, a3, a4, a5);
1438:
1439: return;
1440: }
1441:
1442: /*
1443: * p r e r r p k t
1444: *
1445: * Print contents of error packet received from remote host.
1446: */
1447: prerrpkt(msg)
1448: char *msg;
1449: {
1450: printf("kermit: aborting with following error from remote host:\n%s\n",
1451: msg);
1452: }
1453:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.