xref: /openbsd-src/libexec/getty/subr.c (revision 5d36dfa4f50f8596c86a295eae67a351ca48ea40)
1 /*	$OpenBSD: subr.c,v 1.11 2001/01/28 19:34:28 niklas Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #ifndef lint
37 /*static char sccsid[] = "from: @(#)subr.c	8.1 (Berkeley) 6/4/93";*/
38 static char rcsid[] = "$OpenBSD: subr.c,v 1.11 2001/01/28 19:34:28 niklas Exp $";
39 #endif /* not lint */
40 
41 /*
42  * Melbourne getty.
43  */
44 #define COMPAT_43
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <string.h>
48 #include <termios.h>
49 #include <sys/ioctl.h>
50 
51 #include "gettytab.h"
52 #include "pathnames.h"
53 #include "extern.h"
54 
55 extern	struct termios tmode, omode;
56 
57 static void	compatflags __P((long));
58 
59 /*
60  * Get a table entry.
61  */
62 void
63 gettable(name, buf)
64 	char *name, *buf;
65 {
66 	register struct gettystrs *sp;
67 	register struct gettynums *np;
68 	register struct gettyflags *fp;
69 	long n;
70 	char *dba[2];
71 	dba[0] = _PATH_GETTYTAB;
72 	dba[1] = 0;
73 
74 	if (cgetent(&buf, dba, name) != 0)
75 		return;
76 
77 	for (sp = gettystrs; sp->field; sp++)
78 		cgetstr(buf, sp->field, &sp->value);
79 	for (np = gettynums; np->field; np++) {
80 		if (cgetnum(buf, np->field, &n) == -1)
81 			np->set = 0;
82 		else {
83 			np->set = 1;
84 			np->value = n;
85 		}
86 	}
87 	for (fp = gettyflags; fp->field; fp++) {
88 		if (cgetcap(buf, fp->field, ':') == NULL)
89 			fp->set = 0;
90 		else {
91 			fp->set = 1;
92 			fp->value = 1 ^ fp->invrt;
93 		}
94 	}
95 #ifdef DEBUG
96 	printf("name=\"%s\", buf=\"%s\"\n", name, buf);
97 	for (sp = gettystrs; sp->field; sp++)
98 		printf("cgetstr: %s=%s\n", sp->field, sp->value);
99 	for (np = gettynums; np->field; np++)
100 		printf("cgetnum: %s=%d\n", np->field, np->value);
101 	for (fp = gettyflags; fp->field; fp++)
102 		printf("cgetflags: %s='%c' set='%c'\n", fp->field,
103 		       fp->value + '0', fp->set + '0');
104 	exit(1);
105 #endif /* DEBUG */
106 }
107 
108 void
109 gendefaults()
110 {
111 	register struct gettystrs *sp;
112 	register struct gettynums *np;
113 	register struct gettyflags *fp;
114 
115 	for (sp = gettystrs; sp->field; sp++)
116 		if (sp->value)
117 			sp->defalt = sp->value;
118 	for (np = gettynums; np->field; np++)
119 		if (np->set)
120 			np->defalt = np->value;
121 	for (fp = gettyflags; fp->field; fp++)
122 		if (fp->set)
123 			fp->defalt = fp->value;
124 		else
125 			fp->defalt = fp->invrt;
126 }
127 
128 void
129 setdefaults()
130 {
131 	register struct gettystrs *sp;
132 	register struct gettynums *np;
133 	register struct gettyflags *fp;
134 
135 	for (sp = gettystrs; sp->field; sp++)
136 		if (!sp->value)
137 			sp->value = sp->defalt;
138 	for (np = gettynums; np->field; np++)
139 		if (!np->set)
140 			np->value = np->defalt;
141 	for (fp = gettyflags; fp->field; fp++)
142 		if (!fp->set)
143 			fp->value = fp->defalt;
144 }
145 
146 static char **
147 charnames[] = {
148 	&ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK,
149 	&SU, &DS, &RP, &FL, &WE, &LN, 0
150 };
151 
152 static char *
153 charvars[] = {
154 	&tmode.c_cc[VERASE], &tmode.c_cc[VKILL], &tmode.c_cc[VINTR],
155 	&tmode.c_cc[VQUIT], &tmode.c_cc[VSTART], &tmode.c_cc[VSTOP],
156 	&tmode.c_cc[VEOF], &tmode.c_cc[VEOL], &tmode.c_cc[VSUSP],
157 	&tmode.c_cc[VDSUSP], &tmode.c_cc[VREPRINT], &tmode.c_cc[VDISCARD],
158 	&tmode.c_cc[VWERASE], &tmode.c_cc[VLNEXT], 0
159 };
160 
161 void
162 setchars()
163 {
164 	register int i;
165 	register char *p;
166 
167 	for (i = 0; charnames[i]; i++) {
168 		p = *charnames[i];
169 		if (p && *p)
170 			*charvars[i] = *p;
171 		else
172 			*charvars[i] = _POSIX_VDISABLE;
173 	}
174 }
175 
176 /* Macros to clear/set/test flags. */
177 #define	SET(t, f)	(t) |= (f)
178 #define	CLR(t, f)	(t) &= ~(f)
179 #define	ISSET(t, f)	((t) & (f))
180 
181 void
182 setflags(n)
183 	int n;
184 {
185 	register tcflag_t iflag, oflag, cflag, lflag;
186 
187 #ifdef COMPAT_43
188 	switch (n) {
189 	case 0:
190 		if (F0set) {
191 			compatflags(F0);
192 			return;
193 		}
194 		break;
195 	case 1:
196 		if (F1set) {
197 			compatflags(F1);
198 			return;
199 		}
200 		break;
201 	default:
202 		if (F2set) {
203 			compatflags(F2);
204 			return;
205 		}
206 		break;
207 	}
208 #endif
209 
210 	switch (n) {
211 	case 0:
212 		if (C0set && I0set && L0set && O0set) {
213 			tmode.c_cflag = C0;
214 			tmode.c_iflag = I0;
215 			tmode.c_lflag = L0;
216 			tmode.c_oflag = O0;
217 			return;
218 		}
219 		break;
220 	case 1:
221 		if (C1set && I1set && L1set && O1set) {
222 			tmode.c_cflag = C1;
223 			tmode.c_iflag = I1;
224 			tmode.c_lflag = L1;
225 			tmode.c_oflag = O1;
226 			return;
227 		}
228 		break;
229 	default:
230 		if (C2set && I2set && L2set && O2set) {
231 			tmode.c_cflag = C2;
232 			tmode.c_iflag = I2;
233 			tmode.c_lflag = L2;
234 			tmode.c_oflag = O2;
235 			return;
236 		}
237 		break;
238 	}
239 
240 	iflag = omode.c_iflag;
241 	oflag = omode.c_oflag;
242 	cflag = omode.c_cflag;
243 	lflag = omode.c_lflag;
244 
245 	if (NP) {
246 		CLR(cflag, CSIZE|PARENB);
247 		SET(cflag, CS8);
248 		CLR(iflag, ISTRIP|INPCK|IGNPAR);
249 	} else if (AP || EP || OP) {
250 		CLR(cflag, CSIZE);
251 		SET(cflag, CS7|PARENB);
252 		SET(iflag, ISTRIP);
253 		if (OP && !EP) {
254 			SET(iflag, INPCK|IGNPAR);
255 			SET(cflag, PARODD);
256 			if (AP)
257 				CLR(iflag, INPCK);
258 		} else if (EP && !OP) {
259 			SET(iflag, INPCK|IGNPAR);
260 			CLR(cflag, PARODD);
261 			if (AP)
262 				CLR(iflag, INPCK);
263 		} else if (AP || EP && OP) {
264 			CLR(iflag, INPCK|IGNPAR);
265 			CLR(cflag, PARODD);
266 		}
267 	} /* else, leave as is */
268 
269 	if (UC) {
270 		SET(iflag, IUCLC);
271 		SET(oflag, OLCUC);
272 		SET(lflag, XCASE);
273 	}
274 
275 	if (HC)
276 		SET(cflag, HUPCL);
277 	else
278 		CLR(cflag, HUPCL);
279 
280 	if (MB)
281 		SET(cflag, MDMBUF);
282 	else
283 		CLR(cflag, MDMBUF);
284 
285 	if (NL) {
286 		SET(iflag, ICRNL);
287 		SET(oflag, ONLCR|OPOST);
288 	} else {
289 		CLR(iflag, ICRNL);
290 		CLR(oflag, ONLCR);
291 	}
292 
293 	if (!HT)
294 		SET(oflag, OXTABS|OPOST);
295 	else
296 		CLR(oflag, OXTABS);
297 
298 #ifdef XXX_DELAY
299 	SET(f, delaybits());
300 #endif
301 
302 	if (n == 1) {		/* read mode flags */
303 		if (RW) {
304 			iflag = 0;
305 			CLR(oflag, OPOST);
306 			CLR(cflag, CSIZE|PARENB);
307 			SET(cflag, CS8);
308 			lflag = 0;
309 		} else {
310 			CLR(lflag, ICANON);
311 		}
312 		goto out;
313 	}
314 
315 	if (n == 0)
316 		goto out;
317 
318 #if 0
319 	if (CB)
320 		SET(f, CRTBS);
321 #endif
322 
323 	if (CE)
324 		SET(lflag, ECHOE);
325 	else
326 		CLR(lflag, ECHOE);
327 
328 	if (CK)
329 		SET(lflag, ECHOKE);
330 	else
331 		CLR(lflag, ECHOKE);
332 
333 	if (PE)
334 		SET(lflag, ECHOPRT);
335 	else
336 		CLR(lflag, ECHOPRT);
337 
338 	if (EC)
339 		SET(lflag, ECHO);
340 	else
341 		CLR(lflag, ECHO);
342 
343 	if (XC)
344 		SET(lflag, ECHOCTL);
345 	else
346 		CLR(lflag, ECHOCTL);
347 
348 	if (DX)
349 		SET(lflag, IXANY);
350 	else
351 		CLR(lflag, IXANY);
352 
353 out:
354 	tmode.c_iflag = iflag;
355 	tmode.c_oflag = oflag;
356 	tmode.c_cflag = cflag;
357 	tmode.c_lflag = lflag;
358 }
359 
360 #ifdef COMPAT_43
361 /*
362  * Old TTY => termios, snatched from <sys/kern/tty_compat.c>
363  */
364 void
365 compatflags(flags)
366 register long flags;
367 {
368 	register tcflag_t iflag, oflag, cflag, lflag;
369 
370 	iflag = BRKINT|ICRNL|IMAXBEL|IXON|IXANY;
371 	oflag = OPOST|ONLCR|OXTABS;
372 	cflag = CREAD;
373 	lflag = ICANON|ISIG|IEXTEN;
374 
375 	if (ISSET(flags, TANDEM))
376 		SET(iflag, IXOFF);
377 	else
378 		CLR(iflag, IXOFF);
379 	if (ISSET(flags, ECHO))
380 		SET(lflag, ECHO);
381 	else
382 		CLR(lflag, ECHO);
383 	if (ISSET(flags, CRMOD)) {
384 		SET(iflag, ICRNL);
385 		SET(oflag, ONLCR);
386 	} else {
387 		CLR(iflag, ICRNL);
388 		CLR(oflag, ONLCR);
389 	}
390 	if (ISSET(flags, XTABS))
391 		SET(oflag, OXTABS);
392 	else
393 		CLR(oflag, OXTABS);
394 	if (ISSET(flags, LCASE)) {
395 		SET(iflag, IUCLC);
396 		SET(oflag, OLCUC);
397 		SET(lflag, XCASE);
398 	}
399 	else {
400 		CLR(iflag, IUCLC);
401 		CLR(oflag, OLCUC);
402 		CLR(lflag, XCASE);
403 	}
404 
405 
406 	if (ISSET(flags, RAW)) {
407 		iflag &= IXOFF;
408 		CLR(lflag, ISIG|ICANON|IEXTEN|XCASE);
409 		CLR(cflag, PARENB);
410 	} else {
411 		SET(iflag, BRKINT|IXON|IMAXBEL);
412 		SET(lflag, ISIG|IEXTEN);
413 		if (ISSET(iflag, IUCLC) && ISSET(oflag, OLCUC))
414 			SET(lflag, XCASE);
415 		if (ISSET(flags, CBREAK))
416 			CLR(lflag, ICANON);
417 		else
418 			SET(lflag, ICANON);
419 		switch (ISSET(flags, ANYP)) {
420 		case 0:
421 			CLR(cflag, PARENB);
422 			break;
423 		case ANYP:
424 			SET(cflag, PARENB);
425 			CLR(iflag, INPCK);
426 			break;
427 		case EVENP:
428 			SET(cflag, PARENB);
429 			SET(iflag, INPCK);
430 			CLR(cflag, PARODD);
431 			break;
432 		case ODDP:
433 			SET(cflag, PARENB);
434 			SET(iflag, INPCK);
435 			SET(cflag, PARODD);
436 			break;
437 		}
438 	}
439 
440 	/* Nothing we can do with CRTBS. */
441 	if (ISSET(flags, PRTERA))
442 		SET(lflag, ECHOPRT);
443 	else
444 		CLR(lflag, ECHOPRT);
445 	if (ISSET(flags, CRTERA))
446 		SET(lflag, ECHOE);
447 	else
448 		CLR(lflag, ECHOE);
449 	/* Nothing we can do with TILDE. */
450 	if (ISSET(flags, MDMBUF))
451 		SET(cflag, MDMBUF);
452 	else
453 		CLR(cflag, MDMBUF);
454 	if (ISSET(flags, NOHANG))
455 		CLR(cflag, HUPCL);
456 	else
457 		SET(cflag, HUPCL);
458 	if (ISSET(flags, CRTKIL))
459 		SET(lflag, ECHOKE);
460 	else
461 		CLR(lflag, ECHOKE);
462 	if (ISSET(flags, CTLECH))
463 		SET(lflag, ECHOCTL);
464 	else
465 		CLR(lflag, ECHOCTL);
466 	if (!ISSET(flags, DECCTQ))
467 		SET(iflag, IXANY);
468 	else
469 		CLR(iflag, IXANY);
470 	CLR(lflag, TOSTOP|FLUSHO|PENDIN|NOFLSH);
471 	SET(lflag, ISSET(flags, TOSTOP|FLUSHO|PENDIN|NOFLSH));
472 
473 	if (ISSET(flags, RAW|LITOUT|PASS8)) {
474 		CLR(cflag, CSIZE);
475 		SET(cflag, CS8);
476 		if (!ISSET(flags, RAW|PASS8))
477 			SET(iflag, ISTRIP);
478 		else
479 			CLR(iflag, ISTRIP);
480 		if (!ISSET(flags, RAW|LITOUT))
481 			SET(oflag, OPOST);
482 		else
483 			CLR(oflag, OPOST);
484 	} else {
485 		CLR(cflag, CSIZE);
486 		SET(cflag, CS7);
487 		SET(iflag, ISTRIP);
488 		SET(oflag, OPOST);
489 	}
490 
491 	tmode.c_iflag = iflag;
492 	tmode.c_oflag = oflag;
493 	tmode.c_cflag = cflag;
494 	tmode.c_lflag = lflag;
495 }
496 #endif
497 
498 #ifdef XXX_DELAY
499 struct delayval {
500 	unsigned	delay;		/* delay in ms */
501 	int		bits;
502 };
503 
504 /*
505  * below are random guesses, I can't be bothered checking
506  */
507 
508 struct delayval	crdelay[] = {
509 	{ 1,		CR1 },
510 	{ 2,		CR2 },
511 	{ 3,		CR3 },
512 	{ 83,		CR1 },
513 	{ 166,		CR2 },
514 	{ 0,		CR3 },
515 };
516 
517 struct delayval nldelay[] = {
518 	{ 1,		NL1 },		/* special, calculated */
519 	{ 2,		NL2 },
520 	{ 3,		NL3 },
521 	{ 100,		NL2 },
522 	{ 0,		NL3 },
523 };
524 
525 struct delayval	bsdelay[] = {
526 	{ 1,		BS1 },
527 	{ 0,		0 },
528 };
529 
530 struct delayval	ffdelay[] = {
531 	{ 1,		FF1 },
532 	{ 1750,		FF1 },
533 	{ 0,		FF1 },
534 };
535 
536 struct delayval	tbdelay[] = {
537 	{ 1,		 TAB1 },
538 	{ 2,		 TAB2 },
539 	{ 3,		XTABS },	/* this is expand tabs */
540 	{ 100,		 TAB1 },
541 	{ 0,		 TAB2 },
542 };
543 
544 int
545 delaybits()
546 {
547 	register int f;
548 
549 	f  = adelay(CD, crdelay);
550 	f |= adelay(ND, nldelay);
551 	f |= adelay(FD, ffdelay);
552 	f |= adelay(TD, tbdelay);
553 	f |= adelay(BD, bsdelay);
554 	return (f);
555 }
556 
557 int
558 adelay(ms, dp)
559 	register ms;
560 	register struct delayval *dp;
561 {
562 	if (ms == 0)
563 		return (0);
564 	while (dp->delay && ms > dp->delay)
565 		dp++;
566 	return (dp->bits);
567 }
568 #endif
569 
570 char	editedhost[48];
571 
572 void
573 edithost(pat)
574 	register char *pat;
575 {
576 	register char *host = HN;
577 	register char *res = editedhost;
578 
579 	if (!pat)
580 		pat = "";
581 	while (*pat) {
582 		switch (*pat) {
583 
584 		case '#':
585 			if (*host)
586 				host++;
587 			break;
588 
589 		case '@':
590 			if (*host)
591 				*res++ = *host++;
592 			break;
593 
594 		default:
595 			*res++ = *pat;
596 			break;
597 
598 		}
599 		if (res == &editedhost[sizeof editedhost - 1]) {
600 			*res = '\0';
601 			return;
602 		}
603 		pat++;
604 	}
605 	if (*host)
606 		strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
607 	else
608 		*res = '\0';
609 	editedhost[sizeof editedhost - 1] = '\0';
610 }
611 
612 void
613 makeenv(env)
614 	char *env[];
615 {
616 	static char termbuf[128] = "TERM=";
617 	register char *p, *q;
618 	register char **ep;
619 
620 	ep = env;
621 	if (TT && *TT) {
622 		strlcat(termbuf, TT, sizeof(termbuf));
623 		*ep++ = termbuf;
624 	}
625 	if ((p = EV)) {
626 		q = p;
627 		while ((q = strchr(q, ','))) {
628 			*q++ = '\0';
629 			*ep++ = p;
630 			p = q;
631 		}
632 		if (*p)
633 			*ep++ = p;
634 	}
635 	*ep = (char *)0;
636 }
637 
638 /*
639  * This speed select mechanism is written for the Develcon DATASWITCH.
640  * The Develcon sends a string of the form "B{speed}\n" at a predefined
641  * baud rate. This string indicates the user's actual speed.
642  * The routine below returns the terminal type mapped from derived speed.
643  */
644 struct	portselect {
645 	char	*ps_baud;
646 	char	*ps_type;
647 } portspeeds[] = {
648 	{ "B110",	"std.110" },
649 	{ "B134",	"std.134" },
650 	{ "B150",	"std.150" },
651 	{ "B300",	"std.300" },
652 	{ "B600",	"std.600" },
653 	{ "B1200",	"std.1200" },
654 	{ "B2400",	"std.2400" },
655 	{ "B4800",	"std.4800" },
656 	{ "B9600",	"std.9600" },
657 	{ "B19200",	"std.19200" },
658 	{ 0 }
659 };
660 
661 char *
662 portselector()
663 {
664 	char c, baud[20], *type = "default";
665 	register struct portselect *ps;
666 	int len;
667 
668 	alarm(5*60);
669 	for (len = 0; len < sizeof (baud) - 1; len++) {
670 		if (read(STDIN_FILENO, &c, 1) <= 0)
671 			break;
672 		c &= 0177;
673 		if (c == '\n' || c == '\r')
674 			break;
675 		if (c == 'B')
676 			len = 0;	/* in case of leading garbage */
677 		baud[len] = c;
678 	}
679 	baud[len] = '\0';
680 	for (ps = portspeeds; ps->ps_baud; ps++)
681 		if (strcmp(ps->ps_baud, baud) == 0) {
682 			type = ps->ps_type;
683 			break;
684 		}
685 	sleep(2);	/* wait for connection to complete */
686 	return (type);
687 }
688 
689 /*
690  * This auto-baud speed select mechanism is written for the Micom 600
691  * portselector. Selection is done by looking at how the character '\r'
692  * is garbled at the different speeds.
693  */
694 #include <sys/time.h>
695 
696 char *
697 autobaud()
698 {
699 	fd_set rfds;
700 	struct timeval timeout;
701 	char c, *type = "9600-baud";
702 
703 	(void)tcflush(0, TCIOFLUSH);
704 	FD_ZERO(&rfds);
705 	FD_SET(0, &rfds);
706 	timeout.tv_sec = 5;
707 	timeout.tv_usec = 0;
708 	if (select(1, &rfds, (fd_set *)NULL, (fd_set *)NULL, &timeout) <= 0)
709 		return (type);
710 	if (read(STDIN_FILENO, &c, sizeof(char)) != sizeof(char))
711 		return (type);
712 	timeout.tv_sec = 0;
713 	timeout.tv_usec = 20;
714 	(void) select(0, (fd_set *)NULL, (fd_set *)NULL,
715 	    (fd_set *)NULL, &timeout);
716 	(void)tcflush(0, TCIOFLUSH);
717 	switch (c & 0377) {
718 
719 	case 0200:		/* 300-baud */
720 		type = "300-baud";
721 		break;
722 
723 	case 0346:		/* 1200-baud */
724 		type = "1200-baud";
725 		break;
726 
727 	case  015:		/* 2400-baud */
728 	case 0215:
729 		type = "2400-baud";
730 		break;
731 
732 	default:		/* 4800-baud */
733 		type = "4800-baud";
734 		break;
735 
736 	case 0377:		/* 9600-baud */
737 		type = "9600-baud";
738 		break;
739 	}
740 	return (type);
741 }
742