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