home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / lout2.lzh / LOUT2 / z03.c < prev    next >
Text File  |  1994-02-26  |  37KB  |  903 lines

  1. /*@z03.c:File Service:Declarations, no_fpos@******************************** */
  2. /*                                                                           */
  3. /*  LOUT: A HIGH-LEVEL LANGUAGE FOR DOCUMENT FORMATTING (VERSION 2.05)       */
  4. /*  COPYRIGHT (C) 1993 Jeffrey H. Kingston                                   */
  5. /*                                                                           */
  6. /*  Jeffrey H. Kingston (jeff@cs.su.oz.au)                                   */
  7. /*  Basser Department of Computer Science                                    */
  8. /*  The University of Sydney 2006                                            */
  9. /*  AUSTRALIA                                                                */
  10. /*                                                                           */
  11. /*  This program is free software; you can redistribute it and/or modify     */
  12. /*  it under the terms of the GNU General Public License as published by     */
  13. /*  the Free Software Foundation; either version 1, or (at your option)      */
  14. /*  any later version.                                                       */
  15. /*                                                                           */
  16. /*  This program is distributed in the hope that it will be useful,          */
  17. /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
  18. /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
  19. /*  GNU General Public License for more details.                             */
  20. /*                                                                           */
  21. /*  You should have received a copy of the GNU General Public License        */
  22. /*  along with this program; if not, write to the Free Software              */
  23. /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
  24. /*                                                                           */
  25. /*  FILE:         z03.c                                                      */
  26. /*  MODULE:       File Service                                               */
  27. /*  EXTERNS:      InitFiles(), AddToPath(), DefineFile(), FirstFile(),       */
  28. /*                NextFile(), FileNum(), FileName(), EchoFilePos(),          */
  29. /*                PosOfFile(), OpenFile(), OpenIncGraphicFile(),             */
  30. /*                ReadFromFile(), AppendToFile(), CloseFiles()               */
  31. /*                                                                           */
  32. /*****************************************************************************/
  33. #include "externs"
  34. #define MAX_TYPES     10            /* number of file types      */
  35. #define MAX_PATHS      7            /* number of search paths    */
  36. #define    TAB_MASK    0xFF            /* mask forces <= MAX_FILES  */
  37.  
  38. #define    file_number(x)    word_font(x)        /* file number of file x     */
  39. #define    updated(x)    broken(x)        /* TRUE when x is updated    */
  40. #define    path(x)        back(x, COL)        /* search path for file x    */
  41.  
  42. static    int    file_count;            /* total number of files     */
  43. static    OBJECT    fvec[MAX_FILES] = { nil };    /* the file table            */
  44. static    OBJECT    file_list[MAX_TYPES];        /* files of each type        */
  45. static    OBJECT    file_path[MAX_PATHS];        /* the search paths          */
  46. #if DEBUG_ON
  47. static    char    *file_types[]        /* the type names for debug  */
  48.         = { "source", "include", "incgraphic", "database", "index",
  49.             "font", "prepend", "hyph", "hyphpacked", "encoding" };
  50. #endif
  51.  
  52.  
  53. /*****************************************************************************/
  54. /*                                                                           */
  55. /*  no_fpos                                                                  */
  56. /*                                                                           */
  57. /*  A null file position value.                                              */
  58. /*                                                                           */
  59. /*****************************************************************************/
  60.  
  61. static FILE_POS no_file_pos = {0, 0, 0};
  62. FILE_POS *no_fpos = &no_file_pos;
  63.  
  64. /*****************************************************************************/
  65. /*                                                                           */
  66. /*  #define hash(str, val)                                                   */
  67. /*                                                                           */
  68. /*  Hash the string str and return its value in val.                         */
  69. /*                                                                           */
  70. /*****************************************************************************/
  71.  
  72. #define hash(str, val)                            \
  73. { p = str;                                \
  74.   val = *p++;                                \
  75.   while( *p ) val += *p++;                        \
  76.   val = (val * 8) & TAB_MASK;                        \
  77. }
  78.  
  79. /*@::InitFiles(), AddToPath(), DefineFile()@**********************************/
  80. /*                                                                           */
  81. /*  InitFiles()                                                              */
  82. /*                                                                           */
  83. /*  Initialize this module.                                                  */
  84. /*                                                                           */
  85. /*****************************************************************************/
  86.  
  87. InitFiles()
  88. { int i;
  89.   for( i = 0;  i < MAX_TYPES; i++ )  file_list[i]  = New(ACAT);
  90.   for( i = 0;  i < MAX_PATHS; i++ )  file_path[i] = New(ACAT);
  91.   fvec[0] = file_list[0];    /* so that no files will be given slot 0 */
  92.   file_count = 1;
  93. } /* end InitFiles */
  94.  
  95.  
  96. /*****************************************************************************/
  97. /*                                                                           */
  98. /*  AddToPath(fpath, dirname)                                                */
  99. /*                                                                           */
  100. /*  Add the directory dirname to the end of search path fpath.               */
  101. /*                                                                           */
  102. /*****************************************************************************/
  103.  
  104. AddToPath(fpath, dirname)
  105. int fpath; FULL_CHAR *dirname;
  106. { OBJECT x;
  107.   x = MakeWord(WORD, dirname, no_fpos);
  108.   Link(file_path[fpath], x);
  109. } /* end AddToPath */
  110.  
  111.  
  112. /*****************************************************************************/
  113. /*                                                                           */
  114. /*  FILE_NUM DefineFile(str, suffix, xfpos, ftype, fpath)                    */
  115. /*                                                                           */
  116. /*  Declare a file whose name is str plus suffix and whose fpos is xfpos.    */
  117. /*  The file type is ftype, and its search path is fpath.                    */
  118. /*                                                                           */
  119. /*****************************************************************************/
  120.  
  121. FILE_NUM DefineFile(str, suffix, xfpos, ftype, fpath)
  122. FULL_CHAR *str, *suffix; FILE_POS *xfpos;  int ftype, fpath;
  123. { register FULL_CHAR *p;
  124.   register int i;
  125.   assert( ftype < MAX_TYPES, "DefineFile: ftype!" );
  126.   debug5(DFS, D, "DefineFile(%s, %s,%s, %s, %d)",
  127.     str, suffix, EchoFilePos(xfpos), file_types[ftype], fpath);
  128.   if( ftype == SOURCE_FILE && (i = StringLength(str)) >= 3 )
  129.   {
  130.     /* check that file name does not end in ".li" or ".ld" */
  131.     if( StringEqual(&str[i-StringLength(DATA_SUFFIX)], DATA_SUFFIX) )
  132.       Error(FATAL, xfpos,
  133.     "database file %s where source file expected", str);
  134.     if( StringEqual(&str[i-StringLength(INDEX_SUFFIX)], INDEX_SUFFIX) )
  135.       Error(FATAL, xfpos,
  136.     "database index file %s where source file expected", str);
  137.   }
  138.   if( ++file_count >= MAX_FILES ) Error(FATAL, xfpos, "too many file names");
  139.   hash(str, i);
  140.   while( fvec[i] != nil )
  141.     if( ++i >= MAX_FILES ) i = 0;
  142.   if( StringLength(str) + StringLength(suffix) >= MAX_LINE )
  143.     Error(FATAL, no_fpos, "file name %s%s too long", str, suffix);
  144.   fvec[i] = MakeWordTwo(WORD, str, suffix, xfpos);
  145.   Link(file_list[ftype], fvec[i]);
  146.   file_number(fvec[i]) = i;
  147.   path(fvec[i]) = fpath;
  148.   debug1(DFS, D, "DefineFile returning %s",
  149.     i == NO_FILE ? STR_NONE : FileName( (FILE_NUM) i));
  150.   return (FILE_NUM) i;
  151. } /* end DefineFile */
  152.  
  153.  
  154. /*@::FirstFile(), NextFile(), FileNum()@**************************************/
  155. /*                                                                           */
  156. /*  FILE_NUM FirstFile(ftype)                                                */
  157. /*                                                                           */
  158. /*  Returns first file of type ftype, else NO_FILE.                          */
  159. /*                                                                           */
  160. /*****************************************************************************/
  161.  
  162. FILE_NUM FirstFile(ftype)
  163. int ftype;
  164. { FILE_NUM i;
  165.   OBJECT link, y;
  166.   debug1(DFS, D, "FirstFile( %s )", file_types[ftype]);
  167.   link = Down(file_list[ftype]);
  168.   if( type(link) == ACAT )  i = NO_FILE;
  169.   else
  170.   { Child(y, link);
  171.     i = file_number(y);
  172.   }
  173.   debug1(DFS, D, "FirstFile returning %s", i==NO_FILE ? STR_NONE : FileName(i));
  174.   return i;
  175. } /* end FirstFile */
  176.  
  177.  
  178. /*****************************************************************************/
  179. /*                                                                           */
  180. /*  FILE_NUM NextFile(i)                                                     */
  181. /*                                                                           */
  182. /*  Returns the next file after file i of the type of i, else NO_FILE.       */
  183. /*                                                                           */
  184. /*****************************************************************************/
  185.  
  186. FILE_NUM NextFile(i)
  187. FILE_NUM i;
  188. { OBJECT link, y;
  189.   debug1(DFS, D, "NextFile( %s )", EchoObject(fvec[i]));
  190.   link = NextDown(Up(fvec[i]));
  191.   if( type(link) == ACAT )  i = NO_FILE;
  192.   else
  193.   { Child(y, link);
  194.     i = file_number(y);
  195.   }
  196.   debug1(DFS, D, "NextFile returning %s", i==NO_FILE ? STR_NONE : FileName(i));
  197.   return i;
  198. } /* end NextFile */
  199.  
  200.  
  201. /*****************************************************************************/
  202. /*                                                                           */
  203. /*  FILE_NUM FileNum(str, suffix)                                            */
  204. /*                                                                           */
  205. /*  Return the number of the file with name str plus suffix, else NO_FILE.   */
  206. /*                                                                           */
  207. /*****************************************************************************/
  208.  
  209. FILE_NUM FileNum(str, suffix)
  210. FULL_CHAR *str, *suffix;
  211. { register FULL_CHAR *p;
  212.   register int i;
  213.   FULL_CHAR buff[MAX_LINE];
  214.   debug2(DFS, D, "FileNum(%s, %s)", str, suffix);
  215.   hash(str, i);
  216.   if( StringLength(str) + StringLength(suffix) >= MAX_LINE )
  217.     Error(FATAL, no_fpos, "file name %s%s too long", str, suffix);
  218.   StringCopy(buff, str);
  219.   StringCat(buff, suffix);
  220.   while( fvec[i] != nil && !StringEqual(string(fvec[i]), buff) )
  221.     if( ++i >= MAX_FILES ) i = 0;
  222.   if( fvec[i] == nil ) i = 0;
  223.   debug1(DFS, D, "FileNum returning %s",
  224.     i == NO_FILE ? STR_NONE : FileName( (FILE_NUM) i));
  225.   return (FILE_NUM) i;
  226. } /* end FileNum */
  227.  
  228.  
  229. /*@::FileName(), EchoFilePos(), PosOfFile()@**********************************/
  230. /*                                                                           */
  231. /*  FULL_CHAR *FileName(fnum)                                                */
  232. /*                                                                           */
  233. /*  Return the string name of this file.  This is as given to DefineFile     */
  234. /*  until OpenFile is called, after which it is the full path name.          */
  235. /*                                                                           */
  236. /*****************************************************************************/
  237.  
  238. FULL_CHAR *FileName(fnum)
  239. FILE_NUM fnum;
  240. { OBJECT x;
  241.   assert( fnum > 0 && fvec[fnum] != nil, "FileName: fvec[fnum] == nil!" );
  242.   x = fvec[fnum];  if( Down(x) != x )  Child(x, Down(x));
  243.   return string(x);
  244. } /* end FileName */
  245.  
  246.  
  247. /*****************************************************************************/
  248. /*                                                                           */
  249. /*  FULL_CHAR *EchoFilePos(pos)                                              */
  250. /*                                                                           */
  251. /*  Returns a string reporting the value of file position pos.               */
  252. /*                                                                           */
  253. /*****************************************************************************/
  254.  
  255. static FULL_CHAR buff[2][MAX_LINE];  static bp = 1;
  256.  
  257. static append_fpos(pos)
  258. FILE_POS *pos;
  259. { OBJECT x;
  260.   x = fvec[file_num(*pos)];
  261.   assert( x != nil, "EchoFilePos: fvec[] entry is nil!" );
  262.   if( file_num(fpos(x)) > 0 )
  263.   { append_fpos( &fpos(x) );
  264.     if( StringLength(buff[bp]) + 2 >= MAX_LINE )
  265.       Error(FATAL,no_fpos,"file position %s... is too long to print", buff[bp]);
  266.     StringCat(buff[bp], STR_SPACE);
  267.     StringCat(buff[bp], AsciiToFull("/"));
  268.   }
  269.   if( StringLength(buff[bp]) + StringLength(string(x)) + 13 >= MAX_LINE )
  270.     Error(FATAL, no_fpos, "file position %s... is too long to print", buff[bp]);
  271.   StringCat(buff[bp], STR_SPACE);
  272.   StringCat(buff[bp], STR_QUOTE);
  273.   StringCat(buff[bp], string(x));
  274.   StringCat(buff[bp], STR_QUOTE);
  275.   if( line_num(*pos) != 0 )
  276.   { StringCat(buff[bp], STR_SPACE);
  277.     StringCat(buff[bp], StringInt(line_num(*pos)));
  278.     StringCat(buff[bp], AsciiToFull(","));
  279.     StringCat(buff[bp], StringInt( (int) col_num(*pos)));
  280.   }
  281. } /* end append_fpos */
  282.  
  283. FULL_CHAR *EchoFilePos(pos)
  284. FILE_POS *pos;
  285. { bp = (bp + 1) % 2;
  286.   StringCopy(buff[bp], STR_EMPTY);
  287.   if( file_num(*pos) > 0 )  append_fpos(pos);
  288.   return buff[bp];
  289. } /* end EchoFilePos */
  290.  
  291.  
  292. /*****************************************************************************/
  293. /*                                                                           */
  294. /*  FILE_POS *PosOfFile(fnum)                                                */
  295. /*                                                                           */
  296. /*  Returns a pointer to the file position where file fnum was encountered.  */
  297. /*                                                                           */
  298. /*****************************************************************************/
  299.  
  300. FILE_POS *PosOfFile(fnum)
  301. FILE_NUM fnum;
  302. { OBJECT  x = fvec[fnum];
  303.   assert( x != nil, "PosOfFile: fvec[] entry is nil!" );
  304.   return &fpos(x);
  305. }
  306.  
  307. /*@::SearchPath()@************************************************************/
  308. /*                                                                           */
  309. /*  static FILE *SearchPath(str, fpath, check_ld, check_lt, full_name, xfpos)*/
  310. /*                                                                           */
  311. /*  Search the given path for a file whose name is str.  If found, open      */
  312. /*  it; return the resulting FILE *.                                         */
  313. /*                                                                           */
  314. /*  If check_ld is TRUE, it means that the file to be opened is a .li file   */
  315. /*  and OpenFile() is required to check whether the corresponding .ld file   */
  316. /*  is present.  If it is, then the search must stop.                        */
  317. /*                                                                           */
  318. /*  If check_lt is TRUE, it means that the file to be opened is a source     */
  319. /*  file and OpenFile() is required to check for a .lt suffix version if     */
  320. /*  the file does not open.                                                  */
  321. /*                                                                           */
  322. /*  Also return the full path name in object *full_name if reqd, else nil.   */
  323. /*                                                                           */
  324. /*****************************************************************************/
  325.  
  326. static FILE *SearchPath(str, fpath, check_ld, check_lt, full_name, xfpos)
  327. FULL_CHAR *str;  OBJECT fpath;  BOOLEAN check_ld, check_lt;
  328. OBJECT *full_name;  FILE_POS *xfpos;
  329.   FULL_CHAR buff[MAX_LINE];  OBJECT link, y;  FILE *fp;
  330.   debug4(DFS, DD, "SearchPath(%s, %s, %s, %s, -)", str, EchoObject(fpath),
  331.     bool(check_ld), bool(check_lt));
  332.   *full_name = nil;
  333.   if( StringEqual(str, STR_STDIN) )
  334.   { fp = stdin;
  335.     debug0(DFS, DD, "  opened stdin");
  336.   }
  337.   else if( StringBeginsWith(str, AsciiToFull("/")) )
  338.   { fp = StringFOpen(str, "r");
  339.     debug1(DFS, DD, fp==null ? "  failed on %s" : "  succeeded on %s", str);
  340.   }
  341.   else
  342.   { fp = null;
  343.     for( link = Down(fpath);  fp==null && link != fpath; link = NextDown(link) )
  344.     { Child(y, link);
  345.       if( StringLength(string(y)) == 0 )
  346.       { StringCopy(buff, str);
  347.     fp = StringFOpen(str, "r");
  348.     debug1(DFS, DD, fp==null ? "  failed on %s" : "  succeeded on %s", str);
  349.       }
  350.       else
  351.       {    if( StringLength(string(y)) + 1 + StringLength(str) >= MAX_LINE )
  352.       Error(FATAL, &fpos(y), "file path name %s/%s is too long",
  353.         string(y), str);
  354.     StringCopy(buff, string(y));
  355.     StringCat(buff, AsciiToFull("/"));
  356.     StringCat(buff, str);
  357.     fp = StringFOpen(buff, "r");
  358.     debug1(DFS, DD, fp==null ? "  failed on %s" : "  succeeded on %s",buff);
  359.     if( fp != null ) *full_name = MakeWord(WORD, buff, xfpos);
  360.       }
  361.       if( fp == null && check_ld )
  362.       {    StringCopy(&buff[StringLength(buff) - StringLength(INDEX_SUFFIX)],
  363.       DATA_SUFFIX);
  364.     fp = StringFOpen(buff, "r");
  365.     debug1(DFS,DD,fp==null ? "  failed on %s" : "  succeeded on %s", buff);
  366.     if( fp != null )
  367.     { fclose(fp);
  368.       debug0(DFS, D, "SearchPath returning null (adjacent .ld file)");
  369.       return null;
  370.     }
  371.       }
  372.       if( fp == null && check_lt )
  373.       {    StringCopy(&buff[StringLength(buff)], SOURCE_SUFFIX);
  374.     fp = StringFOpen(buff, "r");
  375.     debug1(DFS,DD,fp==null ? "  failed on %s" : "  succeeded on %s", buff);
  376.     StringCopy(&buff[StringLength(buff) - StringLength(SOURCE_SUFFIX)], STR_EMPTY);
  377.     if( fp != null ) *full_name = MakeWord(WORD, buff, xfpos);
  378.       }
  379.     }
  380.   }
  381.   debug1(DFS, DD, "SearchPath returning (fp %s null)", fp==null ? "==" : "!=");
  382.   return fp;
  383. } /* end SearchPath */
  384.  
  385.  
  386. /*@::OpenFile(), OpenIncGraphicFile()@****************************************/
  387. /*                                                                           */
  388. /*  FILE *OpenFile(fnum, check_ld, check_lt)                                 */
  389. /*                                                                           */
  390. /*  Open for reading the file whose number is fnum.  This involves           */
  391. /*  searching for it along its path if not previously opened.                */
  392. /*                                                                           */
  393. /*  If check_ld is TRUE, it means that the file to be opened is a .li file   */
  394. /*  and OpenFile() is required to check whether the corresponding .ld file   */
  395. /*  is present.  If it is, then the search must stop.                        */
  396. /*                                                                           */
  397. /*  If check_lt is TRUE, it means that the file to be opened is a source     */
  398. /*  file and OpenFile() is required to check for a .lout suffix version      */
  399. /*  if the file does not open without it.                                    */
  400. /*                                                                           */
  401. /*****************************************************************************/
  402.  
  403. FILE *OpenFile(fnum, check_ld, check_lt)
  404. FILE_NUM fnum;  BOOLEAN check_ld, check_lt;
  405. { FILE *fp;  OBJECT full_name, y;
  406.   ifdebug(DPP, D, ProfileOn("OpenFile"));
  407.   debug2(DFS, D, "OpenFile(%s, %s)", FileName(fnum), bool(check_ld));
  408.   if( Down(fvec[fnum]) != fvec[fnum] )
  409.   { Child(y, Down(fvec[fnum]));
  410.     fp = StringFOpen(string(y), "r");
  411.     debug1(DFS,DD,fp==null ? "  failed on %s" : "  succeeded on %s", string(y));
  412.   }
  413.   else
  414.   { fp = SearchPath(string(fvec[fnum]), file_path[path(fvec[fnum])],
  415.        check_ld, check_lt, &full_name, &fpos(fvec[fnum]));
  416.     if( full_name != nil )  Link(fvec[fnum], full_name);
  417.   }
  418.   ifdebug(DPP, D, ProfileOff("OpenFile"));
  419.   debug1(DFS, D, "OpenFile returning (fp %s null)", fp==null ? "==" : "!=");
  420.   return fp;
  421. } /* end OpenFile */
  422.  
  423.  
  424. /*****************************************************************************/
  425. /*                                                                           */
  426. /*  FILE *OpenIncGraphicFile(str, typ, full_name, xfpos)                     */
  427. /*                                                                           */
  428. /*  Open for reading the @IncludeGraphic file str; typ is INCGRAPHIC or      */
  429. /*  SINCGRAPHIC.  Return the full name in full_name.                         */
  430. /*                                                                           */
  431. /*****************************************************************************/
  432.  
  433. FILE *OpenIncGraphicFile(str, typ, full_name, xfpos)
  434. FULL_CHAR *str;  unsigned char typ;  OBJECT *full_name;  FILE_POS *xfpos;
  435. { FILE *fp;  int p;
  436.   debug2(DFS, D, "OpenIncGraphicFile(%s, %s, -)", str, Image(typ));
  437.   assert( typ == INCGRAPHIC || typ == SINCGRAPHIC, "OpenIncGraphicFile!" );
  438.   p = (typ == INCGRAPHIC ? INCLUDE_PATH : SYSINCLUDE_PATH);
  439.   fp = SearchPath(str, file_path[p], FALSE, FALSE, full_name, xfpos);
  440.   if( *full_name == nil )  *full_name = MakeWord(WORD, str, xfpos);
  441.   debug2(DFS, D, "OpenIncGraphicFile returning (fp %s null, *full_name = %s)",
  442.     fp==null ? "==" : "!=", string(*full_name));
  443.   return fp;
  444. } /* end OpenIncGraphicFile */
  445.  
  446.  
  447. /*@::ReadFromFile()@**********************************************************/
  448. /*                                                                           */
  449. /*  OBJECT ReadFromFile(fnum, pos, sym)                                      */
  450. /*                                                                           */
  451. /*  Read an object from file fnum starting at position pos.                  */
  452. /*  The object may include @Env operators defining its environment.          */
  453. /*  If sym != nil, sym is the symbol which is to be read in.                 */
  454. /*                                                                           */
  455. /*****************************************************************************/
  456.  
  457. OBJECT ReadFromFile(fnum, pos, sym)
  458. FILE_NUM fnum; long pos;  OBJECT sym;
  459. { OBJECT t, res; int ipos;
  460.   ifdebug(DPP, D, ProfileOn("ReadFromFile"));
  461.   ifdebug(DFS, D, ipos = (int) pos);
  462.   debug3(DFS, D, "ReadFromFile(%s, %d, %s)", FileName(fnum), ipos,SymName(sym));
  463.   LexPush(fnum, (int) pos, DATABASE_FILE);
  464.   SwitchScope(sym);
  465.   t = LexGetToken();
  466.   if( type(t) != LBR )
  467.   { debug1(DFS, D, "  following because type(t) = %s", Image(type(t)));
  468.     Error(FATAL, &fpos(t),"syntax error (missing %s) in database file", KW_LBR);
  469.   }
  470.   res = Parse(&t, StartSym, FALSE, FALSE);
  471.   if( t != nil || type(res) != CLOSURE )
  472.   { debug1(DFS, D, "  following because of %s", t != nil ? "t" : "type(res)");
  473.     Error(FATAL, &fpos(res), "syntax error in database file");
  474.   }
  475.   UnSwitchScope(sym);
  476.   LexPop();
  477.   debug1(DFS, D, "ReadFromFile returning %s", EchoObject(res));
  478.   ifdebug(DPP, D, ProfileOff("ReadFromFile"));
  479.   return res;
  480. } /* end ReadFromFile */
  481.  
  482.  
  483. static FILE_NUM    last_write_fnum = NO_FILE;
  484. static FILE    *last_write_fp  = null;
  485.  
  486.  
  487. /*@::WriteClosure()@**********************************************************/
  488. /*                                                                           */
  489. /*  static WriteClosure(x)                                                   */
  490. /*                                                                           */
  491. /*  Write closure x to file last_write_fp, without enclosing braces and      */
  492. /*  without any environment attached.                                        */
  493. /*                                                                           */
  494. /*****************************************************************************/
  495.  
  496. static BOOLEAN need_lvis(sym)        /* true if @LVis needed before sym */
  497. OBJECT sym;
  498. { return !visible(sym) &&
  499.      enclosing(sym) != StartSym &&
  500.      type(enclosing(sym)) == LOCAL;
  501. } /* end need_lvis */
  502.  
  503. static WriteClosure(x)
  504. OBJECT x;
  505. { OBJECT y, link, z, sym;
  506.   BOOLEAN npar_seen, name_printed;
  507.   static WriteObject();
  508.  
  509.   sym = actual(x);  npar_seen = FALSE;  name_printed = FALSE;
  510.   for( link = Down(x);  link != x;  link = NextDown(link) )
  511.   { Child(y, link);
  512.     if( type(y) == PAR )  switch( type(actual(y)) )
  513.     {
  514.       case LPAR:
  515.       
  516.     assert( Down(y) != y, "WriteObject/CLOSURE: LPAR!" );
  517.     Child(z, Down(y));
  518.     WriteObject(z, (int) precedence(sym));
  519.     StringFPuts(STR_SPACE, last_write_fp);
  520.     break;
  521.  
  522.  
  523.       case NPAR:
  524.       
  525.     assert( Down(y) != y, "WriteObject/CLOSURE: NPAR!" );
  526.     Child(z, Down(y));
  527.     if( !name_printed )
  528.     { if( need_lvis(sym) )
  529.       { StringFPuts(KW_LVIS, last_write_fp);
  530.         StringFPuts(STR_SPACE, last_write_fp);
  531.       }
  532.       StringFPuts(SymName(sym), last_write_fp);
  533.       name_printed = TRUE;
  534.     }
  535.     StringFPuts(STR_NEWLINE, last_write_fp);
  536.     StringFPuts(STR_SPACE, last_write_fp);
  537.     StringFPuts(STR_SPACE, last_write_fp);
  538.     StringFPuts(STR_SPACE, last_write_fp);
  539.     StringFPuts(SymName(actual(y)), last_write_fp);
  540.     StringFPuts(STR_SPACE, last_write_fp);
  541.     StringFPuts(KW_LBR, last_write_fp);
  542.     StringFPuts(STR_SPACE, last_write_fp);
  543.     WriteObject(z, NO_PREC);
  544.     StringFPuts(STR_SPACE, last_write_fp);
  545.     StringFPuts(KW_RBR, last_write_fp);
  546.     npar_seen = TRUE;
  547.     break;
  548.  
  549.  
  550.       case RPAR:
  551.       
  552.     assert( Down(y) != y, "WriteObject/CLOSURE: RPAR!" );
  553.     Child(z, Down(y));
  554.     if( !name_printed )
  555.     { if( need_lvis(sym) )
  556.       { StringFPuts(KW_LVIS, last_write_fp);
  557.         StringFPuts(STR_SPACE, last_write_fp);
  558.       }
  559.       StringFPuts(SymName(sym), last_write_fp);
  560.       name_printed = TRUE;
  561.     }
  562.     StringFPuts(npar_seen ? STR_NEWLINE : STR_SPACE, last_write_fp);
  563.     if( has_body(sym) )
  564.     {
  565.       StringFPuts(KW_LBR, last_write_fp);
  566.       StringFPuts(STR_SPACE, last_write_fp);
  567.       WriteObject(z, NO_PREC);
  568.       StringFPuts(STR_SPACE, last_write_fp);
  569.       StringFPuts(KW_RBR, last_write_fp);
  570.     }
  571.     else WriteObject(z, (int) precedence(sym));
  572.     break;
  573.  
  574.  
  575.       default:
  576.       
  577.     Error(INTERN, &fpos(y), "WriteClosure: %s", Image(type(actual(y))) );
  578.     break;
  579.  
  580.     } /* end switch */
  581.   } /* end for each parameter */
  582.   if( !name_printed )
  583.   { if( need_lvis(sym) )
  584.     { StringFPuts(KW_LVIS, last_write_fp);
  585.       StringFPuts(STR_SPACE, last_write_fp);
  586.     }
  587.     StringFPuts(SymName(sym), last_write_fp);
  588.     name_printed = TRUE;
  589.   }
  590. } /* end WriteClosure */
  591.  
  592.  
  593. /*@::WriteObject()@***********************************************************/
  594. /*                                                                           */
  595. /*  static WriteObject(x, outer_prec)                                        */
  596. /*                                                                           */
  597. /*  Write object x to file last_write_fp, assuming it is a subobject of an   */
  598. /*  object and the precedence of operators enclosing it is outer_prec.       */
  599. /*                                                                           */
  600. /*****************************************************************************/
  601.  
  602. static WriteObject(x, outer_prec)
  603. OBJECT x;  int outer_prec;
  604. { OBJECT link, y, gap_obj, sym, env;  FULL_CHAR *name;
  605.   int prec, i, last_prec;  BOOLEAN braces_needed;
  606.   switch( type(x) )
  607.   {
  608.  
  609.     case WORD:
  610.  
  611.       if( StringLength(string(x)) == 0 && outer_prec > ACAT_PREC )
  612.       { StringFPuts(KW_LBR, last_write_fp);
  613.     StringFPuts(KW_RBR, last_write_fp);
  614.       }
  615.       else StringFPuts(string(x), last_write_fp);
  616.       break;
  617.  
  618.     
  619.     case QWORD:
  620.  
  621.       StringFPuts(StringQuotedWord(x), last_write_fp);
  622.       break;
  623.  
  624.     
  625.     case VCAT:  prec = VCAT_PREC;  goto ETC;
  626.     case HCAT:  prec = HCAT_PREC;  goto ETC;
  627.     case ACAT:  prec = ACAT_PREC;  goto ETC;
  628.  
  629.       ETC:
  630.       if( prec < outer_prec )  StringFPuts(KW_LBR, last_write_fp);
  631.       last_prec = prec;
  632.       for( link = Down(x);  link != x;  link = NextDown(link) )
  633.       {    Child(y, link);
  634.     if( type(y) == GAP_OBJ )
  635.     { if( Down(y) == y )
  636.       { assert( type(x) == ACAT, "WriteObject: Down(y) == y!" );
  637.         for( i = 1;  i <= vspace(y);  i++ )
  638.           StringFPuts(STR_NEWLINE, last_write_fp);
  639.         for( i = 1;  i <= hspace(y);  i++ )
  640.           StringFPuts(STR_SPACE,  last_write_fp);
  641.         last_prec = (vspace(y) + hspace(y) == 0) ? JUXTA_PREC : ACAT_PREC;
  642.       }
  643.       else
  644.       { Child(gap_obj, Down(y));
  645.         StringFPuts(type(x)==ACAT ? STR_SPACE : STR_NEWLINE, last_write_fp);
  646.         StringFPuts(EchoCatOp(type(x), mark(gap(y)), join(gap(y))),
  647.           last_write_fp);
  648.         if( !is_word(type(gap_obj)) || StringLength(string(gap_obj)) != 0 )
  649.         WriteObject(gap_obj, FORCE_PREC);
  650.         StringFPuts(STR_SPACE, last_write_fp);
  651.         last_prec = prec;
  652.       }
  653.     }
  654.     else
  655.     { if( type(x) == ACAT )
  656.       { OBJECT next_gap;  int next_prec;
  657.         if( NextDown(link) != x )
  658.         { Child(next_gap, NextDown(link));
  659.           assert( type(next_gap) == GAP_OBJ, "WriteObject: next_gap!" );
  660.           next_prec = (vspace(next_gap) + hspace(next_gap) == 0)
  661.                 ? JUXTA_PREC : ACAT_PREC;
  662.         }
  663.         else next_prec = prec;
  664.         WriteObject(y, max(last_prec, next_prec));
  665.       }
  666.       else WriteObject(y, prec);
  667.     }
  668.       }
  669.       if( prec < outer_prec )  StringFPuts(KW_RBR, last_write_fp);
  670.       break;
  671.  
  672.  
  673.     case ENV:
  674.  
  675.       if( Down(x) == x )
  676.       { /* do nothing */
  677.       }
  678.       else if( Down(x) == LastDown(x) )
  679.       {    Child(y, Down(x));
  680.     assert( type(y) == CLOSURE, "WriteObject: ENV/CLOSURE!" );
  681.     assert( LastDown(y) != y, "WriteObject: ENV/LastDown(y)!" );
  682.     Child(env, LastDown(y));
  683.     assert( type(env) == ENV, "WriteObject: ENV/env!" );
  684.     WriteObject(env, NO_PREC);
  685.     StringFPuts(KW_LBR, last_write_fp);
  686.     WriteClosure(y);
  687.     StringFPuts(KW_RBR, last_write_fp);
  688.     StringFPuts(STR_NEWLINE, last_write_fp);
  689.       }
  690.       else
  691.       {    Child(env, LastDown(x));
  692.     assert( type(env) == ENV, "WriteObject: ENV/ENV!" );
  693.     WriteObject(env, NO_PREC);
  694.     Child(y, Down(x));
  695.     assert( type(y) == CLOSURE, "WriteObject: ENV/ENV+CLOSURE!" );
  696.     WriteObject(y, NO_PREC);
  697.       }
  698.       break;
  699.  
  700.  
  701.     case CLOSURE:
  702.  
  703.       sym = actual(x);  env = nil;
  704.       if( LastDown(x) != x )
  705.       {    Child(y, LastDown(x));
  706.     if( type(y) == ENV )  env = y;
  707.       }
  708.  
  709.       braces_needed = env != nil ||
  710.     (precedence(sym) <= outer_prec && (has_lpar(sym) || has_rpar(sym)));
  711.  
  712.       /* print environment */
  713.       if( env != nil )
  714.       {    StringFPuts(KW_ENV, last_write_fp);
  715.           StringFPuts(STR_NEWLINE, last_write_fp);
  716.     WriteObject(env, NO_PREC);
  717.       }
  718.  
  719.       /* print left brace if needed */
  720.       if( braces_needed )  StringFPuts(KW_LBR, last_write_fp);
  721.     
  722.       /* print the closure proper */
  723.       WriteClosure(x);
  724.  
  725.       /* print closing brace if needed */
  726.       if( braces_needed )  StringFPuts(KW_RBR, last_write_fp);
  727.  
  728.       /* print closing environment if needed */
  729.       if( env != nil )
  730.       { StringFPuts(STR_NEWLINE, last_write_fp);
  731.     StringFPuts(KW_CLOS, last_write_fp);
  732.           StringFPuts(STR_NEWLINE, last_write_fp);
  733.       }
  734.       break;
  735.  
  736.  
  737.     case CROSS:
  738.  
  739.       Child(y, Down(x));
  740.       assert( type(y) == CLOSURE, "WriteObject/CROSS: type(y) != CLOSURE!" );
  741.       StringFPuts(SymName(actual(y)), last_write_fp);
  742.       StringFPuts(KW_CROSS, last_write_fp);
  743.       Child(y, LastDown(x));
  744.       WriteObject(y, FORCE_PREC);
  745.       break;
  746.  
  747.  
  748.     case NULL_CLOS:    name = KW_NULL;        goto SETC;
  749.     case ONE_COL:    name = KW_ONE_COL;    goto SETC;
  750.     case ONE_ROW:    name = KW_ONE_ROW;    goto SETC;
  751.     case WIDE:        name = KW_WIDE;        goto SETC;
  752.     case HIGH:        name = KW_HIGH;        goto SETC;
  753.     case HSCALE:    name = KW_HSCALE;    goto SETC;
  754.     case VSCALE:    name = KW_VSCALE;    goto SETC;
  755.     case SCALE:        name = KW_SCALE;    goto SETC;
  756.     case HCONTRACT:    name = KW_HCONTRACT;    goto SETC;
  757.     case VCONTRACT:    name = KW_VCONTRACT;    goto SETC;
  758.     case HEXPAND:    name = KW_HEXPAND;    goto SETC;
  759.     case VEXPAND:    name = KW_VEXPAND;    goto SETC;
  760.     case PADJUST:    name = KW_PADJUST;    goto SETC;
  761.     case HADJUST:    name = KW_HADJUST;    goto SETC;
  762.     case VADJUST:    name = KW_VADJUST;    goto SETC;
  763.     case ROTATE:    name = KW_ROTATE;    goto SETC;
  764.     case CASE:        name = KW_CASE;        goto SETC;
  765.     case YIELD:        name = KW_YIELD;    goto SETC;
  766.     case XCHAR:        name = KW_XCHAR;    goto SETC;
  767.     case FONT:        name = KW_FONT;        goto SETC;
  768.     case SPACE:        name = KW_SPACE;    goto SETC;
  769.     case BREAK:        name = KW_BREAK;    goto SETC;
  770.     case NEXT:        name = KW_NEXT;        goto SETC;
  771.     case OPEN:        name = KW_OPEN;        goto SETC;
  772.     case TAGGED:    name = KW_TAGGED;    goto SETC;
  773.     case INCGRAPHIC:    name = KW_INCGRAPHIC;    goto SETC;
  774.     case SINCGRAPHIC:    name = KW_SINCGRAPHIC;    goto SETC;
  775.     case GRAPHIC:    name = KW_GRAPHIC;    goto SETC;
  776.  
  777.       /* print left parameter, if present */
  778.       SETC:
  779.       if( DEFAULT_PREC <= outer_prec )  StringFPuts(KW_LBR, last_write_fp);
  780.       if( Down(x) != LastDown(x) )
  781.       {    Child(y, Down(x));
  782.     WriteObject(y, DEFAULT_PREC);
  783.     StringFPuts(STR_SPACE, last_write_fp);
  784.       }
  785.  
  786.       /* print the name of the symbol */
  787.       StringFPuts(name, last_write_fp);
  788.  
  789.       /* print right parameter, if present */
  790.       if( LastDown(x) != x )
  791.       {    Child(y, LastDown(x));
  792.     StringFPuts(STR_SPACE, last_write_fp);
  793.     if( type(x) == OPEN )
  794.     { StringFPuts(KW_LBR, last_write_fp);
  795.       WriteObject(y, NO_PREC);
  796.       StringFPuts(KW_RBR, last_write_fp);
  797.     }
  798.     else WriteObject(y, DEFAULT_PREC);
  799.       }
  800.       if( DEFAULT_PREC <= outer_prec )
  801.     StringFPuts(KW_RBR, last_write_fp);
  802.       break;
  803.  
  804.  
  805.     default:
  806.  
  807.       Error(INTERN, &fpos(x), "WriteObject: type(x) = %s", Image(type(x)));
  808.       break;
  809.  
  810.   } /* end switch */
  811. } /* end WriteObject */
  812.  
  813.  
  814. /*@::AppendToFile(), CloseFiles()@********************************************/
  815. /*                                                                           */
  816. /*  AppendToFile(x, fnum, pos)                                               */
  817. /*                                                                           */
  818. /*  Append object x to file fnum, returning its fseek position in *pos.      */
  819. /*  Record the fact that this file has been updated.                         */
  820. /*                                                                           */
  821. /*****************************************************************************/
  822.  
  823. AppendToFile(x, fnum, pos)
  824. OBJECT x;  FILE_NUM fnum;  int *pos;
  825. { FULL_CHAR buff[MAX_LINE], *str;
  826.   debug2(DFS, D, "AppendToFile( %s, %s )", EchoObject(x), FileName(fnum));
  827.  
  828.   /* open file fnum for writing */
  829.   if( last_write_fnum != fnum )
  830.   { if( last_write_fnum != NO_FILE )  fclose(last_write_fp);
  831.     str = FileName(fnum);
  832.     if( StringLength(str) + StringLength(NEW_DATA_SUFFIX) >= MAX_LINE )
  833.       Error(FATAL, PosOfFile(fnum), "file name %s%s is too long",
  834.     str, NEW_DATA_SUFFIX);
  835.     StringCopy(buff, str);  StringCat(buff, NEW_DATA_SUFFIX);
  836.     last_write_fp = StringFOpen(buff, "a");
  837.     if( last_write_fp == null )  Error(FATAL, &fpos(fvec[fnum]),
  838.         "cannot append to database file %s", buff);
  839.     last_write_fnum = fnum;
  840.   }
  841.  
  842.   /* write x out and record the fact that fnum has changed */
  843.   *pos = (int) ftell(last_write_fp);
  844.   StringFPuts(KW_LBR, last_write_fp);
  845.   WriteObject(x, NO_PREC);
  846.   StringFPuts(KW_RBR, last_write_fp);
  847.   StringFPuts(STR_NEWLINE, last_write_fp);
  848.   StringFPuts(STR_NEWLINE, last_write_fp);
  849.   updated(fvec[fnum]) = TRUE;
  850.   debug0(DFS, D, "AppendToFile returning.");
  851. } /* end AppendToFile */
  852.  
  853.  
  854. /*****************************************************************************/
  855. /*                                                                           */
  856. /*  CloseFiles()                                                             */
  857. /*                                                                           */
  858. /*  Close all files and move new versions to the names of old versions.      */
  859. /*                                                                           */
  860. /*****************************************************************************/
  861.  
  862. CloseFiles()
  863. { FILE_NUM fnum;  FULL_CHAR buff[MAX_LINE];
  864.   ifdebug(DPP, D, ProfileOn("CloseFiles"));
  865.   debug0(DFS, D, "CloseFiles()");
  866.  
  867.   /* close off last file opened by AppendToFile above */
  868.   if( last_write_fnum != NO_FILE )  fclose(last_write_fp);
  869.  
  870.   /* get rid of old database files */
  871.   for( fnum = FirstFile(SOURCE_FILE);  fnum != NO_FILE;  fnum = NextFile(fnum) )
  872.   { StringCopy(buff, FileName(fnum));
  873.     StringCat(buff, DATA_SUFFIX);  StringUnlink(buff);
  874.   }
  875.  
  876.   /* move any new database files to the old names, if updated */
  877.   for( fnum = FirstFile(DATABASE_FILE); fnum != NO_FILE; fnum = NextFile(fnum) )
  878.   { if( updated(fvec[fnum]) )
  879.     { StringCopy(buff, string(fvec[fnum]));
  880.       StringCat(buff, NEW_DATA_SUFFIX);
  881.       debug1(DFS, D, "unlink(%s)", string(fvec[fnum]));
  882.       StringUnlink(string(fvec[fnum])); /* may fail if no old version */
  883.       debug2(DFS, D, "link(%s, %s)", buff, string(fvec[fnum]));
  884. #ifndef OSK
  885.       if( StringLink(buff, string(fvec[fnum])) != 0 )
  886.         Error(INTERN, no_fpos, "link(%s, %s) failed", buff, string(fvec[fnum]));
  887.       debug1(DFS, D, "unlink(%s)", buff);
  888.       if( StringUnlink(buff) != 0 )  Error(INTERN, no_fpos, "unlink(%s)", buff);
  889. #else
  890.         sprintf(buff, "rename %s%s %s", string(fvec[fnum]), 
  891.             NEW_DATA_SUFFIX, (index(string(fvec[fnum]),'/') == NULL)
  892.             ? string(fvec[fnum]) : rindex(string(fvec[fnum]),'/')+1);
  893.         system(buff);
  894. #endif
  895.     }
  896.   }
  897.   debug0(DFS, D, "CloseFiles returning.");
  898.   ifdebug(DPP, D, ProfileOff("CloseFiles"));
  899. } /* end CloseFiles */
  900.  
  901.  
  902.