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.2 2003/06/17 04:27:07 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 __P((int)); 126 static int getname __P((void)); 127 static void interrupt __P((int)); 128 static void oflush __P((void)); 129 static void prompt __P((void)); 130 static void putchr __P((int)); 131 static void putf __P((const char *)); 132 static void putpad __P((const char *)); 133 static void puts __P((const char *)); 134 static void timeoverrun __P((int)); 135 static char *getline __P((int)); 136 static void setttymode __P((const char *, int)); 137 static void setdefttymode __P((const char *)); 138 static int opentty __P((const char *, int)); 139 140 int main __P((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 } 791