|
|
1.1 root 1: /*
2: * Copyright (c) 1983, 1990 The Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms are permitted provided
6: * that: (1) source distributions retain this entire copyright notice and
7: * comment, and (2) distributions including binaries display the following
8: * acknowledgement: ``This product includes software developed by the
9: * University of California, Berkeley and its contributors'' in the
10: * documentation or other materials provided with the distribution and in
11: * all advertising materials mentioning features or use of this software.
12: * Neither the name of the University nor the names of its contributors may
13: * be used to endorse or promote products derived from this software without
14: * specific prior written permission.
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
16: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
17: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18: */
19:
20: #ifndef lint
21: char copyright[] =
22: "@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\
23: All rights reserved.\n";
24: #endif /* not lint */
25:
26: #ifndef lint
27: static char sccsid[] = "@(#)rlogin.c 5.29 (Berkeley) 6/27/90";
28: #endif /* not lint */
29:
30: /*
31: * $Source: mit/rlogin/RCS/rlogin.c,v $
32: * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall
33: * Exp Locker: kfall $
34: */
35:
36: /*
37: * rlogin - remote login
38: */
39: #include <sys/param.h>
40: #include <sys/file.h>
41: #include <sys/socket.h>
42: #include <sys/signal.h>
43: #include <sys/time.h>
44: #include <sys/resource.h>
45: #include <sys/wait.h>
46:
47: #include <netinet/in.h>
48: #include <netinet/in_systm.h>
49: #include <netinet/ip.h>
50: #include <netdb.h>
51:
52: #include <sgtty.h>
53: #include <setjmp.h>
54: #include <errno.h>
55: #include <varargs.h>
56: #include <pwd.h>
57: #include <stdio.h>
58: #include <unistd.h>
59: #include <string.h>
60:
61: #ifdef KERBEROS
62: #include <kerberosIV/des.h>
63: #include <kerberosIV/krb.h>
64:
65: CREDENTIALS cred;
66: Key_schedule schedule;
67: int use_kerberos = 1, encrypt;
68: char dst_realm_buf[REALM_SZ], *dest_realm = NULL;
69: extern char *krb_realmofhost();
70: #endif
71:
72: #ifndef TIOCPKT_WINDOW
73: #define TIOCPKT_WINDOW 0x80
74: #endif
75:
76: /* concession to Sun */
77: #ifndef SIGUSR1
78: #define SIGUSR1 30
79: #endif
80:
81: extern int errno;
82: int eight, litout, rem;
83: char cmdchar;
84: char *speeds[] = {
85: "0", "50", "75", "110", "134", "150", "200", "300", "600", "1200",
86: "1800", "2400", "4800", "9600", "19200", "38400"
87: };
88:
89: #ifdef sun
90: struct winsize {
91: unsigned short ws_row, ws_col;
92: unsigned short ws_xpixel, ws_ypixel;
93: };
94: #endif
95: struct winsize winsize;
96:
97: #ifndef sun
98: #define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp)
99: #endif
100:
101: void exit();
102:
103: main(argc, argv)
104: int argc;
105: char **argv;
106: {
107: extern char *optarg;
108: extern int optind;
109: struct passwd *pw;
110: struct servent *sp;
111: struct sgttyb ttyb;
112: long omask;
113: int argoff, ch, dflag, one, uid;
114: char *host, *p, *user, term[1024];
115: void lostpeer();
116: char *getenv();
117:
118: argoff = dflag = 0;
119: one = 1;
120: host = user = NULL;
121: cmdchar = '~';
122:
123: if (p = rindex(argv[0], '/'))
124: ++p;
125: else
126: p = argv[0];
127:
128: if (strcmp(p, "rlogin"))
129: host = p;
130:
131: /* handle "rlogin host flags" */
132: if (!host && argc > 2 && argv[1][0] != '-') {
133: host = argv[1];
134: argoff = 1;
135: }
136:
137: #ifdef KERBEROS
138: #define OPTIONS "8KLde:k:l:x"
139: #else
140: #define OPTIONS "8KLde:l:"
141: #endif
142: while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF)
143: switch(ch) {
144: case '8':
145: eight = 1;
146: break;
147: case 'K':
148: #ifdef KERBEROS
149: use_kerberos = 0;
150: #endif
151: break;
152: case 'L':
153: litout = 1;
154: break;
155: case 'd':
156: dflag = 1;
157: break;
158: case 'e':
159: cmdchar = optarg[0];
160: break;
161: #ifdef KERBEROS
162: case 'k':
163: dest_realm = dst_realm_buf;
164: (void)strncpy(dest_realm, optarg, REALM_SZ);
165: break;
166: #endif
167: case 'l':
168: user = optarg;
169: break;
170: #ifdef KERBEROS
171: case 'x':
172: encrypt = 1;
173: des_set_key(cred.session, schedule);
174: break;
175: #endif
176: case '?':
177: default:
178: usage();
179: }
180: optind += argoff;
181: argc -= optind;
182: argv += optind;
183:
184: /* if haven't gotten a host yet, do so */
185: if (!host && !(host = *argv++))
186: usage();
187:
188: if (*argv)
189: usage();
190:
191: if (!(pw = getpwuid(uid = getuid()))) {
192: (void)fprintf(stderr, "rlogin: unknown user id.\n");
193: exit(1);
194: }
195: if (!user)
196: user = pw->pw_name;
197:
198: sp = NULL;
199: #ifdef KERBEROS
200: if (use_kerberos) {
201: sp = getservbyname((encrypt ? "eklogin" : "klogin"), "tcp");
202: if (sp == NULL) {
203: use_kerberos = 0;
204: warning("can't get entry for %s/tcp service",
205: encrypt ? "eklogin" : "klogin");
206: }
207: }
208: #endif
209: if (sp == NULL)
210: sp = getservbyname("login", "tcp");
211: if (sp == NULL) {
212: (void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n");
213: exit(1);
214: }
215:
216: (void)strcpy(term, (p = getenv("TERM")) ? p : "network");
217: if (ioctl(0, TIOCGETP, &ttyb) == 0) {
218: (void)strcat(term, "/");
219: (void)strcat(term, speeds[ttyb.sg_ospeed]);
220: }
221:
222: (void)get_window_size(0, &winsize);
223:
224: (void)signal(SIGPIPE, lostpeer);
225: /* will use SIGUSR1 for window size hack, so hold it off */
226: omask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1));
227:
228: #ifdef KERBEROS
229: try_connect:
230: if (use_kerberos) {
231: rem = KSUCCESS;
232: errno = 0;
233: if (dest_realm == NULL)
234: dest_realm = krb_realmofhost(host);
235:
236: if (encrypt)
237: rem = krcmd_mutual(&host, sp->s_port, user, term, 0,
238: dest_realm, &cred, schedule);
239: else
240: rem = krcmd(&host, sp->s_port, user, term, 0,
241: dest_realm);
242: if (rem < 0) {
243: use_kerberos = 0;
244: sp = getservbyname("login", "tcp");
245: if (sp == NULL) {
246: (void)fprintf(stderr,
247: "rlogin: unknown service login/tcp.\n");
248: exit(1);
249: }
250: if (errno == ECONNREFUSED)
251: warning("remote host doesn't support Kerberos");
252: if (errno == ENOENT)
253: warning("can't provide Kerberos auth data");
254: goto try_connect;
255: }
256: } else {
257: if (encrypt) {
258: (void)fprintf(stderr,
259: "rlogin: the -x flag requires Kerberos authentication.\n");
260: exit(1);
261: }
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
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;
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((union wait *)0)) > 0 && w != child);
364: }
365: exit(status);
366: }
367:
368: int dosigwinch;
369:
370: /*
371: * This is called when the reader process gets the out-of-band (urgent)
372: * request to turn on the window-changing protocol.
373: */
374: void
375: writeroob()
376: {
377: void sigwinch();
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(&status, WNOHANG|WUNTRACED, (struct rusage *)0);
394: if (pid == 0)
395: return;
396: /* if the child (reader) dies, just quit */
397: if (pid < 0 || pid == child && !WIFSTOPPED(status))
398: done((int)(status.w_termsig | status.w_retcode));
399: }
400: /* NOTREACHED */
401: }
402:
403: /*
404: * writer: write to remote: 0 -> line.
405: * ~. terminate
406: * ~^Z suspend rlogin process.
407: * ~^Y suspend rlogin process, but leave reader alone.
408: */
409: writer()
410: {
411: char c;
412: register int bol, local, n;
413:
414: bol = 1; /* beginning of line */
415: local = 0;
416: for (;;) {
417: n = read(STDIN_FILENO, &c, 1);
418: if (n <= 0) {
419: if (n < 0 && errno == EINTR)
420: continue;
421: break;
422: }
423: /*
424: * If we're at the beginning of the line and recognize a
425: * command character, then we echo locally. Otherwise,
426: * characters are echo'd remotely. If the command character
427: * is doubled, this acts as a force and local echo is
428: * suppressed.
429: */
430: if (bol) {
431: bol = 0;
432: if (c == cmdchar) {
433: bol = 0;
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 != cmdchar) {
450: #ifdef KERBEROS
451: if (encrypt) {
452: (void)des_write(rem, &cmdchar, 1);
453: } else
454: #endif
455: (void)write(rem, &cmdchar, 1);
456: }
457: }
458:
459: #ifdef KERBEROS
460: if (encrypt) {
461: if (des_write(rem, &c, 1) == 0) {
462: msg("line gone");
463: break;
464: }
465: } else
466: #endif
467: if (write(rem, &c, 1) == 0) {
468: msg("line gone");
469: break;
470: }
471: bol = c == defkill || c == deftc.t_eofc ||
472: c == deftc.t_intrc || c == defltc.t_suspc ||
473: c == '\r' || c == '\n';
474: }
475: }
476:
477: echo(c)
478: register char c;
479: {
480: register char *p;
481: char buf[8];
482:
483: p = buf;
484: c &= 0177;
485: *p++ = cmdchar;
486: if (c < ' ') {
487: *p++ = '^';
488: *p++ = c + '@';
489: } else if (c == 0177) {
490: *p++ = '^';
491: *p++ = '?';
492: } else
493: *p++ = c;
494: *p++ = '\r';
495: *p++ = '\n';
496: (void)write(1, buf, p - buf);
497: }
498:
499: stop(cmdc)
500: char cmdc;
501: {
502: mode(0);
503: (void)signal(SIGCHLD, SIG_IGN);
504: (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP);
505: (void)signal(SIGCHLD, catch_child);
506: mode(1);
507: sigwinch(); /* check for size changes */
508: }
509:
510: void
511: sigwinch()
512: {
513: struct winsize ws;
514:
515: if (dosigwinch && get_window_size(0, &ws) == 0 &&
516: bcmp(&ws, &winsize, sizeof(ws))) {
517: winsize = ws;
518: sendwindow();
519: }
520: }
521:
522: /*
523: * Send the window size to the server via the magic escape
524: */
525: sendwindow()
526: {
527: struct winsize *wp;
528: char obuf[4 + sizeof (struct winsize)];
529:
530: wp = (struct winsize *)(obuf+4);
531: obuf[0] = 0377;
532: obuf[1] = 0377;
533: obuf[2] = 's';
534: obuf[3] = 's';
535: wp->ws_row = htons(winsize.ws_row);
536: wp->ws_col = htons(winsize.ws_col);
537: wp->ws_xpixel = htons(winsize.ws_xpixel);
538: wp->ws_ypixel = htons(winsize.ws_ypixel);
539:
540: #ifdef KERBEROS
541: if(encrypt)
542: (void)des_write(rem, obuf, sizeof(obuf));
543: else
544: #endif
545: (void)write(rem, obuf, sizeof(obuf));
546: }
547:
548: /*
549: * reader: read from remote: line -> 1
550: */
551: #define READING 1
552: #define WRITING 2
553:
554: jmp_buf rcvtop;
555: int ppid, rcvcnt, rcvstate;
556: char rcvbuf[8 * 1024];
557:
558: void
559: oob()
560: {
561: struct sgttyb sb;
562: int atmark, n, out, rcvd;
563: char waste[BUFSIZ], mark;
564:
565: out = O_RDWR;
566: rcvd = 0;
567: while (recv(rem, &mark, 1, MSG_OOB) < 0)
568: switch (errno) {
569: case EWOULDBLOCK:
570: /*
571: * Urgent data not here yet. It may not be possible
572: * to send it yet if we are blocked for output and
573: * our input buffer is full.
574: */
575: if (rcvcnt < sizeof(rcvbuf)) {
576: n = read(rem, rcvbuf + rcvcnt,
577: sizeof(rcvbuf) - rcvcnt);
578: if (n <= 0)
579: return;
580: rcvd += n;
581: } else {
582: n = read(rem, waste, sizeof(waste));
583: if (n <= 0)
584: return;
585: }
586: continue;
587: default:
588: return;
589: }
590: if (mark & TIOCPKT_WINDOW) {
591: /* Let server know about window size changes */
592: (void)kill(ppid, SIGUSR1);
593: }
594: if (!eight && (mark & TIOCPKT_NOSTOP)) {
595: (void)ioctl(0, TIOCGETP, (char *)&sb);
596: sb.sg_flags &= ~CBREAK;
597: sb.sg_flags |= RAW;
598: (void)ioctl(0, TIOCSETN, (char *)&sb);
599: notc.t_stopc = -1;
600: notc.t_startc = -1;
601: (void)ioctl(0, TIOCSETC, (char *)¬c);
602: }
603: if (!eight && (mark & TIOCPKT_DOSTOP)) {
604: (void)ioctl(0, TIOCGETP, (char *)&sb);
605: sb.sg_flags &= ~RAW;
606: sb.sg_flags |= CBREAK;
607: (void)ioctl(0, TIOCSETN, (char *)&sb);
608: notc.t_stopc = deftc.t_stopc;
609: notc.t_startc = deftc.t_startc;
610: (void)ioctl(0, TIOCSETC, (char *)¬c);
611: }
612: if (mark & TIOCPKT_FLUSHWRITE) {
613: (void)ioctl(1, TIOCFLUSH, (char *)&out);
614: for (;;) {
615: if (ioctl(rem, SIOCATMARK, &atmark) < 0) {
616: (void)fprintf(stderr, "rlogin: ioctl: %s.\n",
617: strerror(errno));
618: break;
619: }
620: if (atmark)
621: break;
622: n = read(rem, waste, sizeof (waste));
623: if (n <= 0)
624: break;
625: }
626: /*
627: * Don't want any pending data to be output, so clear the recv
628: * buffer. If we were hanging on a write when interrupted,
629: * don't want it to restart. If we were reading, restart
630: * anyway.
631: */
632: rcvcnt = 0;
633: longjmp(rcvtop, 1);
634: }
635:
636: /* oob does not do FLUSHREAD (alas!) */
637:
638: /*
639: * If we filled the receive buffer while a read was pending, longjmp
640: * to the top to restart appropriately. Don't abort a pending write,
641: * however, or we won't know how much was written.
642: */
643: if (rcvd && rcvstate == READING)
644: longjmp(rcvtop, 1);
645: }
646:
647: /* reader: read from remote: line -> 1 */
648: reader(omask)
649: int omask;
650: {
651: void oob();
652:
653: #if !defined(BSD) || BSD < 43
654: int pid = -getpid();
655: #else
656: int pid = getpid();
657: #endif
658: int n, remaining;
659: char *bufp = rcvbuf;
660:
661: (void)signal(SIGTTOU, SIG_IGN);
662: (void)signal(SIGURG, oob);
663: ppid = getppid();
664: (void)fcntl(rem, F_SETOWN, pid);
665: (void)setjmp(rcvtop);
666: (void)sigsetmask(omask);
667: for (;;) {
668: while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) {
669: rcvstate = WRITING;
670: n = write(1, bufp, remaining);
671: if (n < 0) {
672: if (errno != EINTR)
673: return(-1);
674: continue;
675: }
676: bufp += n;
677: }
678: bufp = rcvbuf;
679: rcvcnt = 0;
680: rcvstate = READING;
681:
682: #ifdef KERBEROS
683: if (encrypt)
684: rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf));
685: else
686: #endif
687: rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf));
688: if (rcvcnt == 0)
689: return (0);
690: if (rcvcnt < 0) {
691: if (errno == EINTR)
692: continue;
693: (void)fprintf(stderr, "rlogin: read: %s.\n",
694: strerror(errno));
695: return(-1);
696: }
697: }
698: }
699:
700: mode(f)
701: {
702: struct ltchars *ltc;
703: struct sgttyb sb;
704: struct tchars *tc;
705: int lflags;
706:
707: (void)ioctl(0, TIOCGETP, (char *)&sb);
708: (void)ioctl(0, TIOCLGET, (char *)&lflags);
709: switch(f) {
710: case 0:
711: sb.sg_flags &= ~(CBREAK|RAW|TBDELAY);
712: sb.sg_flags |= defflags|tabflag;
713: tc = &deftc;
714: ltc = &defltc;
715: sb.sg_kill = defkill;
716: sb.sg_erase = deferase;
717: lflags = deflflags;
718: break;
719: case 1:
720: sb.sg_flags |= (eight ? RAW : CBREAK);
721: sb.sg_flags &= ~defflags;
722: /* preserve tab delays, but turn off XTABS */
723: if ((sb.sg_flags & TBDELAY) == XTABS)
724: sb.sg_flags &= ~TBDELAY;
725: tc = ¬c;
726: ltc = &noltc;
727: sb.sg_kill = sb.sg_erase = -1;
728: if (litout)
729: lflags |= LLITOUT;
730: break;
731: default:
732: return;
733: }
734: (void)ioctl(0, TIOCSLTC, (char *)ltc);
735: (void)ioctl(0, TIOCSETC, (char *)tc);
736: (void)ioctl(0, TIOCSETN, (char *)&sb);
737: (void)ioctl(0, TIOCLSET, (char *)&lflags);
738: }
739:
740: void
741: lostpeer()
742: {
743: (void)signal(SIGPIPE, SIG_IGN);
744: msg("\007connection closed.");
745: done(1);
746: }
747:
748: /* copy SIGURGs to the child process. */
749: void
750: copytochild()
751: {
752: (void)kill(child, SIGURG);
753: }
754:
755: msg(str)
756: char *str;
757: {
758: (void)fprintf(stderr, "rlogin: %s\r\n", str);
759: }
760:
761: #ifdef KERBEROS
762: /* VARARGS */
763: warning(va_alist)
764: va_dcl
765: {
766: va_list ap;
767: char *fmt;
768:
769: (void)fprintf(stderr, "rlogin: warning, using standard rlogin: ");
770: va_start(ap);
771: fmt = va_arg(ap, char *);
772: vfprintf(stderr, fmt, ap);
773: va_end(ap);
774: (void)fprintf(stderr, ".\n");
775: }
776: #endif
777:
778: usage()
779: {
780: (void)fprintf(stderr,
781: "usage: rlogin [ -%s]%s[-e char] [ -l username ] host\n",
782: #ifdef KERBEROS
783: "8Lx", " [-k realm] ");
784: #else
785: "8L", " ");
786: #endif
787: exit(1);
788: }
789:
790: /*
791: * The following routine provides compatibility (such as it is) between 4.2BSD
792: * Suns and others. Suns have only a `ttysize', so we convert it to a winsize.
793: */
794: #ifdef sun
795: int
796: get_window_size(fd, wp)
797: int fd;
798: struct winsize *wp;
799: {
800: struct ttysize ts;
801: int error;
802:
803: if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0)
804: return(error);
805: wp->ws_row = ts.ts_lines;
806: wp->ws_col = ts.ts_cols;
807: wp->ws_xpixel = 0;
808: wp->ws_ypixel = 0;
809: return(0);
810: }
811: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.