]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - libexec/getty/main.c
Import tzdata 2018c
[FreeBSD/FreeBSD.git] / libexec / getty / main.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #ifndef lint
33 static const char copyright[] =
34 "@(#) Copyright (c) 1980, 1993\n\
35         The Regents of the University of California.  All rights reserved.\n";
36 #endif /* not lint */
37
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)from: main.c        8.1 (Berkeley) 6/20/93";
41 #endif
42 #endif /* not lint */
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
46 #include <sys/param.h>
47 #include <sys/ioctl.h>
48 #include <sys/time.h>
49 #include <sys/resource.h>
50 #include <sys/stat.h>
51 #include <sys/ttydefaults.h>
52 #include <sys/utsname.h>
53
54 #include <ctype.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <locale.h>
58 #include <libutil.h>
59 #include <setjmp.h>
60 #include <signal.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <syslog.h>
64 #include <termios.h>
65 #include <time.h>
66 #include <unistd.h>
67
68 #include "gettytab.h"
69 #include "extern.h"
70 #include "pathnames.h"
71
72 /*
73  * Set the amount of running time that getty should accumulate
74  * before deciding that something is wrong and exit.
75  */
76 #define GETTY_TIMEOUT   60 /* seconds */
77
78 #undef CTRL
79 #define CTRL(x)  (x&037)
80
81 /* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
82
83 #define PPP_FRAME           0x7e  /* PPP Framing character */
84 #define PPP_STATION         0xff  /* "All Station" character */
85 #define PPP_ESCAPE          0x7d  /* Escape Character */
86 #define PPP_CONTROL         0x03  /* PPP Control Field */
87 #define PPP_CONTROL_ESCAPED 0x23  /* PPP Control Field, escaped */
88 #define PPP_LCP_HI          0xc0  /* LCP protocol - high byte */
89 #define PPP_LCP_LOW         0x21  /* LCP protocol - low byte */
90
91 /* original mode; flags've been reset using values from <sys/ttydefaults.h> */
92 struct termios omode;
93 /* current mode */
94 struct termios tmode;
95
96 int crmod, digit, lower, upper;
97
98 char    hostname[MAXHOSTNAMELEN];
99 char    name[MAXLOGNAME*3];
100 char    dev[] = _PATH_DEV;
101 char    ttyn[32];
102
103 #define OBUFSIZ         128
104 #define TABBUFSIZ       512
105
106 char    defent[TABBUFSIZ];
107 char    tabent[TABBUFSIZ];
108 const   char *tname;
109
110 char    *env[128];
111
112 char partab[] = {
113         0001,0201,0201,0001,0201,0001,0001,0201,
114         0202,0004,0003,0205,0005,0206,0201,0001,
115         0201,0001,0001,0201,0001,0201,0201,0001,
116         0001,0201,0201,0001,0201,0001,0001,0201,
117         0200,0000,0000,0200,0000,0200,0200,0000,
118         0000,0200,0200,0000,0200,0000,0000,0200,
119         0000,0200,0200,0000,0200,0000,0000,0200,
120         0200,0000,0000,0200,0000,0200,0200,0000,
121         0200,0000,0000,0200,0000,0200,0200,0000,
122         0000,0200,0200,0000,0200,0000,0000,0200,
123         0000,0200,0200,0000,0200,0000,0000,0200,
124         0200,0000,0000,0200,0000,0200,0200,0000,
125         0000,0200,0200,0000,0200,0000,0000,0200,
126         0200,0000,0000,0200,0000,0200,0200,0000,
127         0200,0000,0000,0200,0000,0200,0200,0000,
128         0000,0200,0200,0000,0200,0000,0000,0201
129 };
130
131 #define ERASE   tmode.c_cc[VERASE]
132 #define KILL    tmode.c_cc[VKILL]
133 #define EOT     tmode.c_cc[VEOF]
134
135 #define puts    Gputs
136
137 static void     defttymode(void);
138 static void     dingdong(int);
139 static void     dogettytab(void);
140 static int      getname(void);
141 static void     interrupt(int);
142 static void     oflush(void);
143 static void     prompt(void);
144 static void     putchr(int);
145 static void     putf(const char *);
146 static void     putpad(const char *);
147 static void     puts(const char *);
148 static void     timeoverrun(int);
149 static char     *getline(int);
150 static void     setttymode(int);
151 static int      opentty(const char *, int);
152
153 jmp_buf timeout;
154
155 static void
156 dingdong(int signo __unused)
157 {
158         alarm(0);
159         longjmp(timeout, 1);
160 }
161
162 jmp_buf intrupt;
163
164 static void
165 interrupt(int signo __unused)
166 {
167         longjmp(intrupt, 1);
168 }
169
170 /*
171  * Action to take when getty is running too long.
172  */
173 static void
174 timeoverrun(int signo __unused)
175 {
176
177         syslog(LOG_ERR, "getty exiting due to excessive running time");
178         exit(1);
179 }
180
181 int
182 main(int argc, char *argv[])
183 {
184         extern  char **environ;
185         int first_sleep = 1, first_time = 1;
186         struct rlimit limit;
187         int rval;
188
189         signal(SIGINT, SIG_IGN);
190         signal(SIGQUIT, SIG_IGN);
191
192         openlog("getty", LOG_CONS|LOG_PID, LOG_AUTH);
193         gethostname(hostname, sizeof(hostname) - 1);
194         hostname[sizeof(hostname) - 1] = '\0';
195         if (hostname[0] == '\0')
196                 strcpy(hostname, "Amnesiac");
197
198         /*
199          * Limit running time to deal with broken or dead lines.
200          */
201         (void)signal(SIGXCPU, timeoverrun);
202         limit.rlim_max = RLIM_INFINITY;
203         limit.rlim_cur = GETTY_TIMEOUT;
204         (void)setrlimit(RLIMIT_CPU, &limit);
205
206         gettable("default", defent);
207         gendefaults();
208         tname = "default";
209         if (argc > 1)
210                 tname = argv[1];
211
212         /*
213          * The following is a work around for vhangup interactions
214          * which cause great problems getting window systems started.
215          * If the tty line is "-", we do the old style getty presuming
216          * that the file descriptors are already set up for us.
217          * J. Gettys - MIT Project Athena.
218          */
219         if (argc <= 2 || strcmp(argv[2], "-") == 0)
220             strcpy(ttyn, ttyname(STDIN_FILENO));
221         else {
222             strcpy(ttyn, dev);
223             strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev));
224             if (strcmp(argv[0], "+") != 0) {
225                 chown(ttyn, 0, 0);
226                 chmod(ttyn, 0600);
227                 revoke(ttyn);
228
229                 /*
230                  * Do the first scan through gettytab.
231                  * Terminal mode parameters will be wrong until
232                  * defttymode() called, but they're irrelevant for
233                  * the initial setup of the terminal device.
234                  */
235                 dogettytab();
236
237                 /*
238                  * Init or answer modem sequence has been specified.
239                  */
240                 if (IC || AC) {
241                         if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
242                                 exit(1);
243                         defttymode();
244                         setttymode(1);
245                 }
246
247                 if (IC) {
248                         if (getty_chat(IC, CT, DC) > 0) {
249                                 syslog(LOG_ERR, "modem init problem on %s", ttyn);
250                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
251                                 exit(1);
252                         }
253                 }
254
255                 if (AC) {
256                         int i, rfds;
257                         struct timeval to;
258
259                         rfds = 1 << 0;  /* FD_SET */
260                         to.tv_sec = RT;
261                         to.tv_usec = 0;
262                         i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
263                                        (fd_set*)NULL, RT ? &to : NULL);
264                         if (i < 0) {
265                                 syslog(LOG_ERR, "select %s: %m", ttyn);
266                         } else if (i == 0) {
267                                 syslog(LOG_NOTICE, "recycle tty %s", ttyn);
268                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
269                                 exit(0);  /* recycle for init */
270                         }
271                         i = getty_chat(AC, CT, DC);
272                         if (i > 0) {
273                                 syslog(LOG_ERR, "modem answer problem on %s", ttyn);
274                                 (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
275                                 exit(1);
276                         }
277                 } else { /* maybe blocking open */
278                         if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
279                                 exit(1);
280                 }
281             }
282         }
283
284         defttymode();
285         for (;;) {
286
287                 /*
288                  * if a delay was specified then sleep for that 
289                  * number of seconds before writing the initial prompt
290                  */
291                 if (first_sleep && DE) {
292                     sleep(DE);
293                     /* remove any noise */
294                     (void)tcflush(STDIN_FILENO, TCIOFLUSH);
295                 }
296                 first_sleep = 0;
297
298                 setttymode(0);
299                 if (AB) {
300                         tname = autobaud();
301                         dogettytab();
302                         continue;
303                 }
304                 if (PS) {
305                         tname = portselector();
306                         dogettytab();
307                         continue;
308                 }
309                 if (CL && *CL)
310                         putpad(CL);
311                 edithost(HE);
312
313                 /* if this is the first time through this, and an
314                    issue file has been given, then send it */
315                 if (first_time && IF) {
316                         int fd;
317
318                         if ((fd = open(IF, O_RDONLY)) != -1) {
319                                 char * cp;
320
321                                 while ((cp = getline(fd)) != NULL) {
322                                           putf(cp);
323                                 }
324                                 close(fd);
325                         }
326                 }
327                 first_time = 0;
328
329                 if (IMP && *IMP && !(PL && PP))
330                         system(IMP);
331                 if (IM && *IM && !(PL && PP))
332                         putf(IM);
333                 if (setjmp(timeout)) {
334                         cfsetispeed(&tmode, B0);
335                         cfsetospeed(&tmode, B0);
336                         (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
337                         exit(1);
338                 }
339                 if (TO) {
340                         signal(SIGALRM, dingdong);
341                         alarm(TO);
342                 }
343
344                 rval = 0;
345                 if (AL) {
346                         const char *p = AL;
347                         char *q = name;
348
349                         while (*p && q < &name[sizeof name - 1]) {
350                                 if (isupper(*p))
351                                         upper = 1;
352                                 else if (islower(*p))
353                                         lower = 1;
354                                 else if (isdigit(*p))
355                                         digit = 1;
356                                 *q++ = *p++;
357                         }
358                 } else if (!(PL && PP))
359                         rval = getname();
360                 if (rval == 2 || (PL && PP)) {
361                         oflush();
362                         alarm(0);
363                         limit.rlim_max = RLIM_INFINITY;
364                         limit.rlim_cur = RLIM_INFINITY;
365                         (void)setrlimit(RLIMIT_CPU, &limit);
366                         execle(PP, "ppplogin", ttyn, (char *) 0, env);
367                         syslog(LOG_ERR, "%s: %m", PP);
368                         exit(1);
369                 } else if (rval || AL) {
370                         int i;
371
372                         oflush();
373                         alarm(0);
374                         signal(SIGALRM, SIG_DFL);
375                         if (name[0] == '\0')
376                                 continue;
377                         if (name[0] == '-') {
378                                 puts("user names may not start with '-'.");
379                                 continue;
380                         }
381                         if (!(upper || lower || digit)) {
382                                 if (AL) {
383                                         syslog(LOG_ERR,
384                                             "invalid auto-login name: %s", AL);
385                                         exit(1);
386                                 } else
387                                         continue;
388                         }
389                         set_flags(2);
390                         if (crmod) {
391                                 tmode.c_iflag |= ICRNL;
392                                 tmode.c_oflag |= ONLCR;
393                         }
394 #if REALLY_OLD_TTYS
395                         if (upper || UC)
396                                 tmode.sg_flags |= LCASE;
397                         if (lower || LC)
398                                 tmode.sg_flags &= ~LCASE;
399 #endif
400                         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
401                                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
402                                 exit(1);
403                         }
404                         signal(SIGINT, SIG_DFL);
405                         for (i = 0; environ[i] != (char *)0; i++)
406                                 env[i] = environ[i];
407                         makeenv(&env[i]);
408
409                         limit.rlim_max = RLIM_INFINITY;
410                         limit.rlim_cur = RLIM_INFINITY;
411                         (void)setrlimit(RLIMIT_CPU, &limit);
412                         execle(LO, "login", AL ? "-fp" : "-p", name,
413                             (char *) 0, env);
414                         syslog(LOG_ERR, "%s: %m", LO);
415                         exit(1);
416                 }
417                 alarm(0);
418                 signal(SIGALRM, SIG_DFL);
419                 signal(SIGINT, SIG_IGN);
420                 if (NX && *NX) {
421                         tname = NX;
422                         dogettytab();
423                 }
424         }
425 }
426
427 static int
428 opentty(const char *tty, int flags)
429 {
430         int i;
431         int failopenlogged = 0;
432
433         while ((i = open(tty, flags)) == -1)
434         {
435                 if (!failopenlogged) {
436                         syslog(LOG_ERR, "open %s: %m", tty);
437                         failopenlogged = 1;
438                 }
439                 sleep(60);
440         }
441         if (login_tty(i) < 0) { 
442                 if (daemon(0,0) < 0) {
443                         syslog(LOG_ERR,"daemon: %m");
444                         close(i);
445                         return 0;
446                 }
447                 if (login_tty(i) < 0) {
448                         syslog(LOG_ERR, "login_tty %s: %m", tty);
449                         close(i);
450                         return 0;
451                 }
452         }
453         return 1;
454 }
455
456 static void
457 defttymode(void)
458 {
459         struct termios def;
460
461         /* Start with default tty settings. */
462         if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
463                 syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
464                 exit(1);
465         }
466         omode = tmode; /* fill c_cc for dogettytab() */
467         dogettytab();
468         /*
469          * Don't rely on the driver too much, and initialize crucial
470          * things according to <sys/ttydefaults.h>.  Avoid clobbering
471          * the c_cc[] settings however, the console drivers might wish
472          * to leave their idea of the preferred VERASE key value
473          * there.
474          */
475         cfmakesane(&def);
476         tmode.c_iflag = def.c_iflag;
477         tmode.c_oflag = def.c_oflag;
478         tmode.c_lflag = def.c_lflag;
479         tmode.c_cflag = def.c_cflag;
480         if (NC)
481                 tmode.c_cflag |= CLOCAL;
482         omode = tmode;
483 }
484
485 static void
486 setttymode(int raw)
487 {
488         int off = 0;
489
490         (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
491         ioctl(STDIN_FILENO, FIONBIO, &off);     /* turn off non-blocking mode */
492         ioctl(STDIN_FILENO, FIOASYNC, &off);    /* ditto for async mode */
493
494         if (IS)
495                 cfsetispeed(&tmode, speed(IS));
496         else if (SP)
497                 cfsetispeed(&tmode, speed(SP));
498         if (OS)
499                 cfsetospeed(&tmode, speed(OS));
500         else if (SP)
501                 cfsetospeed(&tmode, speed(SP));
502         set_flags(0);
503         setchars();
504         if (raw)
505                 cfmakeraw(&tmode);
506         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
507                 syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
508                 exit(1);
509         }
510 }
511
512
513 static int
514 getname(void)
515 {
516         int c;
517         char *np;
518         unsigned char cs;
519         int ppp_state = 0;
520         int ppp_connection = 0;
521
522         /*
523          * Interrupt may happen if we use CBREAK mode
524          */
525         if (setjmp(intrupt)) {
526                 signal(SIGINT, SIG_IGN);
527                 return (0);
528         }
529         signal(SIGINT, interrupt);
530         set_flags(1);
531         prompt();
532         oflush();
533         if (PF > 0) {
534                 sleep(PF);
535                 PF = 0;
536         }
537         if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
538                 syslog(LOG_ERR, "%s: %m", ttyn);
539                 exit(1);
540         }
541         crmod = digit = lower = upper = 0;
542         np = name;
543         for (;;) {
544                 oflush();
545                 if (read(STDIN_FILENO, &cs, 1) <= 0)
546                         exit(0);
547                 if ((c = cs&0177) == 0)
548                         return (0);
549
550                 /* PPP detection state machine..
551                    Look for sequences:
552                    PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
553                    PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
554                    See RFC1662.
555                    Derived from code from Michael Hancock, <michaelh@cet.co.jp>
556                    and Erik 'PPP' Olson, <eriko@wrq.com>
557                  */
558
559                 if (PP && (cs == PPP_FRAME)) {
560                         ppp_state = 1;
561                 } else if (ppp_state == 1 && cs == PPP_STATION) {
562                         ppp_state = 2;
563                 } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
564                         ppp_state = 3;
565                 } else if ((ppp_state == 2 && cs == PPP_CONTROL)
566                         || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
567                         ppp_state = 4;
568                 } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
569                         ppp_state = 5;
570                 } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
571                         ppp_connection = 1;
572                         break;
573                 } else {
574                         ppp_state = 0;
575                 }
576
577                 if (c == EOT || c == CTRL('d'))
578                         exit(0);
579                 if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
580                         putf("\r\n");
581                         break;
582                 }
583                 if (islower(c))
584                         lower = 1;
585                 else if (isupper(c))
586                         upper = 1;
587                 else if (c == ERASE || c == '\b' || c == 0177) {
588                         if (np > name) {
589                                 np--;
590                                 if (cfgetospeed(&tmode) >= 1200)
591                                         puts("\b \b");
592                                 else
593                                         putchr(cs);
594                         }
595                         continue;
596                 } else if (c == KILL || c == CTRL('u')) {
597                         putchr('\r');
598                         if (cfgetospeed(&tmode) < 1200)
599                                 putchr('\n');
600                         /* this is the way they do it down under ... */
601                         else if (np > name)
602                                 puts("                                     \r");
603                         prompt();
604                         digit = lower = upper = 0;
605                         np = name;
606                         continue;
607                 } else if (isdigit(c))
608                         digit = 1;
609                 if (IG && (c <= ' ' || c > 0176))
610                         continue;
611                 *np++ = c;
612                 putchr(cs);
613         }
614         signal(SIGINT, SIG_IGN);
615         *np = 0;
616         if (c == '\r')
617                 crmod = 1;
618         if ((upper && !lower && !LC) || UC)
619                 for (np = name; *np; np++)
620                         if (isupper(*np))
621                                 *np = tolower(*np);
622         return (1 + ppp_connection);
623 }
624
625 static void
626 putpad(const char *s)
627 {
628         int pad = 0;
629         speed_t ospeed = cfgetospeed(&tmode);
630
631         if (isdigit(*s)) {
632                 while (isdigit(*s)) {
633                         pad *= 10;
634                         pad += *s++ - '0';
635                 }
636                 pad *= 10;
637                 if (*s == '.' && isdigit(s[1])) {
638                         pad += s[1] - '0';
639                         s += 2;
640                 }
641         }
642
643         puts(s);
644         /*
645          * If no delay needed, or output speed is
646          * not comprehensible, then don't try to delay.
647          */
648         if (pad == 0 || ospeed <= 0)
649                 return;
650
651         /*
652          * Round up by a half a character frame, and then do the delay.
653          * Too bad there are no user program accessible programmed delays.
654          * Transmitting pad characters slows many terminals down and also
655          * loads the system.
656          */
657         pad = (pad * ospeed + 50000) / 100000;
658         while (pad--)
659                 putchr(*PC);
660 }
661
662 static void
663 puts(const char *s)
664 {
665         while (*s)
666                 putchr(*s++);
667 }
668
669 char    outbuf[OBUFSIZ];
670 int     obufcnt = 0;
671
672 static void
673 putchr(int cc)
674 {
675         char c;
676
677         c = cc;
678         if (!NP) {
679                 c |= partab[c&0177] & 0200;
680                 if (OP)
681                         c ^= 0200;
682         }
683         if (!UB) {
684                 outbuf[obufcnt++] = c;
685                 if (obufcnt >= OBUFSIZ)
686                         oflush();
687         } else
688                 write(STDOUT_FILENO, &c, 1);
689 }
690
691 static void
692 oflush(void)
693 {
694         if (obufcnt)
695                 write(STDOUT_FILENO, outbuf, obufcnt);
696         obufcnt = 0;
697 }
698
699 static void
700 prompt(void)
701 {
702
703         putf(LM);
704         if (CO)
705                 putchr('\n');
706 }
707
708
709 static char *
710 getline(int fd)
711 {
712         int i = 0;
713         static char linebuf[512];
714
715         /*
716          * This is certainly slow, but it avoids having to include
717          * stdio.h unnecessarily. Issue files should be small anyway.
718          */
719         while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
720                 if (linebuf[i] == '\n') {
721                         /* Don't rely on newline mode, assume raw */
722                         linebuf[i++] = '\r';
723                         linebuf[i++] = '\n';
724                         linebuf[i] = '\0';
725                         return linebuf;
726                 }
727                 ++i;
728         }
729         linebuf[i] = '\0';
730         return i ? linebuf : 0;
731 }
732
733 static void
734 putf(const char *cp)
735 {
736         extern char editedhost[];
737         time_t t;
738         char *slash, db[100];
739
740         static struct utsname kerninfo;
741
742         if (!*kerninfo.sysname)
743                 uname(&kerninfo);
744
745         while (*cp) {
746                 if (*cp != '%') {
747                         putchr(*cp++);
748                         continue;
749                 }
750                 switch (*++cp) {
751
752                 case 't':
753                         slash = strrchr(ttyn, '/');
754                         if (slash == (char *) 0)
755                                 puts(ttyn);
756                         else
757                                 puts(&slash[1]);
758                         break;
759
760                 case 'h':
761                         puts(editedhost);
762                         break;
763
764                 case 'd': {
765                         t = (time_t)0;
766                         (void)time(&t);
767                         if (Lo)
768                                 (void)setlocale(LC_TIME, Lo);
769                         (void)strftime(db, sizeof(db), DF, localtime(&t));
770                         puts(db);
771                         break;
772
773                 case 's':
774                         puts(kerninfo.sysname);
775                         break;
776
777                 case 'm':
778                         puts(kerninfo.machine);
779                         break;
780
781                 case 'r':
782                         puts(kerninfo.release);
783                         break;
784
785                 case 'v':
786                         puts(kerninfo.version);
787                         break;
788                 }
789
790                 case '%':
791                         putchr('%');
792                         break;
793                 }
794                 cp++;
795         }
796 }
797
798 /*
799  * Read a gettytab database entry and perform necessary quirks.
800  */
801 static void
802 dogettytab(void)
803 {
804         
805         /* Read the database entry. */
806         gettable(tname, tabent);
807
808         /*
809          * Avoid inheriting the parity values from the default entry
810          * if any of them is set in the current entry.
811          * Mixing different parity settings is unreasonable.
812          */
813         if (OPset || EPset || APset || NPset)
814                 OPset = EPset = APset = NPset = 1;
815
816         /* Fill in default values for unset capabilities. */
817         setdefaults();
818 }