home *** CD-ROM | disk | FTP | other *** search
/ Der Mediaplex Sampler - Die 6 von Plex / 6_v_plex.zip / 6_v_plex / DISK6 / OS_15 / SH.ZIP / SH8.C < prev    next >
C/C++ Source or Header  |  1994-01-24  |  16KB  |  807 lines

  1. /* MS-DOS SHELL - Unix File I/O Emulation
  2.  *
  3.  * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited
  4.  *
  5.  * This code is subject to the following copyright restrictions:
  6.  *
  7.  * 1.  Redistribution and use in source and binary forms are permitted
  8.  *     provided that the above copyright notice is duplicated in the
  9.  *     source form and the copyright notice in file sh6.c is displayed
  10.  *     on entry to the program.
  11.  *
  12.  * 2.  The sources (or parts thereof) or objects generated from the sources
  13.  *     (or parts of sources) cannot be sold under any circumstances.
  14.  *
  15.  *    $Header: C:/SRC/SHELL/RCS/sh8.c 1.12 90/08/14 23:31:08 Ian_Stewartson Exp Locker: Ian_Stewartson $
  16.  *
  17.  *    $Log:    sh8.c $
  18.  * Revision 1.12  90/08/14  23:31:08  Ian_Stewartson
  19.  * Changes to env structure.
  20.  *
  21.  * Revision 1.11  90/06/21  11:12:13  MS_user
  22.  * Ensure Areanum is set correctly for memory areas
  23.  *
  24.  * Revision 1.10  90/05/31  11:31:53  MS_user
  25.  * Correct misplaced signal restore
  26.  *
  27.  * Revision 1.9  90/05/31  09:50:41  MS_user
  28.  * Add some signal lockouts to prevent corruption
  29.  *
  30.  * Revision 1.8  90/03/26  20:58:11  MS_user
  31.  * Change I/O restore so that "exec >filename" works
  32.  *
  33.  * Revision 1.7  90/03/22  13:48:03  MS_user
  34.  * MSDOS does not handle /dev/ files after find_first correctly
  35.  *
  36.  * Revision 1.6  90/03/14  19:32:42  MS_user
  37.  * Change buffered output to be re-entrant
  38.  *
  39.  * Revision 1.5  90/03/14  16:46:21  MS_user
  40.  * New Open_buffer parameter and Adds_buffer function
  41.  *
  42.  * Revision 1.4  90/03/13  21:20:50  MS_user
  43.  * Add Buffered Output functions
  44.  *
  45.  * Revision 1.3  90/03/06  15:14:03  MS_user
  46.  * Change script detection to look for a character less than 0x08
  47.  *
  48.  * Revision 1.2  90/03/05  13:54:08  MS_user
  49.  * Fix bug in S_dup
  50.  * Change the way we detect shell scripts
  51.  * Add support for alternate command interpreters a la V.4
  52.  *
  53.  * Revision 1.1  90/01/29  17:46:37  MS_user
  54.  * Initial revision
  55.  *
  56.  *
  57.  */
  58.  
  59. #include <sys/types.h>
  60. #include <signal.h>
  61. #include <errno.h>
  62. #include <setjmp.h>
  63. #include <stdlib.h>
  64. #include <stdio.h>
  65. #include <fcntl.h>
  66. #include <io.h>
  67. #include <stdarg.h>
  68. #include <string.h>
  69. #include <unistd.h>
  70. #include <limits.h>
  71. #include <dir.h>
  72. #include <ctype.h>
  73. #include "sh.h"
  74.  
  75. #define F_START        4
  76.  
  77. static char    *nopipe = "can't create pipe - try again\n";
  78.  
  79. /* List of open files to allow us to simulate the Unix open and unlink
  80.  * operation for temporary files
  81.  */
  82.  
  83. typedef struct flist {
  84.     struct flist    *fl_next;    /* Next link            */
  85.     char        *fl_name;    /* File name            */
  86.     bool        fl_close;    /* Delete on close flag        */
  87.     int            fl_size;    /* Size of fl_fd array        */
  88.     int            fl_count;    /* Number of entries in array    */
  89.     int            fl_mode;    /* File open mode        */
  90.     int            *fl_fd;        /* File ID array (for dup)    */
  91. } s_flist;
  92.  
  93. static s_flist        *list_start = (s_flist *)NULL;
  94. static s_flist        *find_entry (int);
  95.  
  96. /*
  97.  * Open a file and add it to the Open file list.  Errors are the same as
  98.  * for a normal open.
  99.  */
  100.  
  101. int    S_open (d_flag, name, mode, ...)
  102. bool    d_flag;
  103. char    *name;
  104. int    mode;
  105. {
  106.     va_list        ap;
  107.     int            pmask;
  108.     s_flist        *fp = (struct s_flist *)NULL;
  109.     int            *f_list = (int *)NULL;
  110.     char        *f_name = (char *)NULL;
  111.     void        (*save_signal)(int);
  112.  
  113. /* Check the permission mask if it exists */
  114.  
  115.     va_start (ap, mode);
  116.     pmask =  va_arg (ap, int);
  117.     va_end (ap);
  118.  
  119. /* Grap some space.  If it fails, free space and return an error */
  120.  
  121.     if (((fp = (s_flist *) space (sizeof (s_flist))) == (s_flist *)NULL) ||
  122.     ((f_list = (int *) space (sizeof (int) * F_START)) == (int *)NULL) ||
  123.     ((f_name = strsave (name, 0)) == null))
  124.     {
  125.     if (f_list == (int *)NULL)
  126.         DELETE (f_list);
  127.  
  128.     if (fp == (s_flist *)NULL)
  129.         DELETE (fp);
  130.  
  131.     errno = ENOMEM;
  132.     return -1;
  133.     }
  134.  
  135. /* Disable signals */
  136.  
  137.     save_signal = signal (SIGINT, SIG_IGN);
  138.  
  139. /* Set up the structure.  Change two Unix device names to the DOS
  140.  * equivalents and disable create
  141.  */
  142.  
  143.     if (strnicmp (name, "/dev/", 5) == 0)
  144.     {
  145.     if (stricmp (&name[5], "tty") == 0)
  146.         strcpy (&name[5], "con");
  147.  
  148.     else if (stricmp (&name[5], "null") == 0)
  149.         strcpy (&name[5], "nul");
  150.  
  151.     mode &= ~(O_CREAT | O_TRUNC);
  152.     }
  153.  
  154.     fp->fl_name  = strcpy (f_name, name);
  155.     fp->fl_close = d_flag;
  156.     fp->fl_size  = F_START;
  157.     fp->fl_count = 1;
  158.     fp->fl_fd    = f_list;
  159.     fp->fl_mode  = mode;
  160.  
  161. /* Open the file */
  162.  
  163.     if ((fp->fl_fd[0] = open (name, mode, pmask)) < 0)
  164.     {
  165.     pmask = errno;
  166.     DELETE (f_name);
  167.     DELETE (f_list);
  168.     DELETE (fp);
  169.     errno = pmask;
  170.     pmask = -1;
  171.     }
  172.  
  173. /* Make sure everything is in area 0 */
  174.  
  175.     else
  176.     {
  177.     setarea ((char *)fp, 0);
  178.     setarea ((char *)f_list, 0);
  179.  
  180. /* List into the list */
  181.  
  182.     fp->fl_next   = list_start;
  183.     list_start = fp;
  184.  
  185. /* Return the file descriptor */
  186.  
  187.     pmask = fp->fl_fd[0];
  188.     }
  189.  
  190. /* Restore signals */
  191.  
  192.     signal (SIGINT, save_signal);
  193.  
  194.     return pmask;
  195. }
  196.  
  197. /*
  198.  * Scan the File list for the appropriate entry for the specified ID
  199.  */
  200.  
  201. static s_flist        *find_entry (fid)
  202. int            fid;
  203. {
  204.     s_flist    *fp = list_start;
  205.     int        i;
  206.  
  207.     while (fp != (s_flist *)NULL)
  208.     {
  209.     for (i = 0; i < fp->fl_count; i++)
  210.     {
  211.         if (fp->fl_fd[i] == fid)
  212.         return fp;
  213.     }
  214.  
  215.     fp = fp->fl_next;
  216.     }
  217.  
  218.     return (s_flist *)NULL;
  219. }
  220.  
  221. /* Close the file
  222.  *
  223.  * We need a version of close that does everything but close for dup2 as
  224.  * new file id is closed.  If c_flag is TRUE, close the file as well.
  225.  */
  226.  
  227. int    S_close (fid, c_flag)
  228. int    fid;
  229. bool    c_flag;
  230. {
  231.     s_flist    *fp = find_entry (fid);
  232.     s_flist    *last = (s_flist *)NULL;
  233.     s_flist    *fp1 = list_start;
  234.     int        i, serrno;
  235.     bool    release = TRUE;
  236.     bool    delete = FALSE;
  237.     char    *fname;
  238.     void    (*save_signal)(int);
  239.  
  240. /* Disable signals */
  241.  
  242.     save_signal = signal (SIGINT, SIG_IGN);
  243.  
  244. /* Find the entry for this ID */
  245.  
  246.     if (fp != (s_flist *)NULL)
  247.     {
  248.     for (i = 0; i < fp->fl_count; i++)
  249.     {
  250.         if (fp->fl_fd[i] == fid)
  251.         fp->fl_fd[i] = -1;
  252.  
  253.         if (fp->fl_fd[i] != -1)
  254.         release = FALSE;
  255.     }
  256.  
  257. /* Are all the Fids closed ? */
  258.  
  259.     if (release)
  260.     {
  261.         fname = fp->fl_name;
  262.         delete = fp->fl_close;
  263.         DELETE (fp->fl_fd);
  264.  
  265. /* Scan the list and remove the entry */
  266.  
  267.         while (fp1 != (s_flist *)NULL)
  268.         {
  269.         if (fp1 != fp)
  270.         {
  271.             last = fp1;
  272.             fp1 = fp1->fl_next;
  273.             continue;
  274.         }
  275.  
  276.         if (last == (s_flist *)NULL)
  277.             list_start = fp->fl_next;
  278.  
  279.         else
  280.             last->fl_next = fp->fl_next;
  281.  
  282.         break;
  283.         }
  284.  
  285. /* OK - delete the area */
  286.  
  287.         DELETE (fp);
  288.     }
  289.     }
  290.  
  291. /* Close the file anyway */
  292.  
  293.     if (c_flag)
  294.     {
  295.     i = close (fid);
  296.     serrno = errno;
  297.     }
  298.  
  299. /* Delete the file ? */
  300.  
  301.     if (delete)
  302.     {
  303.     unlink (fname);
  304.     DELETE (fname);
  305.     }
  306.  
  307. /* Restore signals */
  308.  
  309.     signal (SIGINT, save_signal);
  310.  
  311. /* Restore results and error code */
  312.  
  313.     errno = serrno;
  314.     return i;
  315. }
  316.  
  317. /*
  318.  * Duplicate file handler.  Add the new handler to the ID array for this
  319.  * file.
  320.  */
  321.  
  322. int    S_dup (old_fid)
  323. int    old_fid;
  324. {
  325.     int        new_fid;
  326.  
  327.     if ((new_fid = dup (old_fid)) >= 0)
  328.     S_Remap (old_fid, new_fid);
  329.  
  330.     return new_fid;
  331. }
  332.  
  333. /*
  334.  * Add the ID to the ID array for this file
  335.  */
  336.  
  337. int    S_Remap (old_fid, new_fid)
  338. int    old_fid, new_fid;
  339. {
  340.     s_flist    *fp = find_entry (old_fid);
  341.     int        *flist;
  342.     int        i;
  343.  
  344.     if (fp == (s_flist *)NULL)
  345.     return new_fid;
  346.  
  347. /* Is there an empty slot ? */
  348.  
  349.     for (i = 0; i < fp->fl_count; i++)
  350.     {
  351.     if (fp->fl_fd[i] == -1)
  352.         return (fp->fl_fd[i] = new_fid);
  353.     }
  354.  
  355. /* Is there any room at the end ? No - grap somemore space and effect a
  356.  * re-alloc.  What to do if the re-alloc fails - should really get here.
  357.  * Safty check only??
  358.  */
  359.  
  360.     if (fp->fl_count == fp->fl_size)
  361.     {
  362.     if ((flist = (int *) space ((fp->fl_size + F_START) * sizeof (int)))
  363.         == (int *)NULL)
  364.         return new_fid;
  365.  
  366.     setarea ((char *)flist, 0);
  367.     memcpy ((char *)flist, (char *)fp->fl_fd, sizeof (int) * fp->fl_size);
  368.     DELETE (fp->fl_fd);
  369.  
  370.     fp->fl_fd   = flist;
  371.     fp->fl_size += F_START;
  372.     }
  373.  
  374.     return (fp->fl_fd[fp->fl_count++] = new_fid);
  375. }
  376.  
  377. /*
  378.  * Set Delete on Close flag
  379.  */
  380.  
  381. void    S_Delete (fid)
  382. int    fid;
  383. {
  384.     s_flist    *fp = find_entry (fid);
  385.  
  386.     if (fp != (s_flist *)NULL)
  387.     fp->fl_close = TRUE;
  388. }
  389.  
  390. /*
  391.  * Duplicate file handler onto specific handler
  392.  */
  393.  
  394. int    S_dup2 (old_fid, new_fid)
  395. int    old_fid;
  396. int    new_fid;
  397. {
  398.     int        res = -1;
  399.     int        i;
  400.     Save_IO    *sp;
  401.  
  402. /* If duping onto stdin, stdout or stderr, Search the Save IO stack for an
  403.  * entry matching us
  404.  */
  405.  
  406.     if ((new_fid >= STDIN_FILENO) && (new_fid <= STDERR_FILENO))
  407.     {
  408.     for (sp = SSave_IO, i = 0; (i < NSave_IO_E) &&
  409.                    (SSave_IO[i].depth < Execute_stack_depth);
  410.          i++);
  411.  
  412. /* If depth is greater the Execute_stack_depth - we should panic as this
  413.  * should not happen.  However, for the moment, I'll ignore it
  414.  */
  415.  
  416. /* If there an entry for this depth ? */
  417.  
  418.     if (i == NSave_IO_E)
  419.     {
  420.  
  421. /* Do we need more space? */
  422.  
  423.         if (NSave_IO_E == MSave_IO_E)
  424.         {
  425.         sp = (Save_IO *)space ((MSave_IO_E + SSAVE_IO_SIZE) * sizeof (Save_IO));
  426.  
  427. /* Check for error */
  428.  
  429.         if (sp == (Save_IO *)NULL)
  430.         {
  431.             errno = ENOMEM;
  432.             return -1;
  433.         }
  434.  
  435. /* Save original data */
  436.  
  437.         if (MSave_IO_E != 0)
  438.         {
  439.             memcpy (sp, SSave_IO, sizeof (Save_IO) * MSave_IO_E);
  440.             DELETE (SSave_IO);
  441.         }
  442.  
  443.         setarea ((char *)sp, 1);
  444.         SSave_IO = sp;
  445.         MSave_IO_E += SSAVE_IO_SIZE;
  446.         }
  447.  
  448. /* Initialise the new entry */
  449.  
  450.         sp = &SSave_IO[NSave_IO_E++];
  451.         sp->depth             = Execute_stack_depth;
  452.         sp->fp[STDIN_FILENO]  = -1;
  453.         sp->fp[STDOUT_FILENO] = -1;
  454.         sp->fp[STDERR_FILENO] = -1;
  455.     }
  456.  
  457.     if (sp->fp[new_fid] == -1)
  458.         sp->fp[new_fid] = remap (new_fid);
  459.     }
  460.  
  461. /* OK - Dup the descriptor */
  462.  
  463.     if ((old_fid != -1) && ((res = dup2 (old_fid, new_fid)) >= 0))
  464.     {
  465.     S_close (new_fid, FALSE);
  466.     res = S_Remap (old_fid, new_fid);
  467.     }
  468.  
  469.     return res;
  470. }
  471.  
  472. /*
  473.  * Restore the Stdin, Stdout and Stderr to original values.  If change is
  474.  * FALSE, just remove entries from stack.  A special case for exec.
  475.  */
  476.  
  477. int    restore_std (rv, change)
  478. int    rv;
  479. bool    change;
  480. {
  481.     int        j, i;
  482.     Save_IO    *sp;
  483.  
  484. /* Start at the top and remove any entries above the current execute stack
  485.  * depth
  486.  */
  487.  
  488.     for (j = NSave_IO_E; j > 0; j--)
  489.     {
  490.        sp = &SSave_IO[j - 1];
  491.  
  492.        if (sp->depth < Execute_stack_depth)
  493.        break;
  494.  
  495. /* Reduce number of entries */
  496.  
  497.     --NSave_IO_E;
  498.  
  499. /* If special case (changed at this level) - continue */
  500.  
  501.     if (!change && (sp->depth == Execute_stack_depth))
  502.         continue;
  503.  
  504. /* Close and restore any files */
  505.  
  506.     for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
  507.     {
  508.         if (sp->fp[i] != -1)
  509.         {
  510.         S_close (i, TRUE);
  511.         dup2 (sp->fp[i], i);
  512.         S_close (sp->fp[i], TRUE);
  513.         }
  514.     }
  515.     }
  516.  
  517.     return rv;
  518. }
  519.  
  520. /*
  521.  * Create a Pipe
  522.  */
  523.  
  524. int        openpipe ()
  525. {
  526.     register int    i;
  527.  
  528.     if ((i = S_open (TRUE, g_tempname (), O_PMASK, 0600)) < 0)
  529.     print_error (nopipe);
  530.  
  531.     return i;
  532. }
  533.  
  534. /*
  535.  * Close a pipe
  536.  */
  537.  
  538. void        closepipe (pv)
  539. register int    pv;
  540. {
  541.     if (pv != -1)
  542.     S_close (pv, TRUE);
  543. }
  544.  
  545. /*
  546.  * Write a character to STDERR
  547.  */
  548.  
  549. void    S_putc (c)
  550. int    c;
  551. {
  552.     write (STDERR_FILENO, (char *)&c, 1);
  553. }
  554.  
  555. /*
  556.  * Write a string to STDERR
  557.  */
  558.  
  559. void    S_puts (s)
  560. char    *s;
  561. {
  562.     write (STDERR_FILENO, s, strlen (s));
  563. }
  564.  
  565. /*
  566.  * Check for restricted shell
  567.  */
  568.  
  569. bool    check_rsh (s)
  570. char    *s;
  571. {
  572.     if (r_flag)
  573.     {
  574.     print_error ("%s: restricted\n", s);
  575.     return TRUE;
  576.     }
  577.  
  578.     return FALSE;
  579. }
  580.  
  581. /*
  582.  * Check to see if a file is a shell script.  If it is, return the file
  583.  * handler for the file
  584.  */
  585.  
  586. int    O_for_execute (path, params, nargs)
  587. char    *path;
  588. char    **params;
  589. int    *nargs;
  590. {
  591.     int        i = -1;
  592.     char    *local_path;
  593.  
  594. /* Work on a copy of the path */
  595.  
  596.     if ((local_path = getcell (strlen (path) + 4)) == (char *)NULL)
  597.     return -1;
  598.  
  599. /* Try the file name and then with a .sh appended */
  600.  
  601.     if ((i = Check_Script (strcpy (local_path, path), params, nargs)) < 0)
  602.       if ((i = Check_Script (strcat (local_path, ".sh"), params, nargs)) == 0)
  603.         strcpy(path, local_path);
  604.  
  605.     DELETE (local_path);
  606.     return i;
  607. }
  608.  
  609. /*
  610.  * Check for shell script
  611.  */
  612.  
  613. int    Check_Script (path, params, nargs)
  614. char    *path;
  615. char    **params;
  616. int    *nargs;
  617. {
  618.     char    buf[512];        /* Input buffer            */
  619.     int        fp;            /* File handler            */
  620.     int        nbytes;            /* Number of bytes read        */
  621.     char    *bp;            /* Pointers into buffers    */
  622.     char    *ep;
  623.  
  624.     if ((fp = S_open (FALSE, path, O_RMASK)) < 0)
  625.     return -1;
  626.  
  627. /* zero or less bytes - not a script */
  628.  
  629.     memset (buf, 0, 512);
  630.     nbytes = read (fp, buf, 512);
  631.  
  632.     for (ep = &buf[nbytes], bp = buf; (bp < ep) && ((unsigned char)*bp >= 0x08); ++bp);
  633.  
  634. /* If non-ascii file or lenght is less than 1 - not a script */
  635.  
  636.     if ((bp != ep) || (nbytes < 1))
  637.     {
  638.     S_close (fp, TRUE);
  639.     return -1;
  640.     }
  641.  
  642. /* Ensure end of buffer detected */
  643.  
  644.     buf[511] = 0;
  645.  
  646. /* Initialise the return parameters, if specified */
  647.  
  648.     if (params != (char **)NULL)
  649.     *params = null;
  650.  
  651.     if (nargs != (int *)NULL)
  652.     *nargs = 0;
  653.  
  654. /* We don't care how many bytes were read now, so use it to count the
  655.  * additional arguments
  656.  */
  657.  
  658.     nbytes = 0;
  659.  
  660. /* Find the end of the first line */
  661.  
  662.     if ((bp = strchr (buf, '\n')) != (char *)NULL)
  663.     *bp = 0;
  664.  
  665.     bp = buf;
  666.     ep = (char *)NULL;
  667.  
  668. /* Check for script */
  669.  
  670.     if ((*(bp++) != '#') || (*(bp++) != '!'))
  671.     return fp;
  672.  
  673.     while (*bp)
  674.     {
  675.     while (isspace (*bp))
  676.         ++bp;
  677.  
  678. /* Save the start of the arguments */
  679.  
  680.     if (*bp)
  681.     {
  682.         if (ep == (char *)NULL)
  683.         ep = bp;
  684.  
  685. /* Count the arguments */
  686.  
  687.         ++nbytes;
  688.     }
  689.  
  690.     while (!isspace (*bp) && *bp)
  691.         ++bp;
  692.     }
  693.  
  694. /* Set up the return parameters, if appropriate */
  695.  
  696.     if ((params != (char **)NULL) && (strlen (ep) != 0))
  697.     {
  698.     if ((*params = getcell (strlen (ep) + 1)) == (char *)NULL)
  699.     {
  700.         *params = null;
  701.         S_close (fp, TRUE);
  702.         return -1;
  703.     }
  704.  
  705.     strcpy (*params, ep);
  706.     }
  707.  
  708.     if (nargs != (int *)NULL)
  709.     *nargs = nbytes;
  710.  
  711.     return fp;
  712. }
  713.  
  714. /*
  715.  * Convert slashes to backslashes for MSDOS
  716.  */
  717.  
  718. void    Convert_Slashes (sp)
  719. char    *sp;
  720. {
  721.     while (*sp)
  722.     {
  723.     if (*sp == '/')
  724.         *sp = '\\';
  725.  
  726.     ++sp;
  727.     }
  728. }
  729.  
  730. /*
  731.  * Some buffered Output functions to speed somethings up.
  732.  */
  733.  
  734. /* Open the buffer */
  735.  
  736. Out_Buf    *Open_buffer (fid, f_abort)
  737. int    fid;
  738. bool    f_abort;
  739. {
  740.     Out_Buf    *bp;
  741.  
  742.     if (((bp = (Out_Buf *)getcell (sizeof (Out_Buf))) == (Out_Buf *)NULL) ||
  743.     ((bp->ob_start = getcell (BIO_LENGTH)) == (char *)NULL))
  744.     {
  745.     if (f_abort)
  746.     {
  747.         print_error ("sh: %s\n", strerror (ENOMEM));
  748.         fail ();
  749.     }
  750.  
  751.     return (Out_Buf *)NULL;
  752.     }
  753.  
  754. /* Ok - save info */
  755.  
  756.     bp->ob_fid = fid;
  757.     bp->ob_cur = bp->ob_start;
  758.     return bp;
  759. }
  760.  
  761. /* Add a character to the buffer */
  762.  
  763. void        Add_buffer (c, bp)
  764. char        c;
  765. Out_Buf        *bp;
  766. {
  767.     *(bp->ob_cur++) = c;
  768.  
  769.     if (bp->ob_cur == &bp->ob_start[BIO_LENGTH - 1])
  770.     {
  771.     write (bp->ob_fid, bp->ob_start, BIO_LENGTH - 1);
  772.     bp->ob_cur = bp->ob_start;
  773.     }
  774. }
  775.  
  776. /* Close the buffer */
  777.  
  778. void        Close_buffer (bp)
  779. Out_Buf        *bp;
  780. {
  781.     int        n;
  782.  
  783.     if ((n = (int)(bp->ob_cur - bp->ob_start)))
  784.     write (bp->ob_fid, bp->ob_start, n);
  785.  
  786.     DELETE (bp->ob_start);
  787.     DELETE (bp);
  788. }
  789.  
  790. /* Output string */
  791.  
  792. void        Adds_buffer (s, bp)
  793. char        *s;
  794. Out_Buf        *bp;
  795. {
  796.     while (*s)
  797.     {
  798.     *(bp->ob_cur++) = *(s++);
  799.  
  800.     if (bp->ob_cur == &bp->ob_start[BIO_LENGTH - 1])
  801.     {
  802.         write (bp->ob_fid, bp->ob_start, BIO_LENGTH - 1);
  803.         bp->ob_cur = bp->ob_start;
  804.     }
  805.     }
  806. }
  807.