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