home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / g / gs252src.zip / GS252 / ZFILE.C < prev    next >
C/C++ Source or Header  |  1992-09-15  |  18KB  |  619 lines

  1. /* Copyright (C) 1989, 1992 Aladdin Enterprises.  All rights reserved.
  2.    Distributed by Free Software Foundation, Inc.
  3.  
  4. This file is part of Ghostscript.
  5.  
  6. Ghostscript is distributed in the hope that it will be useful, but
  7. WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  8. to anyone for the consequences of using it or for whether it serves any
  9. particular purpose or works at all, unless he says so in writing.  Refer
  10. to the Ghostscript General Public License for full details.
  11.  
  12. Everyone is granted permission to copy, modify and redistribute
  13. Ghostscript, but only under the conditions described in the Ghostscript
  14. General Public License.  A copy of this license is supposed to have been
  15. given to you along with Ghostscript so you can know your rights and
  16. responsibilities.  It should be in a file named COPYING.  Among other
  17. things, the copyright notice and this notice must be preserved on all
  18. copies.  */
  19.  
  20. /* zfile.c */
  21. /* Non-I/O file operators for Ghostscript */
  22. #include "memory_.h"
  23. #include "string_.h"
  24. #include "ghost.h"
  25. #include "gp.h"
  26. #include "errors.h"
  27. #include "oper.h"
  28. #include "alloc.h"
  29. #include "estack.h"            /* for filenameforall, file_close */
  30. #include "iutil.h"
  31. #include "save.h"            /* for restore */
  32. #include "stream.h"
  33. #include "files.h"            /* must come after stream.h */
  34. #include "store.h"
  35.  
  36. /* Forward references */
  37. int lib_file_open(P6(const byte *, uint, byte *, uint, uint *, ref *));
  38. int file_open(P6(const byte *, uint, const char *, uint, ref *, stream **));
  39. int file_close(P2(const ref *, stream *));
  40. private int open_std_file(P3(os_ptr, const char *, os_ptr));
  41. /* In zfileio.c */
  42. es_ptr zget_current_file(P0());
  43.  
  44. /* Imported from gs.c */
  45. extern char **gs_lib_paths;        /* search path list, */
  46.                     /* terminated by a null pointer */
  47.  
  48. /*
  49.  * Since there can be many file objects referring to the same file/stream,
  50.  * we can't simply free a stream when we close it.  On the other hand,
  51.  * we don't want freed streams to clutter up memory needlessly.
  52.  * Our solution is to retain the freed streams, and reuse them.
  53.  * To prevent an old file object from being able to access a reused stream,
  54.  * we keep a serial number in each stream, and check it against a serial
  55.  * number stored in the file object (as the "size"); when we close a file,
  56.  * we increment its serial number.  If the serial number ever overflows,
  57.  * we leave it at zero, and do not reuse the stream.
  58.  * (This will never happen.)
  59.  *
  60.  * Storage management for this scheme is a little tricky.
  61.  * We maintain an invariant that says that a stream opened at a given
  62.  * save level always uses a stream structure allocated at that level.
  63.  * By doing this, we don't need to keep track separately of streams open
  64.  * at a level vs. streams allocated at a level; this simplifies things.
  65.  */
  66.  
  67. /* Initialize the checking IDs of a stream. */
  68. #define s_init_ids(s) (s)->read_id = (s)->write_id = 1
  69. #define s_init_read_id(s) (s)->read_id = 1, (s)->write_id = 0
  70. #define s_init_write_id(s) (s)->read_id = 0, (s)->write_id = 1
  71. #define s_init_no_id(s) (s)->read_id = (s)->write_id = 0
  72.  
  73. /*
  74.  * The chain of all allocated files.  We need this only so that we can do
  75.  * the right thing for restore (and GC someday).
  76.  * The chain is sorted, with all files opened/allocated at a given save
  77.  * level preceding those of the previous save level.
  78.  * Note that this chain includes both open and closed files.
  79.  */
  80. private stream *file_list;
  81. private ref prev_file_list_ref;        /* t_file */
  82. /*
  83.  * prev_file_list_ref points to the first file allocated at a previous level.
  84.  */
  85. #define prev_file_list prev_file_list_ref.value.pfile
  86.  
  87. /* File buffer sizes.  For real files, this is arbitrary, */
  88. /* since the C library does its own buffering in addition. */
  89. /* stdout and stderr use smaller buffers, */
  90. /* on the assumption that they are usually not real files. */
  91. /* The buffer size for type 1 encrypted files is NOT arbitrary: */
  92. /* it must be at most 512. */
  93. #define default_buffer_size 512
  94. const uint file_default_buffer_size = default_buffer_size;
  95.  
  96. /* Standard file objects: */
  97. /* 0 is stdin, 1 is stdout, 2 is stderr, 3 is lineedit, 4 is statementedit */
  98. #define num_std_files 5
  99. stream invalid_file_entry;
  100. private byte
  101. #define stdin_buf_size 1
  102.     stdin_buf[stdin_buf_size],
  103. #define stdout_buf_size 128
  104.     stdout_buf[stdout_buf_size],
  105. #define stderr_buf_size 128
  106.     stderr_buf[stderr_buf_size],
  107. #define lineedit_buf_size 160
  108.     lineedit_buf[lineedit_buf_size];
  109. /* statementedit is equivalent to lineedit for now */
  110. stream std_files[num_std_files];
  111. private const char *std_file_names[num_std_files] =
  112.    {    "%stdin",
  113.     "%stdout",
  114.     "%stderr",
  115.     "%lineedit",
  116.     "%statementedit"
  117.    };
  118. private int std_file_attrs[num_std_files] =
  119.    {    a_read+a_execute,
  120.     a_write+a_execute,
  121.     a_write+a_execute,
  122.     a_read+a_execute,
  123.     a_read+a_execute
  124.    };
  125.  
  126. /* Initialize the file table */
  127. private void
  128. zfile_init()
  129. {
  130.     /* Create files for stdin, stdout, and stderr. */
  131.     /****** stdin IS NOT IMPLEMENTED PROPERLY ******/
  132.  
  133.     sread_file(&std_files[0], gs_stdin, stdin_buf, stdin_buf_size);
  134.     s_init_read_id(&std_files[0]);
  135.     std_files[0].can_close = 0;
  136.     swrite_file(&std_files[1], gs_stdout, stdout_buf, stdout_buf_size);
  137.     s_init_write_id(&std_files[1]);
  138.     std_files[1].can_close = 0;
  139.     swrite_file(&std_files[2], gs_stderr, stderr_buf, stderr_buf_size);
  140.     s_init_write_id(&std_files[2]);
  141.     std_files[2].can_close = 0;
  142.     s_init_no_id(&std_files[3]);
  143.     s_init_no_id(&std_files[4]);
  144.     s_disable(&invalid_file_entry);
  145.     s_init_no_id(&invalid_file_entry);
  146.  
  147.     /* Initialize the bookkeeping lists. */
  148.     
  149.     file_list = 0;
  150.     make_file(&prev_file_list_ref, 0, 0, 0);
  151. }
  152.  
  153. /* file */
  154. int
  155. zfile(register os_ptr op)
  156. {    char file_access[3];
  157.     ref fname;
  158.     int code;
  159.     stream *s;
  160.     fname = op[-1];
  161.     check_type(fname, t_string);
  162.     check_type(*op, t_string);
  163.     switch ( r_size(op) )
  164.        {
  165. /******
  166.     case 2:
  167.         if ( op->value.bytes[1] != '+' )
  168.             return e_invalidfileaccess;
  169.         file_access[1] = '+';
  170.         file_access[2] = 0;
  171.         break;
  172.  ******/
  173.     case 1:
  174.         file_access[1] = 0;
  175.         break;
  176.     default:
  177.         return e_invalidfileaccess;
  178.        }
  179.     switch ( *op->value.bytes )
  180.        {
  181.     case 'r': case 'w': /****** case 'a': ******/
  182.         break;
  183.     default:
  184.         return e_invalidfileaccess;
  185.        }
  186.     file_access[0] = *op->value.bytes;
  187.     code = open_std_file(op - 1, file_access, op - 1);
  188.     switch ( code )
  189.        {
  190.     case 0:                /* successful open */
  191.         pop(1);
  192.     default:            /* unsuccessful open */
  193.         return code;
  194.     case e_undefinedfilename:    /* not a %file */
  195.         ;
  196.        }
  197.     code = file_open(fname.value.bytes, r_size(&fname),
  198.              file_access, default_buffer_size, op - 1, &s);
  199.     if ( code >= 0 ) pop(1);
  200.     return code;
  201. }
  202.  
  203. /* closefile */
  204. int
  205. zclosefile(register os_ptr op)
  206. {    stream *s;
  207.     int code;
  208.     check_file(s, op);
  209.     if ( s->can_close == 0 )
  210.         return e_invalidaccess;    /* can't close std file */
  211.     if ( (code = file_close(op, s)) >= 0 )
  212.        {    pop(1);
  213.        }
  214.     return code;
  215. }
  216.  
  217. /* ------ Level 2 extensions ------ */
  218.  
  219. /* deletefile */
  220. int
  221. zdeletefile(register os_ptr op)
  222. {    char *str;
  223.     int stat;
  224.     check_read_type(*op, t_string);
  225.     str = ref_to_string(op, "deletefile");
  226.     if ( str == 0 ) return e_VMerror;
  227.     stat = unlink(str);
  228.     alloc_free(str, r_size(op) + 1, 1, "deletefile");
  229.     if ( stat != 0 ) return e_ioerror;
  230.     pop(1);
  231.     return 0;
  232. }
  233.  
  234. /* filenameforall */
  235. private int file_continue(P1(os_ptr));
  236. private int i_file_continue;
  237. int
  238. zfilenameforall(register os_ptr op)
  239. {    file_enum *pfen;
  240.     check_write_type(*op, t_string);
  241.     check_proc(op[-1]);
  242.     check_read_type(op[-2], t_string);
  243.     /* Push a mark, the pattern, the scratch string, the enumerator, */
  244.     /* and the procedure, and invoke the continuation. */
  245.     check_estack(7);
  246.     pfen = gp_enumerate_files_init((char *)op[-2].value.bytes, r_size(op - 2), alloc, alloc_free);
  247.     if ( pfen == 0 ) return e_VMerror;
  248.     mark_estack(es_for);
  249.     *++esp = op[-2];
  250.     *++esp = *op;
  251.     ++esp;
  252.     make_tasv(esp, t_string, a_read+a_execute+a_executable, 0,
  253.           bytes, (byte *)pfen);
  254.     *++esp = op[-1];
  255.     pop(3);  op -= 3;
  256.     return file_continue(op);
  257. }
  258. /* Continuation operator for enumerating files */
  259. private int
  260. file_continue(register os_ptr op)
  261. {    es_ptr pscratch = esp - 2;
  262.     file_enum *pfen = (file_enum *)esp[-1].value.bytes;
  263.     uint len = r_size(pscratch);
  264.     uint code = gp_enumerate_files_next(pfen, (char *)pscratch->value.bytes, len);
  265.     if ( code == ~(uint)0 )        /* all done */
  266.        {    gp_enumerate_files_close(pfen);
  267.         esp -= 4;        /* pop mark, scatch, pfen, proc */
  268.         return o_pop_estack;
  269.        }
  270.     else if ( code > len )        /* overran string */
  271.        {    gp_enumerate_files_close(pfen);
  272.         return e_rangecheck;
  273.        }
  274.     else if ( !string_match(pscratch->value.bytes, code,
  275.                 pscratch[-1].value.bytes,
  276.                 r_size(&pscratch[-1]),
  277.                 gp_file_names_ignore_case)
  278.         )
  279.        {    /* Enumerator was too liberal, ignore this one. */
  280.         push_op_estack(file_continue, i_file_continue);    /* come again */
  281.         return o_push_estack;
  282.        }
  283.     else
  284.        {    push(1);
  285.         ref_assign(op, pscratch);
  286.         r_set_size(op, code);
  287.         push_op_estack(file_continue, i_file_continue);    /* come again */
  288.         *++esp = pscratch[2];    /* proc */
  289.         return o_push_estack;
  290.        }
  291. }
  292.  
  293. /* renamefile */
  294. int
  295. zrenamefile(register os_ptr op)
  296. {    char *str1 = 0, *str2 = 0;
  297.     check_read_type(*op, t_string);
  298.     check_read_type(op[-1], t_string);
  299.     str1 = ref_to_string(op - 1, "renamefile(from)");
  300.     str2 = ref_to_string(op, "renamefile(to)");
  301.     if ( str1 != 0 && str2 != 0 && rename(str1, str2) == 0 )
  302.        {    pop(2);
  303.        }
  304.     if ( str1 != 0 )
  305.         alloc_free(str1, r_size(op - 1) + 1, 1, "renamefile(from)");
  306.     if ( str2 != 0 )
  307.         alloc_free(str2, r_size(op) + 1, 1, "renamefile(to)");
  308.     return 0;
  309. }    
  310.  
  311. /* ------ Ghostscript extensions ------ */
  312.  
  313. /* findlibfile */
  314. int
  315. zfindlibfile(register os_ptr op)
  316. {    int code;
  317. #define maxclen 200
  318.     byte cname[maxclen];
  319.     uint clen;
  320.     check_type(*op, t_string);
  321.     code = open_std_file(op, "r", op - 1);
  322.     switch ( code )
  323.        {
  324.     case 0:                /* successful open */
  325.         push(1);
  326.         make_bool(op, 1);
  327.     default:            /* unsuccessful open */
  328.         return code;
  329.     case e_undefinedfilename:    /* not a %file */
  330.         ;
  331.        }
  332.     code = lib_file_open(op->value.bytes, r_size(op), cname, maxclen,
  333.                  &clen, op);
  334.     if ( code >= 0 )
  335.        {    byte *cstr = (byte *)alloc(clen, 1, "findlibfile");
  336.         if ( cstr == 0 ) return e_VMerror;
  337.         memcpy(cstr, cname, clen);
  338.         push(2);
  339.         op[-1] = op[-2];
  340.         make_tasv(op - 2, t_string, a_all, clen, bytes, cstr);
  341.         make_bool(op, 1);
  342.        }
  343.     else
  344.        {    push(1);
  345.         make_bool(op, 0);
  346.        }
  347.     return 0;
  348. }
  349.  
  350. /* ------ Initialization procedure ------ */
  351.  
  352. op_def zfile_op_defs[] = {
  353.     {"1closefile", zclosefile},
  354.     {"1deletefile", zdeletefile},
  355.     {"2file", zfile},
  356.     {"3filenameforall", zfilenameforall},
  357.     {"1findlibfile", zfindlibfile},
  358.     {"2renamefile", zrenamefile},
  359.         /* Internal operators */
  360.     {"0%file_continue", file_continue, &i_file_continue},
  361.     op_def_end(zfile_init)
  362. };
  363.  
  364. /* ------ Non-operator routines ------ */
  365.  
  366. /* Open a file, using the search paths if necessary. */
  367. /* The startup code calls this to open the initialization file gs_init.ps. */
  368. int
  369. lib_file_open(const byte *fname, uint len, byte *cname, uint max_clen,
  370.   uint *pclen, ref *pfile)
  371. {    int code;
  372.     char **ppath;
  373.     stream *s;
  374.     code = file_open(fname, len, "r", default_buffer_size, pfile, &s);
  375.     if ( code >= 0 )
  376.        {    memcpy(cname, fname, len);
  377.         *pclen = len;
  378.         return code;
  379.        }
  380.     if ( gp_file_name_is_absolute((const char *)fname, len) )
  381.         return e_undefinedfilename;
  382.     /* Go through the list of search paths */
  383.     for ( ppath = gs_lib_paths; *ppath != 0; ppath++ )
  384.        {    char *path = *ppath;
  385.         for ( ; ; )
  386.            {    /* Find the end of the next path */
  387.             char *npath = path;
  388.             uint plen;
  389.             const char *cstr;
  390.             uint clen;
  391.             while ( *npath != 0 && *npath != gp_file_name_list_separator )
  392.                 npath++;
  393.             plen = npath - path;
  394.             cstr = gp_file_name_concat_string(path, plen,
  395.                               (const char *)fname,
  396.                               len);
  397.             /* Concatenate the prefix, combiner, and file name. */
  398.             clen = plen + strlen(cstr) + len;
  399.             if ( clen <= max_clen )    /* otherwise punt */
  400.                {    memcpy(cname, (byte *)path, plen);
  401.                 strcpy((char *)cname + plen, cstr);
  402.                 memcpy(cname + clen - len, fname, len);
  403.                 *pclen = clen;
  404.                 code = file_open(cname, clen, "r",
  405.                          default_buffer_size,
  406.                          pfile, &s);
  407.                 if ( code >= 0 ) return code;
  408.                }
  409.             /****** NYI ******/
  410.             if ( !*npath ) break;
  411.             path = npath + 1;
  412.            }
  413.        }
  414.     return code;
  415. }
  416.  
  417. /* Open a file and create a file object. */
  418. /* Return 0 if successful, error code if not. */
  419. /* If fname==0, set up the file entry, stream, and buffer, */
  420. /* but don't open an OS file or initialize the stream. */
  421. /* The filter routines also use this. */
  422. int
  423. file_open(const byte *fname, uint len, const char *file_access,
  424.   uint buffer_size, ref *pfile, stream **ps)
  425. {    byte *buffer;
  426.     stream *s;
  427.     int reused;
  428.     if ( buffer_size == 0 )
  429.       buffer_size = default_buffer_size;
  430.     if ( len >= buffer_size )
  431.       return e_limitcheck;    /* we copy the file name into the buffer */
  432.     /* Allocate the stream first, since it persists */
  433.     /* even after the file has been closed. */
  434.     /* Look first for a free stream allocated at this level. */
  435.     s = file_list;
  436.     reused = 0;
  437.     while ( s != prev_file_list )
  438.     {    if ( !s_is_valid(s) && s->read_id != 0 /* i.e. !overflowed */ )
  439.         {    reused = 1;
  440.             break;
  441.         }
  442.         s = s->next;
  443.     }
  444.     if ( !reused )
  445.     {    s = (stream *)alloc(1, sizeof(stream), "file_open(stream)");
  446.         if ( s == 0 ) return e_VMerror;
  447.         s_init_ids(s);
  448.     }
  449.     /* Allocate the buffer. */
  450.     buffer = (byte *)alloc(buffer_size, 1, "file_open(buffer)");
  451.     /* Set the close procedure in case we have to free the file */
  452.     /* before actually initializing the stream. */
  453.     s->procs.close = s_std_close;
  454.     if ( buffer == 0 )
  455.        {    if ( !reused )
  456.           alloc_free((char *)s, 1, sizeof(stream),
  457.                  "file_open(stream)");
  458.         return e_VMerror;
  459.        }
  460.     if ( fname != 0 )
  461.        {    /* Copy the name (so we can terminate it with a zero byte.) */
  462.         char *file_name = (char *)buffer;
  463.         char fmode[4];        /* r/w/a, [+], [b], null */
  464.         FILE *file;
  465.         memcpy(file_name, fname, len);
  466.         file_name[len] = 0;        /* terminate string */
  467.         /* Open the file, always in binary mode. */
  468.         strcpy(fmode, file_access);
  469.         strcat(fmode, gp_fmode_binary_suffix);
  470.         file = fopen(file_name, fmode);
  471.         if ( file == 0 )
  472.            {    alloc_free((char *)buffer, buffer_size, 1,
  473.                    "file_open(buffer)");
  474.             if ( !reused )
  475.               alloc_free((char *)s, 1, sizeof(stream),
  476.                      "file_open(stream)");
  477.             return e_undefinedfilename;
  478.            }
  479.         /* Set up the stream. */
  480.         if ( *file_access == 'r' )    /* reading */
  481.             sread_file(s, file, buffer, buffer_size);
  482.         else
  483.             swrite_file(s, file, buffer, buffer_size);
  484.        }
  485.     else                /* save the buffer and size */
  486.        {    s->cbuf = buffer;
  487.         s->bsize = s->cbsize = buffer_size;
  488.        }
  489.     s->can_close = 1;
  490.     s->strm_is_temp = 0;        /* not a temp stream */
  491.     if ( !reused )
  492.     {    /* Add s to the list of files. */
  493.         if ( file_list != 0 )
  494.             file_list->prev = s;
  495.         s->next = file_list;
  496.         s->prev = 0;
  497.         file_list = s;
  498.     }
  499.     if ( *file_access == 'r' )
  500.     {    s->write_id = 0;
  501.         make_file(pfile, a_read+a_execute, s->read_id, s);
  502.     }
  503.     else
  504.     {    s->read_id = 0;
  505.         make_file(pfile, a_write+a_execute, s->write_id, s);
  506.     }
  507.     *ps = s;
  508.     return 0;
  509. }
  510.  
  511. /* Close a file.  The interpreter calls this when */
  512. /* it reaches the end of an executable file. */
  513. int
  514. file_close(const ref *fp /* t_file */, stream *s)
  515. {    switch ( s->can_close )
  516.        {
  517.     case 0:                /* can't close std files, ignore */
  518.         break;
  519.     case -1:            /* ignore on statement/lineedit */
  520.         sclose(s);
  521.         s_disable(s);
  522.         s->read_id = 0;        /* disable access */
  523.         break;
  524.     default:            /* ordinary or filter file */
  525.        {    char *stemp = (s->strm_is_temp ? (char *)s->strm : 0);
  526.         if ( sclose(s) ) return e_ioerror;
  527.         s_disable(s);
  528.         alloc_free((char *)s->cbuf, s->cbsize, 1,
  529.                "file_close(buffer)");
  530.         if ( stemp != 0 )
  531.             alloc_free(stemp, 1, sizeof(stream),
  532.                    "file_close(sub-stream)");
  533.         /* Increment the IDs to prevent further access. */
  534.         s->read_id = s->write_id = (s->read_id | s->write_id) + 1;
  535.        }
  536.        }
  537.     /* If we just closed the file from which the interpreter */
  538.     /* is reading, zap it on the exec stack. */
  539.        {    es_ptr cfp = zget_current_file();
  540.         if ( cfp != 0 && fptr(cfp) == fptr(fp) )
  541.            {    /* A null would confuse the estack parser.... */
  542.             make_tasv(cfp, t_array, a_executable+a_execute, 0, refs, (ref *)0);
  543.             esfile = 0;        /* clear currentfile cache */
  544.            }
  545.        }
  546.     return 0;
  547. }
  548.  
  549. /* ------ Internal routines ------ */
  550.  
  551. /* If a file name refers to one of the standard %files, */
  552. /* 'open' the file and return 0 or an error code, otherwise */
  553. /* return e_undefinedfilename. */
  554. private int
  555. open_std_file(os_ptr pfname, const char *file_access, os_ptr pfile)
  556. {    int i;
  557.     for ( i = 0; i < num_std_files; i++ )
  558.       if ( !bytes_compare(pfname->value.bytes, r_size(pfname),
  559.                   (const byte *)std_file_names[i],
  560.                   strlen(std_file_names[i]))
  561.         )
  562.        {    /* This is a standard file */
  563.         int attrs = (*file_access == 'r' ? a_read+a_execute : a_write+a_execute);
  564.         stream *s = &std_files[i];
  565.         if ( attrs != std_file_attrs[i] )
  566.             return e_invalidaccess;
  567.         make_file(pfile, attrs, 1, s);
  568.         /* If this is %lineedit or %statementedit, */
  569.         /* read a line now. */
  570.         switch ( i )
  571.            {
  572.         case 3: case 4:
  573.            {    uint count;
  574.             int code = zreadline_stdin(lineedit_buf,
  575.                 lineedit_buf_size, &count);
  576.             if ( code < 0 ) return code;
  577.             sread_string(s, lineedit_buf, count);
  578.             s->read_id = 1;        /* enable access */
  579.             s->can_close = -1;
  580.             return 0;
  581.            }
  582.            }
  583.         return 0;
  584.        }
  585.     return e_undefinedfilename;
  586. }
  587.  
  588. /* ------ Memory management ------ */
  589.  
  590. /* Arrange to save the current file list at a save. */
  591. void
  592. file_save()
  593. {    ref_mark_old(&prev_file_list_ref);
  594.     ref_save(&prev_file_list_ref, "file_save");
  595.     prev_file_list = file_list;
  596. }
  597.  
  598. /* Close inaccessible files just before a restore, */
  599. /* or all files just before exiting. */
  600. /* NOTE: we depend on save/restore being LIFO! */
  601. void
  602. file_restore(const alloc_save *save)
  603. {    stream *s = file_list;
  604.     while ( s != prev_file_list )
  605.     {    if ( s_is_valid(s) )
  606.         {    if ( s->can_close > 0 )    /* ignore std & buffered files */
  607.                 sclose(s);
  608.             s_disable(s);
  609.         }
  610.         s = s->next;
  611.     }
  612.     file_list = prev_file_list;
  613. }
  614. void
  615. file_close_all()
  616. {    prev_file_list = 0;
  617.     file_restore((alloc_save *)0);
  618. }
  619.