xref: /netbsd-src/libexec/getty/main.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: main.c,v 1.44 2003/05/20 19:20:03 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 1980, 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 #include <sys/cdefs.h>
37 
38 #ifndef lint
39 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\n\
40 	The Regents of the University of California.  All rights reserved.\n");
41 #endif /* not lint */
42 
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "from: @(#)main.c	8.1 (Berkeley) 6/20/93";
46 #else
47 __RCSID("$NetBSD: main.c,v 1.44 2003/05/20 19:20:03 thorpej Exp $");
48 #endif
49 #endif /* not lint */
50 
51 #include <sys/param.h>
52 #include <sys/stat.h>
53 #include <termios.h>
54 #include <sys/ioctl.h>
55 #include <sys/resource.h>
56 #include <sys/utsname.h>
57 
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <time.h>
61 #include <ctype.h>
62 #include <fcntl.h>
63 #include <pwd.h>
64 #include <setjmp.h>
65 #include <signal.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <syslog.h>
69 #include <time.h>
70 #include <unistd.h>
71 #include <util.h>
72 #include <limits.h>
73 #include <ttyent.h>
74 #include <termcap.h>
75 
76 #include "gettytab.h"
77 #include "pathnames.h"
78 #include "extern.h"
79 
80 extern char **environ;
81 extern char editedhost[];
82 
83 /*
84  * Set the amount of running time that getty should accumulate
85  * before deciding that something is wrong and exit.
86  */
87 #define GETTY_TIMEOUT	60 /* seconds */
88 
89 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
90 
91 #define PPP_FRAME           0x7e  /* PPP Framing character */
92 #define PPP_STATION         0xff  /* "All Station" character */
93 #define PPP_ESCAPE          0x7d  /* Escape Character */
94 #define PPP_CONTROL         0x03  /* PPP Control Field */
95 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
96 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
97 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
98 
99 struct termios tmode, omode;
100 
101 int crmod, digit, lower, upper;
102 
103 char	hostname[MAXHOSTNAMELEN + 1];
104 struct	utsname kerninfo;
105 char	name[LOGIN_NAME_MAX];
106 char	dev[] = _PATH_DEV;
107 char	ttyn[32];
108 char	lockfile[512];
109 uid_t	ttyowner;
110 char	*rawttyn;
111 
112 #define	OBUFSIZ		128
113 #define	TABBUFSIZ	512
114 
115 char	defent[TABBUFSIZ];
116 char	tabent[TABBUFSIZ];
117 
118 char	*env[128];
119 
120 const char partab[] = {
121 	0001,0201,0201,0001,0201,0001,0001,0201,
122 	0202,0004,0003,0205,0005,0206,0201,0001,
123 	0201,0001,0001,0201,0001,0201,0201,0001,
124 	0001,0201,0201,0001,0201,0001,0001,0201,
125 	0200,0000,0000,0200,0000,0200,0200,0000,
126 	0000,0200,0200,0000,0200,0000,0000,0200,
127 	0000,0200,0200,0000,0200,0000,0000,0200,
128 	0200,0000,0000,0200,0000,0200,0200,0000,
129 	0200,0000,0000,0200,0000,0200,0200,0000,
130 	0000,0200,0200,0000,0200,0000,0000,0200,
131 	0000,0200,0200,0000,0200,0000,0000,0200,
132 	0200,0000,0000,0200,0000,0200,0200,0000,
133 	0000,0200,0200,0000,0200,0000,0000,0200,
134 	0200,0000,0000,0200,0000,0200,0200,0000,
135 	0200,0000,0000,0200,0000,0200,0200,0000,
136 	0000,0200,0200,0000,0200,0000,0000,0201
137 };
138 
139 #define	ERASE	tmode.c_cc[VERASE]
140 #define	KILL	tmode.c_cc[VKILL]
141 #define	EOT	tmode.c_cc[VEOF]
142 
143 static void	clearscreen(void);
144 
145 jmp_buf timeout;
146 
147 static void
148 dingdong(int signo)
149 {
150 
151 	alarm(0);
152 	signal(SIGALRM, SIG_DFL);
153 	longjmp(timeout, 1);
154 }
155 
156 jmp_buf	intrupt;
157 
158 static void
159 interrupt(int signo)
160 {
161 
162 	signal(SIGINT, interrupt);
163 	longjmp(intrupt, 1);
164 }
165 
166 /*
167  * Action to take when getty is running too long.
168  */
169 static void
170 timeoverrun(int signo)
171 {
172 
173 	syslog(LOG_ERR, "getty exiting due to excessive running time");
174 	exit(1);
175 }
176 
177 int		main(int, char *[]);
178 static int	getname(void);
179 static void	oflush(void);
180 static void	prompt(void);
181 static void	putchr(int);
182 static void	putf(const char *);
183 static void	putpad(const char *);
184 static void	xputs(const char *);
185 
186 int
187 main(int argc, char *argv[])
188 {
189 	const char *progname;
190 	char *tname;
191 	int repcnt = 0, failopenlogged = 0, uugetty = 0, first_time = 1;
192 	struct rlimit limit;
193 	struct passwd *pw;
194         int rval;
195 
196 #ifdef __GNUC__
197 	(void)&tname;		/* XXX gcc -Wall */
198 #endif
199 
200 	signal(SIGINT, SIG_IGN);
201 /*
202 	signal(SIGQUIT, SIG_DFL);
203 */
204 	openlog("getty", LOG_PID, LOG_AUTH);
205 	gethostname(hostname, sizeof(hostname));
206 	hostname[sizeof(hostname) - 1] = '\0';
207 	if (hostname[0] == '\0')
208 		strlcpy(hostname, "Amnesiac", sizeof(hostname));
209 	uname(&kerninfo);
210 
211 	progname = getprogname();
212 	if (progname[0] == 'u' && progname[1] == 'u')
213 		uugetty = 1;
214 
215 	/*
216 	 * Find id of uucp login (if present) so we can chown tty properly.
217 	 */
218 	if (uugetty && (pw = getpwnam("uucp")))
219 		ttyowner = pw->pw_uid;
220 	else
221 		ttyowner = 0;
222 
223 	/*
224 	 * Limit running time to deal with broken or dead lines.
225 	 */
226 	(void)signal(SIGXCPU, timeoverrun);
227 	limit.rlim_max = RLIM_INFINITY;
228 	limit.rlim_cur = GETTY_TIMEOUT;
229 	(void)setrlimit(RLIMIT_CPU, &limit);
230 
231 	/*
232 	 * The following is a work around for vhangup interactions
233 	 * which cause great problems getting window systems started.
234 	 * If the tty line is "-", we do the old style getty presuming
235 	 * that the file descriptors are already set up for us.
236 	 * J. Gettys - MIT Project Athena.
237 	 */
238 	if (argc <= 2 || strcmp(argv[2], "-") == 0) {
239 	    strlcpy(ttyn, ttyname(0), sizeof(ttyn));
240 	}
241 	else {
242 	    int i;
243 
244 	    rawttyn = argv[2];
245 	    strlcpy(ttyn, dev, sizeof(ttyn));
246 	    strlcat(ttyn, argv[2], sizeof(ttyn));
247 
248 	    if (uugetty)  {
249 		chown(ttyn, ttyowner, 0);
250 		strlcpy(lockfile, _PATH_LOCK, sizeof(lockfile));
251 		strlcat(lockfile, argv[2], sizeof(lockfile));
252 		/* wait for lockfiles to go away before we try to open */
253 		if ( pidlock(lockfile, 0, 0, 0) != 0 )  {
254 		    syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
255 		    exit(1);
256 		}
257 		unlink(lockfile);
258 	    }
259 	    if (strcmp(argv[0], "+") != 0) {
260 		chown(ttyn, ttyowner, 0);
261 		chmod(ttyn, 0600);
262 		revoke(ttyn);
263 		if (ttyaction(ttyn, "getty", "root"))
264 			syslog(LOG_WARNING, "%s: ttyaction failed", ttyn);
265 		/*
266 		 * Delay the open so DTR stays down long enough to be detected.
267 		 */
268 		sleep(2);
269 		while ((i = open(ttyn, O_RDWR)) == -1) {
270 			if ((repcnt % 10 == 0) &&
271 			    (errno != ENXIO || !failopenlogged)) {
272 				syslog(LOG_WARNING, "%s: %m", ttyn);
273 				closelog();
274 				failopenlogged = 1;
275 			}
276 			repcnt++;
277 			sleep(60);
278 		}
279 		if (uugetty && pidlock(lockfile, 0, 0, 0) != 0)  {
280 			syslog(LOG_ERR, "%s: can't create lockfile", ttyn);
281 			exit(1);
282 		}
283 		(void) chown(lockfile, ttyowner, 0);
284 		login_tty(i);
285 	    }
286 	}
287 
288 	/* Start with default tty settings */
289 	if (tcgetattr(0, &tmode) < 0) {
290 		syslog(LOG_ERR, "%s: %m", ttyn);
291 		exit(1);
292 	}
293 	omode = tmode;
294 
295 	gettable("default", defent);
296 	gendefaults();
297 	tname = "default";
298 	if (argc > 1)
299 		tname = argv[1];
300 	for (;;) {
301 		int off;
302 
303 		gettable(tname, tabent);
304 		if (OPset || EPset || APset)
305 			APset++, OPset++, EPset++;
306 		setdefaults();
307 		off = 0;
308 		(void)tcflush(0, TCIOFLUSH);	/* clear out the crap */
309 		ioctl(0, FIONBIO, &off);	/* turn off non-blocking mode */
310 		ioctl(0, FIOASYNC, &off);	/* ditto for async mode */
311 
312 		if (IS)
313 			cfsetispeed(&tmode, IS);
314 		else if (SP)
315 			cfsetispeed(&tmode, SP);
316 		if (OS)
317 			cfsetospeed(&tmode, OS);
318 		else if (SP)
319 			cfsetospeed(&tmode, SP);
320 		setflags(0);
321 		setchars();
322 		if (tcsetattr(0, TCSANOW, &tmode) < 0) {
323 			syslog(LOG_ERR, "%s: %m", ttyn);
324 			exit(1);
325 		}
326 		if (AB) {
327 			tname = autobaud();
328 			continue;
329 		}
330 		if (PS) {
331 			tname = portselector();
332 			continue;
333 		}
334 		if (CS)
335 			clearscreen();
336 		if (CL && *CL)
337 			putpad(CL);
338 		edithost(HE);
339 
340                 /*
341                  * If this is the first time through this, and an
342                  * issue file has been given, then send it.
343                  */
344 		if (first_time != 0 && IF != NULL) {
345 			char buf[_POSIX2_LINE_MAX];
346 			FILE *fd;
347 
348 			if ((fd = fopen(IF, "r")) != NULL) {
349 				while (fgets(buf, sizeof(buf) - 1, fd) != NULL)
350 					putf(buf);
351 				fclose(fd);
352 			}
353 		}
354 		first_time = 0;
355 
356 		if (IM && *IM)
357 			putf(IM);
358 		oflush();
359 		if (setjmp(timeout)) {
360 			tmode.c_ispeed = tmode.c_ospeed = 0;
361 			(void)tcsetattr(0, TCSANOW, &tmode);
362 			exit(1);
363 		}
364 		if (TO) {
365 			signal(SIGALRM, dingdong);
366 			alarm(TO);
367 		}
368 		if (NN) {
369 			name[0] = '\0';
370 			lower = 1;
371 			upper = digit = 0;
372 		} else if (AL) {
373 			const char *p = AL;
374 			char *q = name;
375 
376 			while (*p && q < &name[sizeof name - 1]) {
377 				if (isupper(*p))
378 					upper = 1;
379 				else if (islower(*p))
380 					lower = 1;
381 				else if (isdigit(*p))
382 					digit++;
383 				*q++ = *p++;
384 			}
385 		} else if ((rval = getname()) == 2) {
386 		        execle(PP, "ppplogin", ttyn, (char *) 0, env);
387 		        syslog(LOG_ERR, "%s: %m", PP);
388 		        exit(1);
389 		}
390 
391 		if (rval || AL || NN) {
392 			int i;
393 
394 			oflush();
395 			alarm(0);
396 			signal(SIGALRM, SIG_DFL);
397 			if (name[0] == '-') {
398 				xputs("user names may not start with '-'.");
399 				continue;
400 			}
401 			if (!(upper || lower || digit))
402 				continue;
403 			setflags(2);
404 			if (crmod) {
405 				tmode.c_iflag |= ICRNL;
406 				tmode.c_oflag |= ONLCR;
407 			}
408 #if XXX
409 			if (upper || UC)
410 				tmode.sg_flags |= LCASE;
411 			if (lower || LC)
412 				tmode.sg_flags &= ~LCASE;
413 #endif
414 			if (tcsetattr(0, TCSANOW, &tmode) < 0) {
415 				syslog(LOG_ERR, "%s: %m", ttyn);
416 				exit(1);
417 			}
418 			signal(SIGINT, SIG_DFL);
419 			for (i = 0; environ[i] != (char *)0; i++)
420 				env[i] = environ[i];
421 			makeenv(&env[i]);
422 
423 			limit.rlim_max = RLIM_INFINITY;
424 			limit.rlim_cur = RLIM_INFINITY;
425 			(void)setrlimit(RLIMIT_CPU, &limit);
426 			if (NN)
427 				execle(LO, "login", AL ? "-fp" : "-p",
428 				    NULL, env);
429 			else
430 				execle(LO, "login", AL ? "-fp" : "-p",
431 				    "--", name, NULL, env);
432 			syslog(LOG_ERR, "%s: %m", LO);
433 			exit(1);
434 		}
435 		alarm(0);
436 		signal(SIGALRM, SIG_DFL);
437 		signal(SIGINT, SIG_IGN);
438 		if (NX && *NX)
439 			tname = NX;
440 		unlink(lockfile);
441 	}
442 }
443 
444 static int
445 getname(void)
446 {
447 	int c;
448 	char *np;
449 	unsigned char cs;
450 	int ppp_state, ppp_connection;
451 
452 	/*
453 	 * Interrupt may happen if we use CBREAK mode
454 	 */
455 	if (setjmp(intrupt)) {
456 		signal(SIGINT, SIG_IGN);
457 		return (0);
458 	}
459 	signal(SIGINT, interrupt);
460 	setflags(1);
461 	prompt();
462 	if (PF > 0) {
463 		oflush();
464 		sleep(PF);
465 		PF = 0;
466 	}
467 	if (tcsetattr(0, TCSANOW, &tmode) < 0) {
468 		syslog(LOG_ERR, "%s: %m", ttyn);
469 		exit(1);
470 	}
471 	crmod = digit = lower = upper = 0;
472         ppp_state = ppp_connection = 0;
473 	np = name;
474 	for (;;) {
475 		oflush();
476 		if (read(STDIN_FILENO, &cs, 1) <= 0)
477 			exit(0);
478 		if ((c = cs&0177) == 0)
479 			return (0);
480 
481 		/*
482 		 * PPP detection state machine..
483 		 * Look for sequences:
484 		 * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
485 		 * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
486 		 * See RFC1662.
487 		 * Derived from code from Michael Hancock <michaelh@cet.co.jp>
488 		 * and Erik 'PPP' Olson <eriko@wrq.com>
489 		 */
490 		if (PP && cs == PPP_FRAME) {
491 			ppp_state = 1;
492 		} else if (ppp_state == 1 && cs == PPP_STATION) {
493 			ppp_state = 2;
494 		} else if (ppp_state == 2 && cs == PPP_ESCAPE) {
495 			ppp_state = 3;
496 		} else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
497 		    (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
498 			ppp_state = 4;
499 		} else if (ppp_state == 4 && cs == PPP_LCP_HI) {
500 			ppp_state = 5;
501 		} else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
502 			ppp_connection = 1;
503 			break;
504 		} else {
505 			ppp_state = 0;
506 		}
507 
508 		if (c == EOT)
509 			exit(1);
510 		if (c == '\r' || c == '\n' ||
511 		    np >= &name[LOGIN_NAME_MAX - 1]) {
512 			*np = '\0';
513 			putf("\r\n");
514 			break;
515 		}
516 		if (islower(c))
517 			lower = 1;
518 		else if (isupper(c))
519 			upper = 1;
520 		else if (c == ERASE || c == '#' || c == '\b') {
521 			if (np > name) {
522 				np--;
523 				if (cfgetospeed(&tmode) >= 1200)
524 					xputs("\b \b");
525 				else
526 					putchr(cs);
527 			}
528 			continue;
529 		} else if (c == KILL || c == '@') {
530 			putchr(cs);
531 			putchr('\r');
532 			if (cfgetospeed(&tmode) < 1200)
533 				putchr('\n');
534 			/* this is the way they do it down under ... */
535 			else if (np > name)
536 				xputs(
537 				    "                                     \r");
538 			prompt();
539 			np = name;
540 			continue;
541 		} else if (isdigit(c))
542 			digit++;
543 		if (IG && (c <= ' ' || c > 0176))
544 			continue;
545 		*np++ = c;
546 		putchr(cs);
547 
548 		/*
549 		 * An MS-Windows direct connect PPP "client" won't send its
550 		 * first PPP packet until we respond to its "CLIENT" poll
551 		 * with a CRLF sequence.  We cater to yet another broken
552 		 * implementation of a previously-standard protocol...
553 		 */
554 		*np = '\0';
555 		if (strstr(name, "CLIENT"))
556 		       putf("\r\n");
557 	}
558 	signal(SIGINT, SIG_IGN);
559 	*np = 0;
560 	if (c == '\r')
561 		crmod = 1;
562 	if ((upper && !lower && !LC) || UC)
563 		for (np = name; *np; np++)
564 			if (isupper(*np))
565 				*np = tolower(*np);
566 	return (1 + ppp_connection);
567 }
568 
569 static void
570 putpad(const char *s)
571 {
572 	int pad = 0;
573 	speed_t ospeed = cfgetospeed(&tmode);
574 
575 	if (isdigit(*s)) {
576 		while (isdigit(*s)) {
577 			pad *= 10;
578 			pad += *s++ - '0';
579 		}
580 		pad *= 10;
581 		if (*s == '.' && isdigit(s[1])) {
582 			pad += s[1] - '0';
583 			s += 2;
584 		}
585 	}
586 
587 	xputs(s);
588 	/*
589 	 * If no delay needed, or output speed is
590 	 * not comprehensible, then don't try to delay.
591 	 */
592 	if (pad == 0 || ospeed <= 0)
593 		return;
594 
595 	/*
596 	 * Round up by a half a character frame, and then do the delay.
597 	 * Too bad there are no user program accessible programmed delays.
598 	 * Transmitting pad characters slows many terminals down and also
599 	 * loads the system.
600 	 */
601 	pad = (pad * ospeed + 50000) / 100000;
602 	while (pad--)
603 		putchr(*PC);
604 }
605 
606 static void
607 xputs(const char *s)
608 {
609 	while (*s)
610 		putchr(*s++);
611 }
612 
613 char	outbuf[OBUFSIZ];
614 int	obufcnt = 0;
615 
616 static void
617 putchr(int cc)
618 {
619 	char c;
620 
621 	c = cc;
622 	if (!NP) {
623 		c |= partab[c&0177] & 0200;
624 		if (OP)
625 			c ^= 0200;
626 	}
627 	if (!UB) {
628 		outbuf[obufcnt++] = c;
629 		if (obufcnt >= OBUFSIZ)
630 			oflush();
631 	} else
632 		write(STDOUT_FILENO, &c, 1);
633 }
634 
635 static void
636 oflush(void)
637 {
638 	if (obufcnt)
639 		write(STDOUT_FILENO, outbuf, obufcnt);
640 	obufcnt = 0;
641 }
642 
643 static void
644 prompt(void)
645 {
646 
647 	putf(LM);
648 	if (CO)
649 		putchr('\n');
650 }
651 
652 static void
653 putf(const char *cp)
654 {
655 	time_t t;
656 	char *slash, db[100];
657 
658 	while (*cp) {
659 		if (*cp != '%') {
660 			putchr(*cp++);
661 			continue;
662 		}
663 		switch (*++cp) {
664 
665 		case 't':
666 			slash = strrchr(ttyn, '/');
667 			if (slash == NULL)
668 				xputs(ttyn);
669 			else
670 				xputs(&slash[1]);
671 			break;
672 
673 		case 'h':
674 			xputs(editedhost);
675 			break;
676 
677 		case 'd':
678 			(void)time(&t);
679 			(void)strftime(db, sizeof(db),
680 			    /* SCCS eats %M% */
681 			    "%l:%M" "%p on %A, %d %B %Y", localtime(&t));
682 			xputs(db);
683 			break;
684 
685 		case 's':
686 			xputs(kerninfo.sysname);
687 			break;
688 
689 		case 'm':
690 			xputs(kerninfo.machine);
691 			break;
692 
693 		case 'r':
694 			xputs(kerninfo.release);
695 			break;
696 
697 		case 'v':
698 			xputs(kerninfo.version);
699 			break;
700 
701 		case '%':
702 			putchr('%');
703 			break;
704 		}
705 		if (*cp)
706 			cp++;
707 	}
708 }
709 
710 static void
711 clearscreen(void)
712 {
713 	struct ttyent *typ;
714 	struct tinfo *tinfo;
715 	char *cs;
716 
717 	if (rawttyn == NULL)
718 		return;
719 
720 	typ = getttynam(rawttyn);
721 
722 	if ((typ == NULL) || (typ->ty_type == NULL) ||
723 	    (typ->ty_type[0] == 0))
724 		return;
725 
726 	if (t_getent(&tinfo, typ->ty_type) <= 0)
727 		return;
728 
729 	cs = t_agetstr(tinfo, "cl");
730 	if (cs == NULL)
731 		return;
732 
733 	putpad(cs);
734 }
735