home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-08-11 | 72.4 KB | 1,735 lines |
- Newsgroups: comp.sources.misc
- From: jeff@joyce.cs.su.oz.au (Jeff Kingston)
- Subject: v38i075: lout - Lout document formatting system, v2.05, Part07/35
- Message-ID: <1993Aug8.180734.11487@sparky.sterling.com>
- X-Md4-Signature: c405c80b03104d544f8611d2f2bef066
- Sender: kent@sparky.sterling.com (Kent Landfield)
- Organization: Sterling Software
- Date: Sun, 8 Aug 1993 18:07:34 GMT
- Approved: kent@sparky.sterling.com
-
- Submitted-by: jeff@joyce.cs.su.oz.au (Jeff Kingston)
- Posting-number: Volume 38, Issue 75
- Archive-name: lout/part07
- Environment: UNIX
- Supersedes: lout: Volume 37, Issue 99-128
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # Contents: doc/tr.over/s2 z03.c z36.c
- # Wrapped by kent@sparky on Sun Aug 8 12:29:22 1993
- PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 7 (of 35)."'
- if test -f 'doc/tr.over/s2' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'doc/tr.over/s2'\"
- else
- echo shar: Extracting \"'doc/tr.over/s2'\" \(1739 characters\)
- sed "s/^X//" >'doc/tr.over/s2' <<'END_OF_FILE'
- X@Section
- X @Title { The non-expert's view }
- X@Begin
- X@PP
- XThe non-expert user perceives Lout as text interspersed with special
- Xsymbols, in a style reminiscent of many other batch formatters:
- X@ID @Code {
- X"@Doc @Text @Begin"
- X"@Heading { Standard Integrals }"
- X"@PP"
- X"The following list of standard"
- X"integrals should be memorized:"
- X"@NumberList"
- X"@Item @Eq {int e sup x dx = e sup x}"
- X"@Item @Eq {int dx over"
- X" sqrt { 1 - x sup 2 } = arc sin x}"
- X"@EndList"
- X"@End @Text"
- X}
- XBraces are used for grouping parameters to the features. The symbols
- Xare all taken from two of the standard packages: DocumentLayout, which
- Xprovides headings, paragraphs, lists, footnotes, sections, and so on,
- Xand Eq, which provides mathematical typesetting in a style copied from
- Xthe eqn language of Kernighan and Cherry [{@Ref kernighan75}].
- X@PP
- XAt the time of writing, packages exist for formatting general documents,
- Xtechnical reports, and books, the latter providing an automatic table of
- Xcontents, running page headers and footers, access to bibliographic
- Xdatabases, and a sorted index, among many other features. Specialized
- Xpackages exist for mathematical typesetting, drawing figures, and
- Xformatting Pascal programs.
- X@PP
- XThe advanced features maintain the simple style established above. To
- Xproduce a footnote, for example, one simply types
- X@ID @Code "@FootNote { ... }"
- Xat the appropriate point, and it will be numbered and placed at the
- Xbottom of the page; to add an item to the index,
- X@ID @Code "expert @Index { Expert user }"
- Xis typed, and the right parameter will appear in the index, with a page
- Xnumber, at a place determined by the alphabetical ranking of the left
- Xparameter. No technical knowledge is required to use these features.
- X@End @Section
- END_OF_FILE
- if test 1739 -ne `wc -c <'doc/tr.over/s2'`; then
- echo shar: \"'doc/tr.over/s2'\" unpacked with wrong size!
- fi
- # end of 'doc/tr.over/s2'
- fi
- if test -f 'z03.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'z03.c'\"
- else
- echo shar: Extracting \"'z03.c'\" \(37186 characters\)
- sed "s/^X//" >'z03.c' <<'END_OF_FILE'
- X/*@z03.c:File Service:Declarations, no_fpos@******************************** */
- X/* */
- X/* LOUT: A HIGH-LEVEL LANGUAGE FOR DOCUMENT FORMATTING (VERSION 2.05) */
- X/* COPYRIGHT (C) 1993 Jeffrey H. Kingston */
- X/* */
- X/* Jeffrey H. Kingston (jeff@cs.su.oz.au) */
- X/* Basser Department of Computer Science */
- X/* The University of Sydney 2006 */
- X/* AUSTRALIA */
- X/* */
- X/* This program is free software; you can redistribute it and/or modify */
- X/* it under the terms of the GNU General Public License as published by */
- X/* the Free Software Foundation; either version 1, or (at your option) */
- X/* any later version. */
- X/* */
- X/* This program is distributed in the hope that it will be useful, */
- X/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- X/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- X/* GNU General Public License for more details. */
- X/* */
- X/* You should have received a copy of the GNU General Public License */
- X/* along with this program; if not, write to the Free Software */
- X/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- X/* */
- X/* FILE: z03.c */
- X/* MODULE: File Service */
- X/* EXTERNS: InitFiles(), AddToPath(), DefineFile(), FirstFile(), */
- X/* NextFile(), FileNum(), FileName(), EchoFilePos(), */
- X/* PosOfFile(), OpenFile(), OpenIncGraphicFile(), */
- X/* ReadFromFile(), AppendToFile(), CloseFiles() */
- X/* */
- X/*****************************************************************************/
- X#include "externs"
- X#define MAX_TYPES 10 /* number of file types */
- X#define MAX_PATHS 7 /* number of search paths */
- X#define TAB_MASK 0xFF /* mask forces <= MAX_FILES */
- X
- X#define file_number(x) word_font(x) /* file number of file x */
- X#define updated(x) broken(x) /* TRUE when x is updated */
- X#define path(x) back(x, COL) /* search path for file x */
- X
- Xstatic int file_count; /* total number of files */
- Xstatic OBJECT fvec[MAX_FILES] = { nil }; /* the file table */
- Xstatic OBJECT file_list[MAX_TYPES]; /* files of each type */
- Xstatic OBJECT file_path[MAX_PATHS]; /* the search paths */
- X#if DEBUG_ON
- Xstatic char *file_types[] /* the type names for debug */
- X = { "source", "include", "incgraphic", "database", "index",
- X "font", "prepend", "hyph", "hyphpacked", "encoding" };
- X#endif
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* no_fpos */
- X/* */
- X/* A null file position value. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic FILE_POS no_file_pos = {0, 0, 0};
- XFILE_POS *no_fpos = &no_file_pos;
- X
- X/*****************************************************************************/
- X/* */
- X/* #define hash(str, val) */
- X/* */
- X/* Hash the string str and return its value in val. */
- X/* */
- X/*****************************************************************************/
- X
- X#define hash(str, val) \
- X{ p = str; \
- X val = *p++; \
- X while( *p ) val += *p++; \
- X val = (val * 8) & TAB_MASK; \
- X}
- X
- X/*@::InitFiles(), AddToPath(), DefineFile()@**********************************/
- X/* */
- X/* InitFiles() */
- X/* */
- X/* Initialize this module. */
- X/* */
- X/*****************************************************************************/
- X
- XInitFiles()
- X{ int i;
- X for( i = 0; i < MAX_TYPES; i++ ) file_list[i] = New(ACAT);
- X for( i = 0; i < MAX_PATHS; i++ ) file_path[i] = New(ACAT);
- X fvec[0] = file_list[0]; /* so that no files will be given slot 0 */
- X file_count = 1;
- X} /* end InitFiles */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* AddToPath(fpath, dirname) */
- X/* */
- X/* Add the directory dirname to the end of search path fpath. */
- X/* */
- X/*****************************************************************************/
- X
- XAddToPath(fpath, dirname)
- Xint fpath; FULL_CHAR *dirname;
- X{ OBJECT x;
- X x = MakeWord(WORD, dirname, no_fpos);
- X Link(file_path[fpath], x);
- X} /* end AddToPath */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* FILE_NUM DefineFile(str, suffix, xfpos, ftype, fpath) */
- X/* */
- X/* Declare a file whose name is str plus suffix and whose fpos is xfpos. */
- X/* The file type is ftype, and its search path is fpath. */
- X/* */
- X/*****************************************************************************/
- X
- XFILE_NUM DefineFile(str, suffix, xfpos, ftype, fpath)
- XFULL_CHAR *str, *suffix; FILE_POS *xfpos; int ftype, fpath;
- X{ register FULL_CHAR *p;
- X register int i;
- X assert( ftype < MAX_TYPES, "DefineFile: ftype!" );
- X debug5(DFS, D, "DefineFile(%s, %s,%s, %s, %d)",
- X str, suffix, EchoFilePos(xfpos), file_types[ftype], fpath);
- X if( ftype == SOURCE_FILE && (i = StringLength(str)) >= 3 )
- X {
- X /* check that file name does not end in ".li" or ".ld" */
- X if( StringEqual(&str[i-StringLength(DATA_SUFFIX)], DATA_SUFFIX) )
- X Error(FATAL, xfpos,
- X "database file %s where source file expected", str);
- X if( StringEqual(&str[i-StringLength(INDEX_SUFFIX)], INDEX_SUFFIX) )
- X Error(FATAL, xfpos,
- X "database index file %s where source file expected", str);
- X }
- X if( ++file_count >= MAX_FILES ) Error(FATAL, xfpos, "too many file names");
- X hash(str, i);
- X while( fvec[i] != nil )
- X if( ++i >= MAX_FILES ) i = 0;
- X if( StringLength(str) + StringLength(suffix) >= MAX_LINE )
- X Error(FATAL, no_fpos, "file name %s%s too long", str, suffix);
- X fvec[i] = MakeWordTwo(WORD, str, suffix, xfpos);
- X Link(file_list[ftype], fvec[i]);
- X file_number(fvec[i]) = i;
- X path(fvec[i]) = fpath;
- X debug1(DFS, D, "DefineFile returning %s",
- X i == NO_FILE ? STR_NONE : FileName( (FILE_NUM) i));
- X return (FILE_NUM) i;
- X} /* end DefineFile */
- X
- X
- X/*@::FirstFile(), NextFile(), FileNum()@**************************************/
- X/* */
- X/* FILE_NUM FirstFile(ftype) */
- X/* */
- X/* Returns first file of type ftype, else NO_FILE. */
- X/* */
- X/*****************************************************************************/
- X
- XFILE_NUM FirstFile(ftype)
- Xint ftype;
- X{ FILE_NUM i;
- X OBJECT link, y;
- X debug1(DFS, D, "FirstFile( %s )", file_types[ftype]);
- X link = Down(file_list[ftype]);
- X if( type(link) == ACAT ) i = NO_FILE;
- X else
- X { Child(y, link);
- X i = file_number(y);
- X }
- X debug1(DFS, D, "FirstFile returning %s", i==NO_FILE ? STR_NONE : FileName(i));
- X return i;
- X} /* end FirstFile */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* FILE_NUM NextFile(i) */
- X/* */
- X/* Returns the next file after file i of the type of i, else NO_FILE. */
- X/* */
- X/*****************************************************************************/
- X
- XFILE_NUM NextFile(i)
- XFILE_NUM i;
- X{ OBJECT link, y;
- X debug1(DFS, D, "NextFile( %s )", EchoObject(fvec[i]));
- X link = NextDown(Up(fvec[i]));
- X if( type(link) == ACAT ) i = NO_FILE;
- X else
- X { Child(y, link);
- X i = file_number(y);
- X }
- X debug1(DFS, D, "NextFile returning %s", i==NO_FILE ? STR_NONE : FileName(i));
- X return i;
- X} /* end NextFile */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* FILE_NUM FileNum(str, suffix) */
- X/* */
- X/* Return the number of the file with name str plus suffix, else NO_FILE. */
- X/* */
- X/*****************************************************************************/
- X
- XFILE_NUM FileNum(str, suffix)
- XFULL_CHAR *str, *suffix;
- X{ register FULL_CHAR *p;
- X register int i;
- X FULL_CHAR buff[MAX_LINE];
- X debug2(DFS, D, "FileNum(%s, %s)", str, suffix);
- X hash(str, i);
- X if( StringLength(str) + StringLength(suffix) >= MAX_LINE )
- X Error(FATAL, no_fpos, "file name %s%s too long", str, suffix);
- X StringCopy(buff, str);
- X StringCat(buff, suffix);
- X while( fvec[i] != nil && !StringEqual(string(fvec[i]), buff) )
- X if( ++i >= MAX_FILES ) i = 0;
- X if( fvec[i] == nil ) i = 0;
- X debug1(DFS, D, "FileNum returning %s",
- X i == NO_FILE ? STR_NONE : FileName( (FILE_NUM) i));
- X return (FILE_NUM) i;
- X} /* end FileNum */
- X
- X
- X/*@::FileName(), EchoFilePos(), PosOfFile()@**********************************/
- X/* */
- X/* FULL_CHAR *FileName(fnum) */
- X/* */
- X/* Return the string name of this file. This is as given to DefineFile */
- X/* until OpenFile is called, after which it is the full path name. */
- X/* */
- X/*****************************************************************************/
- X
- XFULL_CHAR *FileName(fnum)
- XFILE_NUM fnum;
- X{ OBJECT x;
- X assert( fnum > 0 && fvec[fnum] != nil, "FileName: fvec[fnum] == nil!" );
- X x = fvec[fnum]; if( Down(x) != x ) Child(x, Down(x));
- X return string(x);
- X} /* end FileName */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* FULL_CHAR *EchoFilePos(pos) */
- X/* */
- X/* Returns a string reporting the value of file position pos. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic FULL_CHAR buff[2][MAX_LINE]; static bp = 1;
- X
- Xstatic append_fpos(pos)
- XFILE_POS *pos;
- X{ OBJECT x;
- X x = fvec[file_num(*pos)];
- X assert( x != nil, "EchoFilePos: fvec[] entry is nil!" );
- X if( file_num(fpos(x)) > 0 )
- X { append_fpos( &fpos(x) );
- X if( StringLength(buff[bp]) + 2 >= MAX_LINE )
- X Error(FATAL,no_fpos,"file position %s... is too long to print", buff[bp]);
- X StringCat(buff[bp], STR_SPACE);
- X StringCat(buff[bp], AsciiToFull("/"));
- X }
- X if( StringLength(buff[bp]) + StringLength(string(x)) + 13 >= MAX_LINE )
- X Error(FATAL, no_fpos, "file position %s... is too long to print", buff[bp]);
- X StringCat(buff[bp], STR_SPACE);
- X StringCat(buff[bp], STR_QUOTE);
- X StringCat(buff[bp], string(x));
- X StringCat(buff[bp], STR_QUOTE);
- X if( line_num(*pos) != 0 )
- X { StringCat(buff[bp], STR_SPACE);
- X StringCat(buff[bp], StringInt(line_num(*pos)));
- X StringCat(buff[bp], AsciiToFull(","));
- X StringCat(buff[bp], StringInt( (int) col_num(*pos)));
- X }
- X} /* end append_fpos */
- X
- XFULL_CHAR *EchoFilePos(pos)
- XFILE_POS *pos;
- X{ bp = (bp + 1) % 2;
- X StringCopy(buff[bp], STR_EMPTY);
- X if( file_num(*pos) > 0 ) append_fpos(pos);
- X return buff[bp];
- X} /* end EchoFilePos */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* FILE_POS *PosOfFile(fnum) */
- X/* */
- X/* Returns a pointer to the file position where file fnum was encountered. */
- X/* */
- X/*****************************************************************************/
- X
- XFILE_POS *PosOfFile(fnum)
- XFILE_NUM fnum;
- X{ OBJECT x = fvec[fnum];
- X assert( x != nil, "PosOfFile: fvec[] entry is nil!" );
- X return &fpos(x);
- X}
- X
- X/*@::SearchPath()@************************************************************/
- X/* */
- X/* static FILE *SearchPath(str, fpath, check_ld, check_lt, full_name, xfpos)*/
- X/* */
- X/* Search the given path for a file whose name is str. If found, open */
- X/* it; return the resulting FILE *. */
- X/* */
- X/* If check_ld is TRUE, it means that the file to be opened is a .li file */
- X/* and OpenFile() is required to check whether the corresponding .ld file */
- X/* is present. If it is, then the search must stop. */
- X/* */
- X/* If check_lt is TRUE, it means that the file to be opened is a source */
- X/* file and OpenFile() is required to check for a .lt suffix version if */
- X/* the file does not open. */
- X/* */
- X/* Also return the full path name in object *full_name if reqd, else nil. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic FILE *SearchPath(str, fpath, check_ld, check_lt, full_name, xfpos)
- XFULL_CHAR *str; OBJECT fpath; BOOLEAN check_ld, check_lt;
- XOBJECT *full_name; FILE_POS *xfpos;
- X{
- X FULL_CHAR buff[MAX_LINE]; OBJECT link, y; FILE *fp;
- X debug4(DFS, DD, "SearchPath(%s, %s, %s, %s, -)", str, EchoObject(fpath),
- X bool(check_ld), bool(check_lt));
- X *full_name = nil;
- X if( StringEqual(str, STR_STDIN) )
- X { fp = stdin;
- X debug0(DFS, DD, " opened stdin");
- X }
- X else if( StringBeginsWith(str, AsciiToFull("/")) )
- X { fp = StringFOpen(str, "r");
- X debug1(DFS, DD, fp==null ? " failed on %s" : " succeeded on %s", str);
- X }
- X else
- X { fp = null;
- X for( link = Down(fpath); fp==null && link != fpath; link = NextDown(link) )
- X { Child(y, link);
- X if( StringLength(string(y)) == 0 )
- X { StringCopy(buff, str);
- X fp = StringFOpen(str, "r");
- X debug1(DFS, DD, fp==null ? " failed on %s" : " succeeded on %s", str);
- X }
- X else
- X { if( StringLength(string(y)) + 1 + StringLength(str) >= MAX_LINE )
- X Error(FATAL, &fpos(y), "file path name %s/%s is too long",
- X string(y), str);
- X StringCopy(buff, string(y));
- X StringCat(buff, AsciiToFull("/"));
- X StringCat(buff, str);
- X fp = StringFOpen(buff, "r");
- X debug1(DFS, DD, fp==null ? " failed on %s" : " succeeded on %s",buff);
- X if( fp != null ) *full_name = MakeWord(WORD, buff, xfpos);
- X }
- X if( fp == null && check_ld )
- X { StringCopy(&buff[StringLength(buff) - StringLength(INDEX_SUFFIX)],
- X DATA_SUFFIX);
- X fp = StringFOpen(buff, "r");
- X debug1(DFS,DD,fp==null ? " failed on %s" : " succeeded on %s", buff);
- X if( fp != null )
- X { fclose(fp);
- X debug0(DFS, D, "SearchPath returning null (adjacent .ld file)");
- X return null;
- X }
- X }
- X if( fp == null && check_lt )
- X { StringCopy(&buff[StringLength(buff)], SOURCE_SUFFIX);
- X fp = StringFOpen(buff, "r");
- X debug1(DFS,DD,fp==null ? " failed on %s" : " succeeded on %s", buff);
- X StringCopy(&buff[StringLength(buff) - StringLength(SOURCE_SUFFIX)], STR_EMPTY);
- X if( fp != null ) *full_name = MakeWord(WORD, buff, xfpos);
- X }
- X }
- X }
- X debug1(DFS, DD, "SearchPath returning (fp %s null)", fp==null ? "==" : "!=");
- X return fp;
- X} /* end SearchPath */
- X
- X
- X/*@::OpenFile(), OpenIncGraphicFile()@****************************************/
- X/* */
- X/* FILE *OpenFile(fnum, check_ld, check_lt) */
- X/* */
- X/* Open for reading the file whose number is fnum. This involves */
- X/* searching for it along its path if not previously opened. */
- X/* */
- X/* If check_ld is TRUE, it means that the file to be opened is a .li file */
- X/* and OpenFile() is required to check whether the corresponding .ld file */
- X/* is present. If it is, then the search must stop. */
- X/* */
- X/* If check_lt is TRUE, it means that the file to be opened is a source */
- X/* file and OpenFile() is required to check for a .lout suffix version */
- X/* if the file does not open without it. */
- X/* */
- X/*****************************************************************************/
- X
- XFILE *OpenFile(fnum, check_ld, check_lt)
- XFILE_NUM fnum; BOOLEAN check_ld, check_lt;
- X{ FILE *fp; OBJECT full_name, y;
- X ifdebug(DPP, D, ProfileOn("OpenFile"));
- X debug2(DFS, D, "OpenFile(%s, %s)", FileName(fnum), bool(check_ld));
- X if( Down(fvec[fnum]) != fvec[fnum] )
- X { Child(y, Down(fvec[fnum]));
- X fp = StringFOpen(string(y), "r");
- X debug1(DFS,DD,fp==null ? " failed on %s" : " succeeded on %s", string(y));
- X }
- X else
- X { fp = SearchPath(string(fvec[fnum]), file_path[path(fvec[fnum])],
- X check_ld, check_lt, &full_name, &fpos(fvec[fnum]));
- X if( full_name != nil ) Link(fvec[fnum], full_name);
- X }
- X ifdebug(DPP, D, ProfileOff("OpenFile"));
- X debug1(DFS, D, "OpenFile returning (fp %s null)", fp==null ? "==" : "!=");
- X return fp;
- X} /* end OpenFile */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* FILE *OpenIncGraphicFile(str, typ, full_name, xfpos) */
- X/* */
- X/* Open for reading the @IncludeGraphic file str; typ is INCGRAPHIC or */
- X/* SINCGRAPHIC. Return the full name in full_name. */
- X/* */
- X/*****************************************************************************/
- X
- XFILE *OpenIncGraphicFile(str, typ, full_name, xfpos)
- XFULL_CHAR *str; unsigned char typ; OBJECT *full_name; FILE_POS *xfpos;
- X{ FILE *fp; int p;
- X debug2(DFS, D, "OpenIncGraphicFile(%s, %s, -)", str, Image(typ));
- X assert( typ == INCGRAPHIC || typ == SINCGRAPHIC, "OpenIncGraphicFile!" );
- X p = (typ == INCGRAPHIC ? INCLUDE_PATH : SYSINCLUDE_PATH);
- X fp = SearchPath(str, file_path[p], FALSE, FALSE, full_name, xfpos);
- X if( *full_name == nil ) *full_name = MakeWord(WORD, str, xfpos);
- X debug2(DFS, D, "OpenIncGraphicFile returning (fp %s null, *full_name = %s)",
- X fp==null ? "==" : "!=", string(*full_name));
- X return fp;
- X} /* end OpenIncGraphicFile */
- X
- X
- X/*@::ReadFromFile()@**********************************************************/
- X/* */
- X/* OBJECT ReadFromFile(fnum, pos, sym) */
- X/* */
- X/* Read an object from file fnum starting at position pos. */
- X/* The object may include @Env operators defining its environment. */
- X/* If sym != nil, sym is the symbol which is to be read in. */
- X/* */
- X/*****************************************************************************/
- X
- XOBJECT ReadFromFile(fnum, pos, sym)
- XFILE_NUM fnum; long pos; OBJECT sym;
- X{ OBJECT t, res; int ipos;
- X ifdebug(DPP, D, ProfileOn("ReadFromFile"));
- X ifdebug(DFS, D, ipos = (int) pos);
- X debug3(DFS, D, "ReadFromFile(%s, %d, %s)", FileName(fnum), ipos,SymName(sym));
- X LexPush(fnum, (int) pos, DATABASE_FILE);
- X SwitchScope(sym);
- X t = LexGetToken();
- X if( type(t) != LBR )
- X { debug1(DFS, D, " following because type(t) = %s", Image(type(t)));
- X Error(FATAL, &fpos(t),"syntax error (missing %s) in database file", KW_LBR);
- X }
- X res = Parse(&t, StartSym, FALSE, FALSE);
- X if( t != nil || type(res) != CLOSURE )
- X { debug1(DFS, D, " following because of %s", t != nil ? "t" : "type(res)");
- X Error(FATAL, &fpos(res), "syntax error in database file");
- X }
- X UnSwitchScope(sym);
- X LexPop();
- X debug1(DFS, D, "ReadFromFile returning %s", EchoObject(res));
- X ifdebug(DPP, D, ProfileOff("ReadFromFile"));
- X return res;
- X} /* end ReadFromFile */
- X
- X
- Xstatic FILE_NUM last_write_fnum = NO_FILE;
- Xstatic FILE *last_write_fp = null;
- X
- X
- X/*@::WriteClosure()@**********************************************************/
- X/* */
- X/* static WriteClosure(x) */
- X/* */
- X/* Write closure x to file last_write_fp, without enclosing braces and */
- X/* without any environment attached. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic BOOLEAN need_lvis(sym) /* true if @LVis needed before sym */
- XOBJECT sym;
- X{ return !visible(sym) &&
- X enclosing(sym) != StartSym &&
- X type(enclosing(sym)) == LOCAL;
- X} /* end need_lvis */
- X
- Xstatic WriteClosure(x)
- XOBJECT x;
- X{ OBJECT y, link, z, sym;
- X BOOLEAN npar_seen, name_printed;
- X static WriteObject();
- X
- X sym = actual(x); npar_seen = FALSE; name_printed = FALSE;
- X for( link = Down(x); link != x; link = NextDown(link) )
- X { Child(y, link);
- X if( type(y) == PAR ) switch( type(actual(y)) )
- X {
- X case LPAR:
- X
- X assert( Down(y) != y, "WriteObject/CLOSURE: LPAR!" );
- X Child(z, Down(y));
- X WriteObject(z, (int) precedence(sym));
- X StringFPuts(STR_SPACE, last_write_fp);
- X break;
- X
- X
- X case NPAR:
- X
- X assert( Down(y) != y, "WriteObject/CLOSURE: NPAR!" );
- X Child(z, Down(y));
- X if( !name_printed )
- X { if( need_lvis(sym) )
- X { StringFPuts(KW_LVIS, last_write_fp);
- X StringFPuts(STR_SPACE, last_write_fp);
- X }
- X StringFPuts(SymName(sym), last_write_fp);
- X name_printed = TRUE;
- X }
- X StringFPuts(STR_NEWLINE, last_write_fp);
- X StringFPuts(STR_SPACE, last_write_fp);
- X StringFPuts(STR_SPACE, last_write_fp);
- X StringFPuts(STR_SPACE, last_write_fp);
- X StringFPuts(SymName(actual(y)), last_write_fp);
- X StringFPuts(STR_SPACE, last_write_fp);
- X StringFPuts(KW_LBR, last_write_fp);
- X StringFPuts(STR_SPACE, last_write_fp);
- X WriteObject(z, NO_PREC);
- X StringFPuts(STR_SPACE, last_write_fp);
- X StringFPuts(KW_RBR, last_write_fp);
- X npar_seen = TRUE;
- X break;
- X
- X
- X case RPAR:
- X
- X assert( Down(y) != y, "WriteObject/CLOSURE: RPAR!" );
- X Child(z, Down(y));
- X if( !name_printed )
- X { if( need_lvis(sym) )
- X { StringFPuts(KW_LVIS, last_write_fp);
- X StringFPuts(STR_SPACE, last_write_fp);
- X }
- X StringFPuts(SymName(sym), last_write_fp);
- X name_printed = TRUE;
- X }
- X StringFPuts(npar_seen ? STR_NEWLINE : STR_SPACE, last_write_fp);
- X if( has_body(sym) )
- X {
- X StringFPuts(KW_LBR, last_write_fp);
- X StringFPuts(STR_SPACE, last_write_fp);
- X WriteObject(z, NO_PREC);
- X StringFPuts(STR_SPACE, last_write_fp);
- X StringFPuts(KW_RBR, last_write_fp);
- X }
- X else WriteObject(z, (int) precedence(sym));
- X break;
- X
- X
- X default:
- X
- X Error(INTERN, &fpos(y), "WriteClosure: %s", Image(type(actual(y))) );
- X break;
- X
- X } /* end switch */
- X } /* end for each parameter */
- X if( !name_printed )
- X { if( need_lvis(sym) )
- X { StringFPuts(KW_LVIS, last_write_fp);
- X StringFPuts(STR_SPACE, last_write_fp);
- X }
- X StringFPuts(SymName(sym), last_write_fp);
- X name_printed = TRUE;
- X }
- X} /* end WriteClosure */
- X
- X
- X/*@::WriteObject()@***********************************************************/
- X/* */
- X/* static WriteObject(x, outer_prec) */
- X/* */
- X/* Write object x to file last_write_fp, assuming it is a subobject of an */
- X/* object and the precedence of operators enclosing it is outer_prec. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic WriteObject(x, outer_prec)
- XOBJECT x; int outer_prec;
- X{ OBJECT link, y, gap_obj, sym, env; FULL_CHAR *name;
- X int prec, i, last_prec; BOOLEAN braces_needed;
- X switch( type(x) )
- X {
- X
- X case WORD:
- X
- X if( StringLength(string(x)) == 0 && outer_prec > ACAT_PREC )
- X { StringFPuts(KW_LBR, last_write_fp);
- X StringFPuts(KW_RBR, last_write_fp);
- X }
- X else StringFPuts(string(x), last_write_fp);
- X break;
- X
- X
- X case QWORD:
- X
- X StringFPuts(StringQuotedWord(x), last_write_fp);
- X break;
- X
- X
- X case VCAT: prec = VCAT_PREC; goto ETC;
- X case HCAT: prec = HCAT_PREC; goto ETC;
- X case ACAT: prec = ACAT_PREC; goto ETC;
- X
- X ETC:
- X if( prec < outer_prec ) StringFPuts(KW_LBR, last_write_fp);
- X last_prec = prec;
- X for( link = Down(x); link != x; link = NextDown(link) )
- X { Child(y, link);
- X if( type(y) == GAP_OBJ )
- X { if( Down(y) == y )
- X { assert( type(x) == ACAT, "WriteObject: Down(y) == y!" );
- X for( i = 1; i <= vspace(y); i++ )
- X StringFPuts(STR_NEWLINE, last_write_fp);
- X for( i = 1; i <= hspace(y); i++ )
- X StringFPuts(STR_SPACE, last_write_fp);
- X last_prec = (vspace(y) + hspace(y) == 0) ? JUXTA_PREC : ACAT_PREC;
- X }
- X else
- X { Child(gap_obj, Down(y));
- X StringFPuts(type(x)==ACAT ? STR_SPACE : STR_NEWLINE, last_write_fp);
- X StringFPuts(EchoCatOp(type(x), mark(gap(y)), join(gap(y))),
- X last_write_fp);
- X if( !is_word(type(gap_obj)) || StringLength(string(gap_obj)) != 0 )
- X WriteObject(gap_obj, FORCE_PREC);
- X StringFPuts(STR_SPACE, last_write_fp);
- X last_prec = prec;
- X }
- X }
- X else
- X { if( type(x) == ACAT )
- X { OBJECT next_gap; int next_prec;
- X if( NextDown(link) != x )
- X { Child(next_gap, NextDown(link));
- X assert( type(next_gap) == GAP_OBJ, "WriteObject: next_gap!" );
- X next_prec = (vspace(next_gap) + hspace(next_gap) == 0)
- X ? JUXTA_PREC : ACAT_PREC;
- X }
- X else next_prec = prec;
- X WriteObject(y, max(last_prec, next_prec));
- X }
- X else WriteObject(y, prec);
- X }
- X }
- X if( prec < outer_prec ) StringFPuts(KW_RBR, last_write_fp);
- X break;
- X
- X
- X case ENV:
- X
- X if( Down(x) == x )
- X { /* do nothing */
- X }
- X else if( Down(x) == LastDown(x) )
- X { Child(y, Down(x));
- X assert( type(y) == CLOSURE, "WriteObject: ENV/CLOSURE!" );
- X assert( LastDown(y) != y, "WriteObject: ENV/LastDown(y)!" );
- X Child(env, LastDown(y));
- X assert( type(env) == ENV, "WriteObject: ENV/env!" );
- X WriteObject(env, NO_PREC);
- X StringFPuts(KW_LBR, last_write_fp);
- X WriteClosure(y);
- X StringFPuts(KW_RBR, last_write_fp);
- X StringFPuts(STR_NEWLINE, last_write_fp);
- X }
- X else
- X { Child(env, LastDown(x));
- X assert( type(env) == ENV, "WriteObject: ENV/ENV!" );
- X WriteObject(env, NO_PREC);
- X Child(y, Down(x));
- X assert( type(y) == CLOSURE, "WriteObject: ENV/ENV+CLOSURE!" );
- X WriteObject(y, NO_PREC);
- X }
- X break;
- X
- X
- X case CLOSURE:
- X
- X sym = actual(x); env = nil;
- X if( LastDown(x) != x )
- X { Child(y, LastDown(x));
- X if( type(y) == ENV ) env = y;
- X }
- X
- X braces_needed = env != nil ||
- X (precedence(sym) <= outer_prec && (has_lpar(sym) || has_rpar(sym)));
- X
- X /* print environment */
- X if( env != nil )
- X { StringFPuts(KW_ENV, last_write_fp);
- X StringFPuts(STR_NEWLINE, last_write_fp);
- X WriteObject(env, NO_PREC);
- X }
- X
- X /* print left brace if needed */
- X if( braces_needed ) StringFPuts(KW_LBR, last_write_fp);
- X
- X /* print the closure proper */
- X WriteClosure(x);
- X
- X /* print closing brace if needed */
- X if( braces_needed ) StringFPuts(KW_RBR, last_write_fp);
- X
- X /* print closing environment if needed */
- X if( env != nil )
- X { StringFPuts(STR_NEWLINE, last_write_fp);
- X StringFPuts(KW_CLOS, last_write_fp);
- X StringFPuts(STR_NEWLINE, last_write_fp);
- X }
- X break;
- X
- X
- X case CROSS:
- X
- X Child(y, Down(x));
- X assert( type(y) == CLOSURE, "WriteObject/CROSS: type(y) != CLOSURE!" );
- X StringFPuts(SymName(actual(y)), last_write_fp);
- X StringFPuts(KW_CROSS, last_write_fp);
- X Child(y, LastDown(x));
- X WriteObject(y, FORCE_PREC);
- X break;
- X
- X
- X case NULL_CLOS: name = KW_NULL; goto SETC;
- X case ONE_COL: name = KW_ONE_COL; goto SETC;
- X case ONE_ROW: name = KW_ONE_ROW; goto SETC;
- X case WIDE: name = KW_WIDE; goto SETC;
- X case HIGH: name = KW_HIGH; goto SETC;
- X case HSCALE: name = KW_HSCALE; goto SETC;
- X case VSCALE: name = KW_VSCALE; goto SETC;
- X case SCALE: name = KW_SCALE; goto SETC;
- X case HCONTRACT: name = KW_HCONTRACT; goto SETC;
- X case VCONTRACT: name = KW_VCONTRACT; goto SETC;
- X case HEXPAND: name = KW_HEXPAND; goto SETC;
- X case VEXPAND: name = KW_VEXPAND; goto SETC;
- X case PADJUST: name = KW_PADJUST; goto SETC;
- X case HADJUST: name = KW_HADJUST; goto SETC;
- X case VADJUST: name = KW_VADJUST; goto SETC;
- X case ROTATE: name = KW_ROTATE; goto SETC;
- X case CASE: name = KW_CASE; goto SETC;
- X case YIELD: name = KW_YIELD; goto SETC;
- X case XCHAR: name = KW_XCHAR; goto SETC;
- X case FONT: name = KW_FONT; goto SETC;
- X case SPACE: name = KW_SPACE; goto SETC;
- X case BREAK: name = KW_BREAK; goto SETC;
- X case NEXT: name = KW_NEXT; goto SETC;
- X case OPEN: name = KW_OPEN; goto SETC;
- X case TAGGED: name = KW_TAGGED; goto SETC;
- X case INCGRAPHIC: name = KW_INCGRAPHIC; goto SETC;
- X case SINCGRAPHIC: name = KW_SINCGRAPHIC; goto SETC;
- X case GRAPHIC: name = KW_GRAPHIC; goto SETC;
- X
- X /* print left parameter, if present */
- X SETC:
- X if( DEFAULT_PREC <= outer_prec ) StringFPuts(KW_LBR, last_write_fp);
- X if( Down(x) != LastDown(x) )
- X { Child(y, Down(x));
- X WriteObject(y, DEFAULT_PREC);
- X StringFPuts(STR_SPACE, last_write_fp);
- X }
- X
- X /* print the name of the symbol */
- X StringFPuts(name, last_write_fp);
- X
- X /* print right parameter, if present */
- X if( LastDown(x) != x )
- X { Child(y, LastDown(x));
- X StringFPuts(STR_SPACE, last_write_fp);
- X if( type(x) == OPEN )
- X { StringFPuts(KW_LBR, last_write_fp);
- X WriteObject(y, NO_PREC);
- X StringFPuts(KW_RBR, last_write_fp);
- X }
- X else WriteObject(y, DEFAULT_PREC);
- X }
- X if( DEFAULT_PREC <= outer_prec )
- X StringFPuts(KW_RBR, last_write_fp);
- X break;
- X
- X
- X default:
- X
- X Error(INTERN, &fpos(x), "WriteObject: type(x) = %s", Image(type(x)));
- X break;
- X
- X } /* end switch */
- X} /* end WriteObject */
- X
- X
- X/*@::AppendToFile(), CloseFiles()@********************************************/
- X/* */
- X/* AppendToFile(x, fnum, pos) */
- X/* */
- X/* Append object x to file fnum, returning its fseek position in *pos. */
- X/* Record the fact that this file has been updated. */
- X/* */
- X/*****************************************************************************/
- X
- XAppendToFile(x, fnum, pos)
- XOBJECT x; FILE_NUM fnum; int *pos;
- X{ FULL_CHAR buff[MAX_LINE], *str;
- X debug2(DFS, D, "AppendToFile( %s, %s )", EchoObject(x), FileName(fnum));
- X
- X /* open file fnum for writing */
- X if( last_write_fnum != fnum )
- X { if( last_write_fnum != NO_FILE ) fclose(last_write_fp);
- X str = FileName(fnum);
- X if( StringLength(str) + StringLength(NEW_DATA_SUFFIX) >= MAX_LINE )
- X Error(FATAL, PosOfFile(fnum), "file name %s%s is too long",
- X str, NEW_DATA_SUFFIX);
- X StringCopy(buff, str); StringCat(buff, NEW_DATA_SUFFIX);
- X last_write_fp = StringFOpen(buff, "a");
- X if( last_write_fp == null ) Error(FATAL, &fpos(fvec[fnum]),
- X "cannot append to database file %s", buff);
- X last_write_fnum = fnum;
- X }
- X
- X /* write x out and record the fact that fnum has changed */
- X *pos = (int) ftell(last_write_fp);
- X StringFPuts(KW_LBR, last_write_fp);
- X WriteObject(x, NO_PREC);
- X StringFPuts(KW_RBR, last_write_fp);
- X StringFPuts(STR_NEWLINE, last_write_fp);
- X StringFPuts(STR_NEWLINE, last_write_fp);
- X updated(fvec[fnum]) = TRUE;
- X debug0(DFS, D, "AppendToFile returning.");
- X} /* end AppendToFile */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* CloseFiles() */
- X/* */
- X/* Close all files and move new versions to the names of old versions. */
- X/* */
- X/*****************************************************************************/
- X
- XCloseFiles()
- X{ FILE_NUM fnum; FULL_CHAR buff[MAX_LINE];
- X ifdebug(DPP, D, ProfileOn("CloseFiles"));
- X debug0(DFS, D, "CloseFiles()");
- X
- X /* close off last file opened by AppendToFile above */
- X if( last_write_fnum != NO_FILE ) fclose(last_write_fp);
- X
- X /* get rid of old database files */
- X for( fnum = FirstFile(SOURCE_FILE); fnum != NO_FILE; fnum = NextFile(fnum) )
- X { StringCopy(buff, FileName(fnum));
- X StringCat(buff, DATA_SUFFIX); StringUnlink(buff);
- X }
- X
- X /* move any new database files to the old names, if updated */
- X for( fnum = FirstFile(DATABASE_FILE); fnum != NO_FILE; fnum = NextFile(fnum) )
- X { if( updated(fvec[fnum]) )
- X { StringCopy(buff, string(fvec[fnum]));
- X StringCat(buff, NEW_DATA_SUFFIX);
- X debug1(DFS, D, "unlink(%s)", string(fvec[fnum]));
- X StringUnlink(string(fvec[fnum])); /* may fail if no old version */
- X debug2(DFS, D, "link(%s, %s)", buff, string(fvec[fnum]));
- X if( StringLink(buff, string(fvec[fnum])) != 0 )
- X Error(INTERN, no_fpos, "link(%s, %s) failed", buff, string(fvec[fnum]));
- X debug1(DFS, D, "unlink(%s)", buff);
- X if( StringUnlink(buff) != 0 ) Error(INTERN, no_fpos, "unlink(%s)", buff);
- X }
- X }
- X debug0(DFS, D, "CloseFiles returning.");
- X ifdebug(DPP, D, ProfileOff("CloseFiles"));
- X} /* end CloseFiles */
- END_OF_FILE
- if test 37186 -ne `wc -c <'z03.c'`; then
- echo shar: \"'z03.c'\" unpacked with wrong size!
- fi
- # end of 'z03.c'
- fi
- if test -f 'z36.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'z36.c'\"
- else
- echo shar: Extracting \"'z36.c'\" \(30891 characters\)
- sed "s/^X//" >'z36.c' <<'END_OF_FILE'
- X/*@z36.c:Hyphenation: Declarations@*******************************************/
- X/* */
- X/* LOUT: A HIGH-LEVEL LANGUAGE FOR DOCUMENT FORMATTING (VERSION 2.05) */
- X/* COPYRIGHT (C) 1993 Jeffrey H. Kingston */
- X/* */
- X/* Jeffrey H. Kingston (jeff@cs.su.oz.au) */
- X/* Basser Department of Computer Science */
- X/* The University of Sydney 2006 */
- X/* AUSTRALIA */
- X/* */
- X/* This program is free software; you can redistribute it and/or modify */
- X/* it under the terms of the GNU General Public License as published by */
- X/* the Free Software Foundation; either version 1, or (at your option) */
- X/* any later version. */
- X/* */
- X/* This program is distributed in the hope that it will be useful, */
- X/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- X/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- X/* GNU General Public License for more details. */
- X/* */
- X/* You should have received a copy of the GNU General Public License */
- X/* along with this program; if not, write to the Free Software */
- X/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
- X/* */
- X/* FILE: z36.c */
- X/* MODULE: Hyphenation */
- X/* EXTERNS: Hyphenate() */
- X/* */
- X/*****************************************************************************/
- X#include "externs"
- X#define MAX_CHAR 256 /* max chars represented in one char */
- X#define TRIE_MAGIC 5361534
- X#define KILL_CLASS 0 /* characters preventing hyphenation */
- X#define PUNCT_CLASS 1 /* characters delimiting hyphenation */
- X
- Xtypedef struct trie_rec
- X{ int magic; /* a magic number to make sure ok */
- X int class_count; /* the number of character classes */
- X unsigned char class[MAX_CHAR]; /* the character classes */
- X short *node_mem; /* the node memory */
- X int node_lim; /* top of node memory */
- X int node_free; /* first free space in node memory */
- X FULL_CHAR *string_mem; /* the string memory */
- X int string_lim; /* top of string memory */
- X int string_first; /* the first (last inserted) string */
- X} *TRIE;
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* static TRIE T */
- X/* */
- X/* The packed hyphenation table, or NULL if not yet read in. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic TRIE T = (TRIE) NULL; /* the compressed hyphenation table */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* ClassConvert(in, out, T) */
- X/* */
- X/* Set out[i] to the character class of in[i] in T, for all i. */
- X/* */
- X/*****************************************************************************/
- X
- X#define ClassConvert(in, out, T) \
- X{ int i; \
- X for( i = 0; in[i] != '\0'; i++ ) \
- X if( T->class[in[i]] != 0 ) out[i] = T->class[in[i]]; \
- X else Error(INTERN, no_fpos, "hyph: \"%s\" has illegal class", in); \
- X out[i] = '\0'; \
- X} /* end ClassConvert */
- X
- X
- X/*@::findrep(), TrieRetrieve(), ShowRate()@***********************************/
- X/* */
- X/* findrep(i, T) Returns one character whose class in T is i. */
- X/* */
- X/*****************************************************************************/
- X#if DEBUG_ON
- X
- Xstatic FULL_CHAR findrep(i, T)
- Xint i; TRIE T;
- X{ int ch;
- X for( ch = 0; ch < MAX_CHAR; ch++ )
- X if( T->class[ch] == i ) return (FULL_CHAR) ch;
- X Error(INTERN, no_fpos, "hyph DoTriePrint: findrep failed");
- X} /* end findrep */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* static FULL_CHAR *TrieRetrieve(key, T) */
- X/* */
- X/* Retrieve the value associated with key in T, or NULL if not present. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic FULL_CHAR *TrieRetrieve(key, T)
- XFULL_CHAR *key; TRIE T;
- X{ FULL_CHAR str[MAX_LINE]; int i, curr_node, next_node, pos;
- X debug1(DHY, DD, "TrieRetrieve(%s, T)", key);
- X ClassConvert(key, str, T);
- X
- X /* invariant: curr_node is an existing node of T with prefix str[0..i-1] */
- X curr_node = i = 0;
- X for(;;)
- X {
- X /* if next_node is 0, the string was never inserted */
- X next_node = T->node_mem[curr_node + str[i]];
- X if( next_node == 0 ) return (FULL_CHAR *) NULL;
- X
- X /* if next_node < 0 it represents an offset into the string memory */
- X if( next_node < 0 )
- X { pos = - next_node;
- X if( str[i] != '\0' )
- X { do
- X { if( str[++i] != T->string_mem[pos++] ) return (FULL_CHAR *) NULL;
- X } while( str[i] != '\0' );
- X }
- X return &(T->string_mem[pos]);
- X }
- X
- X /* otherwise next_node is the trie node to be searched next */
- X curr_node = 2*next_node; i++;
- X }
- X} /* end TrieRetrieve */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* static ShowRate(key, start, stop, rate, fp) */
- X/* */
- X/* Debug print of key[] and rate[] on file fp. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic ShowRate(key, start, stop, rate, fp)
- XFULL_CHAR *key; int start, stop; FULL_CHAR *rate; FILE *fp;
- X{ int i;
- X fprintf(fp, "key: ");
- X for( i = start; i < stop; i++ ) fprintf(fp, " %c", key[i]);
- X fprintf(fp, "\nrate:");
- X for( i = 0; rate[i] != '\0'; i++ ) fprintf(fp, " %c", rate[i]);
- X fprintf(fp, "\n");
- X} /* end ShowRate */
- X
- X
- X/*@::DoTriePrint(), TriePrint()@**********************************************/
- X/* */
- X/* static DoTriePrint(T, node, len, fp) */
- X/* */
- X/* Print on file fp the subset of the entries of trie T stored in node and */
- X/* its descendants. The node has prefix prefix[0..len-1]. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic FULL_CHAR prefix[MAX_LINE];
- X
- Xstatic DoTriePrint(T, node, len, fp)
- XTRIE T; int node, len; FILE *fp;
- X{ int i, next_node, pos;
- X for( i = 0; i < T->class_count; i++ )
- X {
- X /* if next_node < 0, have string to print */
- X next_node = T->node_mem[node + i];
- X if( next_node < 0 )
- X {
- X prefix[len] = '\0';
- X fprintf(fp, "%s", prefix);
- X pos = - next_node;
- X if( i != 0 )
- X {
- X fprintf(fp, "%c", findrep(i, T));
- X while( T->string_mem[pos] != '\0' )
- X { fprintf(fp, "%c", findrep(T->string_mem[pos], T));
- X pos++;
- X }
- X pos++;
- X }
- X fprintf(fp, " %s\n", &(T->string_mem[pos]));
- X }
- X
- X /* else if next_node > 0 have a child node to explore */
- X else if( next_node > 0 )
- X { assert( i > 0, "DoTriePrint: i == 0!" );
- X prefix[len] = findrep(i, T);
- X prefix[len+1] = '\0';
- X DoTriePrint(T, 2*next_node, len+1, fp);
- X }
- X }
- X} /* end DoTriePrint */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* static TriePrint(T, fp) */
- X/* */
- X/* Print trie T on file fp. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic TriePrint(T, fp)
- XTRIE T; FILE *fp;
- X{ int i, j, ch;
- X assert( T-> magic == TRIE_MAGIC, "TriePrint: magic!" );
- X fprintf(fp, "Classes:");
- X for( i = 1; i < T->class_count; i++ )
- X { fprintf(fp, " ");
- X for( ch = 0; ch < MAX_CHAR; ch++ )
- X if( T->class[ch] == i ) fprintf(fp, "%c", ch);
- X }
- X fprintf(fp, "\n");
- X fprintf(fp, "Node space: %d capacity, %d used\n", T->node_lim, T->node_free);
- X fprintf(fp, "String space: %d capacity, %d used\n", T->string_lim,
- X T->string_lim - T->string_first);
- X prefix[0] = '\0';
- X DoTriePrint(T, 0, 0, fp);
- X} /* end TriePrint */
- X#endif
- X
- X
- X/*@::NewTrie(), ClassConvert(), NewTrieString(), NewTrieNode()@***************/
- X/* */
- X/* static TRIE NewTrie(node_lim, string_lim) */
- X/* */
- X/* Initialize a new trie with the this much space for nodes and strings. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic TRIE NewTrie(node_lim, string_lim)
- Xunsigned node_lim, string_lim;
- X{ TRIE T; int i; char *malloc();
- X debug2(DHY, D, "NewTrie(%d, %d)", node_lim, string_lim);
- X T = (TRIE) malloc( sizeof(struct trie_rec)
- X + node_lim*sizeof(short) + string_lim*sizeof(char));
- X if( T == (TRIE) NULL ) Error(FATAL, no_fpos,
- X "run out of memory while constructing hyphenation table");
- X T->magic = TRIE_MAGIC; T->class_count = 1;
- X for( i = 0; i < MAX_CHAR; i++ ) T->class[i] = 0;
- X T->node_mem = (short *) ( (char *) T + sizeof(struct trie_rec));
- X T->node_lim = node_lim; T->node_free = 0;
- X T->string_mem = (FULL_CHAR *) &(T->node_mem[node_lim]);
- X T->string_lim = T->string_first = string_lim;
- X debug0(DHY, D, "NewTrie returning.");
- X return T;
- X} /* end NewTrie */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* static short NewTrieString(str, T) */
- X/* */
- X/* Copy a new string into T, and return its offset in string_mem; */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic short NewTrieString(str, T)
- XFULL_CHAR *str; TRIE T;
- X{ short res = T->string_first - StringLength(str) - 1;
- X if( res < 0 ) Error(INTERN, no_fpos, "hyph: trie string limit exceeded");
- X T->string_first = res; StringCopy(&(T->string_mem[res]), str);
- X return res;
- X} /* end NewTrieString */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* ststic int NewTrieNode(T) */
- X/* */
- X/* Allocate a new empty trie node in T, and return its offset in node_mem. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic int NewTrieNode(T)
- XTRIE T;
- X{ int i; int res;
- X if( T->node_free + T->class_count > T->node_lim )
- X Error(INTERN, no_fpos, "hyph: trie node limit exceeded");
- X res = T->node_free; T->node_free += T->class_count;
- X for( i = res; i < T->node_free; i++ ) T->node_mem[i] = 0;
- X return res;
- X} /* end NewTrieNode */
- X
- X
- X/*@::AddClassToTrie(), TrieInsert()@******************************************/
- X/* */
- X/* static AddClassToTrie(str, T) */
- X/* */
- X/* Add a new character class, whose members are the characters of str, to */
- X/* trie T. This cannot occur after the first insertion. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic AddClassToTrie(str, T)
- XFULL_CHAR *str; TRIE T;
- X{ int i;
- X if( T->string_first != T-> string_lim )
- X Error(INTERN, no_fpos, "hyph AddClassToTrie after first insertion!");
- X for( i = 0; str[i] != '\0'; i++ )
- X if( T->class[str[i]] == 0 ) T->class[str[i]] = T->class_count;
- X else Error(INTERN,no_fpos, "hyph: class of %c may not be changed!", str[i]);
- X T->class_count++;
- X} /* end AddClassToTrie */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* static TrieInsert(key, value, T) */
- X/* */
- X/* Insert a new key and value into trie T. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic TrieInsert(key, value, T)
- XFULL_CHAR *key, *value; TRIE T;
- X{ FULL_CHAR str[MAX_LINE]; int i, curr_node, next_node, pos, ch;
- X debug2(DHY, D, "TrieInsert(%s, %s, T)", key, value);
- X
- X /* if first insertion, add one node after making sure class_count is even */
- X if( T->node_free == 0 )
- X { T->class_count = 2 * ceiling(T->class_count, 2);
- X ch = NewTrieNode(T);
- X }
- X
- X /* invariant: curr_node is an existing node of T with prefix str[0..i-1] */
- X ClassConvert(key, str, T);
- X curr_node = i = 0;
- X for(;;)
- X {
- X /* if str is ended, add value only to string memory */
- X if( str[i] == '\0' )
- X { if( T->node_mem[curr_node] != 0 )
- X Error(INTERN, no_fpos, "hyph string %s already inserted", key);
- X else T->node_mem[curr_node] = - NewTrieString(value, T);
- X debug0(DHY, D, "TrieInsert returning (empty suffix).");
- X return;
- X }
- X
- X /* if next position is unoccupied, store remainder of str and value */
- X next_node = T->node_mem[curr_node + str[i]];
- X if( next_node == 0 )
- X { ch = NewTrieString(value, T);
- X T->node_mem[curr_node + str[i]] = - NewTrieString(&str[i+1], T);
- X debug0(DHY, D, "TrieInsert returning (non-empty suffix).");
- X return;
- X }
- X
- X /* if next position is occupied by a non-empty string, move that */
- X /* string down one level and replace it by a trie node */
- X if( next_node < 0 )
- X { pos = - next_node;
- X ch = T->string_mem[pos];
- X if( T->string_first == pos ) T->string_first++;
- X T->node_mem[curr_node + str[i]] = next_node = NewTrieNode(T)/2;
- X T->node_mem[2*next_node + ch] = -(pos+1);
- X }
- X
- X /* now next is the offset of the next node to be searched */
- X curr_node = 2*next_node; i++;
- X }
- X} /* end TrieInsert */
- X
- X
- X/*@::BeGetChar(), BePutChar(), BeGetShort(), BePutShort(), etc.@**************/
- X/* */
- X/* BeGetChar(fp, pv) */
- X/* BePutChar(fp, v) */
- X/* BeGetShort(fp, pv) */
- X/* BePutShort(fp, v) */
- X/* BeGetInt(fp, pv) */
- X/* BePutInt(fp, v) */
- X/* */
- X/* Get char, short, or int pv from file fp, and put char, short, or int */
- X/* onto file fp. These routines are designed so that the file can be */
- X/* written or read safely by big-endian and little-endian architectures; */
- X/* this is accomplished by reading and writing one byte at a time to and */
- X/* from a big-endian format file. All return 0 on success, -1 on failure. */
- X/* Thanks to David W. Sanderson for this code. */
- X/* */
- X/*****************************************************************************/
- X
- X#define BeGetChar(fp, pv) ( (c = getc(fp)) == EOF ? -1 : (*pv = c & 0xFF, 0) )
- X#define BePutChar(fp, v) ( putc( (char) (v & 0xFF), fp), 0 )
- X
- X#define BeGetShort(fp, pv) \
- X( (c = getc(fp)) == EOF ? -1 : \
- X ( *pv = (c & 0xFF) << 8, \
- X (c = getc(fp)) == EOF ? -1 : (*pv |= c & 0xFF, 0) \
- X ) \
- X)
- X
- X#define BePutShort(fp, v) \
- X( putc((v >> 8) & 0xFF, fp), putc(v & 0xFF, fp), 0 )
- X
- Xint BeGetInt(fp, pv)
- XFILE *fp; int *pv;
- X{ int c;
- X if ((c = getc(fp)) == EOF) return -1;
- X *pv = (c & 0xFF) << 24;
- X if ((c = getc(fp)) == EOF) return -1;
- X *pv |= (c & 0xFF) << 16;
- X if ((c = getc(fp)) == EOF) return -1;
- X *pv |= (c & 0xFF) << 8;
- X if ((c = getc(fp)) == EOF) return -1;
- X *pv |= c & 0xFF;
- X return 0;
- X}
- X
- Xint BePutInt(fp, v)
- XFILE *fp; int v;
- X{
- X putc((v >> 24) & 0xFF, fp);
- X putc((v >> 16) & 0xFF, fp);
- X putc((v >> 8) & 0xFF, fp);
- X putc(v & 0xFF, fp);
- X return 0;
- X}
- X
- X
- X/*@::CompressTrie(), TrieRead(), AccumulateRating()@**************************/
- X/* */
- X/* static CompressTrie(T) */
- X/* */
- X/* Compress trie T and return its length in characters. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic CompressTrie(T)
- XTRIE T;
- X{ FULL_CHAR *p, *q; int len, i;
- X debug0(DHY, D, "CompressTrie(T), T =");
- X ifdebug(DHY, DD, TriePrint(T, stderr));
- X T->node_lim = T->node_free;
- X for( i = 0; i < T->node_lim; i++ )
- X if( T->node_mem[i] < 0 )
- X T->node_mem[i] = - ( -T->node_mem[i] - T->string_first);
- X p = (FULL_CHAR *) &(T->node_mem[T->node_free]);
- X q = &(T->string_mem[T->string_first]);
- X len = T->string_lim - T->string_first;
- X for( i = 0; i < len; i++ ) *p++ = *q++;
- X T->string_mem = (FULL_CHAR *) &(T->node_mem[T->node_lim]);
- X T->string_first = 0;
- X T->string_lim = len;
- X len = sizeof(struct trie_rec) + T->node_lim * sizeof(short)
- X + T->string_lim * sizeof(FULL_CHAR);
- X debug1(DHY, D, "CompressTrie returning; len = %d, T =", len);
- X ifdebug(DHY, DD, TriePrint(T, stderr));
- X} /* end CompressTrie */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* static TRIE TrieRead() */
- X/* */
- X/* Read in a packed trie if possible, otherwise pack an unpacked one. */
- X/* */
- X/*****************************************************************************/
- X
- Xstatic TRIE TrieRead()
- X{ TRIE T; FILE_NUM unpacked_fnum, packed_fnum;
- X FILE *unpacked_fp, *packed_fp; unsigned len; int prev, i, j, c;
- X char *malloc();
- X debug0(DHY, D, "TrieRead()");
- X
- X /* open file, using name stored in file handler */
- X packed_fnum = FirstFile(HYPH_PACKED_FILE);
- X assert( packed_fnum != NO_FILE, "TrieRead: packed_fnum!" );
- X packed_fp = OpenFile(packed_fnum, FALSE, FALSE);
- X if( packed_fp == NULL )
- X {
- X /* no packed file, so open unpacked one instead */
- X FULL_CHAR str[MAX_LINE], key[MAX_LINE], value[MAX_LINE],
- X buff[MAX_LINE+10];
- X unpacked_fnum = FirstFile(HYPH_FILE);
- X assert( unpacked_fnum != NO_FILE, "TrieRead: unpacked unpacked_fnum!" );
- X unpacked_fp = OpenFile(unpacked_fnum, FALSE, FALSE);
- X if( unpacked_fp == NULL )
- X { Error(WARN, no_fpos, "cannot open hyphenation file %s",
- X FileName(unpacked_fnum));
- X return (TRIE) NULL;
- X }
- X
- X /* read in unpacked hyphenation trie from unpacked_fp and compress it */
- X T = NewTrie( (unsigned) 60000, (unsigned) 32767);
- X while( StringFGets(str, MAX_LINE, unpacked_fp) != NULL &&
- X str[0] != CH_NEWLINE )
- X { str[StringLength(str)-1] = '\0';
- X debug1(DHY, D, "adding class %s", str);
- X AddClassToTrie(str, T);
- X }
- X while( StringFGets(str, MAX_LINE, unpacked_fp) != NULL &&
- X str[0] != CH_NEWLINE )
- X { prev = CH_ZERO; j = 0;
- X for( i = 0; str[i] != CH_NEWLINE && str[i] != '\0'; i++ )
- X { if( decimaldigit(str[i]) ) prev = str[i];
- X else key[j] = str[i], value[j++] = prev, prev = CH_ZERO;
- X }
- X key[j] = '\0'; value[j] = prev; value[j+1] = '\0';
- X TrieInsert(key, value, T);
- X }
- X fclose(unpacked_fp);
- X CompressTrie(T);
- X
- X /* write the compressed trie out to the packed file */
- X StringCopy(buff, FileName(unpacked_fnum));
- X StringCat(buff, HYPH_SUFFIX);
- X packed_fp = StringFOpen(buff, "w");
- X if( packed_fp == NULL ) Error(FATAL, no_fpos,
- X "cannot write to hyphenation file %s", buff);
- X BePutInt(packed_fp, T->magic);
- X BePutInt(packed_fp, T->class_count);
- X for( i = 0; i < MAX_CHAR; i++ ) BePutChar(packed_fp, T->class[i]);
- X BePutInt(packed_fp, 0); /* placeholder for node_mem */
- X BePutInt(packed_fp, T->node_lim);
- X BePutInt(packed_fp, T->node_free);
- X BePutInt(packed_fp, 0); /* placeholder for string_mem */
- X BePutInt(packed_fp, T->string_lim);
- X BePutInt(packed_fp, T->string_first);
- X for( i = 0; i < T->node_free; i++ ) BePutShort(packed_fp, T->node_mem[i]);
- X for( i = 0; i < T->string_lim; i++) BePutChar(packed_fp, T->string_mem[i]);
- X fclose(packed_fp);
- X
- X /* now try again to open packed_fnum, the file just written */
- X packed_fp = OpenFile(packed_fnum, FALSE, FALSE);
- X if( packed_fp == NULL ) Error(FATAL, no_fpos,
- X "cannot open hyphenation file %s", FileName(packed_fnum));
- X }
- X
- X /* now packed hyphenation file is open, read it in */
- X fseek(packed_fp,0L,2); len = (unsigned) ftell(packed_fp); rewind(packed_fp);
- X T = (TRIE) malloc(len);
- X if( T == (TRIE) NULL ) Error(FATAL, no_fpos,
- X "run out of memory while reading hyphenation table");
- X if( BeGetInt(packed_fp, &T->magic) != 0 ) Error(FATAL, no_fpos,
- X "error on read from packed hyphenation file %s", FileName(packed_fnum));
- X if( T->magic != TRIE_MAGIC ) Error(FATAL, no_fpos,
- X "bad magic number in hyphenation file %s", FileName(packed_fnum));
- X BeGetInt(packed_fp, &T->class_count);
- X for( i = 0; i < MAX_CHAR; i++ ) BeGetChar(packed_fp, &T->class[i]);
- X BeGetInt(packed_fp, &i); /* placeholder for node_mem */
- X BeGetInt(packed_fp, &T->node_lim);
- X BeGetInt(packed_fp, &T->node_free);
- X BeGetInt(packed_fp, &i); /* placeholder for string_mem */
- X BeGetInt(packed_fp, &T->string_lim);
- X BeGetInt(packed_fp, &T->string_first);
- X T->node_mem = (short *) ( (char *) T + sizeof(struct trie_rec) );
- X T->string_mem = (FULL_CHAR *) &(T->node_mem[T->node_lim]);
- X for( i = 0; i < T->node_free; i++ ) BeGetShort(packed_fp, &T->node_mem[i]);
- X for( i = 0; i < T->string_lim; i++ ) BeGetChar(packed_fp, &T->string_mem[i]);
- X
- X /* debug and exit */
- X debug0(DHY, D, "TrieRead returning, T =");
- X ifdebug(DHY, DD, TriePrint(T, stderr));
- X return T;
- X} /* end TrieRead */
- X
- X
- X/*****************************************************************************/
- X/* */
- X/* AccumulateRating(x, y) */
- X/* */
- X/* Accumulate the hyphenation rating string x into y. */
- X/* */
- X/*****************************************************************************/
- X
- X#define AccumulateRating(x, y) \
- X{ FULL_CHAR *p = x, *q = y; \
- X while( *p ) \
- X { if( *p > *q ) *q = *p; \
- X p++, q++; \
- X } \
- X} /* end AccumulateRating */
- X
- X
- X/*@::Hyphenate@***************************************************************/
- X/* */
- X/* OBJECT Hyphenate(x) */
- X/* */
- X/* Hyphenate ACAT object x, returning the hyphenated result. */
- X/* */
- X/*****************************************************************************/
- X
- XOBJECT Hyphenate(x)
- XOBJECT x;
- X{ OBJECT link, y, z, next_link;
- X FULL_CHAR str[MAX_LINE+2], rate[MAX_LINE+3], *class, *key, *ss, *s, *p, *rem;
- X int start, stop, i, curr_node, next_node, pos;
- X BOOLEAN hyphenated; static ShowRate();
- X static BOOLEAN tried_file = FALSE;
- X assert( type(x) == ACAT, "Hyphenate: type(x) != ACAT!" );
- X debug1(DHY, DD, "Hyphenate(%s)", EchoObject(x));
- X
- X /* if no trie is present, try to get it from a file */
- X if( T == (TRIE) NULL )
- X { if( !tried_file ) T = TrieRead();
- X tried_file = TRUE;
- X if( T == (TRIE) NULL )
- X { debug0(DHY, DD, "Hyphenate returning (no trie).");
- X return x;
- X }
- X }
- X
- X /* for each word y of x, try to hyphenate it */
- X for( link = Down(x); link != x; link = NextDown(link) )
- X { Child(y, link);
- X if( !is_word(type(y)) ) continue;
- X debug1(DHY, DD, "Hyphenate() examining %s", EchoObject(y));
- X
- X /* start := index of first letter of y, stop := index following last */
- X key = string(y); class = T->class;
- X for( start = 0; class[key[start]] == PUNCT_CLASS; start++ );
- X for( stop = start; class[key[stop]] > PUNCT_CLASS; stop++ );
- X
- X /* if a - ended the run, hyphenate there only */
- X if( key[stop] == CH_HYPHEN )
- X { next_link = NextDown(link);
- X z = MakeWord(WORD, &key[stop+1], &fpos(y));
- X word_font(z) = word_font(y);
- X FontWordSize(z);
- X Link(NextDown(link), z);
- X z = New(GAP_OBJ);
- X SetGap(gap(z), FALSE, TRUE, FIXED_UNIT, HYPH_MODE, 0);
- X Link(NextDown(link), z);
- X Link(z, MakeWord(WORD, STR_GAP_ZERO_HYPH, &fpos(y)));
- X key[stop + 1] = '\0';
- X FontWordSize(y);
- X link = PrevDown(next_link);
- X continue;
- X }
- X
- X /* do not hyphenate if less than 5 letters, or a kill char is nearby */
- X if( stop - start < 5 ) continue;
- X if( key[stop] != '\0' && class[key[stop]] == KILL_CLASS ) continue;
- X
- X /* let str[] be the converted substring, let rate[] be all CH_ZERO */
- X str[0] = PUNCT_CLASS; rate[0] = CH_ZERO;
- X for( i = 0; i < stop - start; i++ )
- X { str[i+1] = class[key[start + i]];
- X rate[i+1] = CH_ZERO;
- X }
- X str[i+1] = PUNCT_CLASS; rate[i+1] = CH_ZERO;
- X str[i+2] = '\0'; rate[i+2] = CH_ZERO;
- X rate[i+3] = '\0';
- X ifdebug(DHY, DD, ShowRate(key, start, stop, rate, stderr));
- X
- X /* for each suffix of str[], accumulate patterns matching its prefixes */
- X ss = str;
- X do
- X {
- X ifdebug(DHY, DD,
- X fprintf(stderr, "trying suffix \"");
- X for( p = ss; *p != 0; p++ ) fprintf(stderr, "%c", findrep(*p, T));
- X fprintf(stderr, "\"\n");
- X );
- X
- X /* accumulate all prefixes of ss */
- X curr_node = 0; s = ss;
- X for(;;)
- X {
- X /* if curr_node has empty string, that is one prefix */
- X pos = T->node_mem[curr_node];
- X if( pos < 0 )
- X { AccumulateRating(&T->string_mem[- pos], rate+(ss-str));
- X debug1(DHY, DD, " found %s", &(T->string_mem[- pos]));
- X }
- X
- X /* if ss is finished, no other prefixes are possible */
- X if( *s == '\0' ) break;
- X
- X /* determine next_node and break if empty */
- X next_node = T->node_mem[curr_node + *s];
- X if( next_node == 0 ) break;
- X
- X /* if next_node is a string, check whether it is a prefix of ss */
- X if( next_node < 0 )
- X { rem = &(T->string_mem[-next_node]);
- X do
- X { if( *rem == '\0' )
- X { AccumulateRating(rem+1, rate+(ss-str));
- X debug1(DHY, DD, " found %s", rem+1);
- X break;
- X }
- X } while( *++s == *rem++ );
- X break;
- X }
- X
- X /* otherwise go on to the next trie node */
- X curr_node = 2*next_node; s++;
- X }
- X } while( *(++ss + 1) != PUNCT_CLASS );
- X ifdebug(DHY, DD, ShowRate(key, start, stop, rate, stderr));
- X
- X /* now rate[] has accumulated ratings; use it to perform hyphenations */
- X hyphenated = FALSE;
- X next_link = NextDown(link);
- X for( i = stop - start - 1; i >= 3; i-- )
- X {
- X /* hyphenate at i if rate[i] is odd */
- X if( is_odd(rate[i]) )
- X { z = MakeWord(WORD, &key[start+i-1], &fpos(y));
- X word_font(z) = word_font(y);
- X FontWordSize(z);
- X Link(NextDown(link), z);
- X z = New(GAP_OBJ);
- X SetGap(gap(z), FALSE, TRUE, FIXED_UNIT, HYPH_MODE, 0);
- X Link(NextDown(link), z);
- X Link(z, MakeWord(WORD, STR_GAP_ZERO_HYPH, &fpos(y)));
- X key[start + i - 1] = '\0';
- X hyphenated = TRUE;
- X }
- X }
- X if( hyphenated )
- X { FontWordSize(y);
- X link = PrevDown(next_link);
- X }
- X
- X } /* end for each word */
- X
- X debug1(DHY, DD, "Hyphenate returning %s", EchoObject(x));
- X return x;
- X} /* end Hyphenate */
- END_OF_FILE
- if test 30891 -ne `wc -c <'z36.c'`; then
- echo shar: \"'z36.c'\" unpacked with wrong size!
- fi
- # end of 'z36.c'
- fi
- echo shar: End of archive 7 \(of 35\).
- cp /dev/null ark7isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 35 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
- exit 0 # Just in case...
-