blob: 47ba1d4f6d6b10ea450bf8205f6df4a27289e288 [file] [log] [blame]
/*@z03.c:File Service:Declarations, no_fpos@******************************** */
/* */
/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.23) */
/* COPYRIGHT (C) 1991, 2000 Jeffrey H. Kingston */
/* */
/* Jeffrey H. Kingston (jeff@cs.usyd.edu.au) */
/* Basser Department of Computer Science */
/* The University of Sydney 2006 */
/* AUSTRALIA */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either Version 2, or (at your option) */
/* any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */
/* */
/* FILE: z03.c */
/* MODULE: File Service */
/* EXTERNS: InitFiles(), AddToPath(), DefineFile(), FirstFile(), */
/* NextFile(), FileNum(), FileName(), EchoFilePos(), */
/* PosOfFile(), OpenFile(), OpenIncGraphicFile() */
/* EchoFileFrom() */
/* */
/*****************************************************************************/
#include "externs.h"
#if USE_STAT
#include <sys/types.h>
#include <sys/stat.h>
#endif
#define INIT_TAB 3 /* initial file table size */
#define file_number(x) word_font(x) /* file number of file x */
#define type_of_file(x) word_colour(x) /* type of file x */
#define used_suffix(x) word_hyph(x) /* file needs .lt suffix */
#define updated(x) fwd(x, COLM) /* TRUE when x is updated */
#define line_count(x) fwd(x, ROWM) /* number of lines written */
#define path(x) back(x, COLM) /* search path for file x */
/*****************************************************************************/
/* */
/* FILE_TABLE */
/* */
/* A symbol table permitting access to file records by number or name. */
/* The table will automatically enlarge to accept any number of entries, */
/* but there is an arbitrary limit of 65535 files imposed so that file */
/* numbers can be stored in 16 bit fields. */
/* */
/* ftab_new(newsize) New empty table, newsize capacity */
/* ftab_insert(x, &S) Insert new file object x into S */
/* ftab_retrieve(str, S) Retrieve file object of name str */
/* ftab_num(S, num) Retrieve file object of number num */
/* ftab_debug(S, fp) Debug print of table S to file fp */
/* */
/*****************************************************************************/
typedef struct
{ int filetab_size; /* size of table */
int filetab_count; /* number of files in table */
struct filetab_rec
{ OBJECT by_number; /* file record by number */
OBJECT by_name_hash; /* file record by name hash */
} filetab[1];
} *FILE_TABLE;
#define ftab_size(S) (S)->filetab_size
#define ftab_count(S) (S)->filetab_count
#define ftab_num(S, i) (S)->filetab[i].by_number
#define ftab_name(S, i) (S)->filetab[i].by_name_hash
#define hash(pos, str, S) \
{ FULL_CHAR *p = str; \
pos = *p++; \
while( *p ) pos += *p++; \
pos = pos % ftab_size(S); \
}
static FILE_TABLE ftab_new(int newsize)
{ FILE_TABLE S; int i;
ifdebug(DMA, D, DebugRegisterUsage(MEM_FILES, 1,
2*sizeof(int) + newsize * sizeof(struct filetab_rec)));
S = (FILE_TABLE) malloc(2*sizeof(int) + newsize * sizeof(struct filetab_rec));
if( S == (FILE_TABLE) NULL )
Error(3, 1, "run out of memory when enlarging file table", FATAL, no_fpos);
ftab_size(S) = newsize;
ftab_count(S) = 0;
for( i = 0; i < newsize; i++ )
{ ftab_num(S, i) = ftab_name(S, i) = nilobj;
}
return S;
} /* end ftab_new */
static void ftab_insert(OBJECT x, FILE_TABLE *S);
static FILE_TABLE ftab_rehash(FILE_TABLE S, int newsize)
{ FILE_TABLE NewS; int i;
NewS = ftab_new(newsize);
for( i = 1; i <= ftab_count(S); i++ )
ftab_insert(ftab_num(S, i), &NewS);
for( i = 0; i < ftab_size(S); i++ )
{ if( ftab_name(S, i) != nilobj ) DisposeObject(ftab_name(S, i));
}
ifdebug(DMA, D, DebugRegisterUsage(MEM_FILES, -1,
-(2*sizeof(int) + ftab_size(S) * sizeof(struct filetab_rec))));
free(S);
return NewS;
} /* end ftab_rehash */
static void ftab_insert(OBJECT x, FILE_TABLE *S)
{ int pos, num;
if( ftab_count(*S) == ftab_size(*S) - 1 ) /* one less since 0 unused */
*S = ftab_rehash(*S, 2*ftab_size(*S));
num = ++ftab_count(*S);
if( num > MAX_FILES )
Error(3, 2, "too many files (maximum is %d)",
FATAL, &fpos(x), MAX_FILES);
hash(pos, string(x), *S);
if( ftab_name(*S, pos) == nilobj ) New(ftab_name(*S, pos), ACAT);
Link(ftab_name(*S, pos), x);
file_number(x) = num;
ftab_num(*S, num) = x;
} /* end ftab_insert */
static OBJECT ftab_retrieve(FULL_CHAR *str, FILE_TABLE S)
{ OBJECT x, link, y; int pos;
hash(pos, str, S);
x = ftab_name(S, pos);
if( x == nilobj ) return nilobj;
for( link = Down(x); link != x; link = NextDown(link) )
{ Child(y, link);
if( StringEqual(str, string(y)) ) return y;
}
return nilobj;
} /* end ftab_retrieve */
#if DEBUG_ON
static void ftab_debug(FILE_TABLE S, FILE *fp)
{ int i; OBJECT x, link, y;
fprintf(fp, " table size: %d; current number of files: %d\n",
ftab_size(S), ftab_count(S));
for( i = 0; i < ftab_size(S); i++ )
{ x = ftab_num(S, i);
fprintf(fp, " ftab_num(S, %d) = %s\n", i,
x == nilobj ? AsciiToFull("<nilobj>") :
!is_word(type(x)) ? AsciiToFull("not WORD!") : string(x) );
}
fprintf(fp, "\n");
for( i = 0; i < ftab_size(S); i++ )
{ x = ftab_name(S, i);
fprintf(fp, " ftab_name(S, %d) =", i);
if( x == nilobj )
fprintf(fp, " <nilobj>");
else if( type(x) != ACAT )
fprintf(fp, " not ACAT!");
else for( link = Down(x); link != x; link = NextDown(link) )
{ Child(y, link);
fprintf(fp, " %s",
is_word(type(y)) ? string(y) : AsciiToFull("not-WORD!"));
}
fprintf(fp, "\n");
}
} /* end ftab_debug */
static char *file_types[] /* the type names for debug */
= { "source", "include", "incgraphic", "database", "index",
"font", "prepend", "hyph", "hyphpacked",
"mapping", "filter" };
#endif
static OBJECT empty_path; /* file path with just "" in */
static FILE_TABLE file_tab; /* the file table */
static OBJECT file_type[MAX_TYPES]; /* files of each type */
static OBJECT file_path[MAX_PATHS]; /* the search paths */
static char *file_mode[MAX_TYPES] =
{ READ_TEXT, READ_TEXT, READ_TEXT, READ_TEXT, READ_BINARY, READ_TEXT,
READ_TEXT, READ_TEXT, READ_BINARY, READ_TEXT, READ_TEXT };
/*****************************************************************************/
/* */
/* no_fpos */
/* */
/* A null file position value. */
/* */
/*****************************************************************************/
static FILE_POS no_file_pos = {0, 0, 0, 0, 0};
FILE_POS *no_fpos = &no_file_pos;
/*@::InitFiles(), AddToPath(), DefineFile()@**********************************/
/* */
/* InitFiles() */
/* */
/* Initialize this module. */
/* */
/*****************************************************************************/
void InitFiles(void)
{ int i; OBJECT tmp;
for( i = 0; i < MAX_TYPES; i++ ) New(file_type[i], ACAT);
for( i = 0; i < MAX_PATHS; i++ ) New(file_path[i], ACAT);
file_tab = ftab_new(INIT_TAB);
New(empty_path, ACAT);
tmp = MakeWord(WORD, STR_EMPTY, no_fpos);
Link(empty_path, tmp);
} /* end InitFiles */
/*****************************************************************************/
/* */
/* AddToPath(fpath, dirname) */
/* */
/* Add the directory dirname to the end of search path fpath. */
/* */
/*****************************************************************************/
void AddToPath(int fpath, OBJECT dirname)
{ Link(file_path[fpath], dirname);
} /* end AddToPath */
/*****************************************************************************/
/* */
/* FILE_NUM DefineFile(str, suffix, xfpos, ftype, fpath) */
/* */
/* Declare a file whose name is str plus suffix and whose fpos is xfpos. */
/* The file type is ftype, and its search path is fpath. */
/* */
/*****************************************************************************/
FILE_NUM DefineFile(FULL_CHAR *str, FULL_CHAR *suffix,
FILE_POS *xfpos, int ftype, int fpath)
{ register int i;
OBJECT fname;
assert( ftype < MAX_TYPES, "DefineFile: ftype!" );
debug5(DFS, DD, "DefineFile(%s, %s,%s, %s, %d)",
str, suffix, EchoFilePos(xfpos), file_types[ftype], fpath);
if( ftype == SOURCE_FILE && (i = StringLength(str)) >= 3 )
{
/* check that file name does not end in ".li" or ".ld" */
if( StringEqual(&str[i-StringLength(DATA_SUFFIX)], DATA_SUFFIX) )
Error(3, 3, "database file %s where source file expected",
FATAL, xfpos, str);
if( StringEqual(&str[i-StringLength(INDEX_SUFFIX)], INDEX_SUFFIX) )
Error(3, 4, "database index file %s where source file expected",
FATAL, xfpos, str);
}
if( StringLength(str) + StringLength(suffix) >= MAX_WORD )
Error(3, 5, "file name %s%s is too long", FATAL, no_fpos, str, suffix);
fname = MakeWordTwo(WORD, str, suffix, xfpos);
Link(file_type[ftype], fname);
path(fname) = fpath;
updated(fname) = FALSE;
line_count(fname) = 0;
type_of_file(fname) = ftype;
used_suffix(fname) = FALSE;
ftab_insert(fname, &file_tab);
debug1(DFS, DD, "DefineFile returning %s", string(fname));
ifdebug(DFS, DD, ftab_debug(file_tab, stderr));
return file_number(fname);
} /* end DefineFile */
/*@::FirstFile(), NextFile(), FileNum()@**************************************/
/* */
/* FILE_NUM FirstFile(ftype) */
/* */
/* Returns first file of type ftype, else NO_FILE. */
/* */
/*****************************************************************************/
FILE_NUM FirstFile(int ftype)
{ FILE_NUM i;
OBJECT link, y;
debug1(DFS, DD, "FirstFile( %s )", file_types[ftype]);
link = Down(file_type[ftype]);
if( type(link) == ACAT ) i = NO_FILE;
else
{ Child(y, link);
i = file_number(y);
}
debug1(DFS, DD, "FirstFile returning %s", i==NO_FILE ? STR_NONE : FileName(i));
return i;
} /* end FirstFile */
/*****************************************************************************/
/* */
/* FILE_NUM NextFile(i) */
/* */
/* Returns the next file after file i of the type of i, else NO_FILE. */
/* */
/*****************************************************************************/
FILE_NUM NextFile(FILE_NUM i)
{ OBJECT link, y;
debug1(DFS, DD, "NextFile( %s )", FileName(i));
link = NextDown(Up(ftab_num(file_tab, i)));
if( type(link) == ACAT ) i = NO_FILE;
else
{ Child(y, link);
i = file_number(y);
}
debug1(DFS, DD, "NextFile returning %s", i==NO_FILE ? STR_NONE : FileName(i));
return i;
} /* end NextFile */
/*****************************************************************************/
/* */
/* FILE_NUM FileNum(str, suffix) */
/* */
/* Return the number of the file with name str plus suffix, else NO_FILE. */
/* */
/*****************************************************************************/
FILE_NUM FileNum(FULL_CHAR *str, FULL_CHAR *suffix)
{ register int i; OBJECT fname;
FULL_CHAR buff[MAX_BUFF];
debug2(DFS, DD, "FileNum(%s, %s)", str, suffix);
if( StringLength(str) + StringLength(suffix) >= MAX_BUFF )
Error(3, 6, "file name %s%s is too long", FATAL, no_fpos, str, suffix);
StringCopy(buff, str);
StringCat(buff, suffix);
fname = ftab_retrieve(buff, file_tab);
i = fname == nilobj ? NO_FILE : file_number(fname);
debug1(DFS, DD, "FileNum returning %s",
i == NO_FILE ? STR_NONE : FileName( (FILE_NUM) i));
return (FILE_NUM) i;
} /* end FileNum */
/*****************************************************************************/
/* */
/* FILE_NUM DatabaseFileNum(FILE_POS *xfpos) */
/* */
/* Return a suitable database file number for writing something out whose */
/* file position is xfpos. */
/* */
/*****************************************************************************/
FILE_NUM DatabaseFileNum(FILE_POS *xfpos)
{ OBJECT x;
FILE_NUM fnum; FULL_CHAR *str;
debug2(DFS, D, "DatabaseFileNum(%s %s)", EchoFilePos(xfpos),
EchoFileSource(file_num(*xfpos)));
x = ftab_num(file_tab, file_num(*xfpos));
switch( type_of_file(x) )
{
case SOURCE_FILE:
case INCLUDE_FILE:
/* return the corresponding database file (may need to be defined) */
str = FileName(file_num(*xfpos));
fnum = FileNum(str, DATA_SUFFIX);
if( fnum == NO_FILE )
{ debug0(DFS, DD, " calling DefineFile from DatabaseFileNum");
fnum = DefineFile(str, DATA_SUFFIX, xfpos, DATABASE_FILE, SOURCE_PATH);
}
break;
case DATABASE_FILE:
/* return the enclosing source file (recursively if necessary) */
if( file_num(fpos(x)) == NO_FILE )
{
/* xfpos lies in a cross-reference database file; use itself */
/* ***
Error(3, 18, "DatabaseFileNum: database file position unknown",
INTERN, no_fpos);
*** */
fnum = file_num(*xfpos);
}
else
{
/* xfpos lies in a user-defined database file; use its source */
fnum = DatabaseFileNum(&fpos(x));
}
break;
case FILTER_FILE:
/* return the enclosing source file (recursively if necessary) */
if( file_num(fpos(x)) == NO_FILE )
Error(3, 7, "DatabaseFileNum: filter file position unknown",
INTERN, no_fpos);
fnum = DatabaseFileNum(&fpos(x));
break;
default:
Error(3, 8, "DatabaseFileNum: unexpected file type", INTERN, no_fpos);
fnum = NO_FILE;
break;
}
debug2(DFS, D, "DatabaseFileNum returning %d (%s)", fnum,
fnum == NO_FILE ? AsciiToFull("NO_FILE") : FileName(fnum));
return fnum;
} /* end DatabaseFileNum */
/*@::FileName(), EchoFilePos(), PosOfFile()@**********************************/
/* */
/* FULL_CHAR *FileName(fnum) */
/* FULL_CHAR *FullFileName(fnum) */
/* */
/* Return the string name of this file. This is as given to DefineFile */
/* until OpenFile is called, after which it is the full path name. */
/* */
/* FullFileName is the same except it will add a .lt to the file name */
/* if that was needed when the file was opened for reading. */
/* */
/*****************************************************************************/
FULL_CHAR *FileName(FILE_NUM fnum)
{ OBJECT x;
x = ftab_num(file_tab, fnum);
assert( x != nilobj, "FileName: x == nilobj!" );
if( Down(x) != x ) Child(x, Down(x));
return string(x);
} /* end FileName */
FULL_CHAR *FullFileName(FILE_NUM fnum)
{ OBJECT x;
static FULL_CHAR ffbuff[2][MAX_BUFF];
static int ffbp = 1;
x = ftab_num(file_tab, fnum);
assert( x != nilobj, "FileName: x == nilobj!" );
if( used_suffix(x) )
{
if( Down(x) != x ) Child(x, Down(x));
ffbp = (ffbp + 1) % 2;
StringCopy(ffbuff[ffbp], string(x));
StringCat(ffbuff[ffbp], SOURCE_SUFFIX);
return ffbuff[ffbp];
}
else
{
if( Down(x) != x ) Child(x, Down(x));
return string(x);
}
} /* end FullFileName */
/*****************************************************************************/
/* */
/* FULL_CHAR *EchoFilePos(pos) */
/* */
/* Returns a string reporting the value of file position pos. */
/* */
/*****************************************************************************/
static FULL_CHAR buff[2][MAX_BUFF]; static int bp = 1;
static void append_fpos(FILE_POS *pos)
{ OBJECT x;
x = ftab_num(file_tab, file_num(*pos));
assert( x != nilobj, "EchoFilePos: file_tab entry is nilobj!" );
if( file_num(fpos(x)) > 0 )
{ append_fpos( &fpos(x) );
if( StringLength(buff[bp]) + 2 >= MAX_BUFF )
Error(3, 9, "file position %s... is too long to print",
FATAL, no_fpos, buff[bp]);
StringCat(buff[bp], STR_SPACE);
StringCat(buff[bp], STR_DIR);
}
if( StringLength(buff[bp]) + StringLength(string(x)) + 13 >= MAX_BUFF )
Error(3, 10, "file position %s... is too long to print",
FATAL, no_fpos, buff[bp]);
StringCat(buff[bp], STR_SPACE);
StringCat(buff[bp], STR_QUOTE);
StringCat(buff[bp], string(x));
StringCat(buff[bp], STR_QUOTE);
if( line_num(*pos) != 0 )
{ StringCat(buff[bp], STR_SPACE);
StringCat(buff[bp], StringInt( (int) line_num(*pos)));
StringCat(buff[bp], AsciiToFull(","));
StringCat(buff[bp], StringInt( (int) col_num(*pos)));
}
} /* end append_fpos */
FULL_CHAR *EchoFilePos(FILE_POS *pos)
{ bp = (bp + 1) % 2;
StringCopy(buff[bp], STR_EMPTY);
if( file_num(*pos) > 0 ) append_fpos(pos);
return buff[bp];
} /* end EchoFilePos */
FULL_CHAR *EchoAltFilePos(FILE_POS *pos)
{
bp = (bp + 1) % 2;
StringCopy(buff[bp], STR_EMPTY);
if( file_num(*pos) > 0 )
{
/* *** x = ftab_num(file_tab, file_num(*pos)); *** */
StringCat(buff[bp], FullFileName(file_num(*pos)));
if( line_num(*pos) != 0 )
{ StringCat(buff[bp], AsciiToFull(":"));
StringCat(buff[bp], StringInt( (int) line_num(*pos)));
StringCat(buff[bp], AsciiToFull(":"));
StringCat(buff[bp], StringInt( (int) col_num(*pos)));
}
}
return buff[bp];
} /* end EchoFilePos */
/*@::EchoFileSource(), EchoFileLine(), PosOfFile()@***************************/
/* */
/* FULL_CHAR *EchoFileSource(fnum) */
/* */
/* Returns a string reporting the "file source" information for file fnum. */
/* */
/*****************************************************************************/
FULL_CHAR *EchoFileSource(FILE_NUM fnum)
{ OBJECT x, nextx;
bp = (bp + 1) % 2;
StringCopy(buff[bp], STR_EMPTY);
if( fnum > 0 )
{ StringCat(buff[bp], STR_SPACE);
x = ftab_num(file_tab, fnum);
assert( x != nilobj, "EchoFileSource: x == nilobj!" );
if( type_of_file(x) == FILTER_FILE )
{ StringCat(buff[bp], AsciiToFull(condcatgets(MsgCat, 3, 11, "filter")));
/* for estrip's benefit: Error(3, 11, "filter"); */
StringCat(buff[bp], STR_SPACE);
}
StringCat(buff[bp], AsciiToFull(condcatgets(MsgCat, 3, 12, "file")));
/* for estrip's benefit: Error(3, 12, "file"); */
StringCat(buff[bp], STR_SPACE);
/* *** x = ftab_num(file_tab, fnum); *** */
StringCat(buff[bp], STR_QUOTE);
StringCat(buff[bp], FullFileName(fnum));
StringCat(buff[bp], STR_QUOTE);
if( file_num(fpos(x)) > 0 )
{ StringCat(buff[bp], AsciiToFull(" ("));
for(;;)
{ nextx = ftab_num(file_tab, file_num(fpos(x)));
StringCat(buff[bp], AsciiToFull(condcatgets(MsgCat, 3, 13, "from")));
/* for estrip's benefit: Error(3, 13, "from"); */
StringCat(buff[bp], STR_SPACE);
StringCat(buff[bp], STR_QUOTE);
StringCat(buff[bp], string(nextx));
StringCat(buff[bp], STR_QUOTE);
StringCat(buff[bp], STR_SPACE);
StringCat(buff[bp], AsciiToFull(condcatgets(MsgCat, 3, 14, "line")));
/* for estrip's benefit: Error(3, 14, "line"); */
StringCat(buff[bp], STR_SPACE);
StringCat(buff[bp], StringInt( (int) line_num(fpos(x))));
if( file_num(fpos(nextx)) == 0 ) break;
StringCat(buff[bp], AsciiToFull(", "));
x = nextx;
}
StringCat(buff[bp], AsciiToFull(")"));
}
}
return buff[bp];
} /* end EchoFileSource */
/*****************************************************************************/
/* */
/* FULL_CHAR *EchoFileLine(pos) */
/* */
/* Returns a string reporting the "line source" information for pos. */
/* */
/*****************************************************************************/
FULL_CHAR *EchoFileLine(FILE_POS *pos)
{ bp = (bp + 1) % 2;
StringCopy(buff[bp], STR_EMPTY);
if( file_num(*pos) > 0 && line_num(*pos) != 0 )
{ StringCat(buff[bp], StringInt( (int) line_num(*pos)));
StringCat(buff[bp], AsciiToFull(","));
StringCat(buff[bp], StringInt( (int) col_num(*pos)));
}
return buff[bp];
} /* end EchoFileLIne */
/*****************************************************************************/
/* */
/* FILE_POS *PosOfFile(fnum) */
/* */
/* Returns a pointer to the file position where file fnum was encountered. */
/* */
/*****************************************************************************/
FILE_POS *PosOfFile(FILE_NUM fnum)
{ OBJECT x = ftab_num(file_tab, fnum);
assert( x != nilobj, "PosOfFile: file_tab entry is nilobj!" );
return &fpos(x);
}
/*@::SearchPath()@************************************************************/
/* */
/* static FILE *SearchPath(str, fpath, check_ld, check_lt, full_name, xfpos,*/
/* read_mode) */
/* */
/* Search the given path for a file whose name is str. If found, open */
/* it with mode read_mode; return the resulting FILE *. */
/* */
/* If check_ld is TRUE, it means that the file to be opened is a .li file */
/* and OpenFile() is required to check whether the corresponding .ld file */
/* is present. If it is, then the search must stop. Furthermore, if the */
/* .li file is out of date wrt the .ld file, it is to be removed. */
/* */
/* If check_lt is TRUE, it means that the file to be opened is a source */
/* file and OpenFile() is required to check for a .lt suffix version. */
/* */
/* Also return the full path name in object *full_name if different from */
/* the existing name, else nilobj. */
/* */
/* Set *used_source_suffix to TRUE if the .lt source suffix had to be */
/* added in order to find the file. */
/* */
/*****************************************************************************/
static FILE *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)
{ FULL_CHAR buff[MAX_BUFF], buff2[MAX_BUFF];
OBJECT link, y, cpath; FILE *fp, *fp2;
debug4(DFS, DD, "[ SearchPath(%s, %s, %s, %s, -)", str, EchoObject(fpath),
bool(check_ld), bool(check_lt));
*used_source_suffix = FALSE;
/* if file name is "stdin" just return it */
if( StringEqual(str, STR_STDIN) )
{
debug0(DFS, DD, "] SearchPath returning stdin");
*full_name = nilobj;
return stdin;
}
/* use fpath if relative file name, use empty_path if absolute filename */
cpath = StringBeginsWith(str, STR_DIR) ? empty_path : fpath;
/* try opening each path name in the search path */
fp = null;
for( link = Down(cpath); fp == null && link != cpath; link = NextDown(link) )
{ Child(y, link);
/* set buff to the full path name */
if( StringLength(string(y)) == 0 )
{ StringCopy(buff, str);
}
else
{ if( StringLength(string(y)) + StringLength(STR_DIR) +
StringLength(str) >= MAX_BUFF )
Error(3, 15, "file path name %s%s%s is too long",
FATAL, &fpos(y), string(y), STR_DIR, str);
StringCopy(buff, string(y));
StringCat(buff, STR_DIR);
StringCat(buff, str);
}
/* try opening the full path name */
fp = StringFOpen(buff, read_mode);
debug1(DFS, DD, fp == null ? " fail %s" : " succeed %s", buff);
/* if failed to find .li file, exit if corresponding .ld file */
if( check_ld && fp == null )
{
StringCopy(buff2, buff);
StringCopy(&buff2[StringLength(buff2) - StringLength(INDEX_SUFFIX)],
DATA_SUFFIX);
fp2 = StringFOpen(buff2, READ_TEXT);
debug1(DFS, DD, fp2 == null ? " fail %s" : " succeed %s", buff2);
if( fp2 != null )
{ fclose(fp2);
debug0(DFS, DD, "] SearchPath returning null (adjacent .ld file)");
*full_name = nilobj;
return null;
}
}
#if USE_STAT
/*****************************************************************/
/* */
/* If your compiler won't compile this bit, it is probably */
/* because you either don't have the stat() system call on */
/* your system (it is not ANSI C), or because it can't be */
/* found in the header files declared at the top of this file. */
/* */
/* The simple correct thing to do is to set the USESTAT macro */
/* in the makefile to 0. You won't lose much. */
/* */
/*****************************************************************/
/* if found .li file, compare dates with corresponding .ld file */
if( check_ld && fp != null )
{
struct stat indexstat, datastat;
StringCopy(buff2, buff);
StringCopy(&buff2[StringLength(buff2) - StringLength(INDEX_SUFFIX)],
DATA_SUFFIX);
debug2(DFS, DD, "SearchPath comparing dates of .li %s and .ld %s",
buff, buff2);
if( stat( (char *) buff, &indexstat) == 0 &&
stat( (char *) buff2, &datastat) == 0 )
{
debug2(DFS, DD, "SearchPath mtimes are .li %d and .ld %d",
(int) indexstat.st_mtime, (int) datastat.st_mtime);
if( datastat.st_mtime > indexstat.st_mtime )
{ fclose(fp);
debug1(DFS, DD, "SearchPath calling StringRemove(%s)", buff);
StringRemove(buff);
debug0(DFS, DD, "] SearchPath returning null (.li out of date)");
*full_name = nilobj;
return null;
}
}
}
#endif
/* if check_lt, see if buff.lt exists as well as or instead of buff */
if( check_lt )
{
StringCopy(buff2, buff);
StringCat(buff2, SOURCE_SUFFIX);
fp2 = StringFOpen(buff2, READ_TEXT);
debug1(DFS, DD, fp2 == null ? " fail %s" : " succeed %s", buff2);
if( fp2 != null )
{ if( fp != null )
Error(3, 16, "files %s and %s both exist", FATAL, xfpos,buff,buff2);
fp = fp2;
*used_source_suffix = TRUE;
}
}
}
debug1(DFS, DD, "] SearchPath returning (fp %s null)", fp==null ? "==" : "!=");
*full_name = (fp == null || StringLength(string(y)) == 0) ? nilobj :
MakeWord(WORD, buff, xfpos);
return fp;
} /* end SearchPath */
/*@::OpenFile(), OpenIncGraphicFile()@****************************************/
/* */
/* FILE *OpenFile(fnum, check_ld, check_lt) */
/* */
/* Open for reading the file whose number is fnum. This involves */
/* searching for it along its path if not previously opened. */
/* */
/* If check_ld is TRUE, it means that the file to be opened is a .li file */
/* and OpenFile() is required to check whether the corresponding .ld file */
/* is present. If it is, then the search must stop. Furthermore, if the */
/* .li file is out of date wrt the .ld file, it is to be removed. */
/* */
/* If check_lt is TRUE, it means that the file to be opened is a source */
/* file and OpenFile() is required to check for a .lt suffix version */
/* if the file does not open without it. */
/* */
/*****************************************************************************/
FILE *OpenFile(FILE_NUM fnum, BOOLEAN check_ld, BOOLEAN check_lt)
{ FILE *fp; OBJECT fname, full_name, y; BOOLEAN used_source_suffix;
ifdebug(DPP, D, ProfileOn("OpenFile"));
debug2(DFS, DD, "[ OpenFile(%s, %s)", FileName(fnum), bool(check_ld));
fname = ftab_num(file_tab, fnum);
if( Down(fname) != fname )
{ Child(y, Down(fname));
fp = StringFOpen(string(y), file_mode[type_of_file(fname)]);
debug1(DFS,DD,fp==null ? " failed on %s" : " succeeded on %s", string(y));
}
else
{ fp = SearchPath(string(fname), file_path[path(fname)], check_ld,
check_lt, &full_name, &fpos(fname), file_mode[type_of_file(fname)],
&used_source_suffix);
if( full_name != nilobj ) Link(fname, full_name);
used_suffix(fname) = used_source_suffix;
}
ifdebug(DPP, D, ProfileOff("OpenFile"));
debug1(DFS, DD, "] OpenFile returning (fp %s null)", fp==null ? "==" : "!=");
return fp;
} /* end OpenFile */
/*****************************************************************************/
/* */
/* FILE *OpenIncGraphicFile(str, typ, full_name, xfpos, compressed) */
/* */
/* Open for reading the @IncludeGraphic file str; typ is INCGRAPHIC or */
/* SINCGRAPHIC. Return the full name in full_name. Set compressed to */
/* TRUE if the file was a compressed file. */
/* */
/*****************************************************************************/
#define MAX_COMPRESSED 6
static char *compress_suffixes[MAX_COMPRESSED]
= { ".gz", "-gz", ".z", "-z", "_z", ".Z" };
FILE *OpenIncGraphicFile(FULL_CHAR *str, unsigned char typ,
OBJECT *full_name, FILE_POS *xfpos, BOOLEAN *compressed)
{ FILE *fp; int p, i; BOOLEAN used_source_suffix;
debug2(DFS, DD, "OpenIncGraphicFile(%s, %s, -)", str, Image(typ));
assert( typ == INCGRAPHIC || typ == SINCGRAPHIC, "OpenIncGraphicFile!" );
p = (typ == INCGRAPHIC ? INCLUDE_PATH : SYSINCLUDE_PATH);
fp = SearchPath(str, file_path[p], FALSE, FALSE, full_name, xfpos,
READ_TEXT, &used_source_suffix);
if( *full_name == nilobj ) *full_name = MakeWord(WORD, str, xfpos);
if( fp == null )
{
/* if file didn't open, nothing more to do */
*compressed = FALSE;
fp = null;
}
else
{
/* if file is compressed, uncompress it into file LOUT_EPS */
for( i = 0; i < MAX_COMPRESSED; i++ )
{ if( StringEndsWith(string(*full_name), AsciiToFull(compress_suffixes[i])) )
break;
}
if( i < MAX_COMPRESSED )
{ char buff[MAX_BUFF];
fclose(fp);
sprintf(buff, UNCOMPRESS_COM, (char *) string(*full_name), LOUT_EPS);
if( SafeExecution )
{
Error(3, 17, "safe execution prohibiting command: %s", WARN, xfpos,buff);
*compressed = FALSE;
fp = null;
}
else
{
fprintf(stderr, "Cannot execute system calls!\n");
abort();
// system(buff);
fp = fopen(LOUT_EPS, READ_TEXT);
*compressed = TRUE;
}
}
else *compressed = FALSE;
}
debug2(DFS, DD, "OpenIncGraphicFile returning (fp %s null, *full_name = %s)",
fp==null ? "==" : "!=", string(*full_name));
return fp;
} /* end OpenIncGraphicFile */
/*****************************************************************************/
/* */
/* FileSetUpdated(fnum, newlines) */
/* */
/* Declare that file fnum has been updated, and that it now contains */
/* newlines lines. */
/* */
/*****************************************************************************/
void FileSetUpdated(FILE_NUM fnum, int newlines)
{
debug2(DFS, DD, "FileSetUpdated(%s, %d)", FileName(fnum), newlines);
updated(ftab_num(file_tab, fnum)) = TRUE;
line_count(ftab_num(file_tab, fnum)) = newlines;
debug0(DFS, DD, "FileSetUpdated returning");
} /* end FileSetUpdated */
/*****************************************************************************/
/* */
/* int FileGetLineCount(FILE_NUM fnum) */
/* */
/* Return the number of lines so far written to file fnum. */
/* */
/*****************************************************************************/
int FileGetLineCount(FILE_NUM fnum)
{ int res;
debug1(DFS, DD, "FileGetLineCount(%s)", FileName(fnum));
res = line_count(ftab_num(file_tab, fnum));
debug1(DFS, DD, "FileGetLineCount returning %d", res);
return res;
} /* end FileGetLineCount */
/*****************************************************************************/
/* */
/* BOOLEAN FileTestUpdated(fnum) */
/* */
/* Test whether file fnum has been declared to be updated. */
/* */
/*****************************************************************************/
BOOLEAN FileTestUpdated(FILE_NUM fnum)
{ return (BOOLEAN) updated(ftab_num(file_tab, fnum));
} /* end FileTestUpdated */