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.35 (Berkeley) 06/20/92"; 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 int eight, litout, rem; 70 71 int noescape; 72 u_char escapechar = '~'; 73 74 char *speeds[] = { 75 "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200", 76 "1800", "2400", "4800", "9600", "19200", "38400" 77 }; 78 79 #ifdef sun 80 struct winsize { 81 unsigned short ws_row, ws_col; 82 unsigned short ws_xpixel, ws_ypixel; 83 }; 84 #endif 85 struct winsize winsize; 86 87 #ifndef sun 88 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 89 #endif 90 91 void exit(); 92 93 main(argc, argv) 94 int argc; 95 char **argv; 96 { 97 extern char *optarg; 98 extern int optind; 99 struct passwd *pw; 100 struct servent *sp; 101 struct sgttyb ttyb; 102 long omask; 103 int argoff, ch, dflag, one, uid; 104 char *host, *p, *user, term[1024]; 105 void lostpeer(); 106 u_char getescape(); 107 char *getenv(); 108 109 argoff = dflag = 0; 110 one = 1; 111 host = user = NULL; 112 113 if (p = rindex(argv[0], '/')) 114 ++p; 115 else 116 p = argv[0]; 117 118 if (strcmp(p, "rlogin")) 119 host = p; 120 121 /* handle "rlogin host flags" */ 122 if (!host && argc > 2 && argv[1][0] != '-') { 123 host = argv[1]; 124 argoff = 1; 125 } 126 127 #ifdef KERBEROS 128 #define OPTIONS "8EKLde:k:l:x" 129 #else 130 #define OPTIONS "8EKLde:l:" 131 #endif 132 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF) 133 switch(ch) { 134 case '8': 135 eight = 1; 136 break; 137 case 'E': 138 noescape = 1; 139 break; 140 case 'K': 141 #ifdef KERBEROS 142 use_kerberos = 0; 143 #endif 144 break; 145 case 'L': 146 litout = 1; 147 break; 148 case 'd': 149 dflag = 1; 150 break; 151 case 'e': 152 noescape = 0; 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, WNOHANG|WUNTRACED, NULL); 393 if (pid == 0) 394 return; 395 /* if the child (reader) dies, just quit */ 396 if (pid < 0 || pid == child && !WIFSTOPPED(status)) 397 done((int)(status.w_termsig | status.w_retcode)); 398 } 399 /* NOTREACHED */ 400 } 401 402 /* 403 * writer: write to remote: 0 -> line. 404 * ~. terminate 405 * ~^Z suspend rlogin process. 406 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 407 */ 408 writer() 409 { 410 register int bol, local, n; 411 char c; 412 413 bol = 1; /* beginning of line */ 414 local = 0; 415 for (;;) { 416 n = read(STDIN_FILENO, &c, 1); 417 if (n <= 0) { 418 if (n < 0 && errno == EINTR) 419 continue; 420 break; 421 } 422 /* 423 * If we're at the beginning of the line and recognize a 424 * command character, then we echo locally. Otherwise, 425 * characters are echo'd remotely. If the command character 426 * is doubled, this acts as a force and local echo is 427 * suppressed. 428 */ 429 if (bol) { 430 bol = 0; 431 if (!noescape && c == escapechar) { 432 local = 1; 433 continue; 434 } 435 } else if (local) { 436 local = 0; 437 if (c == '.' || c == deftc.t_eofc) { 438 echo(c); 439 break; 440 } 441 if (c == defltc.t_suspc || c == defltc.t_dsuspc) { 442 bol = 1; 443 echo(c); 444 stop(c); 445 continue; 446 } 447 if (c != escapechar) 448 #ifdef CRYPT 449 #ifdef KERBEROS 450 if (doencrypt) 451 (void)des_write(rem, &escapechar, 1); 452 else 453 #endif 454 #endif 455 (void)write(rem, &escapechar, 1); 456 } 457 458 #ifdef CRYPT 459 #ifdef KERBEROS 460 if (doencrypt) { 461 if (des_write(rem, &c, 1) == 0) { 462 msg("line gone"); 463 break; 464 } 465 } else 466 #endif 467 #endif 468 if (write(rem, &c, 1) == 0) { 469 msg("line gone"); 470 break; 471 } 472 bol = c == defkill || c == deftc.t_eofc || 473 c == deftc.t_intrc || c == defltc.t_suspc || 474 c == '\r' || c == '\n'; 475 } 476 } 477 478 echo(c) 479 register char c; 480 { 481 register char *p; 482 char buf[8]; 483 484 p = buf; 485 c &= 0177; 486 *p++ = escapechar; 487 if (c < ' ') { 488 *p++ = '^'; 489 *p++ = c + '@'; 490 } else if (c == 0177) { 491 *p++ = '^'; 492 *p++ = '?'; 493 } else 494 *p++ = c; 495 *p++ = '\r'; 496 *p++ = '\n'; 497 (void)write(STDOUT_FILENO, buf, p - buf); 498 } 499 500 stop(cmdc) 501 char cmdc; 502 { 503 mode(0); 504 (void)signal(SIGCHLD, SIG_IGN); 505 (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); 506 (void)signal(SIGCHLD, catch_child); 507 mode(1); 508 sigwinch(); /* check for size changes */ 509 } 510 511 void 512 sigwinch() 513 { 514 struct winsize ws; 515 516 if (dosigwinch && get_window_size(0, &ws) == 0 && 517 bcmp(&ws, &winsize, sizeof(ws))) { 518 winsize = ws; 519 sendwindow(); 520 } 521 } 522 523 /* 524 * Send the window size to the server via the magic escape 525 */ 526 sendwindow() 527 { 528 struct winsize *wp; 529 char obuf[4 + sizeof (struct winsize)]; 530 531 wp = (struct winsize *)(obuf+4); 532 obuf[0] = 0377; 533 obuf[1] = 0377; 534 obuf[2] = 's'; 535 obuf[3] = 's'; 536 wp->ws_row = htons(winsize.ws_row); 537 wp->ws_col = htons(winsize.ws_col); 538 wp->ws_xpixel = htons(winsize.ws_xpixel); 539 wp->ws_ypixel = htons(winsize.ws_ypixel); 540 541 #ifdef CRYPT 542 #ifdef KERBEROS 543 if(doencrypt) 544 (void)des_write(rem, obuf, sizeof(obuf)); 545 else 546 #endif 547 #endif 548 (void)write(rem, obuf, sizeof(obuf)); 549 } 550 551 /* 552 * reader: read from remote: line -> 1 553 */ 554 #define READING 1 555 #define WRITING 2 556 557 jmp_buf rcvtop; 558 int ppid, rcvcnt, rcvstate; 559 char rcvbuf[8 * 1024]; 560 561 void 562 oob() 563 { 564 struct sgttyb sb; 565 int atmark, n, out, rcvd; 566 char waste[BUFSIZ], mark; 567 568 out = O_RDWR; 569 rcvd = 0; 570 while (recv(rem, &mark, 1, MSG_OOB) < 0) 571 switch (errno) { 572 case EWOULDBLOCK: 573 /* 574 * Urgent data not here yet. It may not be possible 575 * to send it yet if we are blocked for output and 576 * our input buffer is full. 577 */ 578 if (rcvcnt < sizeof(rcvbuf)) { 579 n = read(rem, rcvbuf + rcvcnt, 580 sizeof(rcvbuf) - rcvcnt); 581 if (n <= 0) 582 return; 583 rcvd += n; 584 } else { 585 n = read(rem, waste, sizeof(waste)); 586 if (n <= 0) 587 return; 588 } 589 continue; 590 default: 591 return; 592 } 593 if (mark & TIOCPKT_WINDOW) { 594 /* Let server know about window size changes */ 595 (void)kill(ppid, SIGUSR1); 596 } 597 if (!eight && (mark & TIOCPKT_NOSTOP)) { 598 (void)ioctl(0, TIOCGETP, (char *)&sb); 599 sb.sg_flags &= ~CBREAK; 600 sb.sg_flags |= RAW; 601 (void)ioctl(0, TIOCSETN, (char *)&sb); 602 notc.t_stopc = -1; 603 notc.t_startc = -1; 604 (void)ioctl(0, TIOCSETC, (char *)¬c); 605 } 606 if (!eight && (mark & TIOCPKT_DOSTOP)) { 607 (void)ioctl(0, TIOCGETP, (char *)&sb); 608 sb.sg_flags &= ~RAW; 609 sb.sg_flags |= CBREAK; 610 (void)ioctl(0, TIOCSETN, (char *)&sb); 611 notc.t_stopc = deftc.t_stopc; 612 notc.t_startc = deftc.t_startc; 613 (void)ioctl(0, TIOCSETC, (char *)¬c); 614 } 615 if (mark & TIOCPKT_FLUSHWRITE) { 616 (void)ioctl(1, TIOCFLUSH, (char *)&out); 617 for (;;) { 618 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 619 (void)fprintf(stderr, "rlogin: ioctl: %s.\n", 620 strerror(errno)); 621 break; 622 } 623 if (atmark) 624 break; 625 n = read(rem, waste, sizeof (waste)); 626 if (n <= 0) 627 break; 628 } 629 /* 630 * Don't want any pending data to be output, so clear the recv 631 * buffer. If we were hanging on a write when interrupted, 632 * don't want it to restart. If we were reading, restart 633 * anyway. 634 */ 635 rcvcnt = 0; 636 longjmp(rcvtop, 1); 637 } 638 639 /* oob does not do FLUSHREAD (alas!) */ 640 641 /* 642 * If we filled the receive buffer while a read was pending, longjmp 643 * to the top to restart appropriately. Don't abort a pending write, 644 * however, or we won't know how much was written. 645 */ 646 if (rcvd && rcvstate == READING) 647 longjmp(rcvtop, 1); 648 } 649 650 /* reader: read from remote: line -> 1 */ 651 reader(omask) 652 int omask; 653 { 654 void oob(); 655 656 #if !defined(BSD) || BSD < 43 657 int pid = -getpid(); 658 #else 659 int pid = getpid(); 660 #endif 661 int n, remaining; 662 char *bufp = rcvbuf; 663 664 (void)signal(SIGTTOU, SIG_IGN); 665 (void)signal(SIGURG, oob); 666 ppid = getppid(); 667 (void)fcntl(rem, F_SETOWN, pid); 668 (void)setjmp(rcvtop); 669 (void)sigsetmask(omask); 670 for (;;) { 671 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 672 rcvstate = WRITING; 673 n = write(STDOUT_FILENO, bufp, remaining); 674 if (n < 0) { 675 if (errno != EINTR) 676 return(-1); 677 continue; 678 } 679 bufp += n; 680 } 681 bufp = rcvbuf; 682 rcvcnt = 0; 683 rcvstate = READING; 684 685 #ifdef CRYPT 686 #ifdef KERBEROS 687 if (doencrypt) 688 rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf)); 689 else 690 #endif 691 #endif 692 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 693 if (rcvcnt == 0) 694 return (0); 695 if (rcvcnt < 0) { 696 if (errno == EINTR) 697 continue; 698 (void)fprintf(stderr, "rlogin: read: %s.\n", 699 strerror(errno)); 700 return(-1); 701 } 702 } 703 } 704 705 mode(f) 706 { 707 struct ltchars *ltc; 708 struct sgttyb sb; 709 struct tchars *tc; 710 int lflags; 711 712 (void)ioctl(0, TIOCGETP, (char *)&sb); 713 (void)ioctl(0, TIOCLGET, (char *)&lflags); 714 switch(f) { 715 case 0: 716 sb.sg_flags &= ~(CBREAK|RAW|TBDELAY); 717 sb.sg_flags |= defflags|tabflag; 718 tc = &deftc; 719 ltc = &defltc; 720 sb.sg_kill = defkill; 721 sb.sg_erase = deferase; 722 lflags = deflflags; 723 break; 724 case 1: 725 sb.sg_flags |= (eight ? RAW : CBREAK); 726 sb.sg_flags &= ~defflags; 727 /* preserve tab delays, but turn off XTABS */ 728 if ((sb.sg_flags & TBDELAY) == XTABS) 729 sb.sg_flags &= ~TBDELAY; 730 tc = ¬c; 731 ltc = &noltc; 732 sb.sg_kill = sb.sg_erase = -1; 733 if (litout) 734 lflags |= LLITOUT; 735 break; 736 default: 737 return; 738 } 739 (void)ioctl(0, TIOCSLTC, (char *)ltc); 740 (void)ioctl(0, TIOCSETC, (char *)tc); 741 (void)ioctl(0, TIOCSETN, (char *)&sb); 742 (void)ioctl(0, TIOCLSET, (char *)&lflags); 743 } 744 745 void 746 lostpeer() 747 { 748 (void)signal(SIGPIPE, SIG_IGN); 749 msg("\007connection closed."); 750 done(1); 751 } 752 753 /* copy SIGURGs to the child process. */ 754 void 755 copytochild() 756 { 757 (void)kill(child, SIGURG); 758 } 759 760 msg(str) 761 char *str; 762 { 763 (void)fprintf(stderr, "rlogin: %s\r\n", str); 764 } 765 766 #ifdef KERBEROS 767 /* VARARGS */ 768 warning(va_alist) 769 va_dcl 770 { 771 va_list ap; 772 char *fmt; 773 774 (void)fprintf(stderr, "rlogin: warning, using standard rlogin: "); 775 va_start(ap); 776 fmt = va_arg(ap, char *); 777 vfprintf(stderr, fmt, ap); 778 va_end(ap); 779 (void)fprintf(stderr, ".\n"); 780 } 781 #endif 782 783 usage() 784 { 785 (void)fprintf(stderr, 786 "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n", 787 #ifdef KERBEROS 788 #ifdef CRYPT 789 "8EKLx", " [-k realm] "); 790 #else 791 "8EKL", " [-k realm] "); 792 #endif 793 #else 794 "8EL", " "); 795 #endif 796 exit(1); 797 } 798 799 /* 800 * The following routine provides compatibility (such as it is) between 4.2BSD 801 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize. 802 */ 803 #ifdef sun 804 get_window_size(fd, wp) 805 int fd; 806 struct winsize *wp; 807 { 808 struct ttysize ts; 809 int error; 810 811 if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0) 812 return(error); 813 wp->ws_row = ts.ts_lines; 814 wp->ws_col = ts.ts_cols; 815 wp->ws_xpixel = 0; 816 wp->ws_ypixel = 0; 817 return(0); 818 } 819 #endif 820 821 u_char 822 getescape(p) 823 register char *p; 824 { 825 long val; 826 int len; 827 828 if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 829 return((u_char)*p); 830 /* otherwise, \nnn */ 831 if (*p == '\\' && len >= 2 && len <= 4) { 832 val = strtol(++p, (char **)NULL, 8); 833 for (;;) { 834 if (!*++p) 835 return((u_char)val); 836 if (*p < '0' || *p > '8') 837 break; 838 } 839 } 840 msg("illegal option value -- e"); 841 usage(); 842 /* NOTREACHED */ 843 } 844