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