rlogin.c revision 1.37 1 /* $NetBSD: rlogin.c,v 1.37 2006/05/09 20:18:09 mrg 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\n\
35 The Regents of the University of California. All rights reserved.\n");
36 #endif /* not lint */
37
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)rlogin.c 8.4 (Berkeley) 4/29/95";
41 #else
42 __RCSID("$NetBSD: rlogin.c,v 1.37 2006/05/09 20:18:09 mrg 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 #include <sys/ioctl.h>
56
57 #include <netinet/in.h>
58 #include <netinet/in_systm.h>
59 #include <netinet/ip.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
78 #ifndef TIOCPKT_WINDOW
79 #define TIOCPKT_WINDOW 0x80
80 #endif
81
82 /* concession to Sun */
83 #ifndef SIGUSR1
84 #define SIGUSR1 30
85 #endif
86
87 #ifndef CCEQ
88 #define CCEQ(val, c) (c == val ? val != _POSIX_VDISABLE : 0)
89 #endif
90
91 int eight, rem;
92 struct termios deftty;
93
94 int noescape;
95 u_char escapechar = '~';
96
97 #ifdef OLDSUN
98 struct winsize {
99 unsigned short ws_row, ws_col;
100 unsigned short ws_xpixel, ws_ypixel;
101 };
102 #else
103 #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
104 #endif
105 struct winsize winsize;
106
107 void catch_child(int);
108 void copytochild(int);
109 void doit(sigset_t *);
110 void done(int);
111 void echo(int);
112 u_int getescape(char *);
113 void lostpeer(int);
114 int main(int, char **);
115 void mode(int);
116 void msg(const char *);
117 void oob(int);
118 int reader(sigset_t *);
119 void sendwindow(void);
120 void setsignal(int);
121 void sigwinch(int);
122 void stop(int);
123 void usage(void);
124 void writer(void);
125 void writeroob(int);
126
127 #ifdef OLDSUN
128 int get_window_size(int, struct winsize *);
129 #endif
130
131 int
132 main(int argc, char *argv[])
133 {
134 struct passwd *pw;
135 struct servent *sp;
136 struct termios tty;
137 sigset_t smask;
138 uid_t uid;
139 int argoff, ch, dflag, one;
140 int i, len, len2;
141 int family = AF_UNSPEC;
142 char *host, *p, *user, *name, term[1024] = "network";
143 speed_t ospeed;
144 struct sigaction sa;
145 char *service = NULL;
146 struct rlimit rlim;
147
148 argoff = dflag = 0;
149 one = 1;
150 host = user = NULL;
151 sp = NULL;
152
153 if (strcmp(getprogname(), "rlogin") != 0) {
154 host = strdup(getprogname());
155 if (host == NULL)
156 err(1, NULL);
157 }
158
159 /* handle "rlogin host flags" */
160 if (!host && argc > 2 && argv[1][0] != '-') {
161 host = argv[1];
162 argoff = 1;
163 }
164
165 #define OPTIONS "468dEe:l:p:"
166 while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != -1)
167 switch(ch) {
168 case '4':
169 family = AF_INET;
170 break;
171 case '6':
172 family = AF_INET6;
173 break;
174 case '8':
175 eight = 1;
176 break;
177 case 'd':
178 dflag = 1;
179 break;
180 case 'E':
181 noescape = 1;
182 break;
183 case 'e':
184 noescape = 0;
185 escapechar = getescape(optarg);
186 break;
187 case 'l':
188 user = optarg;
189 break;
190 case 'p':
191 sp = getport(service = optarg, "tcp");
192 break;
193 case '?':
194 default:
195 usage();
196 }
197 optind += argoff;
198 argc -= optind;
199 argv += optind;
200
201 /* if haven't gotten a host yet, do so */
202 if (!host && !(host = *argv++))
203 usage();
204
205 if (*argv)
206 usage();
207
208 if (!(pw = getpwuid(uid = getuid())))
209 errx(1, "unknown user id.");
210 /* Accept user1@host format, though "-l user2" overrides user1 */
211 p = strchr(host, '@');
212 if (p) {
213 *p = '\0';
214 if (!user && p > host)
215 user = host;
216 host = p + 1;
217 if (*host == '\0')
218 usage();
219 }
220 if ((name = strdup(pw->pw_name)) == NULL)
221 err(1, "malloc");
222 if (!user)
223 user = name;
224
225 if (sp == NULL)
226 sp = getservbyname("login", "tcp");
227 if (sp == NULL)
228 errx(1, "login/tcp: unknown service.");
229
230 if ((p = getenv("TERM")) != NULL)
231 (void)strlcpy(term, p, sizeof(term));
232 len = strlen(term);
233 if (len < (sizeof(term) - 1) && tcgetattr(0, &tty) == 0) {
234 /* start at 2 to include the / */
235 for (ospeed = i = cfgetospeed(&tty), len2 = 2; i > 9; len2++)
236 i /= 10;
237
238 if (len + len2 < sizeof(term))
239 (void)snprintf(term + len, len2 + 1, "/%d", ospeed);
240 }
241
242 (void)get_window_size(0, &winsize);
243
244 sigemptyset(&sa.sa_mask);
245 sa.sa_flags = SA_RESTART;
246 sa.sa_handler = lostpeer;
247 (void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
248 /* will use SIGUSR1 for window size hack, so hold it off */
249 sigemptyset(&smask);
250 sigaddset(&smask, SIGURG);
251 sigaddset(&smask, SIGUSR1);
252 (void)sigprocmask(SIG_SETMASK, &smask, &smask);
253 /*
254 * We set SIGURG and SIGUSR1 below so that an
255 * incoming signal will be held pending rather than being
256 * discarded. Note that these routines will be ready to get
257 * a signal by the time that they are unblocked below.
258 */
259 sa.sa_handler = copytochild;
260 (void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
261 sa.sa_handler = writeroob;
262 (void)sigaction(SIGUSR1, &sa, (struct sigaction *) 0);
263
264 /* don't dump core */
265 rlim.rlim_cur = rlim.rlim_max = 0;
266 if (setrlimit(RLIMIT_CORE, &rlim) < 0)
267 warn("setrlimit");
268
269 rem = rcmd_af(&host, sp->s_port, name, user, term, 0, family);
270
271
272 if (rem < 0)
273 exit(1);
274
275 if (dflag &&
276 setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, sizeof(one)) < 0)
277 warn("setsockopt DEBUG (ignored)");
278 {
279 struct sockaddr_storage ss;
280 socklen_t sslen;
281 sslen = sizeof(ss);
282 if (getsockname(rem, (struct sockaddr *)&ss, &sslen) == 0
283 && ((struct sockaddr *)&ss)->sa_family == AF_INET) {
284 one = IPTOS_LOWDELAY;
285 if (setsockopt(rem, IPPROTO_IP, IP_TOS, (char *)&one,
286 sizeof(int)) < 0) {
287 warn("setsockopt TOS (ignored)");
288 }
289 }
290 }
291
292 (void)setuid(uid);
293 doit(&smask);
294 /*NOTREACHED*/
295 return (0);
296 }
297
298 pid_t child;
299
300 void
301 doit(sigset_t *smask)
302 {
303 struct sigaction sa;
304
305 sigemptyset(&sa.sa_mask);
306 sa.sa_flags = SA_RESTART;
307 sa.sa_handler = SIG_IGN;
308 (void)sigaction(SIGINT, &sa, (struct sigaction *) 0);
309 setsignal(SIGHUP);
310 setsignal(SIGQUIT);
311 mode(1);
312 child = fork();
313 if (child == -1) {
314 warn("fork");
315 done(1);
316 }
317 if (child == 0) {
318 mode(1);
319 if (reader(smask) == 0) {
320 msg("connection closed.");
321 exit(0);
322 }
323 sleep(1);
324 msg("\aconnection 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. When
331 * one of these comes in, the trap copytochild simply copies such
332 * signals to the child. We can now unblock SIGURG and SIGUSR1
333 * that were set above.
334 */
335 (void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
336 sa.sa_handler = catch_child;
337 (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
338 writer();
339 msg("closed connection.");
340 done(0);
341 }
342
343 /* trap a signal, unless it is being ignored. */
344 void
345 setsignal(int sig)
346 {
347 struct sigaction sa;
348 sigset_t sigs;
349
350 sigemptyset(&sigs);
351 sigaddset(&sigs, sig);
352 sigprocmask(SIG_BLOCK, &sigs, &sigs);
353
354 sigemptyset(&sa.sa_mask);
355 sa.sa_handler = exit;
356 sa.sa_flags = SA_RESTART;
357 (void)sigaction(sig, &sa, &sa);
358 if (sa.sa_handler == SIG_IGN)
359 (void)sigaction(sig, &sa, (struct sigaction *) 0);
360
361 (void)sigprocmask(SIG_SETMASK, &sigs, (sigset_t *) 0);
362 }
363
364 void
365 done(int status)
366 {
367 pid_t w;
368 int wstatus;
369 struct sigaction sa;
370
371 mode(0);
372 if (child > 0) {
373 /* make sure catch_child does not snap it up */
374 sigemptyset(&sa.sa_mask);
375 sa.sa_handler = SIG_DFL;
376 sa.sa_flags = 0;
377 (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
378 if (kill(child, SIGKILL) >= 0)
379 while ((w = wait(&wstatus)) > 0 && w != child)
380 continue;
381 }
382 exit(status);
383 }
384
385 int dosigwinch;
386
387 /*
388 * This is called when the reader process gets the out-of-band (urgent)
389 * request to turn on the window-changing protocol.
390 */
391 void
392 writeroob(int signo)
393 {
394 struct sigaction sa;
395
396 if (dosigwinch == 0) {
397 sendwindow();
398 sigemptyset(&sa.sa_mask);
399 sa.sa_handler = sigwinch;
400 sa.sa_flags = SA_RESTART;
401 (void)sigaction(SIGWINCH, &sa, (struct sigaction *) 0);
402 }
403 dosigwinch = 1;
404 }
405
406 void
407 catch_child(int signo)
408 {
409 int status;
410 pid_t pid;
411
412 for (;;) {
413 pid = waitpid(-1, &status, WNOHANG|WUNTRACED);
414 if (pid == 0)
415 return;
416 /* if the child (reader) dies, just quit */
417 if (pid < 0 || (pid == child && !WIFSTOPPED(status)))
418 done(WEXITSTATUS(status) | WTERMSIG(status));
419 }
420 /* NOTREACHED */
421 }
422
423 /*
424 * writer: write to remote: 0 -> line.
425 * ~. terminate
426 * ~^Z suspend rlogin process.
427 * ~<delayed-suspend char> suspend rlogin process, but leave reader alone.
428 */
429 void
430 writer(void)
431 {
432 int bol, local, n;
433 char c;
434
435 bol = 1; /* beginning of line */
436 local = 0;
437 for (;;) {
438 n = read(STDIN_FILENO, &c, 1);
439 if (n <= 0) {
440 if (n < 0 && errno == EINTR)
441 continue;
442 break;
443 }
444 /*
445 * If we're at the beginning of the line and recognize a
446 * command character, then we echo locally. Otherwise,
447 * characters are echo'd remotely. If the command character
448 * is doubled, this acts as a force and local echo is
449 * suppressed.
450 */
451 if (bol) {
452 bol = 0;
453 if (!noescape && c == escapechar) {
454 local = 1;
455 continue;
456 }
457 } else if (local) {
458 local = 0;
459 if (c == '.' || CCEQ(deftty.c_cc[VEOF], c)) {
460 echo((int)c);
461 break;
462 }
463 if (CCEQ(deftty.c_cc[VSUSP], c)) {
464 bol = 1;
465 echo((int)c);
466 stop(1);
467 continue;
468 }
469 if (CCEQ(deftty.c_cc[VDSUSP], c)) {
470 bol = 1;
471 echo((int)c);
472 stop(0);
473 continue;
474 }
475 if (c != escapechar) {
476 (void)write(rem, &escapechar, 1);
477 }
478 }
479
480 if (write(rem, &c, 1) == 0) {
481 msg("line gone");
482 break;
483 }
484
485 bol = CCEQ(deftty.c_cc[VKILL], c) ||
486 CCEQ(deftty.c_cc[VEOF], c) ||
487 CCEQ(deftty.c_cc[VINTR], c) ||
488 CCEQ(deftty.c_cc[VSUSP], c) ||
489 c == '\r' || c == '\n';
490 }
491 }
492
493 void
494 echo(int i)
495 {
496 char c = (char)i;
497 char *p;
498 char buf[8];
499
500 p = buf;
501 c &= 0177;
502 *p++ = escapechar;
503 if (c < ' ') {
504 *p++ = '^';
505 *p++ = c + '@';
506 } else if (c == 0177) {
507 *p++ = '^';
508 *p++ = '?';
509 } else
510 *p++ = c;
511 *p++ = '\r';
512 *p++ = '\n';
513 (void)write(STDOUT_FILENO, buf, p - buf);
514 }
515
516 void
517 stop(int all)
518 {
519 struct sigaction sa;
520
521 mode(0);
522 sigemptyset(&sa.sa_mask);
523 sa.sa_handler = SIG_IGN;
524 sa.sa_flags = SA_RESTART;
525 (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
526 (void)kill(all ? 0 : getpid(), SIGTSTP);
527 sa.sa_handler = catch_child;
528 (void)sigaction(SIGCHLD, &sa, (struct sigaction *) 0);
529 mode(1);
530 sigwinch(0); /* check for size changes */
531 }
532
533 void
534 sigwinch(int signo)
535 {
536 struct winsize ws;
537
538 if (dosigwinch && get_window_size(0, &ws) == 0 &&
539 memcmp(&ws, &winsize, sizeof(ws))) {
540 winsize = ws;
541 sendwindow();
542 }
543 }
544
545 /*
546 * Send the window size to the server via the magic escape
547 */
548 void
549 sendwindow(void)
550 {
551 struct winsize *wp;
552 char obuf[4 + sizeof (struct winsize)];
553
554 wp = (struct winsize *)(obuf+4);
555 obuf[0] = 0377;
556 obuf[1] = 0377;
557 obuf[2] = 's';
558 obuf[3] = 's';
559 wp->ws_row = htons(winsize.ws_row);
560 wp->ws_col = htons(winsize.ws_col);
561 wp->ws_xpixel = htons(winsize.ws_xpixel);
562 wp->ws_ypixel = htons(winsize.ws_ypixel);
563
564 (void)write(rem, obuf, sizeof(obuf));
565 }
566
567 /*
568 * reader: read from remote: line -> 1
569 */
570 #define READING 1
571 #define WRITING 2
572
573 jmp_buf rcvtop;
574 pid_t ppid;
575 int rcvcnt, rcvstate;
576 char rcvbuf[8 * 1024];
577
578 void
579 oob(int signo)
580 {
581 struct termios tty;
582 int atmark, n, rcvd;
583 char waste[BUFSIZ], mark;
584
585 rcvd = 0;
586 while (recv(rem, &mark, 1, MSG_OOB) < 0) {
587 switch (errno) {
588 case EWOULDBLOCK:
589 /*
590 * Urgent data not here yet. It may not be possible
591 * to send it yet if we are blocked for output and
592 * our input buffer is full.
593 */
594 if (rcvcnt < sizeof(rcvbuf)) {
595 n = read(rem, rcvbuf + rcvcnt,
596 sizeof(rcvbuf) - rcvcnt);
597 if (n <= 0)
598 return;
599 rcvd += n;
600 } else {
601 n = read(rem, waste, sizeof(waste));
602 if (n <= 0)
603 return;
604 }
605 continue;
606 default:
607 return;
608 }
609 }
610 if (mark & TIOCPKT_WINDOW) {
611 /* Let server know about window size changes */
612 (void)kill(ppid, SIGUSR1);
613 }
614 if (!eight && (mark & TIOCPKT_NOSTOP)) {
615 (void)tcgetattr(0, &tty);
616 tty.c_iflag &= ~IXON;
617 (void)tcsetattr(0, TCSANOW, &tty);
618 }
619 if (!eight && (mark & TIOCPKT_DOSTOP)) {
620 (void)tcgetattr(0, &tty);
621 tty.c_iflag |= (deftty.c_iflag & IXON);
622 (void)tcsetattr(0, TCSANOW, &tty);
623 }
624 if (mark & TIOCPKT_FLUSHWRITE) {
625 (void)tcflush(1, TCIOFLUSH);
626 for (;;) {
627 if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
628 warn("ioctl SIOCATMARK (ignored)");
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 int
660 reader(sigset_t *smask)
661 {
662 pid_t pid;
663 int n, remaining;
664 char *bufp;
665 struct sigaction sa;
666
667 pid = getpid(); /* modern systems use positives for pid */
668 sigemptyset(&sa.sa_mask);
669 sa.sa_flags = SA_RESTART;
670 sa.sa_handler = SIG_IGN;
671 (void)sigaction(SIGTTOU, &sa, (struct sigaction *) 0);
672 sa.sa_handler = oob;
673 (void)sigaction(SIGURG, &sa, (struct sigaction *) 0);
674 ppid = getppid();
675 (void)fcntl(rem, F_SETOWN, pid);
676 (void)setjmp(rcvtop);
677 (void)sigprocmask(SIG_SETMASK, smask, (sigset_t *) 0);
678 bufp = rcvbuf;
679 for (;;) {
680 while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
681 rcvstate = WRITING;
682 n = write(STDOUT_FILENO, bufp, remaining);
683 if (n < 0) {
684 if (errno != EINTR)
685 return (-1);
686 continue;
687 }
688 bufp += n;
689 }
690 bufp = rcvbuf;
691 rcvcnt = 0;
692 rcvstate = READING;
693
694 rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
695
696 if (rcvcnt == 0)
697 return (0);
698 if (rcvcnt < 0) {
699 if (errno == EINTR)
700 continue;
701 warn("read");
702 return (-1);
703 }
704 }
705 }
706
707 void
708 mode(int f)
709 {
710 struct termios tty;
711
712 switch (f) {
713 case 0:
714 (void)tcsetattr(0, TCSANOW, &deftty);
715 break;
716 case 1:
717 (void)tcgetattr(0, &deftty);
718 tty = deftty;
719 /* This is loosely derived from sys/compat/tty_compat.c. */
720 tty.c_lflag &= ~(ECHO|ICANON|ISIG|IEXTEN);
721 tty.c_iflag &= ~ICRNL;
722 tty.c_oflag &= ~OPOST;
723 tty.c_cc[VMIN] = 1;
724 tty.c_cc[VTIME] = 0;
725 if (eight) {
726 tty.c_iflag &= IXOFF;
727 tty.c_cflag &= ~(CSIZE|PARENB);
728 tty.c_cflag |= CS8;
729 }
730 (void)tcsetattr(0, TCSANOW, &tty);
731 break;
732
733 default:
734 return;
735 }
736 }
737
738 void
739 lostpeer(int signo)
740 {
741 struct sigaction sa;
742 sa.sa_flags = SA_RESTART;
743 sa.sa_handler = SIG_IGN;
744 (void)sigaction(SIGPIPE, &sa, (struct sigaction *)0);
745 msg("\aconnection closed.");
746 done(1);
747 }
748
749 /* copy SIGURGs to the child process. */
750 void
751 copytochild(int signo)
752 {
753
754 (void)kill(child, SIGURG);
755 }
756
757 void
758 msg(const char *str)
759 {
760
761 (void)fprintf(stderr, "rlogin: %s\r\n", str);
762 }
763
764
765 void
766 usage(void)
767 {
768 (void)fprintf(stderr,
769 "usage: rlogin [-%s]%s[-e char] [-l username] [-p port] [username@]host\n",
770 "468Ed", " ");
771 exit(1);
772 }
773
774 /*
775 * The following routine provides compatibility (such as it is) between older
776 * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
777 */
778 #ifdef OLDSUN
779 int
780 get_window_size(fd, wp)
781 int fd;
782 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
797 u_int
798 getescape(char *p)
799 {
800 long val;
801 int 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