1 /* G U N I X I O  --  UNIX i/o module for gkermit */
2 
3 /*
4   UNIX i/o functions for gkermit.
5 
6   Author:
7     Frank da Cruz
8     The Kermit Project
9     Columbia University
10     612 West 115th Street
11     New York NY 10025-7799  USA
12     http://www.columbia.edu/kermit/
13     kermit@columbia.edu
14 
15   Copyright (C) 1999,
16   The Trustees of Columbia University in the City of New York.
17 
18   This program is free software; you can redistribute it and/or modify
19   it under the terms of the GNU General Public License as published by
20   the Free Software Foundation; either version 2 of the License, or
21   (at your option) any later version.
22 
23   This program is distributed in the hope that it will be useful,
24   but WITHOUT ANY WARRANTY; without even the implied warranty of
25   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26   GNU General Public License for more details.
27 
28   You should have received a copy of the GNU General Public License
29   along with this program; if not, write to the Free Software
30   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
31 */
32 
33 /*
34  CONTENTS...
35 
36  Console Output:
37    tmsg   - Type a message
38    tmsgl  - Type a line
39 
40  Communication Device:
41    ttopen - Open
42    ttpkt  - Put in packet mode
43    ttres  - Restore normal mode
44    ttinl  - Input a raw packet
45    ttol   - Send a packet
46    ttchk  - Check if input ready
47    ttflui - Flush input buffer
48 
49  File:
50    zchki  - See if file can be opened for input
51    zopeni - Open input file
52    zopeno - Open output file
53    zclosi - Close input file
54    zcloso - Close output file
55    zrtol  - Remote-to-Local filename conversion
56    zltor  - Local-to-Remote filename conversion
57    zgetc  - Get character from input file
58 */
59 
60 #include <stdio.h>			/* Standard input/output */
61 
62 #ifdef POSIX
63 #include <termios.h>			/* Terminal modes */
64 #else
65 #ifdef SYSV
66 #include <termio.h>
67 #else
68 #include <sgtty.h>
69 #endif /* SYSV */
70 #endif /* POSIX */
71 
72 #include <ctype.h>			/* Character types */
73 #include <sys/types.h>			/* Needed e.g. by <stat.h> */
74 #include <signal.h>			/* Interrupts */
75 #include <setjmp.h>			/* Longjumps */
76 #include <sys/stat.h>			/* File exist, file size */
77 #include <errno.h>			/* Error symbols */
78 #include "gkermit.h"			/* gkermit definitions */
79 
80 /* All versions of HP-UX need Xon/Xoff */
81 
82 #ifdef hpux				/* HP-UX Pre-7.00 */
83 #ifndef __hpux
84 #define __hpux
85 #endif /* __hpux */
86 #endif /* hpux */
87 
88 #ifdef __hpux				/* HP-UX 7.00 and later */
89 #ifndef SETXONXOFF
90 #define SETXONXOFF
91 #endif /* SETXONXOFF */
92 #endif /*  __hpux */
93 
94 #ifdef NOXONXOFF			/* -DNOXONXOFF overrides */
95 #ifdef SETXONXOFF
96 #undef SETXONXOFF
97 #endif /* SETXONXOFF */
98 #endif /* NOXONXOFF */
99 
100 #ifndef TINBUFSIZ			/* read() inpbut buffer */
101 #ifdef USE_GETCHAR
102 #define TINBUFSIZ 0			/* getchar() has its own */
103 #else
104 #ifdef SMALL
105 #define TINBUFSIZ 240
106 #else
107 #define TINBUFSIZ 4080
108 #endif /* SMALL */
109 #endif /* USE_GETCHAR */
110 #endif /* TINBUFSIZ */
111 
112 #ifndef DUMBIO
113 #ifndef USE_GETCHAR
114 #ifndef NOFCNTL_H			/* For nonblocking buffered read() */
115 #ifdef SYS_FCNTL_H
116 #include <sys/fcntl.h>
117 #else
118 #include <fcntl.h>
119 #ifndef O_NDELAY
120 #ifdef O_NONBLOCK
121 #define O_NDELAY O_NONBLOCK
122 #endif /* O_NONBLOCK */
123 #endif /* O_NDELAY */
124 #endif /* SYS_FCNTL_H */
125 #endif /* NOFCNTL_H */
126 #endif /* USE_GETCHAR */
127 #endif /* DUMBIO */
128 
129 #ifdef O_NDELAY				/* For System V R3 and earlier */
130 #ifndef EWOULDBLOCK
131 #ifdef EAGAIN
132 #define EWOULDBLOCK EAGAIN
133 #endif /* EAGAIN */
134 #endif /* EWOULDBLOCK */
135 #endif /* O_NDELAY */
136 
137 #ifndef DUMBIO				/* To force single-char read/write */
138 #ifndef USE_GETCHAR
139 #ifndef O_NDELAY
140 #define DUMBIO
141 #endif /* O_NDELAY */
142 #endif /* USE_GETCHAR */
143 #endif /* DUMBIO */
144 
145 /* Header file deficiencies section... */
146 
147 #ifndef R_OK
148 #define R_OK 4
149 #endif /* R_OK */
150 
151 #ifndef W_OK
152 #define W_OK 2
153 #endif /* W_OK */
154 
155 #ifndef _IFMT
156 #ifdef S_IFMT
157 #define _IFMT S_IFMT
158 #else
159 #define _IFMT 0170000
160 #endif /* S_IFMT */
161 #endif /* _IFMT */
162 
163 #ifndef S_ISREG
164 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
165 #endif /* S_ISREG */
166 
167 /* External variables */
168 
169 extern int literal;			/* Literal filenames */
170 extern int quiet;			/* No messages */
171 extern int keep;			/* Keep incomplete files */
172 extern int streamok;			/* OK to offer streaming */
173 extern int nomodes;			/* Don't get/set tty modes */
174 extern int xonxoff;			/* Set Xon/Xoff */
175 extern int noxonxoff;			/* Don't set Xon/Xoff */
176 extern FILE * db;			/* Debug log file */
177 
178 /* Exported variables */
179 
180 FILE *ifp, *ofp;			/* Input and output file pointers */
181 char zinbuf[MAXRECORD+1];		/* File input buffer */
182 int zincnt = 0;				/* count */
183 char * zinptr = NULL;			/* and pointer. */
184 
185 /* Private global variables */
186 
187 static int havemodes = 0;		/* Have obtained terminal modes */
188 static int ttflags = -1;		/* Terminal flags */
189 static int nonblock = 0;		/* Nonblocking i/o enabled */
190 static char tinbuf[TINBUFSIZ+16];	/* Communications input buffer */
191 static char * tinptr = NULL;		/* Pointer to current item */
192 static int tincnt = 0;			/* Buffer count */
193 static int tlast = 0;			/* Last item in buffer */
194 static int xparity = 0;			/* Parity in use, 0 = none */
195 static int raw = 0;			/* Terminal rawmode flag */
196 static char work[MAXPATHLEN+1];		/* Filename conversion buffer */
197 
198 /* Terminal mode structs */
199 
200 #ifdef POSIX				/* POSIX */
201 static struct termios ttold, ttraw;
202 #else
203 #ifdef SYSV				/* System V */
204 static struct termio ttold = {0};
205 static struct termio ttraw = {0};
206 #else
207 #ifdef BSD				/* 4.2 BSD or UNIX V7 */
208 static struct sgttyb ttold, ttraw;
209 #endif /* BSD */
210 #endif /* SYSV */
211 #endif /* POSIX */
212 
213 static jmp_buf jbuf;			/* Longjump buffer for timeouts */
214 
215 /* Functions... */
216 
217 SIGTYP
doexit(x)218 doexit(x) int x; {			/* Exit routine */
219     if (x)				/* If failure */
220       ttflui();				/* flush pending junk we won't read */
221     ttres();				/* Reset the communication device */
222 #ifdef F_SETFL
223     if (ttflags > -1)			/* Restore its flags */
224       fcntl(0,F_SETFL,ttflags);
225 #endif /* F_SETFL */
226     if (debug) {
227 	fprintf(db,"exit %d\n",x);
228 	fclose(db);
229     }
230     exit(x);
231 }
232 
233 VOID
sysinit()234 sysinit() {				/* To be run first thing */
235 #ifdef F_SETFL
236     ttflags = fcntl(0,F_GETFL,0);	/* Get and save stdin flags */
237 #endif /* F_SETFL */
238 #ifdef SIGINT
239     signal(SIGINT,SIG_IGN);		/* Ignore interrupts */
240 #endif /* SIGINT */
241 #ifdef SIGTSTP
242     signal(SIGTSTP,SIG_IGN);
243 #endif /* SIGTSTP */
244 #ifdef SIGQUIT
245     signal(SIGQUIT,SIG_IGN);
246 #endif /* SIGQUIT */
247     signal(SIGHUP,doexit);		/* Go here on hangup */
248 }
249 
250 /* Console Functions */
251 
252 #ifdef COMMENT				/* (not used) */
253 VOID
tmsg(s)254 tmsg(s) char *s; {			/* tmsg() */
255     if (!quiet)
256       fprintf(stderr,"%s",s);		/* Type message on the screen. */
257 }
258 #endif /* COMMENT */
259 
260 VOID
tmsgl(s)261 tmsgl(s) char *s; {			/* tmsgl() */
262     if (!quiet) {
263 	if (raw)
264 	  fprintf(stderr,"%s\r\n",s);	/* Type message with CRLF */
265 	else
266 	  fprintf(stderr,"%s\n",s);
267     }
268 }
269 
270 /* Debugging functions */
271 
272 VOID
logerr(s)273 logerr(s) char * s; {			/* Log text and errno */
274     if (!s) s = "";
275     if (!debug) return;
276     if (db) fprintf(db,"%s: errno = %d\n",s,errno);
277 }
278 
279 /* Parity function */
280 
281 char
282 #ifdef __STDC__
dopar(char ch)283 dopar(char ch)
284 #else
285 dopar(ch) char ch;
286 #endif /* __STDC__ */
287 {					/* Do parity */
288     unsigned int a;
289     if (!xparity) return(ch); else ch &= 0177;
290     switch (xparity) {
291       case 'm':  return(ch | 128);	/* Mark */
292       case 's':  return(ch & 127);	/* Space */
293       case 'o':				/* Odd (fall thru) */
294       case 'e':				/* Even */
295 	a = (ch & 15) ^ ((ch >> 4) & 15);
296 	a = (a & 3) ^ ((a >> 2) & 3);
297 	a = (a & 1) ^ ((a >> 1) & 1);
298 	if (xparity == 'o') a = 1 - a;	/* Switch sense for odd */
299 	return(ch | (a << 7));
300       default: return(ch);
301     }
302 }
303 
304 /* Communication functions */
305 
306 int
ttopen(ttname)307 ttopen(ttname) char *ttname; {		/* "Open" the communication device */
308     if (debug) {			/* Vital statistics for debug log */
309 #ifdef __STDC__
310 	fprintf(db,"ttopen __STDC__\n");
311 #endif /* __STDC__ */
312 #ifdef SIG_V
313 	fprintf(db,"ttopen SIG_V\n");
314 #else
315 #ifdef SIG_I
316 	fprintf(db,"ttopen SIG_I\n");
317 #endif /* SIG_I */
318 #endif /* SIG_V */
319 #ifdef USE_GETCHAR
320 	fprintf(db,"ttopen getchar/putchar\n");
321 #ifdef BUFSIZ
322 	fprintf(db,"ttopen BUFSIZ = %d\n", BUFSIZ);
323 #endif /* BUFSIZ */
324 #else
325 #ifdef DUMBIO
326 	fprintf(db,"ttopen single-byte read/write\n");
327 #else
328 	fprintf(db,"ttopen nonblocking read/write\n");
329 #endif /* DUMBIO */
330 #endif /* USE_GETCHAR */
331 	fprintf(db,"ttopen TINBUFSIZ = %d\n", TINBUFSIZ);
332 #ifdef __hpux
333 	fprintf(db,"ttopen __hpux\n");
334 #endif /* __hpux */
335 #ifdef pdp11
336 	fprintf(db,"ttopen pdp11\n");
337 #endif /* pdp11 */
338 #ifdef SETXONXOFF
339 	fprintf(db,"ttopen SETXONXOFF\n");
340 #endif /* SETXONXOFF */
341 	fprintf(db,"ttopen xonxoff = %d\n",xonxoff);
342 	fprintf(db,"ttopen noxonxoff = %d\n",noxonxoff);
343 	fprintf(db,"ttopen ttflags %d\n",ttflags);
344 	fprintf(db,"ttopen nomodes %d\n",nomodes);
345     }
346     if (nomodes) {			/* If external protocol */
347 #ifdef SIGINT				/* exit on interrupts */
348 	signal(SIGINT,doexit);
349 #endif /* SIGINT */
350 #ifdef SIGTSTP
351 	signal(SIGTSTP,doexit);
352 #endif /* SIGTSTP */
353 #ifdef SIGQUIT
354 	signal(SIGQUIT,doexit);
355 #endif /* SIGQUIT */
356 	return(0);
357     }
358 
359 #ifndef DUMBIO
360 #ifndef USE_GETCHAR
361 #ifdef O_NDELAY
362 #ifdef F_SETFL
363     if (ttflags != -1) {		/* Set nonbocking i/o on stdin */
364 	errno = 0;
365 	if (fcntl(0, F_SETFL,ttflags|O_NDELAY) == -1)
366 	  logerr("ttopen fcntl(0,F_SETFL,O_NDELAY)");
367 	else
368 	  nonblock = 1;
369     }
370 #endif /* F_SETFL */
371 #endif /* O_NDELAY */
372 #endif /* USE_GETCHAR */
373 #endif /* DUMBIO */
374     if (!nonblock)			/* No streaming without */
375       streamok = -1;			/* nonblocking reads */
376 
377     if (debug)
378       fprintf(db,"ttopen nonblock = %d\n", nonblock);
379 #ifdef POSIX
380     tcgetattr(0,&ttold);		/* Get stdin device attributes */
381     tcgetattr(0,&ttraw);
382 #else
383 #ifdef SYSV
384     ioctl(0,TCGETA,&ttold);
385     ioctl(0,TCGETA,&ttraw);
386 #else
387 #ifdef BSD
388     gtty(0,&ttold);
389     gtty(0,&ttraw);
390 #endif /* BSD */
391 #endif /* SYSV */
392 #endif /* POSIX */
393     havemodes++;
394     return(0);
395 }
396 
397 int
ttpkt(parity)398 ttpkt(parity) int parity; {		/* Put comm device in packet mode */
399 #ifdef BSD
400     int x;
401 #endif /* BSD */
402     xparity = parity;			/* Make local copy of parity */
403     if (nomodes)
404       return(0);
405 
406 #ifdef SVORPOSIX			/* System V or POSIX section... */
407     ttraw.c_iflag |= IGNPAR;
408     ttraw.c_lflag &= ~(ICANON|ECHO);
409     ttraw.c_lflag &= ~ISIG;
410     ttraw.c_lflag |= NOFLSH;
411 #ifdef SETXONXOFF
412     if (!noxonxoff) {
413 	ttraw.c_iflag |= (IXON|IXOFF);
414 	if (debug) fprintf(db,"ttpkt SVORPOSIX Xon/Xoff\n");
415     }
416 #else
417     if (xonxoff) {
418 	if (debug) fprintf(db,"ttpkt SVORPOSIX Xon/Xoff\n");
419 	ttraw.c_iflag |= (IXON|IXOFF);
420     }
421 #endif /* SETXONXOFF */
422 #ifdef IEXTEN
423     ttraw.c_lflag &= ~IEXTEN;
424 #endif /* IEXTEN */
425 #ifdef POSIX
426     ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|INPCK|ISTRIP);
427 #else
428     ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|INPCK|ISTRIP|IXANY);
429 #endif /* POSIX */
430     ttraw.c_oflag &= ~OPOST;
431     ttraw.c_cflag &= ~(CSIZE);
432     ttraw.c_cflag |= (CS8|CREAD|HUPCL);
433     ttraw.c_cflag &= ~(PARENB);
434 
435 #ifndef VEOF
436     ttraw.c_cc[4] = 1;
437 #else
438 #ifdef VMIN
439     ttraw.c_cc[VMIN] = 1;
440 #endif /* VMIN */
441 #endif /* VEOF */
442 
443 #ifndef VEOL
444     ttraw.c_cc[5] = 0;
445 #else
446 #ifdef VTIME
447     ttraw.c_cc[VTIME] = 0;
448 #endif /* VTIME */
449 #endif /* VEOL */
450 
451 #ifdef VINTR
452     ttraw.c_cc[VINTR] = 0;
453 #endif /* VINTR */
454 
455 #ifdef POSIX
456     if (tcsetattr(0,TCSADRAIN,&ttraw) < 0)
457       return(-1);
458 #else
459     if (ioctl(0,TCSETAW,&ttraw) < 0)
460       return(-1);
461 #ifdef SYSV
462 #endif /* SYSV */
463 #endif /* POSIX */
464 
465 #else  /* Not SVORPOSIX */
466 
467 #ifdef BSD
468     ttraw.sg_flags |= RAW;		/* BSD/V7 raw (binary) mode */
469 #ifdef SETXONXOFF
470     if (!noxonxoff) {
471 	ttraw.sg_flags |= TANDEM;
472 	if (debug) fprintf(db,"ttpkt BSD Xon/Xoff\n");
473     }
474 #else
475     if (xonxoff) {
476 	ttraw.sg_flags |= TANDEM;
477 	if (debug) fprintf(db,"ttpkt BSD Xon/Xoff\n");
478     }
479 #endif /* SETXONXOFF */
480     ttraw.sg_flags &= ~(ECHO|CRMOD);    /* No echo, etc */
481     if (stty(0,&ttraw) < 0) return(-1); /* Set modes */
482 #else
483     system("stty raw -echo");
484 #endif /* BSD */
485 #endif /* SVORPOSIX */
486     raw = 1;				/* Flag we're now in raw mode */
487     return(0);
488 }
489 
490 int
ttres()491 ttres() {				/* Reset terminal */
492     int x = 0;
493     if (havemodes) {			/* Restore old modes */
494 #ifdef POSIX
495 	x = tcsetattr(0,TCSADRAIN,&ttold);
496 #else
497 #ifdef SYSV
498 	sleep(1);			/* Let output finish */
499 	x = ioctl(0,TCSETAW,&ttold);
500 #else
501 #ifdef BSD
502 	sleep(1);			/* Let output finish */
503 	x = stty(0,&ttold);
504 #else
505 	x = system("stty -raw echo");
506 #endif /* BSD */
507 #endif /* SYSV */
508 #endif /* POSIX */
509     }
510     write(1,"\015\012",2);
511     raw = 0;
512     return(x);
513 }
514 
515 int
ttchk()516 ttchk() {				/* Check if input ready */
517     int x = 0;
518     if (nonblock) {			/* Try to read */
519 	errno = 0;
520 	x = read(0,&tinbuf[tlast],TINBUFSIZ-tlast);
521 #ifdef EXTRADEBUG
522 	fprintf(db,"ttchk read %d errno = %d\n",x,errno);
523 #endif /* EXTRADEBUG */
524 #ifdef EWOULDBLOCK
525 	if (x < 0 && errno == EWOULDBLOCK) /* Nothing to read */
526 	  x = 0;
527 #endif /* EWOULDBLOCK */
528 	if (x < 0)			/* Fatal i/o error */
529 	  return(-1);
530     }
531     tincnt += x;			/* Buffer bookkeeping */
532     tlast  += x;
533     return(x + tincnt);			/* How much is waiting to be read */
534 }
535 
536 int
ttflui()537 ttflui() {				/* Flush comm device input buffer */
538 #ifdef BSD
539     long n = 1;				/* Specify read queue */
540 #endif /* BSD */
541     int x;
542     tincnt = 0;				/* Our own buffer */
543     tlast = 0;
544     tinptr = tinbuf;
545     errno = 0;
546 #ifdef POSIX
547     x = tcflush(0,TCIFLUSH);		/* kernel/driver buffers */
548 #else
549 #ifdef SYSV
550     x = ioctl(0,TCFLSH,0);
551 #else
552 #ifdef BSD
553     x = ioctl(0,TIOCFLUSH,&n);
554 #endif /* BSD */
555 #endif /* SYSV */
556 #endif /* POSIX */
557     if (debug) fprintf(db,"ttflui = %d, errno = %d\n",x,errno);
558     return(x);
559 }
560 
561 SIGTYP
timerh(dummy)562 timerh(dummy) int dummy; {		/* Timeout handler */
563     longjmp(jbuf,1);
564     SIGRETURN;
565 }
566 
567 /*
568  ttinl() - Read a raw packet.
569 
570  Call with:
571    dest - where to put it
572    max  - maximum length
573    timo - timeout (seconds, 0 = none)
574    eol  - packet terminator
575    turn - half-duplex line turnaround character to wait for, 0 = none
576 
577  Returns length obtained, or -1 if error or timeout, -2 on disconnection.
578 */
579 #ifndef DEBUGWRAP
580 #define DEBUGWRAP 48
581 #endif /* DEBUGWRAP */
582 
583 int
584 #ifdef __STDC__
ttinl(char * dest,int max,int timo,char eol,char soh,int turn)585 ttinl(char * dest, int max, int timo, char eol, char soh, int turn)
586 #else
587 ttinl(dest,max,timo,eol,soh,turn) int max, timo, turn; char eol, soh, *dest;
588 #endif /* __STDC__ */
589 {
590     int n = 0, x = 0, flag = 0, rc = 0, ccn = 0; /* Local variables */
591     char c = NUL;
592     int havelen = 0, pktlen = 0, lplen = 0;
593 
594 #ifdef USE_GETCHAR
595     if (debug) fprintf(db,"ttinl getchar timo = %d\n",timo);
596 #else
597     if (debug) fprintf(db,"ttinl read timo = %d\n",timo);
598 #endif /* USE_GETCHAR */
599     *dest = NUL;			/* Clear destination buffer */
600     if (timo) {
601 	signal(SIGALRM,timerh);		/* Enable timer interrupt */
602 	alarm(timo);			/* Set it. */
603     }
604     if (setjmp(jbuf)) {			/* Timer went off? */
605 	if (debug) fprintf(db,"ttinl timeout\n");
606         rc = -1;			/* Yes, set this return code. */
607     } else {				/* Otherwise... */
608 	while (1) {			/* Read until we have a packet */
609 #ifdef DUMBIO
610 	    x = read(0,&c,1);		/* Dumb blocking read byte loop */
611 	    if (x < 0) {
612 		logerr("ttinl XX read 1");
613 		rc = -2;
614 	    }
615 #else
616 #ifdef USE_GETCHAR
617 	    errno = 0;
618 	    x = getchar();		/* Buffered read with getchar() */
619 	    if (x == EOF) {
620 		if (errno == EINTR)
621 		  continue;
622 		logerr("ttinl getchar");
623 		rc = -2;
624 	    }
625 	    c = x;
626 #else  /* USE_GETCHAR */
627 #ifdef O_NDELAY
628 	    if (nonblock) {		/* Buffered nonblocking read() */
629 		int x;
630 		if (tincnt < 1) {	/* Need to fill our buffer */
631 		    errno = 0;
632 		    tincnt = read(0,tinbuf,TINBUFSIZ);
633 		    if (tincnt > -1) tlast = tincnt;
634 		    if (debug)
635 		      fprintf(db,"ttinl nonblock tincnt=%d errno=%d\n",
636 			      tincnt,errno);
637 		    if (tincnt == 0 || errno == EWOULDBLOCK) {
638 #ifdef F_SETFL
639 			/* Go back to blocking and wait for 1 char */
640 			if (ttflags != -1) {
641 			    errno = 0;
642 			    x = fcntl(0, F_SETFL, ttflags  & ~O_NDELAY);
643 			    if (x == -1 || errno)
644 			      logerr("ttinl fcntl O_NDELAY off");
645 			    errno = 0;
646 			    tincnt = read(0,tinbuf,1);
647 			    if (tincnt < 1 || errno)
648 			      logerr("ttinl BL read");
649 			    errno = 0;
650 			    fcntl(0, F_SETFL, ttflags | O_NDELAY);
651 			    if (x == -1 || errno)
652 			      logerr("ttinl fcntl O_NDELAY on");
653 			}
654 			if (tincnt == 0) { /* Check results */
655 			    continue;
656 			}
657 			if (tincnt < 0) { /* I/O error */
658 			    rc = -2;
659 			    goto xttinl;
660 			}
661 			if (debug)
662 			  fprintf(db,"ttinl blocking read %d\n",tincnt);
663 #else
664 			/* No other form of sleeping is portable */
665 			sleep(1);
666 			continue;
667 #endif /* F_SETFL */
668 		    } else if (tincnt < 0) {
669 			rc = -2;
670 			goto xttinl;
671 		    }
672 		    tinptr = tinbuf;
673 		}
674 		c = *tinptr++;
675 		tincnt--;
676 	    } else {
677 #endif /* O_NDELAY */
678 		x = read(0,&c,1);	/* Dumb read byte loop */
679 		if (x < 0) {
680 		    logerr("ttinl XX read 1");
681 		    rc = -2;
682 		}
683 #ifdef O_NDELAY
684 	    }
685 #endif /* O_NDELAY */
686 #endif /* USE_GETCHAR */
687 #endif /* DUMBIO */
688 	    if (rc < 0)
689 	      break;
690 	    if (xparity)		/* Strip parity */
691 	      c &= 0x7f;
692 #ifdef COMMENT
693 	    /* Only uncomment in emergencies */
694 	    if (debug)
695 	      fprintf(db,"ttinl char=%c flag=%d tincnt=%d\n",c,flag,tincnt);
696 #endif /* COMMENT */
697 	    if (c == '\03') {		/* Got ^C, count it. */
698 	    	if (++ccn > 2) {	/* If more than 2, let them out */
699 		    fprintf(stderr,"^C...");
700 		    ttres();
701 		    if (debug) fprintf(db,"ttinl interrupted\n");
702 		    dest[n = 0] = NUL;
703 		    rc = -9;
704 		    goto xttinl;
705 		}
706 	    } else			/* Not ^C so reset counter*/
707 	      ccn = 0;
708 
709 	    if (!flag && (c != soh))	/* Look for SOH */
710 	      continue;			/* Skip stuff between packets */
711 	    flag = 1;			/* Have SOH */
712 
713 	    if (n >= max) {
714 		if (debug) fprintf(db,"ttinl overflow\n");
715 		rc = -2;
716 		goto xttinl;
717 	    }
718 	    dest[n++] = c;		/* Store the character */
719 #ifdef USE_EOL
720 	    /* Use EOL to determine end of packet */
721 	    if (c == eol) {
722 		dest[n] = NUL;
723 		break;
724 	    }
725 #else
726 	    /* Use length field for framing */
727 	    if (!havelen) {
728 		if (n == 2) {
729 		    pktlen = xunchar(dest[1] & 0x7f);
730 		    if (pktlen > 1) {
731 			if (debug) fprintf(db,"ttinl length = %d\n",pktlen);
732 			havelen = 1;
733 		    }
734 		} else if (n == 5 && pktlen == 0) {
735 		    lplen = xunchar(dest[4] & 0x7f);
736 		} else if (n == 6 && pktlen == 0) {
737 		    pktlen = lplen * 95 + xunchar(dest[5] & 0x7f) + 5;
738 		    if (debug) fprintf(db,"ttinl length = %d\n",pktlen);
739 		    havelen = 1;
740 		}
741 	    }
742 	    if (havelen && (n > pktlen+1)) {
743 		if (turn && c != turn)	/* Wait for turnaround char */
744 		  continue;
745 		dest[n] = NUL;		/* Null-terminate whatever we got */
746 		break;
747 	    }
748 #endif /* USE_EOL */
749 	}
750     }
751   xttinl:				/* Common exit point */
752     if (timo) {
753 	alarm(0);			/* Turn off the alarm */
754 	signal(SIGALRM,SIG_IGN);	/* and associated interrupt */
755     }
756     if (debug && n > 0) {		/* Log packet */
757 #ifndef FULLPACKETS
758 	if (n > DEBUGWRAP) {		/* Truncate if it would wrap */
759 	    dest[n] = NUL;		/* in case of interruption */
760 	    c = dest[DEBUGWRAP];
761 	    dest[DEBUGWRAP] = NUL;
762 	    fprintf(db,"PKT<-[^A%s...](%d) rc=%d\n",&dest[1],n,rc);
763 	    dest[DEBUGWRAP] = c;
764 
765 	} else
766 #endif /* FULLPACKETS */
767 	    fprintf(db,"PKT<-[^A%s](%d) rc=%d\n",&dest[1],n,rc);
768     }
769     if (rc == -9)			/* Interrupted by user */
770       doexit(1);
771     else if (rc > -1)
772       rc = n;
773     return(rc);				/* Return length, or failure. */
774 }
775 
776 int
ttol(s,len)777 ttol(s,len) int len; char *s; {		/* Output string s of given length  */
778     register int i = 0, n = 0, m = 0;
779     int partial = 0;
780 
781     n = len;
782     if (n < 0) {
783 	if (debug) fprintf(db,"ttol len = %d\n",n);
784 	return(-1);
785     }
786     if (xparity) {			/* Add parity if requested */
787 	for (i = 0; i < n; i++)
788 	  s[i] = dopar(s[i]);
789     }
790     if (debug) {			/* Log the packet if requested */
791 	char c;
792 #ifndef FULLPACKETS
793 	if (n > DEBUGWRAP) {
794 	    c = s[DEBUGWRAP];
795 	    s[DEBUGWRAP] = NUL;
796 	    fprintf(db,"PKT->[^A%s...](%d)\n",&s[1],n);
797 	    s[DEBUGWRAP] = c;
798 	} else {
799 #endif /* FULLPACKETS */
800 	    c = s[n-1];
801 	    s[n-1] = NUL;
802 	    fprintf(db,"PKT->[^A%s](%d)\n",&s[1],n);
803 	    s[n-1] = c;
804 #ifndef FULLPACKETS
805 	}
806 #endif /* FULLPACKETS */
807     }
808 #ifdef USE_GETCHAR
809     {					/* Send the packet with putchar() */
810 	register CHAR c; register int i;
811 	for (i = 0; i < n; i++) {
812 	    c = *s++;
813 	    if (putchar(c) == EOF) {
814 		logerr("ttol putchar");
815 		return(-1);
816 	    }
817 	}
818     }
819     fflush(stdout);			/* Push it out */
820     return(n);
821 #else
822     while (n > 0) {			/* Send the packet with write() */
823 	i = write(1,&s[m],n);		/* Allowing for partial results */
824 	if (i < 0) {
825 	    if (errno == EWOULDBLOCK)	/* and even no results at all.. */
826 	      continue;
827 	    logerr("ttol write");
828 	    return(-1);
829 	}
830 	if (i == n)
831 	  break;
832 	partial++;
833 	m += i;
834 	if (debug) fprintf(db,"ttol partial write %d (%d/%d)\n",i,m,len);
835 	n -= i;
836     }
837     if (partial) {
838 	m += i;
839 	if (debug) fprintf(db,"ttol partial write %d (%d/%d)\n",i,m,len);
840 	if (m != len) {
841 	    if (debug) fprintf(db,"ttol foulup %d != %d\n",m,len);
842 	    return(-1);
843 	}
844     }
845     return(len);
846 #endif /* USE_GETCHAR */
847 }
848 
849 /* File Functions */
850 
851 char ofile[MAXPATHLEN];			/* Output filename */
852 long filelength = -1L;
853 
854 long
zchki(fn)855 zchki(fn) char * fn; {			/* Check if file is readable */
856     struct stat buf;
857     if (!fn) return(-1);
858     if (stat(fn,&buf) < 0)
859       return(-1);
860     errno = 0;
861     if (access(fn,R_OK) < 0) {
862 	if (debug)
863 	  fprintf(db,"zchki access %s errno = %d\n",fn,errno);
864 	return(-1);
865     }
866     if (!S_ISREG(buf.st_mode)) {
867 	if (debug)
868 	  fprintf(db,"zchki %s is a directory",fn);
869 	return(-2);
870     }
871     return(buf.st_size);
872 }
873 
874 int
zchko(fn)875 zchko(fn) char *fn; {			/* Check write access */
876     int i, x;
877     char * s;
878 
879     if (!fn)				/* Defend against empty name */
880       fn = "";
881     if (!*fn)
882       return(-1);
883     if (!strcmp(fn,"/dev/null"))	/* Null device is OK. */
884       return(0);
885     if ((x = zchki(fn)) == -2)		/* An existing directory? */
886       return(-1);
887     s = fn;
888     if (x < 0) {			/* If file does not exist */
889 	strncpy(work,fn,MAXPATHLEN);
890 	work[MAXPATHLEN] = NUL;
891 	s = work;
892 	for (i = (int)strlen(s); i > 0; i--) { /* Strip filename from right */
893 	    if (s[i-1] == '/') {	/* and check its directory */
894 		s[i-1] = NUL;
895 		break;
896 	    }
897 	}
898 	if (i == 0)
899 	  s = ".";
900     }
901     errno = 0;
902     x = access(s,W_OK);			/* Check access of path. */
903     if (debug) fprintf(db,"zchko(%s) x = %d errno = %d\n",s,x,errno);
904     return((x < 0) ? -1 : 0);           /* and return. */
905 }
906 
907 int
zopeni(name)908 zopeni(name) char *name; {		/* Open existing file for input */
909     ifp = fopen(name,"r");
910     if (debug) fprintf(db,"zopeni %s: %d\n",name, ifp ? 0 : errno);
911     filelength = zchki(name);
912     if (filelength < 0)
913       return((int)filelength);
914     zincnt = 0;
915     zinptr = zinbuf;
916     return((ifp == NULL) ? -1 : 0);
917 }
918 
919 int
zopeno(name)920 zopeno(name) char *name; {		/* Open new file for output */
921     errno = 0;
922     ofp = fopen(name,"w");
923     if (debug) fprintf(db,"zopeno %s: %d\n",name, ofp ? 0 : errno);
924     if (ofp) {
925 	strncpy(ofile,name,MAXPATHLEN);
926 	ofile[MAXPATHLEN-1] = NUL;
927 	return(0);
928     } else
929       return(-1);
930 }
931 
932 VOID					/* Local to remote file name */
zltor(lclnam,pktnam,maxlen)933 zltor(lclnam,pktnam,maxlen) char *lclnam, *pktnam; int maxlen; {
934     char *p, *np = NULL, *cp, *pp, c;
935     char *dotp = NULL;
936     char *dirp = NULL;
937     int n = 0;
938 
939     if (debug)
940       fprintf(db,"zltor %s: maxlen = %d, literal = %d\n",
941 	      lclnam,maxlen,literal);
942     if (literal) {
943 	p = lclnam;
944 	dirp = p;
945 	while (*p) {
946 	    if (*p == '/') dirp = p+1;
947 	    p++;
948 	}
949 	strncpy(pktnam,dirp,maxlen);
950     } else {
951 	for (p = lclnam; *p; p++) {	/* Point to name part */
952 	    if (*p == '/')
953 	      np = p;
954 	}
955 	if (np) {
956 	    np++;
957 	    if (!*np) np = lclnam;
958 	} else
959 	  np = lclnam;
960 
961 	if (debug)
962 	  fprintf(db,"zltor np %s\n",np);
963 
964 	pp = work;			/* Output buffer */
965 	for (cp = np, n = 0; *cp && n < maxlen; cp++,n++) {
966 	    c = *cp;
967 	    if (islower(c))		/* Uppercase letters */
968 	      *pp++ = toupper(c);	/* Change tilde to hyphen */
969 	    else if (c == '~')
970 	      *pp++ = '-';
971 	    else if (c == '#')		/* Change number sign to 'X' */
972 	      *pp++ = 'X';
973 	    else if (c == '*' || c == '?') /* Change wildcard chars to 'X' */
974 	      *pp++ = 'X';
975 	    else if (c == ' ')		/* Change space to underscore */
976 	      *pp++ = '_';
977 	    else if (c < ' ')		/* Change space and controls to 'X' */
978 	      *pp++ = 'X';
979 	    else if (c == '.') {	/* Change dot to underscore */
980 		dotp = pp;		/* Remember where we last did this */
981 		*pp++ = '_';
982 	    } else {
983 		if (c == '/')
984 		  dirp = pp;
985 		*pp++ = c;
986 	    }
987 	}
988 	*pp = NUL;			/* Tie it off. */
989 	if (dotp > dirp) *dotp = '.';	/* Restore last dot in file name */
990 	cp = pktnam;			/* If nothing before dot, */
991 	if (*work == '.') *cp++ = 'X';	/* insert 'X' */
992 	strncpy(cp,work,maxlen);
993 	cp[maxlen-1] = NUL;
994     }
995     if (debug)
996       fprintf(db,"zltor result: %s\n",pktnam);
997 }
998 
999 int
zbackup(fn)1000 zbackup(fn) char * fn; {		/* Back up existing file */
1001     struct stat buf;
1002     int i, j, k, x, state, flag;
1003     char *p, newname[MAXPATHLEN+12];
1004 
1005     if (!fn)				/* Watch out for null pointers. */
1006       return(-1);
1007     if (!*fn)				/* And empty names. */
1008       return(-1);
1009     if (stat(fn,&buf) < 0)		/* If file doesn't exist */
1010       return(0);			/* no need to back it up. */
1011 
1012     i = strlen(fn);			/* Get length */
1013     if (i > MAXPATHLEN)			/* Guard buffer */
1014       i = MAXPATHLEN;
1015     if (debug)
1016       fprintf(db,"zbackup A %s: %d\n", fn, i);
1017 
1018     strncpy(work,fn,MAXPATHLEN);	/* Make pokeable copy of name */
1019     work[MAXPATHLEN] = NUL;
1020     p = work;				/* Strip any backup prefix */
1021 
1022     i--;
1023     for (flag = state = 0; (!flag && (i > 0)); i--) {
1024 	switch (state) {
1025 	  case 0:			/* State 0 - final char */
1026 	    if (p[i] == '~')		/* Is tilde */
1027 	      state = 1;		/* Switch to next state */
1028 	    else			/* Otherwise */
1029 	      flag = 1;			/* Quit - no backup suffix. */
1030 	    break;
1031 	  case 1:			/* State 1 - digits */
1032 	    if (p[i] == '~'  && p[i-1] == '.') { /* Have suffix */
1033 		p[i-1] = NUL;		/* Trim it */
1034 		flag = 1;		/* done */
1035 	    } else if (p[i] >= '0' && p[i] <= '9') { /* In number part */
1036 		continue;		/* Keep going */
1037 	    } else {			/* Something else */
1038 		flag = 1;		/* Not a backup suffix - quit. */
1039 	    }
1040 	    break;
1041 	}
1042     }
1043     if (debug)
1044       fprintf(db,"zbackup B %s\n", p);
1045     if (!p[0])
1046       p = fn;
1047     j = strlen(p);
1048     strncpy(newname,p,MAXPATHLEN);
1049     for (i = 1; i < 1000; i++) {	/* Search from 1 to 999 */
1050 	if (i < 10)			/* Length of numeric part of suffix */
1051 	  k = 1;
1052 	else if (i < 100)
1053 	  k = 2;
1054 	else
1055 	  k = 3;
1056 	x = j;				/* Where to write suffix */
1057 	if ((x + k + 3) > MAXPATHLEN)
1058 	  x = MAXPATHLEN - k - 3;
1059 	sprintf(&newname[x],".~%d~",i); /* Make a backup name */
1060 	if (stat(newname,&buf) < 0) {	/* If it doesn't exist */
1061 	    errno = 0;
1062 	    if (link(fn,newname) <  0) { /* Rename old file to backup name */
1063 		if (debug)
1064 		  fprintf(db,"zbackup failed: link(%s): %d\n",newname,errno);
1065 		return(-1);
1066 	    } else if (unlink(fn) < 0) {
1067 		if (debug)
1068 		  fprintf(db,"zbackup failed: unlink(%s): %d\n",fn,errno);
1069 		return(-1);
1070 	    } else {
1071 		if (debug)
1072 		  fprintf(db,"zbackup %s: OK\n",newname);
1073 		return(0);
1074 	    }
1075 	}
1076     }
1077     if (debug)
1078       fprintf(db,"zbackup failed: all numbers used\n");
1079     return(-1);
1080 }
1081 
1082 int					/* Remote to local filename */
zrtol(pktnam,lclnam,warn,maxlen)1083 zrtol(pktnam,lclnam,warn,maxlen) char *pktnam, *lclnam; int warn, maxlen; {
1084     int acase = 0, flag = 0, n = 0;
1085     char * p;
1086 
1087     if (literal) {
1088 	strncpy(lclnam,pktnam,maxlen);
1089     } else {
1090 	for (p = lclnam; *pktnam != '\0' && n < maxlen; pktnam++) {
1091 	    if (*pktnam > SP) flag = 1;	/* Strip leading blanks and controls */
1092 	    if (flag == 0 && *pktnam < '!')
1093 	      continue;
1094 	    if (isupper(*pktnam))	/* Check for mixed case */
1095 	      acase |= 1;
1096 	    else if (islower(*pktnam))
1097 	      acase |= 2;
1098 	    *p++ = *pktnam;
1099 	    n++;
1100 	}
1101 	*p-- = NUL;			/* Terminate */
1102 	while (*p < '!' && p > lclnam)	/* Strip trailing blanks & controls */
1103 	  *p-- = '\0';
1104 
1105 	if (!*lclnam) {			/* Nothing left? */
1106 	    strncpy(lclnam,"NONAME",maxlen); /* do this... */
1107 	} else if (acase == 1) {	/* All uppercase? */
1108 	    p = lclnam;			/* So convert all letters to lower */
1109 	    while (*p) {
1110 		if (isupper(*p))
1111 		  *p = tolower(*p);
1112 		p++;
1113 	    }
1114 	}
1115     }
1116     if (warn) {
1117 	if (zbackup(lclnam) < 0)
1118 	  return(-1);
1119     }
1120     return(0);
1121 }
1122 
1123 int
zclosi()1124 zclosi() {				/* Close input file */
1125     int rc;
1126     rc = (fclose(ifp) == EOF) ? -1 : 0;
1127     ifp = NULL;
1128     return(rc);
1129 }
1130 
1131 int
zcloso(cx)1132 zcloso(cx) int cx; {			/* Close output file */
1133     int rc;
1134     rc = (fclose(ofp) == EOF) ? -1 : 0;
1135     if (debug) fprintf(db,"zcloso(%s) cx = %d keep = %d\n", ofile, cx, keep);
1136     if (cx && !keep ) unlink(ofile);	/* Delete if incomplete */
1137     ofp = NULL;
1138     return(rc);
1139 }
1140 
1141 int
zfillbuf(text)1142 zfillbuf(text) int text; {		/* Refill input file buffer */
1143     if (zincnt < 1) {			/* Nothing in buffer - must refill */
1144 	if (text) {			/* Text mode needs LF/CRLF handling */
1145 	    int c;			/* Current character */
1146 	    for (zincnt = 0;		/* Read a line */
1147 		 zincnt < MAXRECORD - 1 && (c = getc(ifp)) != EOF && c != '\n';
1148 		 zincnt++
1149 		 ) {
1150 		zinbuf[zincnt] = c;
1151 	    }
1152 	    if (c == '\n') {		/* Have newline. */
1153 		zinbuf[zincnt++] = '\r'; /* Substitute CRLF */
1154 		zinbuf[zincnt++] = c;
1155 	    }
1156 	} else {			/* Binary - just read raw buffers */
1157 	    zincnt = fread(zinbuf, sizeof(char), MAXRECORD, ifp);
1158 	}
1159 	zinbuf[zincnt] = NUL;		/* Terminate. */
1160 	if (zincnt == 0)		/* Check for EOF */
1161 	  return(-1);
1162 	zinptr = zinbuf;		/* Not EOF - reset pointer */
1163     }
1164 #ifdef EXTRADEBUG				/* Voluminous debugging */
1165     if (debug) fprintf(db,"zfillbuf (%s) zincnt = %d\n",
1166 		       text ? "text" : "binary",
1167 		       zincnt
1168 		       );
1169 #endif /* EXTRADEBUG */
1170     zincnt--;				/* Return first byte. */
1171     return(*zinptr++ & 0xff);
1172 }
1173