1 /* 2 * Copyright (c) 1983, 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)rlogin.c 5.33 (Berkeley) 03/01/91"; 16 #endif /* not lint */ 17 18 /* 19 * $Source: mit/rlogin/RCS/rlogin.c,v $ 20 * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall 21 * Exp Locker: kfall $ 22 */ 23 24 /* 25 * rlogin - remote login 26 */ 27 #include <sys/param.h> 28 #include <sys/file.h> 29 #include <sys/socket.h> 30 #include <sys/signal.h> 31 #include <sys/time.h> 32 #include <sys/resource.h> 33 #include <sys/wait.h> 34 35 #include <netinet/in.h> 36 #include <netinet/in_systm.h> 37 #include <netinet/ip.h> 38 #include <netdb.h> 39 40 #include <sgtty.h> 41 #include <setjmp.h> 42 #include <varargs.h> 43 #include <errno.h> 44 #include <pwd.h> 45 #include <stdio.h> 46 #include <unistd.h> 47 #include <string.h> 48 49 #ifdef KERBEROS 50 #include <kerberosIV/des.h> 51 #include <kerberosIV/krb.h> 52 53 CREDENTIALS cred; 54 Key_schedule schedule; 55 int use_kerberos = 1, doencrypt; 56 char dst_realm_buf[REALM_SZ], *dest_realm = NULL; 57 extern char *krb_realmofhost(); 58 #endif 59 60 #ifndef TIOCPKT_WINDOW 61 #define TIOCPKT_WINDOW 0x80 62 #endif 63 64 /* concession to Sun */ 65 #ifndef SIGUSR1 66 #define SIGUSR1 30 67 #endif 68 69 extern int errno; 70 int eight, litout, rem; 71 72 int noescape; 73 u_char escapechar = '~'; 74 75 char *speeds[] = { 76 "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200", 77 "1800", "2400", "4800", "9600", "19200", "38400" 78 }; 79 80 #ifdef sun 81 struct winsize { 82 unsigned short ws_row, ws_col; 83 unsigned short ws_xpixel, ws_ypixel; 84 }; 85 #endif 86 struct winsize winsize; 87 88 #ifndef sun 89 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 90 #endif 91 92 void exit(); 93 94 main(argc, argv) 95 int argc; 96 char **argv; 97 { 98 extern char *optarg; 99 extern int optind; 100 struct passwd *pw; 101 struct servent *sp; 102 struct sgttyb ttyb; 103 long omask; 104 int argoff, ch, dflag, one, uid; 105 char *host, *p, *user, term[1024]; 106 void lostpeer(); 107 u_char getescape(); 108 char *getenv(); 109 110 argoff = dflag = 0; 111 one = 1; 112 host = user = NULL; 113 114 if (p = rindex(argv[0], '/')) 115 ++p; 116 else 117 p = argv[0]; 118 119 if (strcmp(p, "rlogin")) 120 host = p; 121 122 /* handle "rlogin host flags" */ 123 if (!host && argc > 2 && argv[1][0] != '-') { 124 host = argv[1]; 125 argoff = 1; 126 } 127 128 #ifdef KERBEROS 129 #define OPTIONS "8EKLde:k:l:x" 130 #else 131 #define OPTIONS "8EKLde:l:" 132 #endif 133 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF) 134 switch(ch) { 135 case '8': 136 eight = 1; 137 break; 138 case 'E': 139 noescape = 1; 140 break; 141 case 'K': 142 #ifdef KERBEROS 143 use_kerberos = 0; 144 #endif 145 break; 146 case 'L': 147 litout = 1; 148 break; 149 case 'd': 150 dflag = 1; 151 break; 152 case 'e': 153 escapechar = getescape(optarg); 154 break; 155 #ifdef KERBEROS 156 case 'k': 157 dest_realm = dst_realm_buf; 158 (void)strncpy(dest_realm, optarg, REALM_SZ); 159 break; 160 #endif 161 case 'l': 162 user = optarg; 163 break; 164 #ifdef CRYPT 165 #ifdef KERBEROS 166 case 'x': 167 doencrypt = 1; 168 des_set_key(cred.session, schedule); 169 break; 170 #endif 171 #endif 172 case '?': 173 default: 174 usage(); 175 } 176 optind += argoff; 177 argc -= optind; 178 argv += optind; 179 180 /* if haven't gotten a host yet, do so */ 181 if (!host && !(host = *argv++)) 182 usage(); 183 184 if (*argv) 185 usage(); 186 187 if (!(pw = getpwuid(uid = getuid()))) { 188 (void)fprintf(stderr, "rlogin: unknown user id.\n"); 189 exit(1); 190 } 191 if (!user) 192 user = pw->pw_name; 193 194 sp = NULL; 195 #ifdef KERBEROS 196 if (use_kerberos) { 197 sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp"); 198 if (sp == NULL) { 199 use_kerberos = 0; 200 warning("can't get entry for %s/tcp service", 201 doencrypt ? "eklogin" : "klogin"); 202 } 203 } 204 #endif 205 if (sp == NULL) 206 sp = getservbyname("login", "tcp"); 207 if (sp == NULL) { 208 (void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n"); 209 exit(1); 210 } 211 212 (void)strcpy(term, (p = getenv("TERM")) ? p : "network"); 213 if (ioctl(0, TIOCGETP, &ttyb) == 0) { 214 (void)strcat(term, "/"); 215 (void)strcat(term, speeds[ttyb.sg_ospeed]); 216 } 217 218 (void)get_window_size(0, &winsize); 219 220 (void)signal(SIGPIPE, lostpeer); 221 /* will use SIGUSR1 for window size hack, so hold it off */ 222 omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); 223 224 #ifdef KERBEROS 225 try_connect: 226 if (use_kerberos) { 227 rem = KSUCCESS; 228 errno = 0; 229 if (dest_realm == NULL) 230 dest_realm = krb_realmofhost(host); 231 232 #ifdef CRYPT 233 if (doencrypt) 234 rem = krcmd_mutual(&host, sp->s_port, user, term, 0, 235 dest_realm, &cred, schedule); 236 else 237 #endif /* CRYPT */ 238 rem = krcmd(&host, sp->s_port, user, term, 0, 239 dest_realm); 240 if (rem < 0) { 241 use_kerberos = 0; 242 sp = getservbyname("login", "tcp"); 243 if (sp == NULL) { 244 (void)fprintf(stderr, 245 "rlogin: unknown service login/tcp.\n"); 246 exit(1); 247 } 248 if (errno == ECONNREFUSED) 249 warning("remote host doesn't support Kerberos"); 250 if (errno == ENOENT) 251 warning("can't provide Kerberos auth data"); 252 goto try_connect; 253 } 254 } else { 255 #ifdef CRYPT 256 if (doencrypt) { 257 (void)fprintf(stderr, 258 "rlogin: the -x flag requires Kerberos authentication.\n"); 259 exit(1); 260 } 261 #endif /* CRYPT */ 262 rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); 263 } 264 #else 265 rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); 266 #endif /* KERBEROS */ 267 268 if (rem < 0) 269 exit(1); 270 271 if (dflag && 272 setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 273 (void)fprintf(stderr, "rlogin: setsockopt: %s.\n", 274 strerror(errno)); 275 one = IPTOS_LOWDELAY; 276 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, sizeof(int)) < 0) 277 perror("rlogin: setsockopt TOS (ignored)"); 278 279 (void)setuid(uid); 280 doit(omask); 281 /*NOTREACHED*/ 282 } 283 284 int child, defflags, deflflags, tabflag; 285 char deferase, defkill; 286 struct tchars deftc; 287 struct ltchars defltc; 288 struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 289 struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 290 291 doit(omask) 292 long omask; 293 { 294 struct sgttyb sb; 295 void catch_child(), copytochild(), exit(), writeroob(); 296 297 (void)ioctl(0, TIOCGETP, (char *)&sb); 298 defflags = sb.sg_flags; 299 tabflag = defflags & TBDELAY; 300 defflags &= ECHO | CRMOD; 301 deferase = sb.sg_erase; 302 defkill = sb.sg_kill; 303 (void)ioctl(0, TIOCLGET, (char *)&deflflags); 304 (void)ioctl(0, TIOCGETC, (char *)&deftc); 305 notc.t_startc = deftc.t_startc; 306 notc.t_stopc = deftc.t_stopc; 307 (void)ioctl(0, TIOCGLTC, (char *)&defltc); 308 (void)signal(SIGINT, SIG_IGN); 309 setsignal(SIGHUP, exit); 310 setsignal(SIGQUIT, exit); 311 child = fork(); 312 if (child == -1) { 313 (void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno)); 314 done(1); 315 } 316 if (child == 0) { 317 mode(1); 318 if (reader(omask) == 0) { 319 msg("connection closed."); 320 exit(0); 321 } 322 sleep(1); 323 msg("\007connection closed."); 324 exit(1); 325 } 326 327 /* 328 * We may still own the socket, and may have a pending SIGURG (or might 329 * receive one soon) that we really want to send to the reader. Set a 330 * trap that simply copies such signals to the child. 331 */ 332 (void)signal(SIGURG, copytochild); 333 (void)signal(SIGUSR1, writeroob); 334 (void)sigsetmask(omask); 335 (void)signal(SIGCHLD, catch_child); 336 writer(); 337 msg("closed connection."); 338 done(0); 339 } 340 341 /* trap a signal, unless it is being ignored. */ 342 setsignal(sig, act) 343 int sig; 344 void (*act)(); 345 { 346 int omask = sigblock(sigmask(sig)); 347 348 if (signal(sig, act) == SIG_IGN) 349 (void)signal(sig, SIG_IGN); 350 (void)sigsetmask(omask); 351 } 352 353 done(status) 354 int status; 355 { 356 int w, wstatus; 357 358 mode(0); 359 if (child > 0) { 360 /* make sure catch_child does not snap it up */ 361 (void)signal(SIGCHLD, SIG_DFL); 362 if (kill(child, SIGKILL) >= 0) 363 while ((w = wait(&wstatus)) > 0 && w != child); 364 } 365 exit(status); 366 } 367 368 int dosigwinch; 369 void sigwinch(); 370 371 /* 372 * This is called when the reader process gets the out-of-band (urgent) 373 * request to turn on the window-changing protocol. 374 */ 375 void 376 writeroob() 377 { 378 if (dosigwinch == 0) { 379 sendwindow(); 380 (void)signal(SIGWINCH, sigwinch); 381 } 382 dosigwinch = 1; 383 } 384 385 void 386 catch_child() 387 { 388 union wait status; 389 int pid; 390 391 for (;;) { 392 pid = wait3((int *)&status, 393 WNOHANG|WUNTRACED, (struct rusage *)0); 394 if (pid == 0) 395 return; 396 /* if the child (reader) dies, just quit */ 397 if (pid < 0 || pid == child && !WIFSTOPPED(status)) 398 done((int)(status.w_termsig | status.w_retcode)); 399 } 400 /* NOTREACHED */ 401 } 402 403 /* 404 * writer: write to remote: 0 -> line. 405 * ~. terminate 406 * ~^Z suspend rlogin process. 407 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 408 */ 409 writer() 410 { 411 register int bol, local, n; 412 char c; 413 414 bol = 1; /* beginning of line */ 415 local = 0; 416 for (;;) { 417 n = read(STDIN_FILENO, &c, 1); 418 if (n <= 0) { 419 if (n < 0 && errno == EINTR) 420 continue; 421 break; 422 } 423 /* 424 * If we're at the beginning of the line and recognize a 425 * command character, then we echo locally. Otherwise, 426 * characters are echo'd remotely. If the command character 427 * is doubled, this acts as a force and local echo is 428 * suppressed. 429 */ 430 if (bol) { 431 bol = 0; 432 if (!noescape && c == escapechar) { 433 local = 1; 434 continue; 435 } 436 } else if (local) { 437 local = 0; 438 if (c == '.' || c == deftc.t_eofc) { 439 echo(c); 440 break; 441 } 442 if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 443 bol = 1; 444 echo(c); 445 stop(c); 446 continue; 447 } 448 if (c != escapechar) 449 #ifdef CRYPT 450 #ifdef KERBEROS 451 if (doencrypt) 452 (void)des_write(rem, &escapechar, 1); 453 else 454 #endif 455 #endif 456 (void)write(rem, &escapechar, 1); 457 } 458 459 #ifdef CRYPT 460 #ifdef KERBEROS 461 if (doencrypt) { 462 if (des_write(rem, &c, 1) == 0) { 463 msg("line gone"); 464 break; 465 } 466 } else 467 #endif 468 #endif 469 if (write(rem, &c, 1) == 0) { 470 msg("line gone"); 471 break; 472 } 473 bol = c == defkill || c == deftc.t_eofc || 474 c == deftc.t_intrc || c == defltc.t_suspc || 475 c == '\r' || c == '\n'; 476 } 477 } 478 479 echo(c) 480 register char c; 481 { 482 register char *p; 483 char buf[8]; 484 485 p = buf; 486 c &= 0177; 487 *p++ = escapechar; 488 if (c < ' ') { 489 *p++ = '^'; 490 *p++ = c + '@'; 491 } else if (c == 0177) { 492 *p++ = '^'; 493 *p++ = '?'; 494 } else 495 *p++ = c; 496 *p++ = '\r'; 497 *p++ = '\n'; 498 (void)write(STDOUT_FILENO, buf, p - buf); 499 } 500 501 stop(cmdc) 502 char cmdc; 503 { 504 mode(0); 505 (void)signal(SIGCHLD, SIG_IGN); 506 (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 507 (void)signal(SIGCHLD, catch_child); 508 mode(1); 509 sigwinch(); /* check for size changes */ 510 } 511 512 void 513 sigwinch() 514 { 515 struct winsize ws; 516 517 if (dosigwinch && get_window_size(0, &ws) == 0 && 518 bcmp(&ws, &winsize, sizeof(ws))) { 519 winsize = ws; 520 sendwindow(); 521 } 522 } 523 524 /* 525 * Send the window size to the server via the magic escape 526 */ 527 sendwindow() 528 { 529 struct winsize *wp; 530 char obuf[4 + sizeof (struct winsize)]; 531 532 wp = (struct winsize *)(obuf+4); 533 obuf[0] = 0377; 534 obuf[1] = 0377; 535 obuf[2] = 's'; 536 obuf[3] = 's'; 537 wp->ws_row = htons(winsize.ws_row); 538 wp->ws_col = htons(winsize.ws_col); 539 wp->ws_xpixel = htons(winsize.ws_xpixel); 540 wp->ws_ypixel = htons(winsize.ws_ypixel); 541 542 #ifdef CRYPT 543 #ifdef KERBEROS 544 if(doencrypt) 545 (void)des_write(rem, obuf, sizeof(obuf)); 546 else 547 #endif 548 #endif 549 (void)write(rem, obuf, sizeof(obuf)); 550 } 551 552 /* 553 * reader: read from remote: line -> 1 554 */ 555 #define READING 1 556 #define WRITING 2 557 558 jmp_buf rcvtop; 559 int ppid, rcvcnt, rcvstate; 560 char rcvbuf[8 * 1024]; 561 562 void 563 oob() 564 { 565 struct sgttyb sb; 566 int atmark, n, out, rcvd; 567 char waste[BUFSIZ], mark; 568 569 out = O_RDWR; 570 rcvd = 0; 571 while (recv(rem, &mark, 1, MSG_OOB) < 0) 572 switch (errno) { 573 case EWOULDBLOCK: 574 /* 575 * Urgent data not here yet. It may not be possible 576 * to send it yet if we are blocked for output and 577 * our input buffer is full. 578 */ 579 if (rcvcnt < sizeof(rcvbuf)) { 580 n = read(rem, rcvbuf + rcvcnt, 581 sizeof(rcvbuf) - rcvcnt); 582 if (n <= 0) 583 return; 584 rcvd += n; 585 } else { 586 n = read(rem, waste, sizeof(waste)); 587 if (n <= 0) 588 return; 589 } 590 continue; 591 default: 592 return; 593 } 594 if (mark & TIOCPKT_WINDOW) { 595 /* Let server know about window size changes */ 596 (void)kill(ppid, SIGUSR1); 597 } 598 if (!eight && (mark & TIOCPKT_NOSTOP)) { 599 (void)ioctl(0, TIOCGETP, (char *)&sb); 600 sb.sg_flags &= ~CBREAK; 601 sb.sg_flags |= RAW; 602 (void)ioctl(0, TIOCSETN, (char *)&sb); 603 notc.t_stopc = -1; 604 notc.t_startc = -1; 605 (void)ioctl(0, TIOCSETC, (char *)¬c); 606 } 607 if (!eight && (mark & TIOCPKT_DOSTOP)) { 608 (void)ioctl(0, TIOCGETP, (char *)&sb); 609 sb.sg_flags &= ~RAW; 610 sb.sg_flags |= CBREAK; 611 (void)ioctl(0, TIOCSETN, (char *)&sb); 612 notc.t_stopc = deftc.t_stopc; 613 notc.t_startc = deftc.t_startc; 614 (void)ioctl(0, TIOCSETC, (char *)¬c); 615 } 616 if (mark & TIOCPKT_FLUSHWRITE) { 617 (void)ioctl(1, TIOCFLUSH, (char *)&out); 618 for (;;) { 619 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 620 (void)fprintf(stderr, "rlogin: ioctl: %s.\n", 621 strerror(errno)); 622 break; 623 } 624 if (atmark) 625 break; 626 n = read(rem, waste, sizeof (waste)); 627 if (n <= 0) 628 break; 629 } 630 /* 631 * Don't want any pending data to be output, so clear the recv 632 * buffer. If we were hanging on a write when interrupted, 633 * don't want it to restart. If we were reading, restart 634 * anyway. 635 */ 636 rcvcnt = 0; 637 longjmp(rcvtop, 1); 638 } 639 640 /* oob does not do FLUSHREAD (alas!) */ 641 642 /* 643 * If we filled the receive buffer while a read was pending, longjmp 644 * to the top to restart appropriately. Don't abort a pending write, 645 * however, or we won't know how much was written. 646 */ 647 if (rcvd && rcvstate == READING) 648 longjmp(rcvtop, 1); 649 } 650 651 /* reader: read from remote: line -> 1 */ 652 reader(omask) 653 int omask; 654 { 655 void oob(); 656 657 #if !defined(BSD) || BSD < 43 658 int pid = -getpid(); 659 #else 660 int pid = getpid(); 661 #endif 662 int n, remaining; 663 char *bufp = rcvbuf; 664 665 (void)signal(SIGTTOU, SIG_IGN); 666 (void)signal(SIGURG, oob); 667 ppid = getppid(); 668 (void)fcntl(rem, F_SETOWN, pid); 669 (void)setjmp(rcvtop); 670 (void)sigsetmask(omask); 671 for (;;) { 672 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 673 rcvstate = WRITING; 674 n = write(STDOUT_FILENO, bufp, remaining); 675 if (n < 0) { 676 if (errno != EINTR) 677 return(-1); 678 continue; 679 } 680 bufp += n; 681 } 682 bufp = rcvbuf; 683 rcvcnt = 0; 684 rcvstate = READING; 685 686 #ifdef CRYPT 687 #ifdef KERBEROS 688 if (doencrypt) 689 rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf)); 690 else 691 #endif 692 #endif 693 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 694 if (rcvcnt == 0) 695 return (0); 696 if (rcvcnt < 0) { 697 if (errno == EINTR) 698 continue; 699 (void)fprintf(stderr, "rlogin: read: %s.\n", 700 strerror(errno)); 701 return(-1); 702 } 703 } 704 } 705 706 mode(f) 707 { 708 struct ltchars *ltc; 709 struct sgttyb sb; 710 struct tchars *tc; 711 int lflags; 712 713 (void)ioctl(0, TIOCGETP, (char *)&sb); 714 (void)ioctl(0, TIOCLGET, (char *)&lflags); 715 switch(f) { 716 case 0: 717 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY); 718 sb.sg_flags |= defflags|tabflag; 719 tc = &deftc; 720 ltc = &defltc; 721 sb.sg_kill = defkill; 722 sb.sg_erase = deferase; 723 lflags = deflflags; 724 break; 725 case 1: 726 sb.sg_flags |= (eight ? RAW : CBREAK); 727 sb.sg_flags &= ~defflags; 728 /* preserve tab delays, but turn off XTABS */ 729 if ((sb.sg_flags & TBDELAY) == XTABS) 730 sb.sg_flags &= ~TBDELAY; 731 tc = ¬c; 732 ltc = &noltc; 733 sb.sg_kill = sb.sg_erase = -1; 734 if (litout) 735 lflags |= LLITOUT; 736 break; 737 default: 738 return; 739 } 740 (void)ioctl(0, TIOCSLTC, (char *)ltc); 741 (void)ioctl(0, TIOCSETC, (char *)tc); 742 (void)ioctl(0, TIOCSETN, (char *)&sb); 743 (void)ioctl(0, TIOCLSET, (char *)&lflags); 744 } 745 746 void 747 lostpeer() 748 { 749 (void)signal(SIGPIPE, SIG_IGN); 750 msg("\007connection closed."); 751 done(1); 752 } 753 754 /* copy SIGURGs to the child process. */ 755 void 756 copytochild() 757 { 758 (void)kill(child, SIGURG); 759 } 760 761 msg(str) 762 char *str; 763 { 764 (void)fprintf(stderr, "rlogin: %s\r\n", str); 765 } 766 767 #ifdef KERBEROS 768 /* VARARGS */ 769 warning(va_alist) 770 va_dcl 771 { 772 va_list ap; 773 char *fmt; 774 775 (void)fprintf(stderr, "rlogin: warning, using standard rlogin: "); 776 va_start(ap); 777 fmt = va_arg(ap, char *); 778 vfprintf(stderr, fmt, ap); 779 va_end(ap); 780 (void)fprintf(stderr, ".\n"); 781 } 782 #endif 783 784 usage() 785 { 786 (void)fprintf(stderr, 787 "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n", 788 #ifdef KERBEROS 789 #ifdef CRYPT 790 "8ELx", " [-k realm] "); 791 #else 792 "8EL", " [-k realm] "); 793 #endif 794 #else 795 "8EL", " "); 796 #endif 797 exit(1); 798 } 799 800 /* 801 * The following routine provides compatibility (such as it is) between 4.2BSD 802 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize. 803 */ 804 #ifdef sun 805 get_window_size(fd, wp) 806 int fd; 807 struct winsize *wp; 808 { 809 struct ttysize ts; 810 int error; 811 812 if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0) 813 return(error); 814 wp->ws_row = ts.ts_lines; 815 wp->ws_col = ts.ts_cols; 816 wp->ws_xpixel = 0; 817 wp->ws_ypixel = 0; 818 return(0); 819 } 820 #endif 821 822 u_char 823 getescape(p) 824 register char *p; 825 { 826 long val; 827 int len; 828 829 if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 830 return((u_char)*p); 831 /* otherwise, \nnn */ 832 if (*p == '\\' && len >= 2 && len <= 4) { 833 val = strtol(++p, (char **)NULL, 8); 834 for (;;) { 835 if (!*++p) 836 return((u_char)val); 837 if (*p < '0' || *p > '8') 838 break; 839 } 840 } 841 msg("illegal option value -- e"); 842 usage(); 843 /* NOTREACHED */ 844 } 845