xref: /original-bsd/usr.bin/tip/tip.c (revision 0fc6f013)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)tip.c	5.4 (Berkeley) 04/03/86";
15 #endif not lint
16 
17 /*
18  * tip - UNIX link to other systems
19  *  tip [-v] [-speed] system-name
20  * or
21  *  cu phone-number [-s speed] [-l line] [-a acu]
22  */
23 #include "tip.h"
24 
25 /*
26  * Baud rate mapping table
27  */
28 int bauds[] = {
29 	0, 50, 75, 110, 134, 150, 200, 300, 600,
30 	1200, 1800, 2400, 4800, 9600, 19200, -1
31 };
32 
33 int	disc = OTTYDISC;		/* tip normally runs this way */
34 int	intprompt();
35 int	timeout();
36 int	cleanup();
37 char	*sname();
38 char	PNbuf[256];			/* This limits the size of a number */
39 extern char *sprintf();
40 
41 main(argc, argv)
42 	char *argv[];
43 {
44 	char *system = NOSTR;
45 	register int i;
46 	register char *p;
47 	char sbuf[12];
48 
49 	if (equal(sname(argv[0]), "cu")) {
50 		cumode = 1;
51 		cumain(argc, argv);
52 		goto cucommon;
53 	}
54 
55 	if (argc > 4) {
56 		fprintf(stderr, "usage: tip [-v] [-speed] [system-name]\n");
57 		exit(1);
58 	}
59 	if (!isatty(0)) {
60 		fprintf(stderr, "tip: must be interactive\n");
61 		exit(1);
62 	}
63 
64 	for (; argc > 1; argv++, argc--) {
65 		if (argv[1][0] != '-')
66 			system = argv[1];
67 		else switch (argv[1][1]) {
68 
69 		case 'v':
70 			vflag++;
71 			break;
72 
73 		case '0': case '1': case '2': case '3': case '4':
74 		case '5': case '6': case '7': case '8': case '9':
75 			BR = atoi(&argv[1][1]);
76 			break;
77 
78 		default:
79 			fprintf(stderr, "tip: %s, unknown option\n", argv[1]);
80 			break;
81 		}
82 	}
83 
84 	if (system == NOSTR)
85 		goto notnumber;
86 	if (isalpha(*system))
87 		goto notnumber;
88 	/*
89 	 * System name is really a phone number...
90 	 * Copy the number then stomp on the original (in case the number
91 	 *	is private, we don't want 'ps' or 'w' to find it).
92 	 */
93 	if (strlen(system) > sizeof PNbuf - 1) {
94 		fprintf(stderr, "tip: phone number too long (max = %d bytes)\n",
95 			sizeof PNbuf - 1);
96 		exit(1);
97 	}
98 	strncpy( PNbuf, system, sizeof PNbuf - 1 );
99 	for (p = system; *p; p++)
100 		*p = '\0';
101 	PN = PNbuf;
102 	system = sprintf(sbuf, "tip%d", BR);
103 
104 notnumber:
105 	signal(SIGINT, cleanup);
106 	signal(SIGQUIT, cleanup);
107 	signal(SIGHUP, cleanup);
108 	signal(SIGTERM, cleanup);
109 
110 	if ((i = hunt(system)) == 0) {
111 		printf("all ports busy\n");
112 		exit(3);
113 	}
114 	if (i == -1) {
115 		printf("link down\n");
116 		delock(uucplock);
117 		exit(3);
118 	}
119 	setbuf(stdout, NULL);
120 	loginit();
121 
122 	/*
123 	 * Kludge, their's no easy way to get the initialization
124 	 *   in the right order, so force it here
125 	 */
126 	if ((PH = getenv("PHONES")) == NOSTR)
127 		PH = "/etc/phones";
128 	vinit();				/* init variables */
129 	setparity("even");			/* set the parity table */
130 	if ((i = speed(number(value(BAUDRATE)))) == NULL) {
131 		printf("tip: bad baud rate %d\n", number(value(BAUDRATE)));
132 		delock(uucplock);
133 		exit(3);
134 	}
135 
136 	/*
137 	 * Now that we have the logfile and the ACU open
138 	 *  return to the real uid and gid.  These things will
139 	 *  be closed on exit.  Swap real and effective uid's
140 	 *  so we can get the original permissions back
141 	 *  for removing the uucp lock.
142 	 */
143 	gid = getgid();
144 	egid = getegid();
145 	uid = getuid();
146 	euid = geteuid();
147 	setregid(egid, gid);
148 	setreuid(euid, uid);
149 
150 	/*
151 	 * Hardwired connections require the
152 	 *  line speed set before they make any transmissions
153 	 *  (this is particularly true of things like a DF03-AC)
154 	 */
155 	if (HW)
156 		ttysetup(i);
157 	if (p = connect()) {
158 		printf("\07%s\n[EOT]\n", p);
159 		setreuid(uid, euid);
160 		setregid(gid, egid);
161 		delock(uucplock);
162 		exit(1);
163 	}
164 	if (!HW)
165 		ttysetup(i);
166 cucommon:
167 	/*
168 	 * From here down the code is shared with
169 	 * the "cu" version of tip.
170 	 */
171 
172 	ioctl(0, TIOCGETP, (char *)&defarg);
173 	ioctl(0, TIOCGETC, (char *)&defchars);
174 	ioctl(0, TIOCGLTC, (char *)&deflchars);
175 	ioctl(0, TIOCGETD, (char *)&odisc);
176 	arg = defarg;
177 	arg.sg_flags = ANYP | CBREAK;
178 	tchars = defchars;
179 	tchars.t_intrc = tchars.t_quitc = -1;
180 	ltchars = deflchars;
181 	ltchars.t_suspc = ltchars.t_dsuspc = ltchars.t_flushc
182 		= ltchars.t_lnextc = -1;
183 	raw();
184 
185 	pipe(fildes); pipe(repdes);
186 	signal(SIGALRM, timeout);
187 
188 	/*
189 	 * Everything's set up now:
190 	 *	connection established (hardwired or dialup)
191 	 *	line conditioned (baud rate, mode, etc.)
192 	 *	internal data structures (variables)
193 	 * so, fork one process for local side and one for remote.
194 	 */
195 	printf(cumode ? "Connected\r\n" : "\07connected\r\n");
196 	if (pid = fork())
197 		tipin();
198 	else
199 		tipout();
200 	/*NOTREACHED*/
201 }
202 
203 cleanup()
204 {
205 
206 	if (uid != getuid()) {
207 		setreuid(uid, euid);
208 		setregid(gid, egid);
209 	}
210 	delock(uucplock);
211 	if (odisc)
212 		ioctl(0, TIOCSETD, (char *)&odisc);
213 	exit(0);
214 }
215 
216 /*
217  * put the controlling keyboard into raw mode
218  */
219 raw()
220 {
221 
222 	ioctl(0, TIOCSETP, &arg);
223 	ioctl(0, TIOCSETC, &tchars);
224 	ioctl(0, TIOCSLTC, &ltchars);
225 	ioctl(0, TIOCSETD, (char *)&disc);
226 }
227 
228 
229 /*
230  * return keyboard to normal mode
231  */
232 unraw()
233 {
234 
235 	ioctl(0, TIOCSETD, (char *)&odisc);
236 	ioctl(0, TIOCSETP, (char *)&defarg);
237 	ioctl(0, TIOCSETC, (char *)&defchars);
238 	ioctl(0, TIOCSLTC, (char *)&deflchars);
239 }
240 
241 static	jmp_buf promptbuf;
242 
243 /*
244  * Print string ``s'', then read a string
245  *  in from the terminal.  Handles signals & allows use of
246  *  normal erase and kill characters.
247  */
248 prompt(s, p)
249 	char *s;
250 	register char *p;
251 {
252 	register char *b = p;
253 	int (*oint)(), (*oquit)();
254 
255 	stoprompt = 0;
256 	oint = signal(SIGINT, intprompt);
257 	oint = signal(SIGQUIT, SIG_IGN);
258 	unraw();
259 	printf("%s", s);
260 	if (setjmp(promptbuf) == 0)
261 		while ((*p = getchar()) != EOF && *p != '\n')
262 			p++;
263 	*p = '\0';
264 
265 	raw();
266 	signal(SIGINT, oint);
267 	signal(SIGQUIT, oint);
268 	return (stoprompt || p == b);
269 }
270 
271 /*
272  * Interrupt service routine during prompting
273  */
274 intprompt()
275 {
276 
277 	signal(SIGINT, SIG_IGN);
278 	stoprompt = 1;
279 	printf("\r\n");
280 	longjmp(promptbuf, 1);
281 }
282 
283 /*
284  * ****TIPIN   TIPIN****
285  */
286 tipin()
287 {
288 	char gch, bol = 1;
289 
290 	/*
291 	 * Kinda klugey here...
292 	 *   check for scripting being turned on from the .tiprc file,
293 	 *   but be careful about just using setscript(), as we may
294 	 *   send a SIGEMT before tipout has a chance to set up catching
295 	 *   it; so wait a second, then setscript()
296 	 */
297 	if (boolean(value(SCRIPT))) {
298 		sleep(1);
299 		setscript();
300 	}
301 
302 	while (1) {
303 		gch = getchar()&0177;
304 		if ((gch == character(value(ESCAPE))) && bol) {
305 			if (!(gch = escape()))
306 				continue;
307 		} else if (!cumode && gch == character(value(RAISECHAR))) {
308 			boolean(value(RAISE)) = !boolean(value(RAISE));
309 			continue;
310 		} else if (gch == '\r') {
311 			bol = 1;
312 			pwrite(FD, &gch, 1);
313 			if (boolean(value(HALFDUPLEX)))
314 				printf("\r\n");
315 			continue;
316 		} else if (!cumode && gch == character(value(FORCE)))
317 			gch = getchar()&0177;
318 		bol = any(gch, value(EOL));
319 		if (boolean(value(RAISE)) && islower(gch))
320 			gch = toupper(gch);
321 		pwrite(FD, &gch, 1);
322 		if (boolean(value(HALFDUPLEX)))
323 			printf("%c", gch);
324 	}
325 }
326 
327 /*
328  * Escape handler --
329  *  called on recognition of ``escapec'' at the beginning of a line
330  */
331 escape()
332 {
333 	register char gch;
334 	register esctable_t *p;
335 	char c = character(value(ESCAPE));
336 	extern esctable_t etable[];
337 
338 	gch = (getchar()&0177);
339 	for (p = etable; p->e_char; p++)
340 		if (p->e_char == gch) {
341 			if ((p->e_flags&PRIV) && getuid())
342 				continue;
343 			printf("%s", ctrl(c));
344 			(*p->e_func)(gch);
345 			return (0);
346 		}
347 	/* ESCAPE ESCAPE forces ESCAPE */
348 	if (c != gch)
349 		pwrite(FD, &c, 1);
350 	return (gch);
351 }
352 
353 speed(n)
354 	int n;
355 {
356 	register int *p;
357 
358 	for (p = bauds; *p != -1;  p++)
359 		if (*p == n)
360 			return (p - bauds);
361 	return (NULL);
362 }
363 
364 any(c, p)
365 	register char c, *p;
366 {
367 	while (p && *p)
368 		if (*p++ == c)
369 			return (1);
370 	return (0);
371 }
372 
373 size(s)
374 	register char	*s;
375 {
376 	register int i = 0;
377 
378 	while (s && *s++)
379 		i++;
380 	return (i);
381 }
382 
383 char *
384 interp(s)
385 	register char *s;
386 {
387 	static char buf[256];
388 	register char *p = buf, c, *q;
389 
390 	while (c = *s++) {
391 		for (q = "\nn\rr\tt\ff\033E\bb"; *q; q++)
392 			if (*q++ == c) {
393 				*p++ = '\\'; *p++ = *q;
394 				goto next;
395 			}
396 		if (c < 040) {
397 			*p++ = '^'; *p++ = c + 'A'-1;
398 		} else if (c == 0177) {
399 			*p++ = '^'; *p++ = '?';
400 		} else
401 			*p++ = c;
402 	next:
403 		;
404 	}
405 	*p = '\0';
406 	return (buf);
407 }
408 
409 char *
410 ctrl(c)
411 	char c;
412 {
413 	static char s[3];
414 
415 	if (c < 040 || c == 0177) {
416 		s[0] = '^';
417 		s[1] = c == 0177 ? '?' : c+'A'-1;
418 		s[2] = '\0';
419 	} else {
420 		s[0] = c;
421 		s[1] = '\0';
422 	}
423 	return (s);
424 }
425 
426 /*
427  * Help command
428  */
429 help(c)
430 	char c;
431 {
432 	register esctable_t *p;
433 	extern esctable_t etable[];
434 
435 	printf("%c\r\n", c);
436 	for (p = etable; p->e_char; p++) {
437 		if ((p->e_flags&PRIV) && getuid())
438 			continue;
439 		printf("%2s", ctrl(character(value(ESCAPE))));
440 		printf("%-2s %c   %s\r\n", ctrl(p->e_char),
441 			p->e_flags&EXP ? '*': ' ', p->e_help);
442 	}
443 }
444 
445 /*
446  * Set up the "remote" tty's state
447  */
448 ttysetup(speed)
449 	int speed;
450 {
451 	unsigned bits = LDECCTQ;
452 
453 	arg.sg_ispeed = arg.sg_ospeed = speed;
454 	arg.sg_flags = RAW;
455 	if (boolean(value(TAND)))
456 		arg.sg_flags |= TANDEM;
457 	ioctl(FD, TIOCSETP, (char *)&arg);
458 	ioctl(FD, TIOCLBIS, (char *)&bits);
459 }
460 
461 /*
462  * Return "simple" name from a file name,
463  * strip leading directories.
464  */
465 char *
466 sname(s)
467 	register char *s;
468 {
469 	register char *p = s;
470 
471 	while (*s)
472 		if (*s++ == '/')
473 			p = s;
474 	return (p);
475 }
476 
477 static char partab[0200];
478 
479 /*
480  * Do a write to the remote machine with the correct parity.
481  * We are doing 8 bit wide output, so we just generate a character
482  * with the right parity and output it.
483  */
484 pwrite(fd, buf, n)
485 	int fd;
486 	char *buf;
487 	register int n;
488 {
489 	register int i;
490 	register char *bp;
491 	extern int errno;
492 
493 	bp = buf;
494 	for (i = 0; i < n; i++) {
495 		*bp = partab[(*bp) & 0177];
496 		bp++;
497 	}
498 	if (write(fd, buf, n) < 0) {
499 		if (errno == EIO)
500 			abort("Lost carrier.");
501 		/* this is questionable */
502 		perror("write");
503 	}
504 }
505 
506 /*
507  * Build a parity table with appropriate high-order bit.
508  */
509 setparity(defparity)
510 	char *defparity;
511 {
512 	register int i;
513 	char *parity;
514 	extern char evenpartab[];
515 
516 	if (value(PARITY) == NOSTR)
517 		value(PARITY) = defparity;
518 	parity = value(PARITY);
519 	for (i = 0; i < 0200; i++)
520 		partab[i] = evenpartab[i];
521 	if (equal(parity, "even"))
522 		return;
523 	if (equal(parity, "odd")) {
524 		for (i = 0; i < 0200; i++)
525 			partab[i] ^= 0200;	/* reverse bit 7 */
526 		return;
527 	}
528 	if (equal(parity, "none") || equal(parity, "zero")) {
529 		for (i = 0; i < 0200; i++)
530 			partab[i] &= ~0200;	/* turn off bit 7 */
531 		return;
532 	}
533 	if (equal(parity, "one")) {
534 		for (i = 0; i < 0200; i++)
535 			partab[i] |= 0200;	/* turn on bit 7 */
536 		return;
537 	}
538 	fprintf(stderr, "%s: unknown parity value\n", PA);
539 	fflush(stderr);
540 }
541