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