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