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