home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / contrib / usr.x25 / nimd / nim.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-10-24  |  14.2 KB  |  770 lines

  1. /*
  2.  * Network Interface Machine Server
  3.  *
  4.  * Frank Pronk
  5.  * Copyright (c) 1984
  6.  */
  7.  
  8. #include <sys/types.h>
  9. #include <sys/socket.h>
  10. #include <sys/time.h>
  11.  
  12. #include <signal.h>
  13. #include <errno.h>
  14. #include <setjmp.h>
  15. #include <sgtty.h>
  16. #include <pwd.h>
  17.  
  18. #include "../h/x29.h"
  19.  
  20. #include "buf.h"
  21. #include "nim.h"
  22.  
  23. extern    char chartab[128];
  24.  
  25. jmp_buf    JmpBuf;            /* non-local goto buffer for interval timer */
  26. short    PtyFd = -1, NetFd = -1;
  27. short    LogFd = -1;
  28. char    *LogDev;
  29. short    TimerOn;        /* interval timer armed */
  30. char    Banner[] = "NIM daemon\r";
  31. extern    int errno;
  32. char    *TraceFile;        /* trace file name */
  33. char    *PtyName;
  34. struct    bufhd ptyoqueue, netoqueue;
  35. struct    buf *packet;
  36. char    user_name[50];
  37.  
  38. struct    netinfo NetInfo = { CCITT1980, NX29_1980_PARMS, 128 };
  39.  
  40. struct    PtyPacket {
  41.     char    p_type;
  42.     char    p_data[1];    /* usually more than one byte */
  43. };
  44.  
  45. char    *x25err();
  46.  
  47. main(argc, argv)
  48. register char **argv;
  49. {
  50.     register int s;
  51.     register char *p;
  52.     int on = 1, uid;
  53.     struct passwd *pw;
  54.  
  55. #ifdef waterloo
  56.     /*
  57.      * If this host doesn't support X.25, give up.
  58.      */
  59.     s = socket(AF_CCITT, SOCK_STREAM, 0);
  60.     if (s < 0 && errno == EPROTONOSUPPORT) {
  61.         fprint(2, "nimd: X.25 is not supported on this machine\n");
  62.         exit(1);
  63.     }
  64.     close(s);
  65. #endif
  66.  
  67.     for (argv++; argc > 1; argc--, argv++)
  68.         if (**argv == '-')
  69.             for (p = *argv+1; *p; p++)
  70.             switch (*p) {
  71.             case 'c':
  72.                 if (argc > 1) {
  73.                     argc--; argv++;
  74.                     if (strcmp (*argv, "1978") == 0) {
  75.                         NetInfo.n_nparms = NX29_1978_PARMS;
  76.                         break;
  77.                     } else if (strcmp (*argv, "1980") == 0) {
  78.                         NetInfo.n_nparms = NX29_1980_PARMS;
  79.                         break;
  80.                     }
  81.                 }
  82.                 fatal ("1978 or 1980 expected after -c");
  83.                 break;
  84.  
  85.             case 't':
  86.                 if (argc > 1) {
  87.                     argc--; argv++;
  88.                     TraceFile = *argv;
  89.                 } else
  90.                     fatal ("trace file name expected");
  91.                 break;
  92.  
  93.             default:
  94.                 fatal("usage: nimd [-c ccitt-date] [-h size] [-n size] [-t trace_file] pty_name");
  95.             }
  96.         else
  97.             PtyName = *argv;
  98.  
  99.     if (PtyName == 0)
  100.         fatal ("No pseudo-tty specified");
  101.  
  102.     if (fork())
  103.         exit(0);
  104.     for (s = 0; s < 10; s++)
  105.         (void) close(s);
  106.     (void) open("/", 0);
  107.     (void) dup2(0, 1);
  108.     (void) dup2(0, 2);
  109.     { int tt = open("/dev/tty", 2);
  110.       if (tt > 0) {
  111.         ioctl(tt, TIOCNOTTY, (char *)0);
  112.         close(tt);
  113.       }
  114.     }
  115.  
  116.     OpenLog ();
  117.     packet = getbuf (MAXPSIZ);
  118.     ResetBufs ();
  119.     signal (SIGPIPE, SIG_IGN);
  120.  
  121.     for (;;) {
  122.         int bits;
  123.  
  124.         if ((PtyFd = open (PtyName, 2)) < 0)
  125.             fatal (x25err(PtyName));
  126.         ioctl (PtyFd, FIONBIO, (char *)&on);
  127.         ioctl (PtyFd, TIOCPKT, (char *)&on);
  128.         ioctl (PtyFd, TIOCREMOTE, (char *)&on);
  129.         bits = (1 << PtyFd);
  130.         select (16, (int *)0, &bits, (int *)0, (struct timeval *)0);
  131.         sleep (1);    /* wait for pty to settle down */
  132. #ifdef TIOCGSUID
  133.         /*
  134.          * Get the slave's uid.
  135.          */
  136.         uid = -1;
  137.         if (ioctl(PtyFd, TIOCGSUID, &uid) < 0) {
  138.             perror("nimd: ioctl TIOCGSUID");
  139.             close(PtyFd);
  140.             continue;
  141.         }
  142.         if (uid < 0 || (pw = getpwuid(uid)) == 0) {
  143.             message("nimd: uid %d: Who are you?\n", uid);
  144.             close(PtyFd);
  145.             continue;
  146.         }
  147.         strcpy(user_name, pw->pw_name);
  148.         /*
  149.          * Set effective uid to the slave
  150.          * so he/she will be charged for X.25 usage.
  151.          */
  152.         setreuid(0, pw->pw_uid);
  153. #endif
  154.         nim ();
  155.         sleep (1);    /* wait for slave to disconnect */
  156.         setreuid(0, 0);
  157.         CloseLog(); OpenLog();    /* allow log rollover */
  158.     }
  159.     /*NOTREACHED*/
  160. }
  161.  
  162. fatal (msg)
  163. char *msg;
  164. {
  165.     OpenLog ();
  166.     log ("%s", msg);
  167.     print ("nimd: %s\n", msg);
  168.     exit (1);
  169. }
  170.  
  171. /*VARARGS*/
  172. message(fmt, a1, a2, a3, a4, a5)
  173. char *fmt;
  174. {
  175.     char buf[128];
  176.  
  177.     sprint (buf, fmt, a1, a2, a3, a4, a5);
  178.     ToPty (buf, strlen (buf), FROMNIM);
  179. }
  180.  
  181. error()
  182. {
  183.     register char *errp;
  184.  
  185.     errp = x25err ((char *)0);
  186.     log (errp);
  187.     message ("nimd: %s\r", errp);
  188. }
  189.  
  190.  
  191. /*
  192.  * Main loop.  Select from pty and network, and
  193.  * hand data to nim receiver.
  194.  */
  195.  
  196. nim()
  197. {
  198.     register int len;
  199.     char buf[MAXPSIZ];
  200.     int on = 1, ibits, obits, first = 1;
  201.  
  202.     InitProfile (DEFAULT_PROFILE);
  203.     message (Banner);
  204.     State = ST_COMMAND;
  205.     OpenLog ();
  206.     if (user_name[0])
  207.         log("slave connect: %s", user_name);
  208.     else
  209.         log ("slave connect");
  210.  
  211.     for (;;) {
  212.         (void) setjmp (JmpBuf);
  213.         if (first) {
  214.             obits = (1 << PtyFd);
  215.             ibits = 0;
  216.             first = 0;
  217.             goto do_io;
  218.         }
  219.         ibits = obits = 0;
  220.  
  221.         /*
  222.          * Never look for input if there's still
  223.          * stuff in the corresponding output buffer
  224.          */
  225.  
  226.         if (PtyFd >= 0)
  227.             if (!QEMPTY(&ptyoqueue))
  228.                 obits |= (1 << PtyFd);
  229.             else
  230.                 if (NetFd >= 0 && State == ST_DATA)
  231.                     ibits |= (1 << NetFd);
  232.  
  233.         if (!QEMPTY(&netoqueue) && NetFd >= 0) {
  234.             if (!OutputBlocked)
  235.                 obits |= (1 << NetFd);
  236.         } else
  237.             if (PtyFd >= 0)
  238.                 ibits |= (1 << PtyFd);
  239.  
  240.         if (ibits == 0 && obits == 0)    /* nothing to do; go home */
  241.             break;
  242.  
  243.         if (State & ST_COMMAND) {
  244.             struct timeval TimeOut;
  245.  
  246.             TimeOut.tv_sec = 60;
  247.             TimeOut.tv_usec = 0;
  248.             if (select (16, &ibits, &obits, (int *)0, &TimeOut) <= 0) {
  249.                 log ("slave inactivity timeout");
  250.                 break;
  251.             }
  252.         } else {
  253.             if (TimerOn)
  254.                 sigsetmask (0);
  255.             (void) select (16, &ibits, &obits, (int *)0, (struct timeval *)0);
  256.             if (TimerOn)
  257.                 sigsetmask (1 << (SIGALRM -1 ));
  258.         }
  259.  
  260.     do_io:
  261.         /*
  262.          * Something to read from the network...
  263.          */
  264.         if (ibits & (1 << NetFd))
  265.             if ((len = ReadAndTrace(NetFd, buf, MAXPSIZ, "net rx")) == 0) {
  266.                 if (errno != EWOULDBLOCK)
  267.                     NetworkError ();
  268.             } else
  269.                 FromNet (buf, len);
  270.  
  271.         /*
  272.          * Something to read from the pty...
  273.          */
  274.         if (ibits & (1 << PtyFd))
  275.             if ((len = ReadAndTrace (PtyFd, buf, NetInfo.n_psize+1, "pty rx")) == 0) {
  276.                 if (errno != EWOULDBLOCK) {
  277.                     close (PtyFd);
  278.                     PtyFd = -1;
  279.                 }
  280.             } else
  281.                 FromPty ((struct PtyPacket *)buf, len);
  282.  
  283.         if (obits & (1<<NetFd)) {
  284.             if (FlushQueue (NetFd, &netoqueue, "net tx") < 0 && 
  285.                 errno != EWOULDBLOCK)
  286.                 NetworkError ();
  287.         }
  288.  
  289.         if (obits & (1 << PtyFd))
  290.             if (FlushQueue (PtyFd, &ptyoqueue, "pty tx") < 0 &&
  291.                 errno != EWOULDBLOCK) {
  292.                 close (PtyFd);
  293.                 PtyFd = -1;
  294.             }
  295.     }
  296.     cleanup ();
  297. }
  298.  
  299. ReadAndTrace (fd, buf, len, who)
  300. char *buf, *who;
  301. {
  302.     register int bytes;
  303.  
  304.     bytes = read (fd, buf, len);
  305.     if (bytes <= 0)
  306.         return (0);
  307.     NimTrace (who, buf, bytes);
  308.     return (bytes);
  309. }
  310.  
  311. NetworkError ()
  312. {
  313.  
  314.     error ();
  315.     State = ST_COMMAND;
  316.     close (NetFd);
  317.     NetFd = -1;
  318.     message (Banner);
  319. }
  320.  
  321. cleanup ()
  322. {
  323.     log ("slave disconnect");
  324.     close (NetFd);
  325.     close (PtyFd);
  326.     NetFd = PtyFd = -1;
  327.     if (TimerOn)
  328.         ClearTimer ();
  329.     ResetBufs ();
  330. }
  331.  
  332. ResetBufs ()
  333. {
  334.     InitQueue (&ptyoqueue);
  335.     InitQueue (&netoqueue);
  336.     RESET (packet);
  337. }
  338.  
  339. ToPty (str, len, from)
  340. register char *str;
  341. {
  342.     register char *end = str + len, c, c1;
  343.     register struct buf *bp = 0;
  344.     register int lfcode;
  345.  
  346.     while (str < end) {
  347.         c = *str++;
  348.         c1 = c & 0177;
  349.         if (CurrentX29Parms[X29_AUX_DEV_CONTROL_CODE]) {
  350.             if (c1 == 023) {
  351.                 OutputBlocked++;
  352.                 continue;
  353.             }
  354.             if (c1 == 021) {
  355.                 OutputBlocked = 0;
  356.                 continue;
  357.             }
  358.         }
  359.         if (ptyoqueue.b_count > 256)
  360.             continue;
  361.         if (bp == 0)
  362.             bp = getbuf (MAXPSIZ);
  363.         PUTCHAR (c, bp);
  364.         if (SIZE (bp) >= MAXPSIZ-1) {
  365.             enqueue (bp, &ptyoqueue);
  366.             bp = 0;
  367.         }
  368.         if (c1 != '\r' || (lfcode = CurrentX29Parms[X29_LF_AFTER_CR]) <= 0)
  369.             continue;
  370.         if ((lfcode & 01) && from == FROMNET ||
  371.             (lfcode & 04) && from == FROMPTY ||
  372.              from == FROMNIM)
  373.             PUTCHAR ('\n', bp);
  374.     }
  375.     if (bp)
  376.         enqueue (bp, &ptyoqueue);
  377. }
  378.  
  379. FromPty(pp, len)
  380. register struct PtyPacket *pp;
  381. {
  382.     register int echo;
  383.     register struct buf *tp = packet;
  384.     register char *cp, c;
  385.     char c1;
  386.  
  387.     if (pp->p_type != TIOCPKT_DATA) {    /* fetch control byte */
  388.         PtyControl (pp);
  389.         return;
  390.     }
  391.     if (State & ST_UGLY_50_BAUD_BREAK_IN_PROGRESS)
  392.         return;
  393.     cp = pp->p_data;
  394.     echo = CurrentX29Parms[X29_ECHO_CODE] > 0 && ptyoqueue.b_count < 256;
  395.     while (cp < ((char *)pp)+len) {
  396.         c = (c1 = *cp++) & 0177;
  397.         if (State & ST_ESCSEEN && C_TYPE(c) != C_ESCAPE)
  398.             EnterCommandState ();
  399.         switch (C_TYPE(c)) {
  400.         case C_ERASE:
  401.             if (!ISEMPTY(tp)) {
  402.                 tp->b_top--;
  403.                 if (echo)
  404.                     if (c == '\b')
  405.                         ToPty ("\b \b", 3, FROMPTY);
  406.                     else
  407.                         ToPty (&c1, 1, FROMPTY);
  408.             }
  409.             continue;
  410.  
  411.         case C_KILL:
  412.             RESET (tp);
  413.             if (echo)
  414.                 ToPty ("*poof*\r", 7, FROMPTY);
  415.             continue;
  416.  
  417.         case C_DISPLAY:
  418.             ToPty (tp->b_bot, SIZE (tp), FROMPTY);
  419.             continue;
  420.  
  421.         case C_ESCAPE:
  422.             if ((State & (ST_COMMAND|ST_ESCSEEN)) == 0) {
  423.                 State |= ST_ESCSEEN;
  424.                 continue;
  425.             }
  426.             State &= ~ST_ESCSEEN;
  427.             /* fall through */
  428.  
  429.         default:
  430.             if (State & ST_COMMAND) {
  431.                 if (c == '\r') {
  432.                     if (echo)
  433.                         ToPty (&c1, 1, FROMPTY);
  434.                     PUTCHAR ('\0', tp);
  435.                     FlushQueue (PtyFd, &ptyoqueue, "pty tx");
  436.                     NimCommand (tp->b_bot);
  437.                     RESET(tp);
  438.                     continue;
  439.                 }
  440.                 if (SIZE (tp) < MAXPSIZ-1) {
  441.                     PUTCHAR (c, tp);
  442.                     if (echo)
  443.                         ToPty (&c1, 1, FROMPTY);
  444.                 } else
  445. /*                    ToPty ("\007", 1, FROMPTY)*/;
  446.             } else {
  447.                 PUTCHAR (c1, tp);
  448.                 if (echo)
  449.                     ToPty (&c1, 1, FROMPTY);
  450.                 if (ISFORWARD(c) || SIZE (tp) >= NetInfo.n_psize) {
  451.                     ForwardPacket ();
  452.                     continue;
  453.                 }
  454.             }
  455.         }
  456.     }
  457.     if (!ISEMPTY (tp) && (State & ST_COMMAND) == 0)
  458.         SetTimer ();
  459. }
  460.  
  461. PtyControl(pp)
  462. register struct PtyPacket *pp;
  463. {
  464. #ifdef notdef
  465.     if ((pp->p_type & (TIOCPKT_FLUSHWRITE|TIOCPKT_FLUSHREAD)) ==
  466.        (TIOCPKT_FLUSHWRITE|TIOCPKT_FLUSHREAD)) {    /* break indication from pty */
  467.         if (State & ST_COMMAND)
  468.             RESET (packet);
  469.         else
  470.             Break (CurrentX29Parms[X29_BREAK_PROCEDURE_CODE]);
  471.         return;
  472.        }
  473. #endif
  474. #ifdef TIOCPKT_IOCTL
  475.     if (pp->p_type & TIOCPKT_IOCTL) {    /* some kind of set tty done by slave */
  476.         static short UnixToX29Speed[] = {
  477.             0, 10, 5, 0, 1, 6, 8, 2, 4, 3,    /* B0 thru B1200 */
  478.             12, 13, 14, 15, 16        /* B2400 thru EXTB */
  479.         };
  480.         struct sgttyb sg;
  481.  
  482.         ioctl(PtyFd, TIOCGETP, (char *)&sg);
  483.         if (sg.sg_ospeed == B50) {
  484.             State |= ST_UGLY_50_BAUD_BREAK_IN_PROGRESS;
  485.             return;
  486.         }
  487.         CurrentX29Parms[X29_TRANSMISSION_SPEED_CODE]
  488.             = UnixToX29Speed[sg.sg_ospeed];
  489.         if (State & ST_UGLY_50_BAUD_BREAK_IN_PROGRESS && sg.sg_ospeed != B50) {
  490.             State &= ~ST_UGLY_50_BAUD_BREAK_IN_PROGRESS;
  491.             if (State & ST_COMMAND)
  492.                 RESET (packet);
  493.             else
  494.                 Break (CurrentX29Parms[X29_BREAK_PROCEDURE_CODE]);
  495.         }
  496.     }
  497. #endif
  498. }
  499.  
  500. EnterCommandState ()
  501. {
  502.     State &= ~ST_ESCSEEN;
  503.     State |= ST_COMMAND | ST_ESCCOMM;
  504.     ForwardPacket ();
  505. }
  506.  
  507. ExitDataState (cause)
  508. char *cause;
  509. {
  510.     ResetBufs ();
  511.     close (NetFd);
  512.     NetFd = -1;
  513.     State = ST_COMMAND;
  514.     OutputBlocked = 0;
  515.     CurrentX29Parms[X29_DISCARD_OUTPUT_CODE] = 0;
  516.     message ("nimd: Call cleared - %s\r", cause);
  517.     log ("Call cleared - %s", cause);
  518. }
  519.  
  520. ForwardPacket ()
  521. {
  522.     register struct buf *bp, *tp = packet;
  523.  
  524.     if (!ISEMPTY(tp) && (State & ST_COMMAND) == 0) {
  525.         AddParity (tp->b_bot, SIZE (tp));
  526.         bp = getbuf (SIZE (tp) + 1);
  527.         PUTCHAR(0, bp);
  528.         BufCopy(tp, bp);
  529.         enqueue (bp, &netoqueue);
  530.     }
  531.     RESET (tp);
  532.     if (TimerOn)
  533.         ClearTimer ();
  534. }
  535.  
  536. ToNet (pp, len)
  537. struct packet *pp;
  538. {
  539.     register struct buf *bp;
  540.  
  541.     /*
  542.      * round buffer size up to a multiple of 64 bytes
  543.      * to reduce accumulation of small and usually
  544.      * useless buffers in the free list.  This speeds
  545.      * up malloc().
  546.      */
  547.     bp = getbuf ((len + 63) & ~63);
  548.     bcopy ((char *)pp, bp->b_bot, len);
  549.     bp->b_top = bp->b_bot + len;
  550.     enqueue (bp, &netoqueue);
  551. }
  552.  
  553. timeout()
  554. {
  555.     TimerOn = 0;
  556.     ForwardPacket ();
  557.     longjmp (JmpBuf, 0);
  558. }
  559.  
  560. SetTimer ()
  561. {
  562.     register int t;
  563.     struct itimerval itv;
  564.  
  565.     if (TimerOn || (t = CurrentX29Parms[X29_IDLE_TIMER_CODE]) <= 0)
  566.         return;
  567.     itv.it_interval.tv_sec = 0;
  568.     itv.it_interval.tv_usec = 0;
  569.     itv.it_value.tv_sec = t / 20;
  570.     itv.it_value.tv_usec = t % 20 * 1000000 / 20;
  571.     signal (SIGALRM, SIG_IGN);    /* cancel possible pending signal */
  572.     signal (SIGALRM, timeout);
  573.     sigsetmask (1 << (SIGALRM - 1));
  574.     TimerOn++;
  575.     setitimer (ITIMER_REAL, &itv, (struct itimerval *)0);
  576. }
  577.  
  578. ClearTimer ()
  579. {
  580.     struct itimerval itv;
  581.  
  582.     signal (SIGALRM, SIG_IGN);
  583.     bzero ((char *)&itv, sizeof (itv));
  584.     setitimer (ITIMER_REAL, &itv, (struct itimerval *)0);
  585.     TimerOn = 0;
  586. }
  587.  
  588. FromNet (bp, len)
  589. char *bp;
  590. {
  591.     if ((*bp & Q_BIT) == 0) {
  592.         register struct x25packet *xp = (struct x25packet *)bp;
  593.  
  594.         if (CurrentX29Parms[X29_DISCARD_OUTPUT_CODE] == 0) {
  595.             AddParity (xp->p_x25data, len - 1);
  596.             ToPty (xp->p_x25data, len - 1, FROMNET);
  597.         }
  598.         return;
  599.     }
  600.     X29ControlMessage ((struct x29packet *)bp, len);
  601. }
  602.  
  603. SendX25Interrupt()
  604. {
  605.     char c = 0x77;
  606.  
  607.     send (NetFd, &c, 1, MSG_OOB);
  608. }
  609.  
  610. #ifdef fastidious    /* we need stdio */
  611. /*
  612.  * Sorry about this...
  613.  * Defining this dummy procedure prevents the stdio package
  614.  * (about 17K bytes worth) from being loaded.  This program
  615.  * does not require any support from the 4.2bsd stdio library.
  616.  */
  617.  
  618. #ifdef vax
  619. _cleanup()
  620. {
  621. }
  622. #endif
  623. #endif
  624.  
  625. NimTrace(who, bp, n)
  626. char *who, *bp;
  627. {
  628.     static int fd;
  629.  
  630.     if (TraceFile == 0)
  631.         return;
  632.     if(fd <= 0 && (fd = creat(TraceFile, 0640)) < 0)
  633.         return;
  634.     DisplayPacketContents (fd, who, bp, n);
  635. }
  636.  
  637. OpenLog ()
  638. {
  639.  
  640.     if (LogFd >= 0)
  641.         return;
  642.     if (LogDev = rindex (PtyName, '/'))
  643.         LogDev++;
  644.     else
  645.         LogDev = PtyName;
  646.     if ((LogFd = open (LOGFILE, 1)) >= 0)
  647.         return;
  648.     LogFd = creat (LOGFILE, 0640);
  649. }
  650.  
  651. CloseLog()
  652. {
  653.     if (LogFd >= 0) {
  654.         close(LogFd);
  655.         LogFd = -1;
  656.     }
  657. }
  658.  
  659. /*VARARGS*/
  660. log(fmt, a1, a2, a3, a4, a5)
  661. char *fmt;
  662. {
  663.     register char *DateTime;
  664.     char format[128], *ctime ();
  665.     time_t t;
  666.  
  667.     if (LogFd < 0)
  668.         return;
  669.     (void) time (&t);
  670.     DateTime = ctime (&t);
  671.     DateTime[19] = '\0';
  672.     sprint (format, "%s %s %s\n", DateTime+4, LogDev, fmt);
  673.     lseek (LogFd, (long)0, 2);
  674.     fprint (LogFd, format, a1, a2, a3, a4, a5);
  675. }
  676.  
  677. LogPacket (bp, len)
  678. char *bp;
  679. {
  680.     if (LogFd < 0)
  681.         return;
  682.     DisplayPacketContents (LogFd, "net rx", bp, len);
  683. }
  684.  
  685. DisplayPacketContents (fd, from, pp, len)
  686. char *from;
  687. register char *pp;
  688. register int len;
  689. {
  690.     register int first = 1;
  691.     char buf[128];
  692.  
  693.     lseek (fd, (long)0, 2);
  694.     do {
  695.         ConvertToOctal (pp, len, buf);
  696.         if (first) {
  697.             fprint (fd, "%s[%d]\t%s\n", from, len, buf);
  698.             first = 0;
  699.         } else
  700.             fprint (fd, "\t\t%s\n", buf);
  701.         ConvertToAscii (pp, len, buf);
  702.         fprint (fd, "\t\t%s\n", buf);
  703.         pp += 16;
  704.         len -= 16;
  705.     } while (len > 0);
  706. }
  707.  
  708. ConvertToOctal (start, len, bp)
  709. register char *start, *bp;
  710. {
  711.     register char *cp;
  712.  
  713.     if (len > 16)
  714.         len = 16;
  715.     for (cp = start; cp - start < len; cp++) {
  716.         *bp++ = ((*cp & 0300) >> 6) + '0';
  717.         *bp++ = ((*cp & 070) >> 3) + '0';
  718.         *bp++ = (*cp & 07) + '0';
  719.         *bp++ = ' ';
  720.     }
  721.     bp[-1] = '\0';
  722. }
  723.             
  724. ConvertToAscii (start, len, bp)
  725. register char *start, *bp;
  726. {
  727.     register char *cp;
  728.  
  729.     if (len > 16)
  730.         len = 16;
  731.     for (cp = start; cp - start < len; cp++) {
  732.         *bp++ = ' ';
  733.         switch (*cp) {
  734.         case '\b':
  735.             *bp++ = '\\';
  736.             *bp++ = 'b';
  737.             break;
  738.  
  739.         case '\t':
  740.             *bp++ = '\\';
  741.             *bp++ = 't';
  742.             break;
  743.  
  744.         case '\n':
  745.             *bp++ = '\\';
  746.             *bp++ = 'n';
  747.             break;
  748.  
  749.         case '\f':
  750.             *bp++ = '\\';
  751.             *bp++ = 'f';
  752.             break;
  753.  
  754.         case '\r':
  755.             *bp++ = '\\';
  756.             *bp++ = 'r';
  757.             break;
  758.  
  759.         default:
  760.             *bp++ = ' ';
  761.             if ((*cp&0177) > ' ' && (*cp&0177) < 0177)
  762.                 *bp++ = *cp;
  763.             else
  764.                 *bp++ = ' ';
  765.         }
  766.         *bp++ = ' ';
  767.     }
  768.     bp[-1] = '\0';
  769. }
  770.