1/* $NetBSD: rlogin.c,v 1.48 2021/08/03 23:21:07 chs Exp $ */ 2 3/* 4 * Copyright (c) 1983, 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__COPYRIGHT("@(#) Copyright (c) 1983, 1990, 1993\ 35 The Regents of the University of California. All rights reserved."); 36#endif /* not lint */ 37 38#ifndef lint 39#if 0 40static char sccsid[] = "@(#)rlogin.c 8.4 (Berkeley) 4/29/95"; 41#else 42__RCSID("$NetBSD: rlogin.c,v 1.48 2021/08/03 23:21:07 chs Exp $"); 43#endif 44#endif /* not lint */ 45 46/* 47 * rlogin - remote login 48 */ 49#include <sys/param.h> 50#include <sys/ioctl.h> 51#include <sys/socket.h> 52#include <sys/time.h> 53#include <sys/resource.h> 54#include <sys/wait.h> 55 56#include <netinet/in.h> 57#include <netinet/in_systm.h> 58#include <netinet/ip.h> 59#include <netinet/tcp.h> 60 61#include <err.h> 62#include <errno.h> 63#include <fcntl.h> 64#include <netdb.h> 65#include <pwd.h> 66#include <setjmp.h> 67#include <signal.h> 68#include <stdarg.h> 69#include <stdio.h> 70#include <stdlib.h> 71#include <string.h> 72#include <termios.h> 73#include <unistd.h> 74 75#include "getport.h" 76 77#ifndef TIOCPKT_WINDOW 78#define TIOCPKT_WINDOW 0x80 79#endif 80 81/* concession to Sun */ 82#ifndef SIGUSR1 83#define SIGUSR1 30 84#endif 85 86#ifndef CCEQ 87#define CCEQ(val, c) (c == val ? val != _POSIX_VDISABLE : 0) 88#endif 89 90static int eight, rem; 91static struct termios deftty; 92 93static int noescape; 94static u_char escapechar = '~'; 95 96#ifdef OLDSUN 97struct winsize { 98 unsigned short ws_row, ws_col; 99 unsigned short ws_xpixel, ws_ypixel; 100}; 101#else 102#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) 103#endif 104static struct winsize winsize; 105 106static void catch_child(int); 107static void copytochild(int); 108__dead static void doit(sigset_t *); 109__dead static void done(int); 110static void echo(int); 111static u_int getescape(char *); 112__dead static void lostpeer(int); 113static void mode(int); 114static void msg(const char *); 115static void oob(int); 116static int reader(sigset_t *); 117static void sendwindow(void); 118static void setsignal(int); 119static void sigwinch(int); 120static void stop(int); 121__dead static void usage(void); 122static void writer(void); 123static void writeroob(int); 124 125#ifdef OLDSUN 126static int get_window_size(int, struct winsize *); 127#endif 128 129int 130main(int argc, char *argv[]) 131{ 132 struct passwd *pw; 133 struct servent *sp; 134 struct termios tty; 135 sigset_t imask, omask; 136 uid_t uid; 137 int argoff, ch, dflag, nflag, one; 138 int i, len, len2; 139 int family = AF_UNSPEC; 140 char *host, *p, *user, *name, term[1024] = "network"; 141 speed_t ospeed; 142 struct sigaction sa; 143 char *service = NULL; 144 struct rlimit rlim; 145 146 argoff = dflag = nflag = 0; 147 one = 1; 148 host = user = NULL; 149 sp = NULL; 150 151 if (strcmp(getprogname(), "rlogin") != 0) { 152 host = strdup(getprogname()); 153 if (host == NULL) 154 err(1, NULL); 155 } 156 157 /* handle "rlogin host flags" */ 158 if (!host && argc > 2 && argv[1][0] != '-') { 159 host = argv[1]; 160 argoff = 1; 161 } 162 163#define OPTIONS "468dEe:l:np:" 164 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1) 165 switch(ch) { 166 case '4': 167 family = AF_INET; 168 break; 169 case '6': 170 family = AF_INET6; 171 break; 172 case '8': 173 eight = 1; 174 break; 175 case 'd': 176 dflag = 1; 177 break; 178 case 'E': 179 noescape = 1; 180 break; 181 case 'e': 182 noescape = 0; 183 escapechar = getescape(optarg); 184 break; 185 case 'l': 186 user = optarg; 187 break; 188 case 'n': 189 nflag = 1; 190 break; 191 case 'p': 192 sp = getport(service = optarg, "tcp"); 193 break; 194 case '?': 195 default: 196 usage(); 197 } 198 optind += argoff; 199 argc -= optind; 200 argv += optind; 201 202 /* if haven't gotten a host yet, do so */ 203 if (!host && !(host = *argv++)) 204 usage(); 205 206 if (*argv) 207 usage(); 208 209 if (!(pw = getpwuid(uid = getuid()))) 210 errx(1, "unknown user id."); 211 /* Accept user1@host format, though "-l user2" overrides user1 */ 212 p = strchr(host, '@'); 213 if (p) { 214 *p = '\0'; 215 if (!user && p > host) 216 user = host; 217 host = p + 1; 218 if (*host == '\0') 219 usage(); 220 } 221 if ((name = strdup(pw->pw_name)) == NULL) 222 err(1, "malloc"); 223 if (!user) 224 user = name; 225 226 if (sp == NULL) 227 sp = getservbyname("login", "tcp"); 228 if (sp == NULL) 229 errx(1, "login/tcp: unknown service."); 230 231 if ((p = getenv("TERM")) != NULL) 232 (void)strlcpy(term, p, sizeof(term)); 233 len = strlen(term); 234 if (len < (int)(sizeof(term) - 1) && tcgetattr(0, &tty) == 0) { 235 /* start at 2 to include the / */ 236 for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++) 237 i /= 10; 238 239 if (len + len2 < (int)sizeof(term)) 240 (void)snprintf(term + len, len2 + 1, "/%d", ospeed); 241 } 242 243 (void)get_window_size(0, &winsize); 244 245 sigemptyset(&sa.sa_mask); 246 sa.sa_flags = SA_RESTART; 247 sa.sa_handler = lostpeer; 248 (void)sigaction(SIGPIPE, &sa, NULL); 249 /* will use SIGUSR1 for window size hack, so hold it off */ 250 sigemptyset(&imask); 251 sigaddset(&imask, SIGURG); 252 sigaddset(&imask, SIGUSR1); 253 (void)sigprocmask(SIG_SETMASK, &imask, &omask); 254 /* 255 * We set SIGURG and SIGUSR1 below so that an 256 * incoming signal will be held pending rather than being 257 * discarded. Note that these routines will be ready to get 258 * a signal by the time that they are unblocked below. 259 */ 260 sa.sa_handler = copytochild; 261 (void)sigaction(SIGURG, &sa, NULL); 262 sa.sa_handler = writeroob; 263 (void)sigaction(SIGUSR1, &sa, NULL); 264 265 /* don't dump core */ 266 rlim.rlim_cur = rlim.rlim_max = 0; 267 if (setrlimit(RLIMIT_CORE, &rlim) < 0) 268 warn("setrlimit"); 269 270 rem = rcmd_af(&host, sp->s_port, name, user, term, 0, family); 271 if (rem < 0) 272 exit(1); 273 274 if (dflag && 275 setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0) 276 warn("setsockopt DEBUG (ignored)"); 277 if (nflag && 278 setsockopt(rem, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) < 0) 279 warn("setsockopt NODELAY (ignored)"); 280 281 { 282 struct sockaddr_storage ss; 283 socklen_t sslen = sizeof(ss); 284 if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0 285 && ((struct sockaddr *)&ss)->sa_family == AF_INET) { 286 one = IPTOS_LOWDELAY; 287 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one, 288 sizeof(int)) < 0) 289 warn("setsockopt TOS (ignored)"); 290 } 291 } 292 293 (void)setuid(uid); 294 doit(&omask); 295 /*NOTREACHED*/ 296 return (0); 297} 298 299static pid_t child; 300 301static void 302doit(sigset_t *smask) 303{ 304 struct sigaction sa; 305 306 sigemptyset(&sa.sa_mask); 307 sa.sa_flags = SA_RESTART; 308 sa.sa_handler = SIG_IGN; 309 (void)sigaction(SIGINT, &sa, NULL); 310 setsignal(SIGHUP); 311 setsignal(SIGQUIT); 312 mode(1); 313 child = fork(); 314 if (child == -1) { 315 warn("fork"); 316 done(1); 317 } 318 if (child == 0) { 319 mode(1); 320 if (reader(smask) == 0) { 321 msg("connection closed."); 322 exit(0); 323 } 324 sleep(1); 325 msg("\aconnection closed."); 326 exit(1); 327 } 328 329 /* 330 * We may still own the socket, and may have a pending SIGURG (or might 331 * receive one soon) that we really want to send to the reader. When 332 * one of these comes in, the trap copytochild simply copies such 333 * signals to the child. We can now unblock SIGURG and SIGUSR1 334 * that were set above. 335 */ 336 (void)sigprocmask(SIG_SETMASK, smask, NULL); 337 sa.sa_handler = catch_child; 338 (void)sigaction(SIGCHLD, &sa, NULL); 339 writer(); 340 msg("closed connection."); 341 done(0); 342} 343 344/* trap a signal, unless it is being ignored. */ 345static void 346setsignal(int sig) 347{ 348 struct sigaction isa, osa; 349 sigset_t isigs, osigs; 350 351 sigemptyset(&isigs); 352 sigaddset(&isigs, sig); 353 sigprocmask(SIG_BLOCK, &isigs, &osigs); 354 355 sigemptyset(&isa.sa_mask); 356 isa.sa_handler = exit; 357 isa.sa_flags = SA_RESTART; 358 (void)sigaction(sig, &isa, &osa); 359 if (osa.sa_handler == SIG_IGN) 360 (void)sigaction(sig, &osa, NULL); 361 362 (void)sigprocmask(SIG_SETMASK, &osigs, NULL); 363} 364 365static void 366done(int status) 367{ 368 pid_t w; 369 int wstatus; 370 struct sigaction sa; 371 372 mode(0); 373 if (child > 0) { 374 /* make sure catch_child does not snap it up */ 375 sigemptyset(&sa.sa_mask); 376 sa.sa_handler = SIG_DFL; 377 sa.sa_flags = 0; 378 (void)sigaction(SIGCHLD, &sa, NULL); 379 if (kill(child, SIGKILL) >= 0) 380 while ((w = wait(&wstatus)) > 0 && w != child) 381 continue; 382 } 383 exit(status); 384} 385 386static int dosigwinch; 387 388/* 389 * This is called when the reader process gets the out-of-band (urgent) 390 * request to turn on the window-changing protocol. 391 */ 392static void 393writeroob(int signo) 394{ 395 struct sigaction sa; 396 397 if (dosigwinch == 0) { 398 sendwindow(); 399 sigemptyset(&sa.sa_mask); 400 sa.sa_handler = sigwinch; 401 sa.sa_flags = SA_RESTART; 402 (void)sigaction(SIGWINCH, &sa, NULL); 403 } 404 dosigwinch = 1; 405} 406 407static void 408catch_child(int signo) 409{ 410 int status; 411 pid_t pid; 412 413 for (;;) { 414 pid = waitpid(-1, &status, WNOHANG|WUNTRACED); 415 if (pid == 0) 416 return; 417 /* if the child (reader) dies, just quit */ 418 if (pid < 0 || (pid == child && !WIFSTOPPED(status))) 419 done(WEXITSTATUS(status) | WTERMSIG(status)); 420 } 421 /* NOTREACHED */ 422} 423 424/* 425 * writer: write to remote: 0 -> line. 426 * ~. terminate 427 * ~^Z suspend rlogin process. 428 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. 429 */ 430static void 431writer(void) 432{ 433 int bol, local; 434 ssize_t n; 435 char c; 436 437 bol = 1; /* beginning of line */ 438 local = 0; 439 for (;;) { 440 n = read(STDIN_FILENO, &c, 1); 441 if (n <= 0) { 442 if (n < 0 && errno == EINTR) 443 continue; 444 break; 445 } 446 /* 447 * If we're at the beginning of the line and recognize a 448 * command character, then we echo locally. Otherwise, 449 * characters are echo'd remotely. If the command character 450 * is doubled, this acts as a force and local echo is 451 * suppressed. 452 */ 453 if (bol) { 454 bol = 0; 455 if (!noescape && c == escapechar) { 456 local = 1; 457 continue; 458 } 459 } else if (local) { 460 local = 0; 461 if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) { 462 echo((int)c); 463 break; 464 } 465 if (CCEQ(deftty.c_cc[VSUSP], c)) { 466 bol = 1; 467 echo((int)c); 468 stop(1); 469 continue; 470 } 471 if (CCEQ(deftty.c_cc[VDSUSP], c)) { 472 bol = 1; 473 echo((int)c); 474 stop(0); 475 continue; 476 } 477 if (c != escapechar) { 478 (void)write(rem, &escapechar, 1); 479 } 480 } 481 482 if (write(rem, &c, 1) == 0) { 483 msg("line gone"); 484 break; 485 } 486 487 bol = CCEQ(deftty.c_cc[VKILL], c) || 488 CCEQ(deftty.c_cc[VEOF], c) || 489 CCEQ(deftty.c_cc[VINTR], c) || 490 CCEQ(deftty.c_cc[VSUSP], c) || 491 c == '\r' || c == '\n'; 492 } 493} 494 495static void 496echo(int i) 497{ 498 char c = (char)i; 499 char *p; 500 char buf[8]; 501 502 p = buf; 503 c &= 0177; 504 *p++ = escapechar; 505 if (c < ' ') { 506 *p++ = '^'; 507 *p++ = c + '@'; 508 } else if (c == 0177) { 509 *p++ = '^'; 510 *p++ = '?'; 511 } else 512 *p++ = c; 513 *p++ = '\r'; 514 *p++ = '\n'; 515 (void)write(STDOUT_FILENO, buf, p - buf); 516} 517 518static void 519stop(int all) 520{ 521 struct sigaction sa; 522 523 mode(0); 524 sigemptyset(&sa.sa_mask); 525 sa.sa_handler = SIG_IGN; 526 sa.sa_flags = SA_RESTART; 527 (void)sigaction(SIGCHLD, &sa, NULL); 528 (void)kill(all ? 0 : getpid(), SIGTSTP); 529 sa.sa_handler = catch_child; 530 (void)sigaction(SIGCHLD, &sa, NULL); 531 mode(1); 532 sigwinch(0); /* check for size changes */ 533} 534 535static void 536sigwinch(int signo) 537{ 538 struct winsize ws; 539 540 if (dosigwinch && get_window_size(0, &ws) == 0 && 541 memcmp(&ws, &winsize, sizeof(ws))) { 542 winsize = ws; 543 sendwindow(); 544 } 545} 546 547/* 548 * Send the window size to the server via the magic escape 549 */ 550static void 551sendwindow(void) 552{ 553 struct winsize *wp; 554 char obuf[4 + sizeof (struct winsize)]; 555 556 wp = (struct winsize *)(obuf+4); 557 obuf[0] = 0377; 558 obuf[1] = 0377; 559 obuf[2] = 's'; 560 obuf[3] = 's'; 561 wp->ws_row = htons(winsize.ws_row); 562 wp->ws_col = htons(winsize.ws_col); 563 wp->ws_xpixel = htons(winsize.ws_xpixel); 564 wp->ws_ypixel = htons(winsize.ws_ypixel); 565 566 (void)write(rem, obuf, sizeof(obuf)); 567} 568 569/* 570 * reader: read from remote: line -> 1 571 */ 572#define READING 1 573#define WRITING 2 574 575static jmp_buf rcvtop; 576static pid_t ppid; 577static ssize_t rcvcnt, rcvstate; 578static char rcvbuf[8 * 1024]; 579 580static void 581oob(int signo) 582{ 583 struct termios tty; 584 int atmark; 585 ssize_t n, rcvd; 586 char waste[BUFSIZ], mark; 587 588 rcvd = 0; 589 while (recv(rem, &mark, 1, MSG_OOB) == -1) { 590 switch (errno) { 591 case EWOULDBLOCK: 592 /* 593 * Urgent data not here yet. It may not be possible 594 * to send it yet if we are blocked for output and 595 * our input buffer is full. 596 */ 597 if (rcvcnt < (ssize_t)sizeof(rcvbuf)) { 598 n = read(rem, rcvbuf + rcvcnt, 599 sizeof(rcvbuf) - rcvcnt); 600 if (n <= 0) 601 return; 602 rcvd += n; 603 } else { 604 n = read(rem, waste, sizeof(waste)); 605 if (n <= 0) 606 return; 607 } 608 continue; 609 default: 610 return; 611 } 612 } 613 if (mark & TIOCPKT_WINDOW) { 614 /* Let server know about window size changes */ 615 (void)kill(ppid, SIGUSR1); 616 } 617 if (!eight && (mark & TIOCPKT_NOSTOP)) { 618 (void)tcgetattr(0, &tty); 619 tty.c_iflag &= ~IXON; 620 (void)tcsetattr(0, TCSANOW, &tty); 621 } 622 if (!eight && (mark & TIOCPKT_DOSTOP)) { 623 (void)tcgetattr(0, &tty); 624 tty.c_iflag |= (deftty.c_iflag & IXON); 625 (void)tcsetattr(0, TCSANOW, &tty); 626 } 627 if (mark & TIOCPKT_FLUSHWRITE) { 628 (void)tcflush(1, TCIOFLUSH); 629 for (;;) { 630 if (ioctl(rem, SIOCATMARK, &atmark) < 0) { 631 warn("ioctl SIOCATMARK (ignored)"); 632 break; 633 } 634 if (atmark) 635 break; 636 n = read(rem, waste, sizeof (waste)); 637 if (n <= 0) 638 break; 639 } 640 /* 641 * Don't want any pending data to be output, so clear the recv 642 * buffer. If we were hanging on a write when interrupted, 643 * don't want it to restart. If we were reading, restart 644 * anyway. 645 */ 646 rcvcnt = 0; 647 longjmp(rcvtop, 1); 648 } 649 650 /* oob does not do FLUSHREAD (alas!) */ 651 652 /* 653 * If we filled the receive buffer while a read was pending, longjmp 654 * to the top to restart appropriately. Don't abort a pending write, 655 * however, or we won't know how much was written. 656 */ 657 if (rcvd && rcvstate == READING) 658 longjmp(rcvtop, 1); 659} 660 661/* reader: read from remote: line -> 1 */ 662static int 663reader(sigset_t *smask) 664{ 665 pid_t pid; 666 ssize_t n, remaining; 667 char *bufp; 668 struct sigaction sa; 669 670 pid = getpid(); /* modern systems use positives for pid */ 671 sigemptyset(&sa.sa_mask); 672 sa.sa_flags = SA_RESTART; 673 sa.sa_handler = SIG_IGN; 674 (void)sigaction(SIGTTOU, &sa, NULL); 675 sa.sa_handler = oob; 676 (void)sigaction(SIGURG, &sa, NULL); 677 ppid = getppid(); 678 (void)fcntl(rem, F_SETOWN, pid); 679 (void)setjmp(rcvtop); 680 (void)sigprocmask(SIG_SETMASK, smask, NULL); 681 bufp = rcvbuf; 682 for (;;) { 683 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { 684 rcvstate = WRITING; 685 n = write(STDOUT_FILENO, bufp, remaining); 686 if (n < 0) { 687 if (errno != EINTR) 688 return (-1); 689 continue; 690 } 691 bufp += n; 692 } 693 bufp = rcvbuf; 694 rcvcnt = 0; 695 rcvstate = READING; 696 697 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); 698 if (rcvcnt == 0) 699 return (0); 700 if (rcvcnt < 0) { 701 if (errno == EINTR) 702 continue; 703 warn("read"); 704 return (-1); 705 } 706 } 707} 708 709static void 710mode(int f) 711{ 712 struct termios tty; 713 714 switch (f) { 715 case 0: 716 (void)tcsetattr(0, TCSANOW, &deftty); 717 break; 718 case 1: 719 (void)tcgetattr(0, &deftty); 720 tty = deftty; 721 /* This is loosely derived from sys/compat/tty_compat.c. */ 722 tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN); 723 tty.c_iflag &= ~ICRNL; 724 tty.c_oflag &= ~OPOST; 725 tty.c_cc[VMIN] = 1; 726 tty.c_cc[VTIME] = 0; 727 if (eight) { 728 tty.c_iflag &= IXOFF; 729 tty.c_cflag &= ~(CSIZE|PARENB); 730 tty.c_cflag |= CS8; 731 } 732 (void)tcsetattr(0, TCSANOW, &tty); 733 break; 734 735 default: 736 return; 737 } 738} 739 740static void 741lostpeer(int signo) 742{ 743 struct sigaction sa; 744 sa.sa_flags = SA_RESTART; 745 sa.sa_handler = SIG_IGN; 746 sigemptyset(&sa.sa_mask); 747 (void)sigaction(SIGPIPE, &sa, NULL); 748 msg("\aconnection closed."); 749 done(1); 750} 751 752/* copy SIGURGs to the child process. */ 753static void 754copytochild(int signo) 755{ 756 757 (void)kill(child, SIGURG); 758} 759 760static void 761msg(const char *str) 762{ 763 764 (void)fprintf(stderr, "rlogin: %s\r\n", str); 765} 766 767static void 768usage(void) 769{ 770 (void)fprintf(stderr, 771 "Usage: %s [-468dEn] [-e char] [-l username] [-p port] " 772 "[username@]host\n", getprogname()); 773 exit(1); 774} 775 776/* 777 * The following routine provides compatibility (such as it is) between older 778 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize. 779 */ 780#ifdef OLDSUN 781static int 782get_window_size(int fd, struct winsize *wp) 783{ 784 struct ttysize ts; 785 int error; 786 787 if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0) 788 return (error); 789 wp->ws_row = ts.ts_lines; 790 wp->ws_col = ts.ts_cols; 791 wp->ws_xpixel = 0; 792 wp->ws_ypixel = 0; 793 return (0); 794} 795#endif 796 797static u_int 798getescape(char *p) 799{ 800 long val; 801 size_t len; 802 803 if ((len = strlen(p)) == 1) /* use any single char, including '\' */ 804 return ((u_int)*p); 805 /* otherwise, \nnn */ 806 if (*p == '\\' && len >= 2 && len <= 4) { 807 val = strtol(++p, NULL, 8); 808 for (;;) { 809 if (!*++p) 810 return ((u_int)val); 811 if (*p < '0' || *p > '8') 812 break; 813 } 814 } 815 msg("illegal option value -- e"); 816 usage(); 817 /* NOTREACHED */ 818 return (0); 819} 820