1 /*
2 * X.29 server
3 *
4 * Frank Pronk (...!ubc-vision!pronk)
5 * April, September 1984
6 *
7 * Laboratory for Computational Vision
8 * University of British Columbia
9 * Copyright (c)
10 */
11
12 #include <sys/param.h>
13 #include <sys/socket.h>
14 #include <sys/stat.h>
15 #include <sys/wait.h>
16
17 #include <netccitt/x25.h>
18
19 #include <errno.h>
20 #include <netdb.h>
21 #include <signal.h>
22 #include <sys/ioctl.h>
23 #include <sys/termios.h>
24 #include <paths.h>
25
26 #include "../h/x29.h"
27
28 #define BUFSIZ 1024
29 #define MAXARGS 10 /* maximum size of server argument list */
30
31 #define X25NET 0 /* no ITI parameters */
32 #define CCITT1978 1 /* 1978 CCITT standard parameter set */
33 #define CCITT1980 2 /* 1980 CCITT standard parameter set */
34
35
36 char pibuf[BUFSIZ], fibuf[BUFSIZ];
37 int pty, net;
38 extern char **environ;
39 extern int errno;
40 char line[MAXPATHLEN];
41 char console[] = "/dev/console";
42 short packet_size;
43 short debug;
44 char *tracefn; /* trace file name */
45 char *server;
46 short send_banner;
47 struct termios pt, old_pt;
48 struct sockaddr_x25 sock;
49
50 int reapchild();
51 struct net *lookup ();
52
53 char ccitt1978_prof[] = { /* initial profile */
54 Q_BIT, X29_SET_AND_READ_PARMS,
55 X29_ECHO_CODE, 1, /* echo on */
56 X29_FORWARDING_SIGNAL_CODE, 126, /* forward on all cntl */
57 X29_IDLE_TIMER_CODE, 0, /* off */
58 X29_AUX_DEV_CONTROL_CODE, 0, /* off */
59 X29_RECEIVE_NET_MSGS_CODE, 1, /* xmit network msgs */
60 X29_BREAK_PROCEDURE_CODE, 21,
61 X29_PADDING_CODE, 0, /* off */
62 X29_LINE_FOLDING_CODE, 0, /* off */
63 X29_TRANSMISSION_SPEED_CODE, 0,
64 X29_XON_XOFF_CODE, 1, /* enable XON/XOFF */
65 };
66
67 char ccitt1980_prof[] = { /* initial profile */
68 Q_BIT, X29_SET_AND_READ_PARMS,
69 X29_ECHO_CODE, 1, /* echo on */
70 X29_FORWARDING_SIGNAL_CODE, 126, /* forward on all cntl */
71 X29_IDLE_TIMER_CODE, 0, /* off */
72 X29_AUX_DEV_CONTROL_CODE, 0, /* off */
73 X29_RECEIVE_NET_MSGS_CODE, 1, /* xmit network msgs */
74 X29_BREAK_PROCEDURE_CODE, 21,
75 X29_PADDING_CODE, 0, /* off */
76 X29_LINE_FOLDING_CODE, 0, /* off */
77 X29_TRANSMISSION_SPEED_CODE, 0,
78 X29_XON_XOFF_CODE, 1, /* enable XON/XOFF */
79
80 X29_LF_AFTER_CR, 4, /* lf after cr from terminal */
81 X29_EDITING, 1, /* on */
82 X29_CHARACTER_DELETE, CERASE,
83 X29_LINE_DELETE, CKILL,
84 X29_LINE_DISPLAY, CRPRNT,
85 };
86
87 char datapac_prof[] = { /* Canadian X.25 network */
88 Q_BIT, X29_SET_AND_READ_PARMS,
89 X29_ECHO_CODE, 1, /* echo on */
90 X29_FORWARDING_SIGNAL_CODE, 126, /* forward on all cntl */
91 X29_IDLE_TIMER_CODE, 0, /* off */
92 X29_AUX_DEV_CONTROL_CODE, 0, /* off */
93 X29_RECEIVE_NET_MSGS_CODE, 1, /* xmit network msgs */
94 X29_BREAK_PROCEDURE_CODE, 21,
95 X29_PADDING_CODE, 0, /* off */
96 X29_LINE_FOLDING_CODE, 0, /* off */
97 X29_TRANSMISSION_SPEED_CODE, 0,
98 X29_XON_XOFF_CODE, 1, /* enable XON/XOFF */
99
100 X29_LF_AFTER_CR, 4, /* lf after cr from terminal */
101 X29_EDITING, 1, /* on */
102 X29_CHARACTER_DELETE, CERASE,
103 X29_LINE_DELETE, CKILL,
104 X29_LINE_DISPLAY, CRPRNT,
105
106 /*
107 * This rubbish can be removed when Datapac
108 * adopts the 1980 standard parameter set.
109 */
110
111 0, 0, /* national parameter marker */
112 123, 0, /* parity off */
113 };
114
115 struct net {
116 char *n_name; /* generic name */
117 short n_type; /* see defines above */
118 char *n_profile; /* initial profile */
119 short n_proflen; /* length of n_profile */
120 } *netp, nets[] = {
121 "x.25", X25NET, 0, 0,
122 "1978", CCITT1978, ccitt1978_prof, sizeof(ccitt1978_prof),
123 "ccitt1978", CCITT1978, ccitt1978_prof, sizeof(ccitt1978_prof),
124 "1980", CCITT1980, ccitt1980_prof, sizeof(ccitt1980_prof),
125 "ccitt1980", CCITT1980, ccitt1980_prof, sizeof(ccitt1980_prof),
126 "datapac", CCITT1980, datapac_prof, sizeof(datapac_prof),
127 0, 0, 0, 0
128 };
129
main(argc,argv)130 main(argc, argv)
131 register char **argv;
132 {
133 register int s, pid;
134 register char *p;
135
136 /*
137 * If this host doesn't support X.25, give up.
138 */
139 s = socket(AF_CCITT, SOCK_STREAM, 0);
140 if (s < 0 && errno == EPROTONOSUPPORT)
141 fatal(2, "X.25 is not supported on this machine");
142 close(s);
143 netp = lookup ("ccitt1978");
144 sock.x25_family = AF_CCITT;
145 sock.x25_len = sizeof(sock);
146 sock.x25_opts.op_flags = X25_MQBIT;
147 sock.x25_udata[0] = ITI_CALL;
148 sock.x25_udlen = 4;
149
150 for (argv++; argc > 1; argc--, argv++)
151 if (**argv == '-')
152 for (p = *argv+1; *p; p++)
153 switch (*p) {
154 case 'b':
155 send_banner++;
156 break;
157
158 case 'c':
159 if (argc > 1) {
160 argc--; argv++;
161 if ((netp = lookup (*argv)) == 0)
162 fatal(1, "Unknown network type");
163 }
164 break;
165
166 case 'p':
167 if (argc > 1) {
168 argc--; argv++;
169 strcpy (sock.x25_udata, *argv);
170 }
171 break;
172
173 case 'r':
174 sock.x25_opts.op_flags |= X25_REVERSE_CHARGE;
175 break;
176
177 case 'd':
178 debug++;
179 break;
180
181 case 't':
182 if (argc > 1) {
183 argc--; argv++;
184 tracefn = *argv;
185 }
186 else fatal(1, "missing trace file");
187 break;
188
189 default:
190 fatal (1, "usage: x29d -b -c nettype -p protocol -r -t trace_file server");
191 }
192 else
193 server = *argv;
194 if (server == 0)
195 fatal (1, "no server specified");
196 if (debug == 0)
197 daemon(0, 0);
198
199 while ((s = socket(AF_CCITT, SOCK_STREAM, 0)) < 0)
200 sleep(60);
201 while (bind(s, (caddr_t)&sock, sizeof (sock)) < 0)
202 sleep(60);
203 signal(SIGCHLD, reapchild);
204 listen(s, 5);
205
206 for (;;) {
207 struct sockaddr_x25 from;
208 int fromlen = sizeof (from);
209
210 if ((net = accept(s, (caddr_t)&from, &fromlen)) < 0) {
211 if (errno != EINTR)
212 sleep (60);
213 continue;
214 }
215 while ((pid = fork()) < 0)
216 sleep(60);
217 if (pid == 0) {
218 signal(SIGCHLD, SIG_DFL);
219 doit(&from);
220 }
221 close(net);
222 }
223 /*NOTREACHED*/
224 }
225
226 struct net *
lookup(name)227 lookup (name)
228 char *name;
229 {
230 register struct net *np;
231
232 for (np = nets; np->n_name; np++)
233 if (strcmp (np->n_name, name) == 0)
234 return (np);
235 return (0);
236 }
237
reapchild()238 reapchild()
239 {
240 union wait status;
241
242 while (wait3(&status, WNOHANG, 0) > 0)
243 ;
244 }
245
246 char *envinit[] = { "TERM=ccitt", 0 };
247 int cleanup();
248 struct termios term;
249
250 /*
251 * Get a pty, scan input lines.
252 */
253 doit(who)
254 struct sockaddr_x25 *who;
255 {
256 register char *cp;
257 int i, p, t;
258
259 packet_size = 1 << who->x25_opts.op_psize;
260 i = forkpty(&pty, line, &term, 0);
261 if (i > 0)
262 x29d();
263 if (i < 0)
264 fatalperror("fork", errno);
265 environ = envinit;
266 call_server (who);
267 /*NOTREACHED*/
268 }
269
270 call_server (who)
271 struct sockaddr_x25 *who;
272 {
273 register struct hostent *hp = 0;
274 register char *p, **ap;
275 char *args[MAXARGS];
276 struct stat st;
277 struct hostent *getx25hostbyaddr();
278 int ccitt = 0;
279
280 p = server;
281 while (*p && *p != ' ' && *p != '\t') /* split program from args */
282 p++;
283 if (*p)
284 *p++ = '\0';
285 ap = args;
286 while (*p) {
287 while (*p == ' ' || *p == '\t')
288 p++;
289 if (ap < &args[MAXARGS-2])
290 *ap++ = p;
291 if (strcmp(p, "-ccitt") == 0)
292 ccitt = 1;
293 while (*p && *p != ' ' && *p != '\t')
294 p++;
295 if (*p)
296 *p++ = '\0';
297 }
298 if (stat (server, &st) < 0)
299 fatalperror (server, errno);
300 /*
301 * For security: if running as root, switch to user
302 * and group id of server. This prevents privately
303 * maintainted or bogus servers from getting super-
304 * user permissions.
305 */
306 if (getuid() == 0) {
307 setgid (st.st_gid);
308 setuid (st.st_uid);
309 }
310 if (hp = getx25hostbyaddr (who->x25_addr))
311 *ap++ = hp->h_name;
312 else
313 *ap++ = (char *)who->x25_addr;
314 /*
315 * If the -ccitt flag was given, add another argument
316 * to tell login if charging is being reversed or not.
317 */
318 if (ccitt)
319 *ap++ = (who->x25_opts.op_flags & X25_REVERSE_CHARGE) ? "y" : "n";
320 *ap = 0;
321 execv (server, args);
322 fatalperror (server, errno);
323 /*NOTREACHED*/
324 }
325
fatal(f,msg)326 fatal(f, msg)
327 int f;
328 char *msg;
329 {
330 register char *p;
331 char buf[BUFSIZ], *index();
332
333 p = buf;
334 if (f == net)
335 *p++ = 0;
336 strcpy(p, "x29d: ");
337 strcat(p, msg);
338 strcat(p, "\n");
339 (void) write(f, p, (index(p, '\n')-p)+1);
340 exit(1);
341 }
342
fatalperror(msg,err)343 fatalperror(msg, err)
344 char *msg;
345 {
346 char buf[BUFSIZ];
347 extern char *sys_errlist[];
348
349 strcpy(buf, msg);
350 strcat(buf, ": ");
351 strcat(buf, sys_errlist[err]);
352 fatal(net, buf);
353 }
354
355 /*
356 * Main loop. Select from pty and network, and
357 * hand data to iti receiver.
358 */
x29d()359 x29d()
360 {
361 register int pcc, fcc, cc;
362 register char *fbp;
363 int pgrp, x25_interrupt(), on = 1;
364 char hostname[32];
365
366 ioctl(net, FIONBIO, (char *)&on);
367 ioctl(pty, FIONBIO, (char *)&on);
368 /*ioctl(pty, TIOCREMECHO, (char *)&on); /* enable special pty mode */
369 /* new equivalent is no processing in pty, no echo, but let
370 user set modes and have either remote end do line mode processing
371 or do it in daemon */
372 ioctl(pty, TIOCEXT, (char *)&on);
373 ioctl(pty, TIOCPKT, (char *)&on);
374 ioctl(pty, TIOCGETA, (char *)&pt);
375 signal(SIGPIPE, SIG_IGN); /* why not cleanup? --kwl */
376 signal(SIGTSTP, SIG_IGN);
377 signal(SIGCHLD, cleanup);
378 signal(SIGHUP, cleanup);
379
380 signal(SIGTTOU, SIG_IGN);
381 signal(SIGURG, x25_interrupt); /* for out-of-band data */
382
383 if (netp->n_proflen)
384 (void) write(net, netp->n_profile, netp->n_proflen);
385
386 /*
387 * Show banner that getty never gave.
388 */
389 if (send_banner) {
390 gethostname(hostname, sizeof (hostname));
391 #ifdef BSD4_3
392 strcpy(pibuf+1, "\r\n\r\n4.3 BSD UNIX (");
393 #else
394 strcpy(pibuf+1, "\r\n\r\n4.2 BSD UNIX (");
395 #endif
396 strcat(pibuf+1, hostname);
397 strcat(pibuf+1, ")\r\n\r\n");
398 pcc = strlen(pibuf+1) + 1;
399 } else
400 pcc = 0;
401
402 fcc = 0;
403 for (;;) {
404 int ibits, obits;
405
406 ibits = obits = 0;
407 /*
408 * Never look for input if there's still
409 * stuff in the corresponding output buffer
410 */
411 if (fcc >= 0) /* net connection alive? */
412 if (fcc && pcc >= 0) /* output pending? */
413 obits |= (1 << pty);
414 else
415 if (pcc >= 0) /* pty still alive? */
416 ibits |= (1 << net);
417 if (pcc >= 0) /* pty connection alive? */
418 if (pcc && fcc >= 0) /* output pending? */
419 obits |= (1 << net);
420 else
421 if (fcc >= 0) /* net still alive? */
422 ibits |= (1 << pty);
423 if (ibits == 0 && obits == 0)
424 break;
425 (void) select(16, &ibits, &obits, (int *)0, 0);
426 if (ibits == 0 && obits == 0) {
427 sleep(5);
428 continue;
429 }
430
431 /*
432 * Something to read from the network...
433 */
434 if (fcc == 0 && (ibits & (1 << net))) {
435 fcc = read(net, fibuf, BUFSIZ);
436 fbp = fibuf+1;
437 if (fcc < 0 && errno == EWOULDBLOCK)
438 fcc = 0;
439 else if (fcc <= 0)
440 fcc = -1;
441 else {
442 if (tracefn)
443 x29d_trace("netread", fibuf, fcc);
444 if (fibuf[0] & Q_BIT) {
445 x29_qbit(fcc);
446 fcc = 0;
447 } else
448 fcc--;
449 }
450 }
451
452 /*
453 * Something to read from the pty...
454 */
455 if (ibits & (1 << pty)) {
456 pcc = read(pty, pibuf, packet_size+1);
457 if (pcc < 0 && errno == EWOULDBLOCK)
458 pcc = 0;
459 else if (pcc <= 0)
460 pcc = -1;
461 else if (pibuf[0] != 0) { /* non-data packet */
462 if (pibuf[0] & TIOCPKT_IOCTL) {
463 if (--pcc > sizeof(pt))
464 pcc = sizeof(pt);
465 old_pt = pt;
466 bcopy(pibuf + 1, (char *)&pt, pcc);
467 pcc = set_x29_parameters();
468 } else
469 pcc = 0;
470 } else /* data packet */
471 pibuf[0] = 0;
472 }
473
474 if ((obits & (1<<net)) && pcc > 0)
475 if ((cc = write(net, pibuf, pcc)) == pcc) {
476 if (tracefn)
477 x29d_trace("netwrite", pibuf, pcc);
478 pcc = 0;
479 } else {
480 extern char *sys_errlist[];
481
482 if (tracefn)
483 x29d_trace("netwrite",
484 sys_errlist[errno],
485 strlen(sys_errlist[errno]));
486
487 }
488
489 if ((obits & (1 << pty)) && fcc > 0) {
490 cc = ptywrite(fbp, fcc);
491 if (cc > 0) {
492 fcc -= cc;
493 fbp += cc;
494 }
495 }
496 }
497 cleanup();
498 }
499
500
501 /*
502 * Send interrupt to process on other side of pty.
503 * If it is in raw mode, just write NULL;
504 * otherwise, write intr char.
505 */
506
x25_interrupt()507 x25_interrupt()
508 {
509 struct termios tt;
510 int zero = 0;
511
512 signal(SIGURG, x25_interrupt);
513 tcgetattr(pty, &tt);
514 if (tt.c_lflag & ISIG) {
515 tcsetattr(pty, TCSAFLUSH, &tt);
516 (void) write(pty, &tt.c_cc[VINTR], 1);
517 } else
518 (void) write(pty, "\0", 1);
519 }
520
cleanup()521 cleanup()
522 {
523 char *p;
524
525 p = line + sizeof(_PATH_DEV) - 1;
526 if (logout(p))
527 logwtmp(p, "", "");
528 (void)chmod(line, 0666);
529 (void)chown(line, 0, 0);
530 *p = 'p';
531 (void)chmod(line, 0666);
532 (void)chown(line, 0, 0);
533 shutdown(net, 2);
534 exit(1);
535 }
536
537 /*
538 * Map unix tty modes and special characters
539 * into x29 parameters.
540 */
541
set_x29_parameters()542 set_x29_parameters()
543 {
544 register char *p;
545 int f;
546 char *lim = p + sizeof (pt);
547
548 if (netp->n_type == X25NET)
549 return (0);
550 if ((old_pt.c_lflag & ICANON) != (pt.c_lflag & ICANON)) {
551 f = pt.c_lflag & ICANON;
552 ioctl(pty, TIOCEXT, &f);
553 /* this precipitates more junk of the same
554 * sort that caused our call here, but we can't
555 * turn it off since something may be going on in our progeny.
556 *
557 * Instead, we'll check the next time around to see if nothing
558 * has changed, and skip informing the network.
559 */
560 }
561 if (bcmp((char *)&pt, (char *)&old_pt, sizeof (pt)) == 0)
562 return;
563 p = pibuf;
564 *p++ = Q_BIT;
565 *p++ = X29_SET_PARMS;
566 /* *p++ = X29_ESCAPE_TO_CMD_CODE; *p++ = (f & (RAW|CBREAK)) == 0;*/
567 *p++ = X29_ESCAPE_TO_CMD_CODE; *p++ = (pt.c_lflag & ICANON) != 0;
568
569 *p++ = X29_ECHO_CODE; *p++ = (pt.c_lflag & ECHO) != 0;
570 *p++ = X29_FORWARDING_SIGNAL_CODE;
571 *p++ = (pt.c_lflag & ISIG) ? 0 : 126;
572
573 /*
574 * The value of 10 (0.5 seconds) for the idle timer when
575 * in raw or cbreak mode is a compromise value. For good
576 * interactive response this value should be as low as
577 * possible; for reasonable efficiency with file transfers
578 * this value should be at fairly high. This number should
579 * be changed to suit local requirements.
580 */
581
582 /**p++ = X29_IDLE_TIMER_CODE; *p++ = (f & (RAW|CBREAK)) ? 10 : 0;*/
583 *p++ = X29_IDLE_TIMER_CODE; *p++ = (pt.c_lflag & ICANON) ? 0 : 10;
584
585 /**p++ = X29_AUX_DEV_CONTROL_CODE;*p++ = (f & TANDEM) != 0;*/
586 *p++ = X29_AUX_DEV_CONTROL_CODE;*p++ = (pt.c_iflag & IXOFF) != 0;
587 *p++ = X29_XON_XOFF_CODE; *p++ = (pt.c_iflag & IXON) != 0;
588 if (netp->n_type == CCITT1980) {
589 *p++ = X29_LF_AFTER_CR;
590 /* *p++ = (f & (RAW|CBREAK) || (f & ECHO) == 0) ? 0 : 4; */
591 *p++ = ((pt.c_lflag & (ICANON | ECHO)) != (ICANON | ECHO)) ?
592 0 : 4;
593
594 *p++ = X29_EDITING; *p++ = (pt.c_lflag & ICANON) != 0;
595 #define ctlchar(x) \
596 (0 == (pt.c_lflag & ICANON) || pt.c_cc[x] == _POSIX_VDISABLE) ? 0 : pt.c_cc[x]
597 *p++ = X29_CHARACTER_DELETE; *p++ = ctlchar(VERASE);
598 *p++ = X29_LINE_DELETE; *p++ = ctlchar(VKILL);
599 *p++ = X29_LINE_DISPLAY; *p++ = ctlchar(VREPRINT);
600 }
601 #undef ctlchar
602 return (p - pibuf);
603 }
604
605 /* Have to be careful writing to pty. The pad will forward control
606 * characters without necessarily sending an interrupt so if ISIG and
607 * ICANNON are set, must inspect line for quit or interrupt or suspend.
608 */
ptywrite(buf,n)609 ptywrite(buf, n)
610 char *buf;
611 int n;
612 {
613 register char *cp, *lim;
614 char *last;
615 #define is_ctl(x) (pt.c_cc[x] == *(cc_t *)cp)
616
617 if ((pt.c_lflag & EXTPROC) && (pt.c_lflag & ISIG)) {
618 for (cp = buf, lim = buf + n; cp < lim; cp ++) {
619 if (is_ctl(VLNEXT))
620 { cp++; continue; }
621 if (is_ctl(VSUSP) || is_ctl(VDSUSP) ||
622 is_ctl(VINTR) || is_ctl(VQUIT)) {
623 int onoff = 0;
624 tcflag_t old_echo = pt.c_lflag & ECHO;
625
626 ioctl(pty, TIOCPKT, (char *)&onoff);
627 ioctl(pty, FIONBIO, (char *)&onoff);
628 ioctl(pty, TIOCEXT, (char *)&onoff);
629 if (old_echo) {
630 pt.c_lflag &= ~ECHO;
631 ioctl(pty, TIOCSETA, (char *)&pt);
632 }
633 n = write(pty, buf, n);
634 onoff = 1;
635 if (old_echo) {
636 pt.c_lflag |= ECHO;
637 ioctl(pty, TIOCSETA, (char *)&pt);
638 }
639 ioctl(pty, TIOCEXT, (char *)&onoff);
640 ioctl(pty, FIONBIO, (char *)&onoff);
641 ioctl(pty, TIOCPKT, (char *)&onoff);
642 return (n);
643 }
644 }
645 }
646 return write(pty, buf, n);
647 }
648
649 /*
650 * Process Q BIT (control) packets from the net.
651 * The only message that we are interested in are
652 * those indicating output is being discarded.
653 */
654
x29_qbit(n)655 x29_qbit(n)
656 {
657 register char *p;
658
659 switch (fibuf[1]) {
660 case X29_SET_PARMS:
661 case X29_SET_AND_READ_PARMS:
662 case X29_PARAMETER_INDICATION:
663 case X29_INDICATION_OF_BREAK:
664 for (p = &fibuf[2]; p < fibuf+n; p++) {
665 if (*p == X29_TRANSMISSION_SPEED_CODE) {
666 static char speeds[] = {
667 B110, B0, B300, B1200, B600,
668 B0, B0, B0, B0, B0, B0, B0,
669 B2400, B4800, B9600, EXTA };
670
671 if (*++p >= 0 && *p < sizeof(speeds)) {
672 cfsetspeed(&pt, speeds[*p]);
673 tcsetattr(pty, TCSANOW, &pt);
674 }
675 } else if (*p == X29_DISCARD_OUTPUT_CODE && *++p != 0) {
676 char message[4];
677
678 /*
679 * Always re-enable normal output
680 */
681 message[0] = Q_BIT;
682 message[1] = X29_SET_PARMS;
683 message[2] = X29_DISCARD_OUTPUT_CODE;
684 message[3] = 0;
685 (void) write(net, message, sizeof(message));
686 if (tracefn)
687 x29d_trace("netwrite", message, 4);
688 }
689 }
690 return;
691
692 default: {
693 register char *p2;
694 char buf[BUFSIZ*4];
695 static int fd;
696
697 /*
698 * Bad news - we received an x29 error message or
699 * some other unknown packet. Dump the contents
700 * of the packet on the console.
701 */
702 p = buf;
703 for (p2 = "x29d: unknown q-bit packet: "; *p++ = *p2++; );
704 for (p2 = fibuf+1; p2 < fibuf+n; p2++)
705 if (*p2 >= ' ' && *p2 < 0177)
706 *p++ = *p2;
707 else {
708 *p++ = '\\';
709 *p++ = ((*p2 & 0300) >> 6) + '0';
710 *p++ = ((*p2 & 070) >> 3) + '0';
711 *p++ = (*p2 & 07) + '0';
712 }
713 *p++ = '\n';
714 if (fd <= 0)
715 fd = open(console, 1);
716 (void) write(fd, buf, p-buf);
717 }
718 }
719 }
720
x29d_trace(s,bp,n)721 x29d_trace(s, bp, n)
722 char *s, *bp;
723 {
724 static int fd;
725 char buf[BUFSIZ*4];
726 register char *p1, *p2;
727
728 for (p1 = buf; *s; *p1++ = *s++);
729 *p1++ = ':';
730 *p1++ = ' ';
731 for (p2=bp; p2 < bp+n; p2++)
732 if (*p2 >= ' ' && *p2 < 0177)
733 *p1++ = *p2;
734 else {
735 *p1++ = '\\';
736 *p1++ = ((*p2 & 0300) >> 6) + '0';
737 *p1++ = ((*p2 & 070) >> 3) + '0';
738 *p1++ = (*p2 & 07) + '0';
739 }
740 *p1++ = '\n';
741 if (fd <= 0)
742 fd = creat(tracefn, 0666);
743 (void) write(fd, buf, p1-buf);
744 }
745