blob: 65ae3553d83f40cf6e79c9ab2179444c3619e333 [file] [log] [blame]
/*@z41.c:Object Input-Output:AppendToFile, ReadFromFile@**********************/
/* */
/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.24) */
/* 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: z41.c */
/* MODULE: Object Input-Output */
/* EXTERNS: ReadFromFile(), AppendToFile(), CloseFiles() */
/* */
/*****************************************************************************/
#include "externs.h"
static FILE_NUM last_write_fnum = NO_FILE;
static FILE *last_write_fp = null;
/*****************************************************************************/
/* */
/* OBJECT ReadFromFile(fnum, pos, lnum) */
/* */
/* Read an object from file fnum starting at position pos. */
/* The object may include @Env operators defining its environment, or */
/* not, but in any case ReadFromFile assumes that the correct scope is set. */
/* lnum is the line number of the spot you end up at when you seek to pos. */
/* */
/*****************************************************************************/
OBJECT ReadFromFile(FILE_NUM fnum, long pos, int lnum)
{ OBJECT t, res;
#if DEBUG_ON
int ipos;
#endif
ifdebug(DPP, D, ProfileOn("ReadFromFile"));
ifdebug(DIO, D, ipos = (int) pos);
debug3(DIO, D, "ReadFromFile(%s, %d, %d)", FileName(fnum), ipos, lnum);
LexPush(fnum, (int) pos, DATABASE_FILE, lnum, FALSE);
t = LexGetToken();
if( type(t) != LBR )
{ debug1(DIO, D, " following because type(t) = %s", Image(type(t)));
Error(41, 1, "database index file seems to be out of date",
FATAL, &fpos(t));
}
res = Parse(&t, StartSym, FALSE, FALSE);
if( t != nilobj || type(res) != CLOSURE )
{ debug1(DIO, D, " following because of %s", t!=nilobj ? "t" : "type(res)");
Error(41, 2, "syntax error in database file", FATAL, &fpos(res));
}
LexPop();
debug1(DIO, D, "ReadFromFile returning %s", EchoObject(res));
ifdebug(DPP, D, ProfileOff("ReadFromFile"));
return res;
} /* end ReadFromFile */
/*****************************************************************************/
/* */
/* static Optimize(x, env) */
/* */
/*****************************************************************************/
static void OptimizeParameterList(OBJECT x, OBJECT env);
static void Optimize(OBJECT x, OBJECT env)
{ OBJECT tmp;
if( Down(x) != x )
{ OptimizeParameterList(x, env);
}
tmp = ParameterCheck(x, env);
if( tmp != nilobj )
{ ReplaceNode(tmp, x);
DisposeObject(x);
}
}
/*****************************************************************************/
/* */
/* OptimizeParameterList(x, env) */
/* */
/* Optimize the space required to print the parameters of x by evaluating */
/* them in environment env if this is feasible. */
/* */
/*****************************************************************************/
static void OptimizeParameterList(OBJECT x, OBJECT env)
{ OBJECT y, z, link, t, tlink;
assert( type(x) == CLOSURE, "OptimizeParameterList: type(x) != CLOSURE!" );
if( env == nilobj ) return;
for( link = Down(x); link != x; link = NextDown(link) )
{ Child(y, link);
if( type(y) == PAR )
{ Child(z, Down(y));
if( type(z) == CLOSURE )
{
Optimize(z, env);
}
else if( type(z) == ACAT )
{ for( tlink = Down(z); tlink != z; tlink = NextDown(tlink) )
{ Child(t, Down(tlink));
if( type(t) == CLOSURE )
Optimize(t, env);
}
}
}
}
} /* end OptimizeParameter */
/*@::WriteClosure()@**********************************************************/
/* */
/* static WriteClosure(x, linecount, fnum, env) */
/* */
/* Write closure x to file last_write_fp, without enclosing braces and */
/* without any environment attached. If x happens to be a closure that */
/* was previously read as a @Use clause, write only @LUse and the name. */
/* Increment *linecount by the number of lines written. */
/* The file being written to is fnum; the environment is env (for optim.) */
/* */
/*****************************************************************************/
static void WriteObject(OBJECT x, int outer_prec, int *linecount, FILE_NUM fnum);
static BOOLEAN need_lvis(OBJECT sym) /* true if @LVis needed before sym */
{ return !visible(sym) &&
enclosing(sym) != StartSym &&
type(enclosing(sym)) == LOCAL;
} /* end need_lvis */
static void WriteClosure(OBJECT x, int *linecount, FILE_NUM fnum, OBJECT env)
{ OBJECT y, link, z, sym;
BOOLEAN npar_written, name_printed;
debug2(DIO, D, "[ WriteClosure(%s %s)", Image(type(x)), EchoObject(x));
sym = actual(x);
/* *** if( use_invocation(sym) == x ) *** */
if( use_invocation(sym) != nilobj )
{ StringFPuts(KW_LUSE, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(SymName(sym), last_write_fp);
}
else
{ npar_written = FALSE; name_printed = FALSE;
OptimizeParameterList(x, env);
for( link = Down(x); link != x; link = NextDown(link) )
{ Child(y, link);
if( type(y) == PAR ) switch( type(actual(y)) )
{
case LPAR:
assert( Down(y) != y, "WriteObject/CLOSURE: LPAR!" );
Child(z, Down(y));
WriteObject(z, (int) precedence(sym), linecount, fnum);
StringFPuts(STR_SPACE, last_write_fp);
break;
case NPAR:
assert( Down(y) != y, "WriteObject/CLOSURE: NPAR!" );
Child(z, Down(y));
if( !name_printed )
{ if( need_lvis(sym) )
{ StringFPuts(KW_LVIS, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
}
StringFPuts(SymName(sym), last_write_fp);
name_printed = TRUE;
}
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
if( npar_code(actual(y)) != ' ' )
{ StringFPuts(STR_ESCAPE, last_write_fp);
fprintf(last_write_fp, "%c", (char) npar_code(actual(y)));
}
else
{ StringFPuts(SymName(actual(y)), last_write_fp);
}
StringFPuts(KW_LBR, last_write_fp);
WriteObject(z, NO_PREC, linecount, fnum);
StringFPuts(KW_RBR, last_write_fp);
npar_written = TRUE;
break;
case RPAR:
assert( Down(y) != y, "WriteObject/CLOSURE: RPAR!" );
Child(z, Down(y));
if( !name_printed )
{ if( need_lvis(sym) )
{ StringFPuts(KW_LVIS, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
}
StringFPuts(SymName(sym), last_write_fp);
name_printed = TRUE;
}
if( npar_written )
{ StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
}
else
{ StringFPuts(STR_SPACE, last_write_fp);
}
/* old version: if( filter(sym) != nilobj ) */
if( filter(sym) != nilobj && type(z) == FILTERED ) /* ??? */
{
debug1(DIO, D, " filter(sym) != nilobj, type(z) == %s",
Image(type(z)));
assert( type(z) == FILTERED, "WriteClosure: filter!" );
WriteObject(z, NO_PREC, linecount, fnum);
}
else if( has_body(sym) )
{
StringFPuts(KW_LBR, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
WriteObject(z, NO_PREC, linecount, fnum);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_RBR, last_write_fp);
}
else WriteObject(z, (int) precedence(sym), linecount, fnum);
break;
default:
assert1(FALSE, "WriteClosure:", Image(type(actual(y))));
break;
} /* end switch */
} /* end for each parameter */
if( !name_printed )
{ if( need_lvis(sym) )
{ StringFPuts(KW_LVIS, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
}
StringFPuts(SymName(sym), last_write_fp);
name_printed = TRUE;
}
}
debug0(DIO, D, "] WriteClosure returning");
} /* end WriteClosure */
/*@::WriteObject()@***********************************************************/
/* */
/* static WriteObject(x, outer_prec, linecount, fnum) */
/* */
/* Write object x to file last_write_fp, assuming it is a subobject of an */
/* object and the precedence of operators enclosing it is outer_prec. */
/* Increment *linecount by the number of lines written. */
/* The file being written to is fnum. */
/* */
/*****************************************************************************/
static void WriteObject(OBJECT x, int outer_prec, int *linecount, FILE_NUM fnum)
{ OBJECT link, y, z, gap_obj, sym, env; FULL_CHAR *name; int offset, lnum;
int prec, i, last_prec; BOOLEAN braces_needed;
debug2(DIO, D, "[ WriteObject(%s %s)", Image(type(x)), EchoObject(x));
switch( type(x) )
{
case WORD:
if( StringLength(string(x)) == 0 && outer_prec > ACAT_PREC )
{ StringFPuts(KW_LBR, last_write_fp);
StringFPuts(KW_RBR, last_write_fp);
}
else StringFPuts(string(x), last_write_fp);
break;
case QWORD:
StringFPuts(StringQuotedWord(x), last_write_fp);
break;
case VCAT: prec = VCAT_PREC; goto ETC;
case HCAT: prec = HCAT_PREC; goto ETC;
case ACAT: prec = ACAT_PREC; goto ETC;
ETC:
if( prec < outer_prec ) StringFPuts(KW_LBR, last_write_fp);
last_prec = prec;
for( link = Down(x); link != x; link = NextDown(link) )
{ Child(y, link);
if( type(y) == GAP_OBJ )
{ if( Down(y) == y )
{ assert( type(x) == ACAT, "WriteObject: Down(y) == y!" );
for( i = 1; i <= vspace(y); i++ )
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += vspace(y);
for( i = 1; i <= hspace(y); i++ )
StringFPuts(STR_SPACE, last_write_fp);
last_prec = (vspace(y) + hspace(y) == 0) ? JUXTA_PREC : ACAT_PREC;
}
else
{ Child(gap_obj, Down(y));
if( type(x)==ACAT )
StringFPuts(STR_SPACE, last_write_fp);
else
{ StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
}
StringFPuts(EchoCatOp(type(x), mark(gap(y)), join(gap(y))),
last_write_fp);
if( !is_word(type(gap_obj)) || StringLength(string(gap_obj)) != 0 )
WriteObject(gap_obj, FORCE_PREC, linecount, fnum);
StringFPuts(STR_SPACE, last_write_fp);
last_prec = prec;
}
}
else
{ if( type(x) == ACAT )
{ OBJECT next_gap; int next_prec;
if( NextDown(link) != x )
{ Child(next_gap, NextDown(link));
assert( type(next_gap) == GAP_OBJ, "WriteObject: next_gap!" );
next_prec = (vspace(next_gap) + hspace(next_gap) == 0)
? JUXTA_PREC : ACAT_PREC;
}
else next_prec = prec;
WriteObject(y, find_max(last_prec, next_prec), linecount, fnum);
}
else WriteObject(y, prec, linecount, fnum);
}
}
if( prec < outer_prec ) StringFPuts(KW_RBR, last_write_fp);
break;
case ENV:
if( Down(x) == x )
{
/* environment is empty */
StringFPuts(KW_ENVC, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
}
else if( EnvWriteRetrieve(x, fnum, &offset, &lnum) )
{
/* environment was previously written to this file */
StringFPuts(KW_ENVD, last_write_fp);
fprintf(last_write_fp, " \"%d %d\"", offset, lnum);
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
}
else
{
/* record the position of this environment */
EnvWriteInsert(x, fnum, (int) ftell(last_write_fp), *linecount);
/* write the environment */
if( Down(x) == LastDown(x) )
{
/* envt contains just one closure (with its environment) */
Child(y, Down(x));
assert( type(y) == CLOSURE, "WriteObject: ENV/CLOSURE!" );
StringFPuts(KW_LBR, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_ENVA, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_LBR, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
WriteObject(y, NO_PREC, linecount, fnum);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_RBR, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_RBR, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
}
else
{
/* envt contains a closure (with envt) plus an environment */
Child(env, LastDown(x));
assert( type(env) == ENV, "WriteObject: ENV/ENV!" );
StringFPuts(KW_LBR, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_ENVB, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_LBR, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
WriteObject(env, NO_PREC, linecount, fnum);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_RBR, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
Child(y, Down(x));
assert( type(y) == CLOSURE, "WriteObject: ENV/ENV+CLOSURE!" );
StringFPuts(KW_LBR, last_write_fp);
WriteObject(y, NO_PREC, linecount, fnum);
StringFPuts(KW_RBR, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_RBR, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
}
}
break;
case CLOSURE:
sym = actual(x); env = nilobj;
if( LastDown(x) != x )
{ Child(y, LastDown(x));
if( type(y) == ENV ) env = y;
}
braces_needed = env != nilobj ||
(precedence(sym) <= outer_prec && (has_lpar(sym) || has_rpar(sym))) ||
outer_prec >= JUXTA_PREC;
/* print environment */
if( env != nilobj )
{ StringFPuts(KW_CENV, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_LBR, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
WriteObject(env, NO_PREC, linecount, fnum);
StringFPuts(KW_RBR, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
}
/* print left brace if needed */
if( braces_needed ) StringFPuts(KW_LBR, last_write_fp);
/* print the closure proper */
WriteClosure(x, linecount, fnum, env);
/* print closing brace if needed */
if( braces_needed ) StringFPuts(KW_RBR, last_write_fp);
/* print closing environment if needed */
/* ***
if( env != nilobj )
{ StringFPuts(KW_RBR, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
}
*** */
break;
case CROSS:
case FORCE_CROSS:
Child(y, Down(x));
assert( type(y) == CLOSURE, "WriteObject/CROSS: type(y) != CLOSURE!" );
if( DEFAULT_PREC <= outer_prec ) StringFPuts(KW_LBR, last_write_fp);
if( need_lvis(actual(y)) )
{ StringFPuts(KW_LVIS, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
}
StringFPuts(SymName(actual(y)), last_write_fp);
StringFPuts(type(x) == CROSS ? KW_CROSS : KW_FORCE_CROSS, last_write_fp);
Child(y, LastDown(x));
WriteObject(y, FORCE_PREC, linecount, fnum);
if( DEFAULT_PREC <= outer_prec ) StringFPuts(KW_RBR, last_write_fp);
break;
case NULL_CLOS: name = KW_NULL; goto SETC;
case PAGE_LABEL: name = KW_PAGE_LABEL; goto SETC;
case BEGIN_HEADER: name = KW_BEGIN_HEADER; goto SETC;
case END_HEADER: name = KW_END_HEADER; goto SETC;
case SET_HEADER: name = KW_SET_HEADER; goto SETC;
case CLEAR_HEADER: name = KW_CLEAR_HEADER; goto SETC;
case ONE_COL: name = KW_ONE_COL; goto SETC;
case ONE_ROW: name = KW_ONE_ROW; goto SETC;
case WIDE: name = KW_WIDE; goto SETC;
case HIGH: name = KW_HIGH; goto SETC;
case HSHIFT: name = KW_HSHIFT; goto SETC;
case VSHIFT: name = KW_VSHIFT; goto SETC;
case HSCALE: name = KW_HSCALE; goto SETC;
case VSCALE: name = KW_VSCALE; goto SETC;
case HCOVER: name = KW_HCOVER; goto SETC;
case VCOVER: name = KW_VCOVER; goto SETC;
case SCALE: name = KW_SCALE; goto SETC;
case KERN_SHRINK: name = KW_KERN_SHRINK; goto SETC;
case HCONTRACT: name = KW_HCONTRACT; goto SETC;
case VCONTRACT: name = KW_VCONTRACT; goto SETC;
case HLIMITED: name = KW_HLIMITED; goto SETC;
case VLIMITED: name = KW_VLIMITED; goto SETC;
case HEXPAND: name = KW_HEXPAND; goto SETC;
case VEXPAND: name = KW_VEXPAND; goto SETC;
case START_HVSPAN: name = KW_STARTHVSPAN; goto SETC;
case START_HSPAN: name = KW_STARTHSPAN; goto SETC;
case START_VSPAN: name = KW_STARTVSPAN; goto SETC;
case HSPAN: name = KW_HSPAN; goto SETC;
case VSPAN: name = KW_VSPAN; goto SETC;
case PADJUST: name = KW_PADJUST; goto SETC;
case HADJUST: name = KW_HADJUST; goto SETC;
case VADJUST: name = KW_VADJUST; goto SETC;
case ROTATE: name = KW_ROTATE; goto SETC;
case BACKGROUND: name = KW_BACKGROUND; goto SETC;
case CASE: name = KW_CASE; goto SETC;
case YIELD: name = KW_YIELD; goto SETC;
case BACKEND: name = KW_BACKEND; goto SETC;
case XCHAR: name = KW_XCHAR; goto SETC;
case FONT: name = KW_FONT; goto SETC;
case SPACE: name = KW_SPACE; goto SETC;
case YUNIT: name = KW_YUNIT; goto SETC;
case ZUNIT: name = KW_ZUNIT; goto SETC;
case BREAK: name = KW_BREAK; goto SETC;
case UNDERLINE: name = KW_UNDERLINE; goto SETC;
case COLOUR: name = KW_COLOUR; goto SETC;
case OUTLINE: name = KW_OUTLINE; goto SETC;
case LANGUAGE: name = KW_LANGUAGE; goto SETC;
case CURR_LANG: name = KW_CURR_LANG; goto SETC;
case CURR_FAMILY: name = KW_CURR_FAMILY; goto SETC;
case CURR_FACE: name = KW_CURR_FACE; goto SETC;
case CURR_YUNIT: name = KW_CURR_YUNIT; goto SETC;
case CURR_ZUNIT: name = KW_CURR_ZUNIT; goto SETC;
case COMMON: name = KW_COMMON; goto SETC;
case RUMP: name = KW_RUMP; goto SETC;
case MELD: name = KW_MELD; goto SETC;
case INSERT: name = KW_INSERT; goto SETC;
case ONE_OF: name = KW_ONE_OF; goto SETC;
case NEXT: name = KW_NEXT; goto SETC;
case PLUS: name = KW_PLUS; goto SETC;
case MINUS: name = KW_MINUS; goto SETC;
case OPEN: name = KW_OPEN; goto SETC;
case TAGGED: name = KW_TAGGED; goto SETC;
case INCGRAPHIC: name = KW_INCGRAPHIC; goto SETC;
case SINCGRAPHIC: name = KW_SINCGRAPHIC; goto SETC;
case PLAIN_GRAPHIC: name = KW_PLAINGRAPHIC; goto SETC;
case GRAPHIC: name = KW_GRAPHIC; goto SETC;
case LINK_SOURCE: name = KW_LINK_SOURCE; goto SETC;
case LINK_DEST: name = KW_LINK_DEST; goto SETC;
/* print left parameter, if present */
SETC:
if( DEFAULT_PREC <= outer_prec ) StringFPuts(KW_LBR, last_write_fp);
if( Down(x) != LastDown(x) )
{ Child(y, Down(x));
WriteObject(y, DEFAULT_PREC, linecount, fnum);
StringFPuts(STR_SPACE, last_write_fp);
}
/* print the name of the symbol */
StringFPuts(name, last_write_fp);
/* print right parameter, if present */
if( LastDown(x) != x )
{ Child(y, LastDown(x));
StringFPuts(STR_SPACE, last_write_fp);
if( type(x) == OPEN )
{ StringFPuts(KW_LBR, last_write_fp);
WriteObject(y, NO_PREC, linecount, fnum);
StringFPuts(KW_RBR, last_write_fp);
}
else WriteObject(y, DEFAULT_PREC, linecount, fnum);
}
if( DEFAULT_PREC <= outer_prec ) StringFPuts(KW_RBR, last_write_fp);
break;
case RAW_VERBATIM:
case VERBATIM:
StringFPuts(type(x) == VERBATIM ? KW_VERBATIM : KW_RAWVERBATIM, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(KW_BEGIN, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
Child(y, Down(x));
if( type(y) == WORD )
{
StringFPuts(string(y), last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
}
else
{
assert( type(y) == VCAT, "WriteObject/VERBATIM!" );
for( link = Down(y); link != y; link = NextDown(link) )
{ Child(z, link);
if( type(z) == GAP_OBJ ) continue;
assert( type(z) == WORD, "WriteObject/VERBATIM/WORD!");
StringFPuts(string(z), last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
*linecount += 1;
}
}
StringFPuts(KW_END, last_write_fp);
StringFPuts(STR_SPACE, last_write_fp);
StringFPuts(type(x) == VERBATIM ? KW_VERBATIM : KW_RAWVERBATIM, last_write_fp);
break;
case FILTERED:
FilterWrite(x, last_write_fp, linecount);
break;
default:
assert1(FALSE, "WriteObject:", Image(type(x)));
break;
} /* end switch */
debug0(DIO, D, "] WriteObject returning");
} /* end WriteObject */
/*@::AppendToFile(), CloseFiles()@********************************************/
/* */
/* AppendToFile(x, fnum, pos, lnum) */
/* */
/* Append object x to file fnum, returning its fseek position in *pos. */
/* and its line number in lnum. Record that this file has been updated. */
/* */
/*****************************************************************************/
void AppendToFile(OBJECT x, FILE_NUM fnum, int *pos, int *lnum)
{ FULL_CHAR buff[MAX_BUFF], *str; int linecount;
debug2(DIO, D, "[ AppendToFile( %s, %s )", EchoObject(x), FileName(fnum));
/* open file fnum for writing */
if( last_write_fnum != fnum )
{ if( last_write_fnum != NO_FILE ) fclose(last_write_fp);
str = FileName(fnum);
if( StringLength(str) + StringLength(NEW_DATA_SUFFIX) >= MAX_BUFF )
Error(41, 3, "file name %s%s is too long",
FATAL, PosOfFile(fnum), str, NEW_DATA_SUFFIX);
StringCopy(buff, str); StringCat(buff, NEW_DATA_SUFFIX);
last_write_fp = StringFOpen(buff,
FileTestUpdated(fnum) ? APPEND_TEXT : WRITE_TEXT);
if( last_write_fp == null )
Error(41, 4, "cannot append to database file %s", FATAL, no_fpos, buff);
last_write_fnum = fnum;
(void) fseek(last_write_fp, 0L, SEEK_END);
}
/* write x out and record the fact that fnum has changed */
*pos = (int) ftell(last_write_fp);
StringFPuts(KW_LBR, last_write_fp);
linecount = FileGetLineCount(fnum);
*lnum = linecount + 1;
WriteObject(x, NO_PREC, &linecount, fnum);
StringFPuts(KW_RBR, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
StringFPuts(STR_NEWLINE, last_write_fp);
FileSetUpdated(fnum, linecount + 2);
debug0(DIO, D, "] AppendToFile returning.");
} /* end AppendToFile */
/*****************************************************************************/
/* */
/* CloseFiles() */
/* */
/* Close all files and move new versions to the names of old versions. */
/* */
/*****************************************************************************/
void CloseFiles(void)
{ FILE_NUM fnum; FULL_CHAR oldname[MAX_BUFF], newname[MAX_BUFF];
FILE *fp;
ifdebug(DPP, D, ProfileOn("CloseFiles"));
debug0(DIO, D, "CloseFiles()");
/* close off last file opened by AppendToFile above */
if( last_write_fnum != NO_FILE ) fclose(last_write_fp);
/* get rid of old database files */
for( fnum=FirstFile(SOURCE_FILE); fnum != NO_FILE; fnum = NextFile(fnum) )
{ StringCopy(oldname, FileName(fnum));
StringCat(oldname, DATA_SUFFIX);
debug1(DIO, D, "remove(%s)", oldname);
StringRemove(oldname);
}
/* move any new database files to the old names, if updated */
/* just to avoid confusion: the "new name" means the ".ldx" */
/* temporary file name; the "old name" means the permanent */
/* name, i.e. ".ld". So we have to move the new name to */
/* the old name. */
for( fnum=FirstFile(DATABASE_FILE); fnum != NO_FILE; fnum = NextFile(fnum) )
{ if( FileTestUpdated(fnum) )
{
/* construct new and old file names */
StringCopy(oldname, FileName(fnum));
StringCopy(newname, oldname);
StringCat(newname, NEW_DATA_SUFFIX);
/* guaranteed portable algorithm for changing the name of file */
/* "newname" to "oldname": if "oldname" exists already, then */
/* remove it (avoids removing a non-existent file, which can */
/* be a problem); then rename "newname" to be "oldname" (avoids */
/* overwriting an existing file "oldname", another problem) */
if( (fp = StringFOpen(oldname, READ_TEXT)) != NULL )
{ fclose(fp);
StringRemove(oldname);
}
debug2(DIO, D, "rename(%s, %s)", newname, oldname);
if( StringRename(newname, oldname) != 0 )
Error(41, 5, "rename(%s, %s) failed", INTERN, no_fpos,newname,oldname);
}
}
debug0(DIO, D, "CloseFiles returning.");
ifdebug(DPP, D, ProfileOff("CloseFiles"));
} /* end CloseFiles */