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