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