1 /*@z03.c:File Service:Declarations, no_fpos@******************************** */
2 /*                                                                           */
3 /*  THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.39)                       */
4 /*  COPYRIGHT (C) 1991, 2008 Jeffrey H. Kingston                             */
5 /*                                                                           */
6 /*  Jeffrey H. Kingston (jeff@it.usyd.edu.au)                                */
7 /*  School of Information Technologies                                       */
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 3, 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., 59 Temple Place, Suite 330, Boston MA 02111-1307 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 /*                EchoFileFrom()                                             */
31 /*                                                                           */
32 /*****************************************************************************/
33 #include "externs.h"
34 #if USE_STAT
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #endif
38 
39 #define	INIT_TAB 	  3			/* initial file table size   */
40 
41 
42 /*****************************************************************************/
43 /*                                                                           */
44 /*  FILE_TABLE                                                               */
45 /*                                                                           */
46 /*  A symbol table permitting access to file records by number or name.      */
47 /*  The table will automatically enlarge to accept any number of entries,    */
48 /*  but there is an arbitrary limit of 65535 files imposed so that file      */
49 /*  numbers can be stored in 16 bit fields.                                  */
50 /*                                                                           */
51 /*     ftab_new(newsize)                 New empty table, newsize capacity   */
52 /*     ftab_insert(x, &S)                Insert new file object x into S     */
53 /*     ftab_retrieve(str, S)             Retrieve file object of name str    */
54 /*     ftab_num(S, num)                  Retrieve file object of number num  */
55 /*     ftab_debug(S, fp)                 Debug print of table S to file fp   */
56 /*                                                                           */
57 /*****************************************************************************/
58 
59 typedef struct
60 { int filetab_size;				/* size of table             */
61   int filetab_count;				/* number of files in table  */
62   struct filetab_rec
63   {	OBJECT	by_number;			/* file record by number     */
64 	OBJECT	by_name_hash;			/* file record by name hash  */
65   } filetab[1];
66 } *FILE_TABLE;
67 
68 #define	ftab_size(S)	(S)->filetab_size
69 #define	ftab_count(S)	(S)->filetab_count
70 #define	ftab_num(S, i)	(S)->filetab[i].by_number
71 #define	ftab_name(S, i)	(S)->filetab[i].by_name_hash
72 
73 #define hash(pos, str, S)						\
74 { FULL_CHAR *p = str;							\
75   pos = *p++;								\
76   while( *p ) pos += *p++;						\
77   pos = pos % ftab_size(S);						\
78 }
79 
ftab_new(int newsize)80 static FILE_TABLE ftab_new(int newsize)
81 { FILE_TABLE S;  int i;
82   ifdebug(DMA, D, DebugRegisterUsage(MEM_FILES, 1,
83     2*sizeof(int) + newsize * sizeof(struct filetab_rec)));
84   S = (FILE_TABLE) malloc(2*sizeof(int) + newsize * sizeof(struct filetab_rec));
85   if( S == (FILE_TABLE) NULL )
86     Error(3, 1, "run out of memory when enlarging file table", FATAL, no_fpos);
87   ftab_size(S) = newsize;
88   ftab_count(S) = 0;
89   for( i = 0;  i < newsize;  i++ )
90   { ftab_num(S, i) = ftab_name(S, i) = nilobj;
91   }
92   return S;
93 } /* end ftab_new */
94 
95 static void ftab_insert(OBJECT x, FILE_TABLE *S);
96 
ftab_rehash(FILE_TABLE S,int newsize)97 static FILE_TABLE ftab_rehash(FILE_TABLE S, int newsize)
98 { FILE_TABLE NewS;  int i;
99   NewS = ftab_new(newsize);
100   for( i = 1;  i <= ftab_count(S);  i++ )
101      ftab_insert(ftab_num(S, i), &NewS);
102   for( i = 0;  i < ftab_size(S);  i++ )
103   { if( ftab_name(S, i) != nilobj )  DisposeObject(ftab_name(S, i));
104   }
105   ifdebug(DMA, D, DebugRegisterUsage(MEM_FILES, -1,
106     -(2*sizeof(int) + ftab_size(S) * sizeof(struct filetab_rec))));
107   free(S);
108   return NewS;
109 } /* end ftab_rehash */
110 
ftab_insert(OBJECT x,FILE_TABLE * S)111 static void ftab_insert(OBJECT x, FILE_TABLE *S)
112 { int pos, num;
113   if( ftab_count(*S) == ftab_size(*S) - 1 )	/* one less since 0 unused */
114     *S = ftab_rehash(*S, 2*ftab_size(*S));
115   num = ++ftab_count(*S);
116   if( num > MAX_FILES )
117     Error(3, 2, "too many files (maximum is %d)",
118       FATAL, &fpos(x), MAX_FILES);
119   hash(pos, string(x), *S);
120   if( ftab_name(*S, pos) == nilobj )  New(ftab_name(*S, pos), ACAT);
121   Link(ftab_name(*S, pos), x);
122   file_number(x) = num;
123   ftab_num(*S, num) = x;
124 } /* end ftab_insert */
125 
ftab_retrieve(FULL_CHAR * str,FILE_TABLE S)126 static OBJECT ftab_retrieve(FULL_CHAR *str, FILE_TABLE S)
127 { OBJECT x, link, y;  int pos;
128   hash(pos, str, S);
129   x = ftab_name(S, pos);
130   if( x == nilobj )  return nilobj;
131   for( link = Down(x);  link != x;  link = NextDown(link) )
132   { Child(y, link);
133     if( StringEqual(str, string(y)) )  return y;
134   }
135   return nilobj;
136 } /* end ftab_retrieve */
137 
138 #if DEBUG_ON
ftab_debug(FILE_TABLE S)139 static void ftab_debug(FILE_TABLE S)
140 { int i;  OBJECT x, link, y;
141   FULL_CHAR buff[MAX_BUFF];
142   debug2(DFS, D, "  table size: %d;  current number of files: %d",
143     ftab_size(S), ftab_count(S));
144   for( i = 0;  i < ftab_size(S);  i++ )
145   { x = ftab_num(S, i);
146     debug2(DFS, D, "  ftab_num(S, %d) = %s", i,
147       x == nilobj ? AsciiToFull("<nilobj>") :
148       !is_word(type(x)) ? AsciiToFull("not WORD!") : string(x) );
149   }
150   debug0(DFS, D, "");
151   for( i = 0;  i < ftab_size(S);  i++ )
152   { x = ftab_name(S, i);
153     if( x == nilobj )
154     {
155       debug1(DFS, D, "  ftab_name(S, %d) = <nilobj>", i);
156     }
157     else if( type(x) != ACAT )
158     {
159       debug1(DFS, D, "  ftab_name(S, %d) = not ACAT!>", i);
160     }
161     else
162     {
163       StringCopy(buff, STR_EMPTY);
164       for( link = Down(x);  link != x;  link = NextDown(link) )
165       { Child(y, link);
166 	StringCat(buff, STR_SPACE);
167 	StringCat(buff, is_word(type(y)) ? string(y):AsciiToFull("not-WORD!"));
168       }
169       debug2(DFS, D, "  ftab_name(S, %d) =%s", i, buff);
170     }
171   }
172 } /* end ftab_debug */
173 
174 static	char	*file_types[]		/* the type names for debug  */
175 		= { "source", "include", "incgraphic", "database", "index",
176 		    "font", "prepend", "hyph", "hyphpacked",
177 		    "mapping", "filter" };
178 #endif
179 
180 
181 static	OBJECT		empty_path;		/* file path with just "" in */
182 static	FILE_TABLE	file_tab;		/* the file table            */
183 static	OBJECT		file_type[MAX_TYPES];	/* files of each type        */
184 static	OBJECT		file_path[MAX_PATHS];	/* the search paths          */
185 /* ***don't need these any more
186 static	char		*file_mode[MAX_TYPES] =
187 { READ_TEXT, READ_TEXT, READ_TEXT, READ_BINARY, READ_TEXT,
188   READ_TEXT, READ_TEXT, READ_BINARY, READ_TEXT, READ_TEXT };
189 *** */
190 
191 
192 /*****************************************************************************/
193 /*                                                                           */
194 /*  no_fpos                                                                  */
195 /*                                                                           */
196 /*  A null file position value.                                              */
197 /*                                                                           */
198 /*****************************************************************************/
199 
200 static FILE_POS no_file_pos = {0, 0, 0, 0, 0};
201 FILE_POS *no_fpos = &no_file_pos;
202 
203 
204 /*@::InitFiles(), AddToPath(), DefineFile()@**********************************/
205 /*                                                                           */
206 /*  InitFiles()                                                              */
207 /*                                                                           */
208 /*  Initialize this module.                                                  */
209 /*                                                                           */
210 /*****************************************************************************/
211 
InitFiles(void)212 void InitFiles(void)
213 { int i;  OBJECT tmp;
214   for( i = 0;  i < MAX_TYPES; i++ )  New(file_type[i], ACAT);
215   for( i = 0;  i < MAX_PATHS; i++ )  New(file_path[i], ACAT);
216   file_tab = ftab_new(INIT_TAB);
217   New(empty_path, ACAT);
218   tmp = MakeWord(WORD, STR_EMPTY, no_fpos);
219   Link(empty_path, tmp);
220 } /* end InitFiles */
221 
222 
223 /*****************************************************************************/
224 /*                                                                           */
225 /*  AddToPath(fpath, dirname)                                                */
226 /*                                                                           */
227 /*  Add the directory dirname to the end of search path fpath.               */
228 /*                                                                           */
229 /*****************************************************************************/
230 
AddToPath(int fpath,OBJECT dirname)231 void AddToPath(int fpath, OBJECT dirname)
232 { Link(file_path[fpath], dirname);
233 } /* end AddToPath */
234 
235 
236 /*****************************************************************************/
237 /*                                                                           */
238 /*  FILE_NUM DefineFile(str, suffix, xfpos, ftype, fpath)                    */
239 /*                                                                           */
240 /*  Declare a file whose name is str plus suffix and whose fpos is xfpos.    */
241 /*  The file type is ftype, and its search path is fpath.                    */
242 /*                                                                           */
243 /*  If ignore_dup is TRUE, any repeated definition of the same file with     */
244 /*  the same type will be ignored.  The result in that case will be the      */
245 /*                                                                           */
246 /*****************************************************************************/
247 
DefineFile(FULL_CHAR * str,FULL_CHAR * suffix,FILE_POS * xfpos,int ftype,int fpath)248 FILE_NUM DefineFile(FULL_CHAR *str, FULL_CHAR *suffix,
249 FILE_POS *xfpos, int ftype, int fpath)
250 { register int i;
251   OBJECT fname;
252   assert( ftype < MAX_TYPES, "DefineFile: ftype!" );
253   debug5(DFS, DD, "DefineFile(%s, %s,%s, %s, %d)",
254     str, suffix, EchoFilePos(xfpos), file_types[ftype], fpath);
255   if( ftype == SOURCE_FILE && (i = StringLength(str)) >= 3 )
256   {
257     /* check that file name does not end in ".li" or ".ld" */
258     if( StringEqual(&str[i-StringLength(DATA_SUFFIX)], DATA_SUFFIX) )
259       Error(3, 3, "database file %s where source file expected",
260         FATAL, xfpos, str);
261     if( StringEqual(&str[i-StringLength(INDEX_SUFFIX)], INDEX_SUFFIX) )
262       Error(3, 4, "database index file %s where source file expected",
263         FATAL, xfpos, str);
264   }
265   if( StringLength(str) + StringLength(suffix) >= MAX_WORD )
266     Error(3, 5, "file name %s%s is too long", FATAL, no_fpos, str, suffix);
267   fname = MakeWordTwo(WORD, str, suffix, xfpos);
268   Link(file_type[ftype], fname);
269   path(fname) = fpath;
270   updated(fname) = FALSE;
271   line_count(fname) = 0;
272   type_of_file(fname) = ftype;
273   used_suffix(fname) = FALSE;
274   ftab_insert(fname, &file_tab);
275   debug1(DFS, DD, "DefineFile returning %s", string(fname));
276   ifdebug(DFS, DD, ftab_debug(file_tab));
277   return file_number(fname);
278 } /* end DefineFile */
279 
280 
281 /*@::FirstFile(), NextFile(), FileNum()@**************************************/
282 /*                                                                           */
283 /*  FILE_NUM FirstFile(ftype)                                                */
284 /*                                                                           */
285 /*  Returns first file of type ftype, else NO_FILE.                          */
286 /*                                                                           */
287 /*****************************************************************************/
288 
FirstFile(int ftype)289 FILE_NUM FirstFile(int ftype)
290 { FILE_NUM i;
291   OBJECT link, y;
292   debug1(DFS, DD, "FirstFile( %s )", file_types[ftype]);
293   link = Down(file_type[ftype]);
294   if( type(link) == ACAT )  i = NO_FILE;
295   else
296   { Child(y, link);
297     i = file_number(y);
298   }
299   debug1(DFS, DD, "FirstFile returning %s", i==NO_FILE ? STR_NONE : FileName(i));
300   return i;
301 } /* end FirstFile */
302 
303 
304 /*****************************************************************************/
305 /*                                                                           */
306 /*  FILE_NUM NextFile(i)                                                     */
307 /*                                                                           */
308 /*  Returns the next file after file i of the type of i, else NO_FILE.       */
309 /*                                                                           */
310 /*****************************************************************************/
311 
NextFile(FILE_NUM i)312 FILE_NUM NextFile(FILE_NUM i)
313 { OBJECT link, y;
314   debug1(DFS, DD, "NextFile( %s )", FileName(i));
315   link = NextDown(Up(ftab_num(file_tab, i)));
316   if( type(link) == ACAT )  i = NO_FILE;
317   else
318   { Child(y, link);
319     i = file_number(y);
320   }
321   debug1(DFS, DD, "NextFile returning %s", i==NO_FILE ? STR_NONE : FileName(i));
322   return i;
323 } /* end NextFile */
324 
325 
326 /*****************************************************************************/
327 /*                                                                           */
328 /*  FILE_NUM FileNum(str, suffix)                                            */
329 /*                                                                           */
330 /*  Return the number of the file with name str plus suffix, else NO_FILE.   */
331 /*                                                                           */
332 /*****************************************************************************/
333 
FileNum(FULL_CHAR * str,FULL_CHAR * suffix)334 FILE_NUM FileNum(FULL_CHAR *str, FULL_CHAR *suffix)
335 { register int i;  OBJECT fname;
336   FULL_CHAR buff[MAX_BUFF];
337   debug2(DFS, DD, "FileNum(%s, %s)", str, suffix);
338   if( StringLength(str) + StringLength(suffix) >= MAX_BUFF )
339     Error(3, 6, "file name %s%s is too long", FATAL, no_fpos, str, suffix);
340   StringCopy(buff, str);
341   StringCat(buff, suffix);
342   fname = ftab_retrieve(buff, file_tab);
343   i = fname == nilobj ? NO_FILE : file_number(fname);
344   debug1(DFS, DD, "FileNum returning %s",
345     i == NO_FILE ? STR_NONE : FileName( (FILE_NUM) i));
346   return (FILE_NUM) i;
347 } /* end FileNum */
348 
349 
350 /*****************************************************************************/
351 /*                                                                           */
352 /*  FILE_NUM DatabaseFileNum(FILE_POS *xfpos)                                */
353 /*                                                                           */
354 /*  Return a suitable database file number for writing something out whose   */
355 /*  file position is xfpos.                                                  */
356 /*                                                                           */
357 /*****************************************************************************/
358 
DatabaseFileNum(FILE_POS * xfpos)359 FILE_NUM DatabaseFileNum(FILE_POS *xfpos)
360 { OBJECT x;
361   FILE_NUM fnum;  FULL_CHAR *str;
362   debug2(DFS, D, "DatabaseFileNum(%s %s)", EchoFilePos(xfpos),
363     EchoFileSource(file_num(*xfpos)));
364   x = ftab_num(file_tab, file_num(*xfpos));
365   switch( type_of_file(x) )
366   {
367     case SOURCE_FILE:
368     case INCLUDE_FILE:
369 
370       /* return the corresponding database file (may need to be defined) */
371       str = FileName(file_num(*xfpos));
372       fnum = FileNum(str, DATA_SUFFIX);
373       if( fnum == NO_FILE )
374       { debug0(DFS, DD, "  calling DefineFile from DatabaseFileNum");
375 	fnum = DefineFile(str, DATA_SUFFIX, xfpos, DATABASE_FILE, SOURCE_PATH);
376       }
377       break;
378 
379 
380     case DATABASE_FILE:
381 
382       /* return the enclosing source file (recursively if necessary) */
383       if( file_num(fpos(x)) == NO_FILE )
384       {
385 	/* xfpos lies in a cross-reference database file; use itself */
386 	/* ***
387 	Error(3, 18, "DatabaseFileNum: database file position unknown",
388 	  INTERN, no_fpos);
389 	*** */
390 	fnum = file_num(*xfpos);
391       }
392       else
393       {
394 	/* xfpos lies in a user-defined database file; use its source */
395         fnum = DatabaseFileNum(&fpos(x));
396       }
397       break;
398 
399 
400     case FILTER_FILE:
401 
402       /* return the enclosing source file (recursively if necessary) */
403       if( file_num(fpos(x)) == NO_FILE )
404 	Error(3, 7, "DatabaseFileNum: filter file position unknown",
405 	  INTERN, no_fpos);
406       fnum = DatabaseFileNum(&fpos(x));
407       break;
408 
409 
410     default:
411 
412       Error(3, 8, "DatabaseFileNum: unexpected file type", INTERN, no_fpos);
413       fnum = NO_FILE;
414       break;
415 
416   }
417   debug2(DFS, D, "DatabaseFileNum returning %d (%s)", fnum,
418     fnum == NO_FILE ? AsciiToFull("NO_FILE") : FileName(fnum));
419   return fnum;
420 } /* end DatabaseFileNum */
421 
422 
423 /*@::FileName(), EchoFilePos(), PosOfFile()@**********************************/
424 /*                                                                           */
425 /*  FULL_CHAR *FileName(fnum)                                                */
426 /*  FULL_CHAR *FullFileName(fnum)                                            */
427 /*                                                                           */
428 /*  Return the string name of this file.  This is as given to DefineFile     */
429 /*  until OpenFile is called, after which it is the full path name.          */
430 /*                                                                           */
431 /*  FullFileName is the same except it will add a .lt to the file name       */
432 /*  if that was needed when the file was opened for reading.                 */
433 /*                                                                           */
434 /*****************************************************************************/
435 
FileName(FILE_NUM fnum)436 FULL_CHAR *FileName(FILE_NUM fnum)
437 { OBJECT x;
438   x = ftab_num(file_tab, fnum);
439   assert( x != nilobj, "FileName: x == nilobj!" );
440   if( Down(x) != x )  Child(x, Down(x));
441   return string(x);
442 } /* end FileName */
443 
444 
FullFileName(FILE_NUM fnum)445 FULL_CHAR *FullFileName(FILE_NUM fnum)
446 { OBJECT x;
447   static FULL_CHAR ffbuff[2][MAX_BUFF];
448   static int ffbp = 1;
449 
450   x = ftab_num(file_tab, fnum);
451   assert( x != nilobj, "FileName: x == nilobj!" );
452   if( used_suffix(x) )
453   {
454     if( Down(x) != x )  Child(x, Down(x));
455     ffbp = (ffbp + 1) % 2;
456     StringCopy(ffbuff[ffbp], string(x));
457     StringCat(ffbuff[ffbp], SOURCE_SUFFIX);
458     return ffbuff[ffbp];
459   }
460   else
461   {
462     if( Down(x) != x )  Child(x, Down(x));
463     return string(x);
464   }
465 } /* end FullFileName */
466 
467 
468 /*****************************************************************************/
469 /*                                                                           */
470 /*  int FileType(FILE_NUM fnum)                                              */
471 /*                                                                           */
472 /*  Return the type of file fnum.                                            */
473 /*                                                                           */
474 /*****************************************************************************/
475 
FileType(FILE_NUM fnum)476 int FileType(FILE_NUM fnum)
477 { OBJECT x;
478   x = ftab_num(file_tab, fnum);
479   assert( x != nilobj, "FileType: x == nilobj!" );
480   if( Down(x) != x )  Child(x, Down(x));
481   return type_of_file(x);
482 } /* end FileType */
483 
484 
485 /*****************************************************************************/
486 /*                                                                           */
487 /*  FULL_CHAR *EchoFilePos(pos)                                              */
488 /*                                                                           */
489 /*  Returns a string reporting the value of file position pos.               */
490 /*                                                                           */
491 /*****************************************************************************/
492 
493 static FULL_CHAR buff[2][MAX_BUFF];  static int bp = 1;
494 
append_fpos(FILE_POS * pos)495 static void append_fpos(FILE_POS *pos)
496 { OBJECT x;
497   x = ftab_num(file_tab, file_num(*pos));
498   assert( x != nilobj, "EchoFilePos: file_tab entry is nilobj!" );
499   if( file_num(fpos(x)) > 0 )
500   { append_fpos( &fpos(x) );
501     if( StringLength(buff[bp]) + 2 >= MAX_BUFF )
502       Error(3, 9, "file position %s... is too long to print",
503         FATAL, no_fpos, buff[bp]);
504     StringCat(buff[bp], STR_SPACE);
505     StringCat(buff[bp], STR_DIR);
506   }
507   if( StringLength(buff[bp]) + StringLength(string(x)) + 13 >= MAX_BUFF )
508     Error(3, 10, "file position %s... is too long to print",
509       FATAL, no_fpos, buff[bp]);
510   StringCat(buff[bp], STR_SPACE);
511   StringCat(buff[bp], STR_QUOTE);
512   StringCat(buff[bp], string(x));
513   StringCat(buff[bp], STR_QUOTE);
514   if( line_num(*pos) != 0 )
515   { StringCat(buff[bp], STR_SPACE);
516     StringCat(buff[bp], StringInt( (int) line_num(*pos)));
517     StringCat(buff[bp], AsciiToFull(","));
518     StringCat(buff[bp], StringInt( (int) col_num(*pos)));
519   }
520 } /* end append_fpos */
521 
EchoFilePos(FILE_POS * pos)522 FULL_CHAR *EchoFilePos(FILE_POS *pos)
523 { bp = (bp + 1) % 2;
524   StringCopy(buff[bp], STR_EMPTY);
525   if( file_num(*pos) > 0 )  append_fpos(pos);
526   return buff[bp];
527 } /* end EchoFilePos */
528 
EchoAltFilePos(FILE_POS * pos)529 FULL_CHAR *EchoAltFilePos(FILE_POS *pos)
530 {
531   bp = (bp + 1) % 2;
532   StringCopy(buff[bp], STR_EMPTY);
533   if( file_num(*pos) > 0 )
534   {
535     /* *** x = ftab_num(file_tab, file_num(*pos)); *** */
536     StringCat(buff[bp], FullFileName(file_num(*pos)));
537     if( line_num(*pos) != 0 )
538     { StringCat(buff[bp], AsciiToFull(":"));
539       StringCat(buff[bp], StringInt( (int) line_num(*pos)));
540       StringCat(buff[bp], AsciiToFull(":"));
541       StringCat(buff[bp], StringInt( (int) col_num(*pos)));
542     }
543   }
544   return buff[bp];
545 } /* end EchoFilePos */
546 
547 
548 /*@::EchoFileSource(), EchoFileLine(), PosOfFile()@***************************/
549 /*                                                                           */
550 /*  FULL_CHAR *EchoFileSource(fnum)                                          */
551 /*                                                                           */
552 /*  Returns a string reporting the "file source" information for file fnum.  */
553 /*                                                                           */
554 /*****************************************************************************/
555 
EchoFileSource(FILE_NUM fnum)556 FULL_CHAR *EchoFileSource(FILE_NUM fnum)
557 { OBJECT x, nextx;
558   bp = (bp + 1) % 2;
559   StringCopy(buff[bp], STR_EMPTY);
560   if( fnum > 0 )
561   { StringCat(buff[bp], STR_SPACE);
562     x = ftab_num(file_tab, fnum);
563     assert( x != nilobj, "EchoFileSource: x == nilobj!" );
564     if( type_of_file(x) == FILTER_FILE )
565     { StringCat(buff[bp], AsciiToFull(condcatgets(MsgCat, 3, 11, "filter")));
566       /* for estrip's benefit: Error(3, 11, "filter"); */
567       StringCat(buff[bp], STR_SPACE);
568     }
569     StringCat(buff[bp], AsciiToFull(condcatgets(MsgCat, 3, 12, "file")));
570     /* for estrip's benefit: Error(3, 12, "file"); */
571     StringCat(buff[bp], STR_SPACE);
572     /* *** x = ftab_num(file_tab, fnum); *** */
573     StringCat(buff[bp], STR_QUOTE);
574     StringCat(buff[bp], FullFileName(fnum));
575     StringCat(buff[bp], STR_QUOTE);
576     if( file_num(fpos(x)) > 0 )
577     { StringCat(buff[bp], AsciiToFull(" ("));
578       for(;;)
579       { nextx = ftab_num(file_tab, file_num(fpos(x)));
580 	StringCat(buff[bp], AsciiToFull(condcatgets(MsgCat, 3, 13, "from")));
581 	/* for estrip's benefit: Error(3, 13, "from"); */
582 	StringCat(buff[bp], STR_SPACE);
583         StringCat(buff[bp], STR_QUOTE);
584         StringCat(buff[bp], string(nextx));
585         StringCat(buff[bp], STR_QUOTE);
586 	StringCat(buff[bp], STR_SPACE);
587         StringCat(buff[bp],  AsciiToFull(condcatgets(MsgCat, 3, 14, "line")));
588 	/* for estrip's benefit: Error(3, 14, "line"); */
589 	StringCat(buff[bp], STR_SPACE);
590         StringCat(buff[bp], StringInt( (int) line_num(fpos(x))));
591 	if( file_num(fpos(nextx)) == 0 )  break;
592 	StringCat(buff[bp], AsciiToFull(", "));
593 	x = nextx;
594       }
595       StringCat(buff[bp], AsciiToFull(")"));
596     }
597   }
598   return buff[bp];
599 } /* end EchoFileSource */
600 
601 
602 /*****************************************************************************/
603 /*                                                                           */
604 /*  FULL_CHAR *EchoFileLine(pos)                                             */
605 /*                                                                           */
606 /*  Returns a string reporting the "line source" information for pos.        */
607 /*                                                                           */
608 /*****************************************************************************/
609 
EchoFileLine(FILE_POS * pos)610 FULL_CHAR *EchoFileLine(FILE_POS *pos)
611 { bp = (bp + 1) % 2;
612   StringCopy(buff[bp], STR_EMPTY);
613   if( file_num(*pos) > 0 && line_num(*pos) != 0 )
614   { StringCat(buff[bp], StringInt( (int) line_num(*pos)));
615     StringCat(buff[bp], AsciiToFull(","));
616     StringCat(buff[bp], StringInt( (int) col_num(*pos)));
617   }
618   return buff[bp];
619 } /* end EchoFileLIne */
620 
621 
622 /*****************************************************************************/
623 /*                                                                           */
624 /*  FILE_POS *PosOfFile(fnum)                                                */
625 /*                                                                           */
626 /*  Returns a pointer to the file position where file fnum was encountered.  */
627 /*                                                                           */
628 /*****************************************************************************/
629 
PosOfFile(FILE_NUM fnum)630 FILE_POS *PosOfFile(FILE_NUM fnum)
631 { OBJECT  x = ftab_num(file_tab, fnum);
632   assert( x != nilobj, "PosOfFile: file_tab entry is nilobj!" );
633   return &fpos(x);
634 }
635 
636 /*@::SearchPath()@************************************************************/
637 /*                                                                           */
638 /*  static FILE *SearchPath(str, fpath, check_ld, check_lt, full_name, xfpos,*/
639 /*                          read_mode)                                       */
640 /*                                                                           */
641 /*  Search the given path for a file whose name is str.  If found, open      */
642 /*  it with mode read_mode; return the resulting FILE *.                     */
643 /*                                                                           */
644 /*  If check_ld is TRUE, it means that the file to be opened is a .li file   */
645 /*  and OpenFile() is required to check whether the corresponding .ld file   */
646 /*  is present.  If it is, then the search must stop.  Furthermore, if the   */
647 /*  .li file is out of date wrt the .ld file, it is to be removed.           */
648 /*                                                                           */
649 /*  If check_lt is TRUE, it means that the file to be opened is a source     */
650 /*  file and OpenFile() is required to check for a .lt suffix version.       */
651 /*                                                                           */
652 /*  Also return the full path name in object *full_name if different from    */
653 /*  the existing name, else nilobj.                                          */
654 /*                                                                           */
655 /*  Set *used_source_suffix to TRUE if the .lt source suffix had to be       */
656 /*  added in order to find the file.                                         */
657 /*                                                                           */
658 /*****************************************************************************/
659 
SearchPath(FULL_CHAR * str,OBJECT fpath,BOOLEAN check_ld,BOOLEAN check_lt,OBJECT * full_name,FILE_POS * xfpos,char * read_mode,BOOLEAN * used_source_suffix)660 static FILE *SearchPath(FULL_CHAR *str, OBJECT fpath, BOOLEAN check_ld,
661 BOOLEAN check_lt, OBJECT *full_name, FILE_POS *xfpos, char *read_mode,
662 BOOLEAN *used_source_suffix)
663 { FULL_CHAR buff[MAX_BUFF], buff2[MAX_BUFF];
664   OBJECT link, y = nilobj, cpath;  FILE *fp, *fp2;
665   debug4(DFS, DD, "[ SearchPath(%s, %s, %s, %s, -)", str, EchoObject(fpath),
666 	bool(check_ld), bool(check_lt));
667 
668   *used_source_suffix = FALSE;
669 
670   /* if file name is "stdin" just return it */
671   if( StringEqual(str, STR_STDIN) )
672   {
673     debug0(DFS, DD, "] SearchPath returning stdin");
674     *full_name = nilobj;
675     return stdin;
676   }
677 
678   /* use fpath if relative file name, use empty_path if absolute filename */
679   cpath = StringBeginsWith(str, STR_DIR) ? empty_path : fpath;
680 
681   /* try opening each path name in the search path */
682   fp = null;
683   for( link = Down(cpath);  fp == null && link != cpath;  link = NextDown(link) )
684   { Child(y, link);
685 
686     /* set buff to the full path name */
687     if( StringLength(string(y)) == 0 )
688     { StringCopy(buff, str);
689     }
690     else
691     { if( StringLength(string(y)) + StringLength(STR_DIR) +
692 	  StringLength(str) >= MAX_BUFF )
693 	Error(3, 15, "file path name %s%s%s is too long",
694 	  FATAL, &fpos(y), string(y), STR_DIR, str);
695       StringCopy(buff, string(y));
696       StringCat(buff, STR_DIR);
697       StringCat(buff, str);
698     }
699 
700     /* try opening the full path name */
701     fp = StringFOpen(buff, read_mode);
702     debug1(DFS, DD, fp == null ? "  fail %s" : "  succeed %s", buff);
703 
704     /* if failed to find .li file, exit if corresponding .ld file */
705     if( check_ld && fp == null )
706     {
707         StringCopy(buff2, buff);
708         StringCopy(&buff2[StringLength(buff2) - StringLength(INDEX_SUFFIX)],
709 	  DATA_SUFFIX);
710         fp2 = StringFOpen(buff2, READ_FILE);
711         debug1(DFS, DD, fp2 == null ? "  fail %s" : "  succeed %s", buff2);
712         if( fp2 != null )
713         { fclose(fp2);
714 	  debug0(DFS, DD, "] SearchPath returning null (adjacent .ld file)");
715 	  *full_name = nilobj;
716 	  return null;
717         }
718     }
719 
720 #if USE_STAT
721     /*****************************************************************/
722     /*                                                               */
723     /*  If your compiler won't compile this bit, it is probably      */
724     /*  because you either don't have the stat() system call on      */
725     /*  your system (it is not ANSI C), or because it can't be       */
726     /*  found in the header files declared at the top of this file.  */
727     /*                                                               */
728     /*  The simple correct thing to do is to set the USESTAT macro   */
729     /*  in the makefile to 0.  You won't lose much.                  */
730     /*                                                               */
731     /*****************************************************************/
732 
733     /* if found .li file, compare dates with corresponding .ld file  */
734     if( check_ld && fp != null )
735     {
736       struct stat indexstat, datastat;
737       StringCopy(buff2, buff);
738       StringCopy(&buff2[StringLength(buff2) - StringLength(INDEX_SUFFIX)],
739 	DATA_SUFFIX);
740       debug2(DFS, DD, "SearchPath comparing dates of .li %s and .ld %s",
741 	buff, buff2);
742       if( stat( (char *) buff, &indexstat) == 0 &&
743 	  stat( (char *) buff2, &datastat) == 0 )
744       {
745 	debug2(DFS, DD, "SearchPath mtimes are .li %d and .ld %d",
746 	  (int) indexstat.st_mtime, (int) datastat.st_mtime);
747 	if( datastat.st_mtime > indexstat.st_mtime )
748 	{ fclose(fp);
749 	  debug1(DFS, DD, "SearchPath calling StringRemove(%s)", buff);
750 	  StringRemove(buff);
751 	  debug0(DFS, DD, "] SearchPath returning null (.li out of date)");
752 	  *full_name = nilobj;
753 	  return null;
754 	}
755       }
756     }
757 #endif
758 
759     /* if check_lt, see if buff.lt exists as well as or instead of buff */
760     if( check_lt )
761     {
762       StringCopy(buff2, buff);
763       StringCat(buff2, SOURCE_SUFFIX);
764       fp2 = StringFOpen(buff2, READ_FILE);
765       debug1(DFS, DD, fp2 == null ? "  fail %s" : "  succeed %s", buff2);
766       if( fp2 != null )
767       { if( fp != null )
768 	  Error(3, 16, "files %s and %s both exist", FATAL, xfpos,buff,buff2);
769 	fp = fp2;
770 	*used_source_suffix = TRUE;
771       }
772     }
773 
774   }
775   debug1(DFS, DD, "] SearchPath returning (fp %s null)", fp==null ? "==" : "!=");
776   *full_name = (fp == null || StringLength(string(y)) == 0) ? nilobj :
777     MakeWord(WORD, buff, xfpos);
778   return fp;
779 } /* end SearchPath */
780 
781 
782 /*@::OpenFile(), OpenIncGraphicFile()@****************************************/
783 /*                                                                           */
784 /*  FILE *OpenFile(fnum, check_ld, check_lt)                                 */
785 /*                                                                           */
786 /*  Open for reading the file whose number is fnum.  This involves           */
787 /*  searching for it along its path if not previously opened.                */
788 /*                                                                           */
789 /*  If check_ld is TRUE, it means that the file to be opened is a .li file   */
790 /*  and OpenFile() is required to check whether the corresponding .ld file   */
791 /*  is present.  If it is, then the search must stop.  Furthermore, if the   */
792 /*  .li file is out of date wrt the .ld file, it is to be removed.           */
793 /*                                                                           */
794 /*  If check_lt is TRUE, it means that the file to be opened is a source     */
795 /*  file and OpenFile() is required to check for a .lt suffix version        */
796 /*  if the file does not open without it.                                    */
797 /*                                                                           */
798 /*****************************************************************************/
799 
OpenFile(FILE_NUM fnum,BOOLEAN check_ld,BOOLEAN check_lt)800 FILE *OpenFile(FILE_NUM fnum, BOOLEAN check_ld, BOOLEAN check_lt)
801 { FILE *fp;  OBJECT fname, full_name, y;  BOOLEAN used_source_suffix;
802   ifdebug(DPP, D, ProfileOn("OpenFile"));
803   debug2(DFS, DD, "[ OpenFile(%s, %s)", FileName(fnum), bool(check_ld));
804   fname = ftab_num(file_tab, fnum);
805   if( Down(fname) != fname )
806   { Child(y, Down(fname));
807     /* fp = StringFOpen(string(y), file_mode[type_of_file(fname)]); */
808     fp = StringFOpen(string(y), READ_FILE);
809     debug1(DFS,DD,fp==null ? "  failed on %s" : "  succeeded on %s", string(y));
810   }
811   else
812   {
813     /* ***
814     fp = SearchPath(string(fname), file_path[path(fname)], check_ld,
815 	   check_lt, &full_name, &fpos(fname), file_mode[type_of_file(fname)],
816 	   &used_source_suffix);
817     *** */
818     fp = SearchPath(string(fname), file_path[path(fname)], check_ld, check_lt,
819 	   &full_name, &fpos(fname), READ_FILE, &used_source_suffix);
820     if( full_name != nilobj )  Link(fname, full_name);
821     used_suffix(fname) = used_source_suffix;
822   }
823   ifdebug(DPP, D, ProfileOff("OpenFile"));
824   debug1(DFS, DD, "] OpenFile returning (fp %s null)", fp==null ? "==" : "!=");
825   return fp;
826 } /* end OpenFile */
827 
828 
829 /*****************************************************************************/
830 /*                                                                           */
831 /*  FILE *OpenIncGraphicFile(str, typ, full_name, xfpos, compressed)         */
832 /*                                                                           */
833 /*  Open for reading the @IncludeGraphic file str; typ is INCGRAPHIC or      */
834 /*  SINCGRAPHIC; xfpos is the file position of the file name.                */
835 /*                                                                           */
836 /*  Return the full name in full_name.  Set compressed to TRUE if the file   */
837 /*  was a compressed file.                                                   */
838 /*                                                                           */
839 /*****************************************************************************/
840 #define MAX_COMPRESSED 6
841 static char *compress_suffixes[MAX_COMPRESSED]
842   = { ".gz", "-gz", ".z", "-z", "_z", ".Z" };
843 
OpenIncGraphicFile(FULL_CHAR * str,unsigned char typ,OBJECT * full_name,FILE_POS * xfpos,BOOLEAN * compressed)844 FILE *OpenIncGraphicFile(FULL_CHAR *str, unsigned char typ,
845 OBJECT *full_name, FILE_POS *xfpos, BOOLEAN *compressed)
846 { FILE *fp;  int p, i;  BOOLEAN used_source_suffix;
847   debug2(DFS, DD, "OpenIncGraphicFile(%s, %s, -)", str, Image(typ));
848   assert( typ == INCGRAPHIC || typ == SINCGRAPHIC, "OpenIncGraphicFile!" );
849   p = (typ == INCGRAPHIC ? INCLUDE_PATH : SYSINCLUDE_PATH);
850   fp = SearchPath(str, file_path[p], FALSE, FALSE, full_name, xfpos,
851 	READ_FILE, &used_source_suffix);
852   if( *full_name == nilobj )  *full_name = MakeWord(WORD, str, xfpos);
853 
854   if( fp == null )
855   {
856     /* if file didn't open, nothing more to do */
857     *compressed = FALSE;
858     fp = null;
859   }
860   else
861   {
862     /* if file is compressed, uncompress it into file LOUT_EPS */
863     for( i = 0;  i < MAX_COMPRESSED; i++ )
864     { if( StringEndsWith(string(*full_name), AsciiToFull(compress_suffixes[i])) )
865         break;
866     }
867     if( i < MAX_COMPRESSED )
868     { char buff[MAX_BUFF];
869       fclose(fp);
870       sprintf(buff, UNCOMPRESS_COM, (char *) string(*full_name), LOUT_EPS);
871       if( SafeExecution )
872       {
873         Error(3, 17, "safe execution prohibiting command: %s", WARN, xfpos,buff);
874         *compressed = FALSE;
875         fp = null;
876       }
877       else
878       {
879         system(buff);
880         fp = fopen(LOUT_EPS, READ_FILE);
881         *compressed = TRUE;
882       }
883     }
884     else *compressed = FALSE;
885   }
886 
887   debug2(DFS, DD, "OpenIncGraphicFile returning (fp %s null, *full_name = %s)",
888     fp==null ? "==" : "!=", string(*full_name));
889   return fp;
890 } /* end OpenIncGraphicFile */
891 
892 
893 /*****************************************************************************/
894 /*                                                                           */
895 /*  FileSetUpdated(fnum, newlines)                                           */
896 /*                                                                           */
897 /*  Declare that file fnum has been updated, and that it now contains        */
898 /*  newlines lines.                                                          */
899 /*                                                                           */
900 /*****************************************************************************/
901 
FileSetUpdated(FILE_NUM fnum,int newlines)902 void FileSetUpdated(FILE_NUM fnum, int newlines)
903 {
904   debug2(DFS, DD, "FileSetUpdated(%s, %d)", FileName(fnum), newlines);
905   updated(ftab_num(file_tab, fnum)) = TRUE;
906   line_count(ftab_num(file_tab, fnum)) = newlines;
907   debug0(DFS, DD, "FileSetUpdated returning");
908 } /* end FileSetUpdated */
909 
910 
911 /*****************************************************************************/
912 /*                                                                           */
913 /*  int FileGetLineCount(FILE_NUM fnum)                                      */
914 /*                                                                           */
915 /*  Return the number of lines so far written to file fnum.                  */
916 /*                                                                           */
917 /*****************************************************************************/
918 
FileGetLineCount(FILE_NUM fnum)919 int FileGetLineCount(FILE_NUM fnum)
920 { int res;
921   debug1(DFS, DD, "FileGetLineCount(%s)", FileName(fnum));
922   res = line_count(ftab_num(file_tab, fnum));
923   debug1(DFS, DD, "FileGetLineCount returning %d", res);
924   return res;
925 } /* end FileGetLineCount */
926 
927 
928 /*****************************************************************************/
929 /*                                                                           */
930 /*  BOOLEAN FileTestUpdated(fnum)                                            */
931 /*                                                                           */
932 /*  Test whether file fnum has been declared to be updated.                  */
933 /*                                                                           */
934 /*****************************************************************************/
935 
FileTestUpdated(FILE_NUM fnum)936 BOOLEAN FileTestUpdated(FILE_NUM fnum)
937 { return (BOOLEAN) updated(ftab_num(file_tab, fnum));
938 } /* end FileTestUpdated */
939