DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T e

⟦3617d99a1⟧ TextFile

    Length: 36212 (0x8d74)
    Types: TextFile
    Names: »exf.c«

Derivation

└─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12
    └─⟦af5ba6c8e⟧ »unix3.0/DVIWARE.tar.Z« 
        └─⟦ca79c7339⟧ 
            └─⟦this⟧ »DVIware/laser-setters/dvi-to-ps/TeXPS/lib/exf.c« 

TextFile

/* Copyright 1988 Stephan v. Bechtolsheim */

/* This file is part of the TeXPS Software Package.

The TeXPS Software Package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.  Refer to the TeXPS Software Package
General Public License for full details.

Everyone is granted permission to copy, modify and redistribute
the TeXPS Software Package, but only under the conditions described in the
TeXPS Software Package General Public License.   A copy of this license is
supposed to have been given to you along with TeXPS Software Package so you
can know your rights and responsibilities.  It should be in a
file named CopyrightLong.  Among other things, the copyright notice
and this notice must be preserved on all copies.  */

/* Extended file handling procedures. */

#include <stdio.h>
#if SYS_V == 1
#include <string.h>
#else
#include <strings.h>
#endif

#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <ctype.h>
#include "extfil.h"

#define TRUE 1
#define FALSE 0

/* The following definitions are for loading some of the elements of EX_FILES.
   These constants are only used by this program here, never by the user directly. */

/* ef_type: The following constants identify a file type in EX_FILES, field ef_fype. */
#define FT_ILLEGAL                 -2 /* An illegal type. */
#define FT_INIT                    -1 /* An initialized data field for this file,
					 but never gone through a "real" FExOpen()
					 type of call. */
#define FT_NORMAL_INPUT_FILE        0 /* An ordinary input file. */
#define FT_INPUT_RCS_CO_FILE        1 /* An ordinary input file, but it was generated
					 by a 'co' operation and input occurs from
					 a temporary file which must be removed upon
					 FExClose. */
#define FT_STDIN_DIRECT             2 /* Input is stdin, directly. */
#define FT_STDIN_TMP_FILE           3 /* Input is stdin, temporary file. */
#define FT_STDIN_TMP_FILE_R1        4 /* Input is stdin, temporary file. There
					 will be, after FExClose(), another FExOpen()
					 for stdin followed by another FExClose(). */
#define FT_STDIN_TMP_FILE_R2        5 /* State after FT_STDIN_TMP_FILE_R1, entered
					 when FExClose on FT_STDIN_TMP_FILE_R1 is done. */
#define FT_STDIN_IS_REALLY_A_FILE   6 /* Input is stdin, but really a file. */
#define FT_INPUT_FROM_TMP_FILE      7 /* Input is NOT stdin, the original
					 file is copied to a temporary file from which all
					 the reads occur. This temporary file is removed
					 when the FExClose occurs? NOT IMPLEMENTED. */
#define FT_NORMAL_OUTPUT_FILE       8 /* Output, ordinary file. */
#define FT_STDOUT_DIRECTLY          9 /* Output to stdout directly. */
#define FT_STDOUT_TMP_FILE         10 /* Output to stdout, currently to temporary file. */
#define FT_OUTPUT_TO_TMP_STAND     11 /* A standard output file, but output occurs to
					 temporary file which is copied on top of
					 desired output file when FExClose() occurs. */
#define FT_WRITE_SHADOW_E          12 /* Shadow: output occurs to requested file directly. */
#define FT_WRITE_SHADOW_T          13 /* Shadow: output occurs to temporary file. */

#define FT_TEMP_WRITE_1            14 /* Temporary file: output occurs currently. When FExClose()
					 occurs reopen file for input. */
#define FT_TEMP_WRITE_2            15 /* Temporary file: output occurs currently. When FExClose()
					 occurs let file stay around. Will be read in by some
					 other program. */
#define FT_TEMP_READ               16 /* Temporary file: currently being read in. */
#define FT_TEMP_EXISTS             17 /* Temporary file: file is being read in by other program.
					 This file is therefore NOT open for input. */
#define FT_OUT_DEV_NULL            18 /* Output goes to "/dev/null" */

/* Define various interrupt handling of those files. These constants are used
   to load ef_intr. */
#define FIT_IGNORE              0 /* Ignore this file if an interrupt occurs. */
#define FIT_CLOSE_ONLY          1 /* Close the file in case of an interrupt. */
#define FIT_CLOSE_AND_REMOVE    2 /* On interrupt close and remove file. */
#define FIT_REMOVE              3 /* On interrupt remove this file. */

extern char *ProgNameS; /* Name of program (short). */
extern char *TmpDirForFiles; /* Directory temporary files go to. */
extern FILE *fopen();
extern char *StrcpyAlloc();
extern char *StrncpyAlloc();
extern char *HandleFileNameExtension();
extern char *GenerateTmpFileName();
extern char *GetWd();
extern void Fatal();
extern void Fatal2();
extern void Fatal3();

/* Forward declarations. */
void UnLink();
void UnLinkSoftError();
void UnLinkIfFileExists();
void addToLinkedListOfFiles();
void removeFromLinkedListOfFiles();
void CleanUpFiles();
void FExClose();
void FExSeek();
void FExPrintCache();
void cannotOpenFile();

/* File business: print some messages? This variable can be set by the
   user of below routines at any time. */
int FileBusinessVerbose;

/* Caching businss: print messages? Also this variable can be set
   by the user of the below routines at any time. */
int FileCachingVerbose;

/* Serial counter, assigned to ef_sn of each EX_FILES data structure,
   when file is opened. */
int FExOpenSerialCounter;

/* Build a linked list of all relevant files here. Points to the first element,
   the rest are linked through ef_next. */
EX_FILES   linkedFileListHeader;

/* Temporary file name counter. */
int TmpFileNameCounter;

/* If the extension is empty, just save file name, otherwise do
   the file name extension business. */
#define FEX_LOAD_FILE_NAME \
  if (Strlen(ext) != 0) {\
    if (Strlen(fn) == 0)\
      Fatal ("FExOpen(): file name is empty /1/.");\
    ep->ef_fn    = HandleFileNameExtension(1, fn, ext);\
    ep->ef_fn_ne = HandleFileNameExtension(0, fn, ext);\
  } else {\
    if (Strlen(fn) == 0)\
      Fatal ("FExOpen(): file name is empty /2/.");\
    ep->ef_fn = StrcpyAlloc(fn);\
    ep->ef_fn_ne = StrcpyAlloc(fn);\
  }\
  if (FileBusinessVerbose)\
    fprintf (stderr, "FEX_LOAD_FILE_NAME(): file name: \"%s\"\n", ep->ef_fn);

/* This variable should be documented better. */
#define F_EX_CACHE_MAX 10

/* The routines FExOpen() and FExClose() support caching. For that purpose
   we maintain a list of those extended file structure pointers which
   are part of the cache AND which are opened. */
int FExCacheIndex; /* Points to the first free entry in the extended file
		      pointer cache table below. If this value reaches
		      F_EX_CACHE_MAX, then the cache is full. */
EX_FILES_P FExCache[F_EX_CACHE_MAX];

/*
 * cannotOpenFile
 * **************
 * This function is used by FExOpen to generate an error message
 * about a file which cannot be opened for input or output.
 *
 * fn: file name
 * rflag: TRUE trying to open for input.
 *        FALSE trying to open for output.
 */
void
cannotOpenFile(fn, rflag)
     char *fn;
     int rflag;
{
  char *cwd;

  if (Strlen(fn) == 0)
    Fatal ("cannotOpenFile(): empty file name.");

  /* Different error messages depending on whether an absolute file
     name is given or not. */
  if (fn[0] == '/')
    Fatal3 ("FExOpen(): cannot open \"%s\" for %s",
	    fn, rflag ? "input":"output");
  else {
    cwd = GetWd();
    Fatal4 ("FExOpen(): cannot open \"%s\" for %s (cwd: \"%s\")",
	    fn, rflag ? "input":"output", cwd);
  }
}


/*
 * FExInit
 * *******
 * Initializes extended file handling. This procedure should be
 * called by the main program before any of the other programs are used.
 */
void
FExInit()
{
  TmpFileNameCounter = 0;
  FExOpenSerialCounter = 0;
  FExCacheIndex = 0;
  FileBusinessVerbose = FALSE;
  FileCachingVerbose = FALSE;
  linkedFileListHeader.ef_next = NULL;
  linkedFileListHeader.ef_prev = NULL;
}

/*
 * FExOpen
 * *******
 * The parameters of this function are as follows:
 *
 * ep:    EX_FILES_P: pointer to the data structure allocated by the user.
 * type:  EFT_*
 * qual:  EFQ_* additional qualifications.
 * fn:    filename. In case a file is opened for temporary output,
 *        filename is discarded.
 * ext:   file extension, without or without period, can be empty.
 *        File extensions are enforced. File extensions if /dev/null
 *        is specified as output file, are ignored.
 */
int
FExOpen(ep, type, qual, fn, ext)
     EX_FILES_P ep;
     char *fn;
     int type;
     int qual;
     char *ext;
{
  int c; /* Some character or such. */
  int i; /* Used as a loop counter, for instance. */
  char tmp[256]; /* A general temporary buffer used for various purposes. */
  struct stat status_b; /* fstat operations buffer. Used to determine whether
			   stdin is really a redirected file or not. */
  int ci; /* Cache index: remove this element from the cache. */
  int ciu; /* Cache, use counter. File with biggest value of this
	      entry is removed. */
  int ok_file_name; /* Is the file name ok? */

  /* Verbose "file mode": print a message. */
  if (FileBusinessVerbose)
    fprintf (stderr, "FExOpen(): BEGIN\n");

  /* If the file name is already loaded into the file data structure used
     by the routines here, we will take out the file name from this
     data structure and generate a recursive call so that the file name
     appears as argument and everything proceeds as normal. */
  if (qual & EFQ_FILE_NAME_LOADED) {
    if (Strlen(fn) != 0)
      Fatal2 ("FExOpen(): EFQ_FILE_NAME_LOADED: argument fn must be empty, fn = \"%s\".", fn);
    if (Strlen(ep->ef_fn) == 0)
      Fatal ("FExOpen(): EFQ_FILE_NAME_LOADED: file name in data structure is empty.");
    /* Mask out file name bit, provide file name as argument now, call
       recursively. */
    qual = qual & ~EFQ_FILE_NAME_LOADED;
    return (FExOpen(ep, type, qual, StrcpyAlloc(ep->ef_fn), ext));
  }

  /* "Normalize" the extension: remove the leading period if such. */
  if (Strlen(ext) != 0 && *ext == '.')
    ext = StrcpyAlloc(sprintf (tmp, "%s", ext+1));

  /* Generate error if there are "nonsense characters" in a file name. */
  if (Strlen(fn) != 0) {
    ok_file_name = TRUE;
    for (i=0; i<Strlen(fn); i++) {
      c = *(fn+i);
      if (c > 040 || c < 0177)
	continue;
      ok_file_name = FALSE;
    }
    if (! ok_file_name)
      Fatal2 ("FExOpen(): file name \"%s\" not acceptable", fn);
  }

  /* Caching must be taken care of partially now. If the file is already in
     the cache, the file is open and nothing is to be done. */
  if (qual & EFQ_CACHE) {
    if (type != EFT_READ)
      Fatal ("FExOpen(): EFQ_CACHE only if opening for input.");
    if (Strcmp(fn, "-") == 0)
      Fatal ("FExOpen(): EFQ_CACHE not with stdin.");
    FExPrintCache ("FExOpen / beginning");
    for (i=0; i<FExCacheIndex; i++)
      if (ep == FExCache[i]) {
	if (FileCachingVerbose)
	  fprintf (stderr, "FExOpen(): file \"%s\" in cache and open.\n", ep->ef_fn);
	ep->ef_in_cache = TRUE;
	return (TRUE);
      }
    /* If we ever get here the file is to be opened for input and
       must be cached. A separate question is whether there is still
       room in the cache. */
    if (FileCachingVerbose)
      fprintf (stderr, "FExOpen(): file \"%s\" not in cache.\n", ep->ef_fn);
  }

  /* Deal with the issue of using stdin etc. */
  if ((qual & EFQ_STDIN_TWICE_SECOND_TIME) && (Strcmp(fn, "-") == 0)) {

  } else {
    /* Initialize the whole data structure, just in case we forget a field
       somewhere below. */
    ep->ef_type = FT_ILLEGAL;
    ep->ef_fn = NULL;
    ep->ef_fn_ne = NULL;
    ep->ef_filep = NULL;
    ep->ef_filep_2 = NULL;
    ep->ef_fn_sec = NULL;
    ep->ef_intr_1 = FIT_IGNORE;
    ep->ef_intr_2 = FIT_IGNORE;
    ep->ef_next = NULL;
    ep->ef_prev = NULL;
    ep->ef_sn = FExOpenSerialCounter++;
    ep->ef_dup = FALSE;
    ep->ef_open = TRUE; /* Let's assume below we succeed. */
  }

  /* Here branch according to which type of file opening was requested. */
  switch (type) {
    case EFT_READ:
      if (Strlen(fn) == 0)
	Fatal ("FExOpen(): EFT_READ: empty file name.");
      if (Strcmp(fn, "/dev/null") == 0)
	Fatal ("FExOpen(): input from \"/dev/null\" is illegal.");

      /* Stdin? */
      if (Strcmp(fn, "-") == 0) {
	/* Yes. Is it legal? */
	if (qual & EFQ_NO_STDIN)
	  Fatal ("FExOpen(): EFT_READ: input from stdin not allowed.");
	/* Stdin is legal, is stdin really already a file? This is the
	   case if a program xx is invoked as follows: xx - < file.some.
	   Then we will use this file instead of stdin. */
	if (FileBusinessVerbose)
	  fprintf (stderr, "FExOpen(): input from stdin.\n");
	if (fstat(0, &status_b) != 0)
	  Fatal ("FExOpen(): fstat on stdin failed.");
	if (status_b.st_mode & S_IFREG) {
	  /* Stdin is really a regular file. Use this! */
	  ep->ef_fn = StrcpyAlloc("-");
	  ep->ef_filep = stdin;
	  ep->ef_type = FT_STDIN_IS_REALLY_A_FILE;
	  ep->ef_intr_1 = FIT_IGNORE;
	  FSeek(stdin, 0, FSEEK_ABS); /* Reposition to the beginning. */
	  if (FileBusinessVerbose)
	    fprintf (stderr, "FExOpen(): stdin is really a file.\n");
	  addToLinkedListOfFiles(ep);
	} else {
	  /* Stdin is NOT really a file. Now check whether this is
	     a second FExOpen() call on a temporary file previously
	     filled. */
	  if (FileBusinessVerbose)
	    fprintf (stderr, "FExOpen(): stdin is not really a file.\n");
	  if (qual & EFQ_STDIN_TWICE_SECOND_TIME) {
	    FExSeek(ep, 0, FSEEK_ABS);
	    ep->ef_type = FT_STDIN_TMP_FILE_R2;
	  } else {
	    /* This is a first call to open stdin for input. Does the
	       user want to use a temporary file. Before deciding this
	       using a temporary file is forced, if the user declared
	       his intentions to read stdin twice. */
	    if (qual & EFQ_STDIN_TWICE)
	      qual |=  EFQ_STDIN_TMP_FILE;
	    if (qual & EFQ_STDIN_TMP_FILE) {
	      /* Yes. Now there are two cases: the temporary file will be
		 used only to read in once, and will be thrown away later,
		 or it will be used twice. */
	      if (qual & EFQ_STDIN_TWICE)
		ep->ef_type = FT_STDIN_TMP_FILE_R1;
	      else
		ep->ef_type = FT_STDIN_TMP_FILE;
	      ep->ef_fn =    StrcpyAlloc("-");
	      ep->ef_fn_ne = StrcpyAlloc("-");
	      /* Generate temporary file name. */
	      ep->ef_fn_sec = GenerateTmpFileName(ext);
	      if (FileBusinessVerbose)
		fprintf (stderr, "FExOpen(): write stdin to file \"%s\"\n",
			 ep->ef_fn_sec);
	      /* Open file, read in stdin and write to file, reopen
		 temporary file for input. */
	      if ((ep->ef_filep = fopen(ep->ef_fn_sec, "w")) == NULL)
		cannotOpenFile (ep->ef_fn_sec, FALSE);
	      ep->ef_intr_1 = FIT_IGNORE;
	      ep->ef_intr_2 = FIT_CLOSE_AND_REMOVE;
	      addToLinkedListOfFiles(ep);
	      while ((c=getchar()) != EOF)
		putc (c, ep->ef_filep);
	      fclose(ep->ef_filep);
	      if ((ep->ef_filep = fopen(ep->ef_fn_sec, "r")) == NULL)
		cannotOpenFile (ep->ef_fn_sec, TRUE);
	    } else {
	      /* No, read in stdin directly. */
	      ep->ef_type = FT_STDIN_DIRECT;
	      ep->ef_filep = stdin;
	      ep->ef_fn = StrcpyAlloc("-");
	      ep->ef_intr_1 = FIT_IGNORE;
	      addToLinkedListOfFiles(ep);
	    }
	  }
	}
      } else {
	/* Input of an ordinary file. */
	FEX_LOAD_FILE_NAME; /* File extension: get that right. */
	/* It's an ordinary file input will occur from. Check first whether file
	   exists, and if not, whether this should cause an error. */
	if (access(ep->ef_fn, R_OK)) {
	  /* File cannot be accessed for read. If the user has NOT specified
	     EFQ_RCS_CO, then it's time to give up. Also, even if the user
	     has specified EFQ_RCS_CO, but if there is no RCS file, then
	     it's time to give up. */
	  ep->ef_open = FALSE;
	  if (! (qual & EFQ_RCS_CO) || (qual & EFQ_RCS_CO) && !FileHasRcsFile(ep->ef_fn)) {
	    if (qual & EFQ_NO_FILE_NO_ERROR)
	      return (FALSE);
	    else
	      cannotOpenFile(ep->ef_fn, TRUE);
	  }
	  /* Now we know that EFQ_RCS_CO was specified AND that there is an RCS file.
	     So need to check out the file and copy it to a temporary file. */
	  ep->ef_fn_sec = GenerateTmpFileName(NULL);
	  sprintf(tmp, "co -p %s > %s", ep->ef_fn, ep->ef_fn_sec);
	  if (system (tmp))
	    Fatal2 ("FExOpen(); [EFQ_RCS_CO]: %s failed.", tmp);
	  /* The temporary file is now open. Need to read in from it and also need
	     to make a note that when an FExClose occurs this file needs to be removed. */
	  if ((ep->ef_filep = fopen(ep->ef_fn_sec, "r")) == NULL)
	    cannotOpenFile (ep->ef_fn_sec, TRUE);
	  ep->ef_type = FT_INPUT_RCS_CO_FILE;
	  addToLinkedListOfFiles(ep);
	  return(TRUE);
	}
	
	/* Now open file for input. */
	ep->ef_type = FT_NORMAL_INPUT_FILE;
	if ((ep->ef_filep = fopen(ep->ef_fn, "r")) == NULL)
	  cannotOpenFile (ep->ef_fn, TRUE);
	addToLinkedListOfFiles(ep);
	ep->ef_intr_1 = FIT_CLOSE_ONLY;
	/* Caching going on? */
	if (qual & EFQ_CACHE) {
	  /* Yes, this file must be cached. Note that this file
	     had to be opened, because the case where the file was
	     already in the cache was dealt with before.
	     Now check whether there is room in the cache. */
	  if (FExCacheIndex == F_EX_CACHE_MAX) {
	    /* The cache is full. Must now find the entry with the
	       smallest ef_cache_value setting. */
	    if (FileCachingVerbose)
	      fprintf (stderr, "FExOpen(): cache full.\n", ep->ef_fn);
	    FExPrintCache ("FExOpen(): cache full");
	    ci = 0;
	    ciu = FExCache[0]->ef_cache_value;
	    for (i=1; i<F_EX_CACHE_MAX; i++) {
	      if (ciu >= FExCache[i]->ef_cache_value)
		continue;
	      ci = i;
	      ciu = FExCache[i]->ef_cache_value;
	    } /* for */
	    if (FileCachingVerbose)
	      fprintf (stderr, "FExOpen(): remove element %d (\"%s\") from cache\n",
		       ci, FExCache[ci]->ef_fn);
	    /* Close file, removes it also from cache and compacts cache.
	       Add new element to cache. */
	    FExCache[ci]->ef_in_cache = FALSE;
	    FExClose(FExCache[ci]);
	    if (FExCacheIndex != (F_EX_CACHE_MAX-1))
	      Fatal2 ("FExOpen(): FExCacheIndex error, value = %d.", FExCacheIndex);
	    /* Cache is full again now. */
	    FExCache[F_EX_CACHE_MAX-1] = ep;
	    ep->ef_in_cache = TRUE;
	    FExCacheIndex++;
	    FExPrintCache("FExOpen(): added element to cache, full now.");
	  } else {
	    /* There is room in the cache. Add file to the cache. */
	    FExCache[FExCacheIndex] = ep;
	    ep->ef_in_cache = TRUE;
	    if (FileCachingVerbose)
	      fprintf (stderr, "FExOpen(): cache was not full, added \"%s\" as element %d.\n",
		       ep->ef_fn, FExCacheIndex);
	    FExCacheIndex++;
	    FExPrintCache ("FExOpen(): after added an element.");
	  }
	}
      }
      return (TRUE);
      
    /* Write */
    case EFT_WRITE:
      if (Strlen(fn) == 0)
	Fatal ("FExOpen(): EFT_WRITE: empty file name.");
      /* Is output to "/dev/null" ? */
      if (Strcmp(fn, "/dev/null") == 0) {
	ep->ef_fn = StrcpyAlloc("/dev/null");
	ep->ef_fn_ne = StrcpyAlloc("/dev/null");
	ep->ef_filep = fopen("/dev/null", "w");
	ep->ef_type = FT_OUT_DEV_NULL;
	ep->ef_intr_1 = FIT_IGNORE;
	return(TRUE);
      }

      /* Stdout ? */
      if (Strcmp(fn, "-") == 0) {
	/* Yes, it is stdout. Is it legal? */
	if (qual & EFQ_NO_STDOUT)
	  Fatal ("FExOpen(): EFT_WRITE: no stdout allowed.");
	/* Stdout is legal. When writing to shadow files, then
	   writing to stdout does not make sense. So generate error in this case. */
	if (qual & EFQ_SHADOW)
	  Fatal ("FExOpen: EFT_WRITE to stdout and EFQ_SHADOW incompatible.");

	/* Do we go through a temporary file? */
	if (qual & EFQ_STDOUT_TMP_FILE || qual & EFQ_FORCE_TMP_FILE) {
	  /* Yes */
	  ep->ef_fn =    StrcpyAlloc("-");
	  ep->ef_fn_ne = StrcpyAlloc("-");
	  ep->ef_type = FT_STDOUT_TMP_FILE;
	  ep->ef_fn_sec = GenerateTmpFileName(ext);
	  if ((ep->ef_filep = fopen(ep->ef_fn_sec, "w")) == NULL)
	    cannotOpenFile (ep->ef_fn_sec, FALSE);
	  addToLinkedListOfFiles(ep);
	  ep->ef_intr_2 = FIT_CLOSE_AND_REMOVE;
	} else {
	  /* No: output goes to stdout directly. */
	  ep->ef_fn =    StrcpyAlloc("-");
	  ep->ef_fn_ne = StrcpyAlloc("-");
	  ep->ef_filep = stdout;
	  addToLinkedListOfFiles(ep);
	  ep->ef_type = FT_STDOUT_DIRECTLY;
	  ep->ef_intr_1 = FIT_IGNORE;
	}
      } else {
	/* Not stdout. */
	if (qual & EFQ_SHADOW) {
	  /* Shadow output. Error if EFQ_DELETE_FILE_IF_EXISTS is specified. */
	  if (qual & EFQ_DELETE_FILE_IF_EXISTS)
	    Fatal2 ("FExOpen(): EFT_WRITE: EFQ_DELETE_FILE_IF_EXISTS and EFQ_SHADOW incompatible, file = \"%s\".",
		    fn);
	  if (qual & EFQ_FORCE_TMP_FILE)
	    Fatal ("FExOpen(): EFT_WRITE: EFQ_TMP_FILE and EFQ_SHADOW incompatible.");
	  /* Does the output file already exist? */
	  if (! access(fn, F_OK)) {
	    /* Yes, it does. Output to a temporary file now, later compare
	       both files. If this is done later it must be possible to
	       write to the main file. If not, this is a fatal error. */
	    if (access(fn, W_OK))
	      Fatal2 ("FExOpen(): no write access to \"%s\"", fn);
	    ep->ef_type = FT_WRITE_SHADOW_T;
	    ep->ef_fn_sec = GenerateTmpFileName(ext);
	    if ((ep->ef_filep = fopen(ep->ef_fn_sec, "w")) == NULL)
	      cannotOpenFile (ep->ef_fn_sec, FALSE);
	    addToLinkedListOfFiles(ep);
	    ep->ef_intr_2 = FIT_CLOSE_AND_REMOVE;
	  } else {
	    /* No, output occurs directly to the new file. */
	    ep->ef_type = FT_WRITE_SHADOW_E;
	    if ((ep->ef_filep = fopen(ep->ef_fn, "w")) == NULL)
	      cannotOpenFile (ep->ef_fn, FALSE);
	    addToLinkedListOfFiles(ep);
	    ep->ef_intr_1 = FIT_CLOSE_AND_REMOVE;
	  }
	} else {
	  /* Not shadow output. Output occurs to an ordinary file. */
	  FEX_LOAD_FILE_NAME;
	  /* EFQ_DELETE_FILE_IF_EXISTS, if specified, requires us to
	     delete the file if it exists. */
	  if (qual & EFQ_DELETE_FILE_IF_EXISTS)
	    UnLinkIfFileExists (ep->ef_fn);
	  /* Did the user request output to a temporary file? If so, that file
	     is establised now. */
	  if (qual & EFQ_FORCE_TMP_FILE) {
	    ep->ef_fn_sec = GenerateTmpFileName(ext);
	    if ((ep->ef_filep = fopen(ep->ef_fn_sec, "w")) == NULL)
	      cannotOpenFile (ep->ef_fn_sec, FALSE);
	    ep->ef_intr_1 = FIT_IGNORE;
	    ep->ef_intr_2 = FIT_CLOSE_AND_REMOVE;
	    addToLinkedListOfFiles(ep);
	    ep->ef_type = FT_OUTPUT_TO_TMP_STAND;
	  } else {
	    if ((ep->ef_filep = fopen(ep->ef_fn, "w")) == NULL)
	      cannotOpenFile (ep->ef_fn, FALSE);
	    addToLinkedListOfFiles(ep);
	    ep->ef_type = FT_NORMAL_OUTPUT_FILE;
	    ep->ef_intr_1 = FIT_CLOSE_AND_REMOVE;
	  }
	}
      }
      break;

    /* Temporary file for output. */
    case EFT_TEMP:
      if (Strlen(fn) != 0)
	fprintf (stderr, "FExOpen(): [EFT_TEMP]: non-empty filename, ignored.");
      if (Strlen(ext) == 0)
	Fatal ("FExOpen(): EFT_TEMP must provide file extension.");
      if (qual & EFQ_TEMP_MODE_1)
	ep->ef_type = FT_TEMP_WRITE_1;
      if (qual & EFQ_TEMP_MODE_2)
	ep->ef_type = FT_TEMP_WRITE_2;
      if (ep->ef_type == FT_ILLEGAL)
	Fatal ("FExOpen(): EFT_TEMP, EFQ_TEMP_MODE* specification missing.");

      ep->ef_fn = GenerateTmpFileName(ext);
      ep->ef_fn_ne = HandleFileNameExtension (0, ep->ef_fn, ext);
      if ((ep->ef_filep = fopen(ep->ef_fn, "w")) == NULL)
	cannotOpenFile (ep->ef_fn, FALSE);

      addToLinkedListOfFiles(ep);
      ep->ef_intr_1 = FIT_CLOSE_AND_REMOVE;
      break;
      
    /* Data strucuture is cleared, no file is open. A close on this file has no effect. */
    case EFT_INIT:
      ep->ef_open = FALSE;
      ep->ef_type = FT_INIT;
      return(FALSE);

    default:
      Fatal ("FExOpen(): illegal first argument, EFT_...");
    } /* switch */
  return (TRUE); /* Value is usually irrelevant, except for READ,
		    when EFQ_NO_FILE_NO_ERROR was given. */
}

/*
 * FExClose
 * ********
 * Close one of the files described thourgh an extended file structure.
 * If the FExClose() occurs on a duplicate extended file structure nothing
 * happens. Also if FExClose tries to close a file which was never open
 * then nothing happens.
 *
 * ep: extended file format: pointer to such a structure.
 */
void
FExClose(ep)
     EX_FILES_P ep;
{
  FILE *f1, *f2;
  int c1, c2, c;
  int i, j;

  if (FileBusinessVerbose)
    fprintf (stderr, "FExClose(): BEGIN, file type %d\n", ep->ef_type);

  /* Do nothing if this file is a duplicate. */
  if (ep->ef_dup)
    return;

  /* Check whether this file is in the cache and if so remove
     it from cache and call this procedure recursively. */
  for (i=0; i<FExCacheIndex; i++) {
    if (ep != FExCache[i])
      continue;
    ep->ef_in_cache = FALSE;
    if (FileCachingVerbose)
      fprintf (stderr, "FExClose(): file \"%s\" in cache, element %d, remove.\n", ep->ef_fn, i);
    /* File is in cache, compact cache to remove it. */
    FExPrintCache ("FExClose(): before compacting");
    for (j=i; j<FExCacheIndex-1; j++)
      FExCache[j] = FExCache[j+1];
    FExCacheIndex--; /* One more free element */
    FExPrintCache ("FExClose(): after compacting"); 
    FExClose(ep); /* "Close again", this time file is not in cache. */
    return;
  }

  /* Switch according to file type. */
  switch (ep->ef_type) {
    case FT_NORMAL_INPUT_FILE:
      fclose(ep->ef_filep);
      ep->ef_open = FALSE;
      removeFromLinkedListOfFiles(ep);
      break;
    case FT_INPUT_RCS_CO_FILE:
      fclose(ep->ef_filep);
      ep->ef_open = FALSE;
      UnLink(ep->ef_fn_sec);
      removeFromLinkedListOfFiles(ep);
      break;
    case FT_STDIN_DIRECT:
      removeFromLinkedListOfFiles(ep);
      ep->ef_open = FALSE;
      break;
    case FT_STDIN_TMP_FILE:
      fclose(ep->ef_filep);
      removeFromLinkedListOfFiles(ep);
      UnLink(ep->ef_fn_sec);
      ep->ef_open = FALSE;
      break;
    case FT_STDIN_TMP_FILE_R1:
      break;
    case FT_STDIN_TMP_FILE_R2:
      fclose(ep->ef_filep);
      removeFromLinkedListOfFiles(ep);
      UnLink(ep->ef_fn_sec);
      ep->ef_open = FALSE;
      break;
    case FT_STDIN_IS_REALLY_A_FILE:
      removeFromLinkedListOfFiles(ep);
      ep->ef_open = FALSE;
      break;
    case FT_NORMAL_OUTPUT_FILE:
      fclose(ep->ef_filep);
      removeFromLinkedListOfFiles(ep);
      ep->ef_open = FALSE;
      break;
    case FT_STDOUT_DIRECTLY:
      removeFromLinkedListOfFiles(ep);
      ep->ef_open = FALSE;
      break;
    case FT_STDOUT_TMP_FILE:
      fclose(ep->ef_filep);
    /* Output to stdout was redirected to a temporary file, copy
       this file to stdout now. */
      if ((f1 = fopen(ep->ef_fn_sec, "r")) == NULL)
	cannotOpenFile (ep->ef_fn_sec, TRUE);
      while ((c=getc(f1)) != EOF)
	putchar(c);
      fclose(f1);
      removeFromLinkedListOfFiles(ep);
      UnLink(ep->ef_fn_sec);
      ep->ef_open = FALSE;
      break;
    case FT_OUTPUT_TO_TMP_STAND:
      fclose(ep->ef_filep);
      /* Write file to real destination now. */
      if ((f1 = fopen(ep->ef_fn_sec, "r")) == NULL)
	cannotOpenFile (ep->ef_fn_sec, TRUE);
      if ((f2 = fopen(ep->ef_fn, "w")) == NULL)
	cannotOpenFile (ep->ef_fn, FALSE);
      while ((c=getc(f1)) != EOF)
	putc(c, f2);
      fclose(f1);
      fclose(f2);
      removeFromLinkedListOfFiles(ep);
      UnLink(ep->ef_fn_sec);
      ep->ef_open = FALSE;
      break;

    case FT_WRITE_SHADOW_E:
      fclose(ep->ef_filep);
      removeFromLinkedListOfFiles(ep);
      ep->ef_open = FALSE;
      break;
    case FT_WRITE_SHADOW_T:
      fclose(ep->ef_filep);

      /* Now compare the two files! */
      if ((f1 = fopen(ep->ef_fn, "r")) == NULL)
	Fatal2 ("FExClose(): FT_WRITE_SHADOW_T: cannot open \"%s\"", ep->ef_fn);
      if ((f2 = fopen(ep->ef_fn_sec, "r")) == NULL)
	Fatal2 ("FExClose(): FT_WRITE_SHADOW_T: cannot open \"%s\"", ep->ef_fn_sec);
      for (;;) {
	c1 = getc(f1);
	c2 = getc(f2);
	if (c1 == c2) {
	  /* Both characters are identical, are we done? */
	  if (c1 == EOF) {
	    fclose(f1);
	    fclose(f2);
	    UnLink(ep->ef_fn_sec);
	    removeFromLinkedListOfFiles(ep);
	    return;
	  }
	} else
	  break; /* different: break out of loop. */
      }

      /* The two files differ. Copy the temporary file on the old file. */
      fclose (f1);
      fclose (f2);
      FileCopy (ep->ef_fn_sec, ep->ef_fn);
      UnLink(ep->ef_fn_sec);
      removeFromLinkedListOfFiles(ep);
      ep->ef_open = FALSE;
      break;

    case FT_TEMP_WRITE_1:
      fclose(ep->ef_filep); /* Close for output, reopen for input. */
      if ((ep->ef_filep = fopen(ep->ef_fn, "r")) == NULL)
	Fatal2 ("FExClose(): FT_TEMP_WRITE_1, cannot open \"%s\"", ep->ef_fn);
      ep->ef_type = FT_TEMP_READ;
      break;

    case FT_TEMP_WRITE_2:
      fclose(ep->ef_filep); /* File stays around as of now. */
      ep->ef_type = FT_TEMP_EXISTS;
      ep->ef_open = FALSE;
      break;

    case FT_TEMP_READ: /* Tempoary file: done with reading, discard now. */
      fclose(ep->ef_filep);
      UnLink(ep->ef_fn);
      removeFromLinkedListOfFiles(ep);
      ep->ef_open = FALSE;
      break;

    case FT_TEMP_EXISTS: /* Temporary file no more needed. */
      UnLink(ep->ef_fn);
      removeFromLinkedListOfFiles(ep);
      ep->ef_open = FALSE;
      break;

    case FT_OUT_DEV_NULL:
      ep->ef_open = FALSE;
      break;

    case FT_INIT: /* File has been never opened: FExClose() is a NOP. */
      ep->ef_type = FT_ILLEGAL;
      break;

    default:
      Fatal ("FExClose(): default: illegal file type.");
    }
}

/*
 * FExPrintCache
 * *************
 * Print the file cache.
 *
 * s: some additional identifying message.
 */
void
FExPrintCache(s)
     char *s;
{
  int i;

  if (! FileCachingVerbose)
    return;

  fprintf (stderr, "FExPrintCache() [%s]: begin, %d elements\n", s, FExCacheIndex);
  for (i=0; i<FExCacheIndex; i++) {
    fprintf (stderr, "\t%2d (val: %4d): \"%s\"\n", i, FExCache[i]->ef_cache_value,
	     FExCache[i]->ef_fn);
  }
  fprintf (stderr, "FExPrintCache(): end\n");
}

/*
 * FExSeek
 * *******
 * Like fseek, but first argument is a pointer to the extended file
 * structure.
 *
 * ep: extended file structure pointer.
 * offset: offset value
 * seek_mode: absolute from beginning or end of file, relative.
 */
void
FExSeek (ep, offset, seek_mode)
     EX_FILES_P ep;
     int offset;
     int seek_mode;
{
  long l;

  l = offset;
  if (fseek(ep->ef_filep, l, seek_mode) == -1)
    Fatal2 ("FExSeek(): failed on file \"%s\"", ep->ef_fn);
}

/*
 * FExTell
 * *******
 * Like ftell, but argument is a pointer to the extended file
 * structure.
 *
 * ep: extended file structure pointer.
 */
int
FExTell (ep)
     EX_FILES_P ep;
{
  return (ftell(ep->ef_filep));
}

/*
 * FExFlush
 * ********
 * Like fflush, but argument is a pointer to the extended file
 * structure.
 *
 * ep: extended file structure pointer.
 */
void
FExFlush (ep)
     EX_FILES_P ep;
{
  if (fflush(ep->ef_filep) == EOF)
    Fatal2 ("FExFlush(): failed on file \"%s\"", ep->ef_fn);
}

/*
 * DuplicateExF
 * ************
 * Duplicate an extended file pointer. This is done so that
 * it is possible to have more than one extended file structure
 * to handle one single file. Note that you are responsible for properly
 * handling such duplicates: an FExClose() on such a duplicate is a
 * noop.
 *
 * fdest: destination extended file pointer.
 * fsource: source extented file pointer.
 */
void
DuplicateExF (fdest, fsource)
     EX_FILES_P fdest, fsource;
{
  *fdest = *fsource;
  fdest->ef_dup = TRUE;
  fdest->ef_next = NULL;
  fdest->ef_prev = NULL;
}

/*
 * addToLinkedListOfFiles
 * **********************
 * Add file identified by "ep" to the linked list of active files (at the front).
 *
 * ep: a pointer to one of the file structures.
 */
void
addToLinkedListOfFiles(ep)
     EX_FILES_P ep;
{
  EX_FILES_P tmp;

  if (FileBusinessVerbose)
    fprintf (stderr, "addToLinkedListOfFiles(): file serial number %d\n", ep->ef_sn);

  ep->ef_next = NULL;
  ep->ef_prev = NULL;

  /* Is the list empty sofar? */
  if (linkedFileListHeader.ef_next == NULL) {
    linkedFileListHeader.ef_next = ep;
    ep->ef_prev = &linkedFileListHeader;
  } else {
    /* No it is not empty. */
    tmp = linkedFileListHeader.ef_next;
    linkedFileListHeader.ef_next = ep;
    ep->ef_next = tmp;
    tmp->ef_prev = ep;
    ep->ef_prev = &linkedFileListHeader;
  }
}

/*
 * removeFromLinkedListOfFiles
 * ***************************
 * Remove a file identified by "ep" from the linked list of active files.
 *
 * ep: a pointer to one of the file structures.
 */
void
removeFromLinkedListOfFiles(ep)
     EX_FILES_P ep;
{
  if (FileBusinessVerbose)
    fprintf (stderr, "removeFromLinkedListOfFiles(): file serial number %d\n", ep->ef_sn);

  if (linkedFileListHeader.ef_next == NULL)
    Fatal ("removeFromLinkedListOfFiles(): list is empty.");

  if (ep->ef_prev == NULL)
    Fatal ("removeFromLinkedListOfFiles(): ef_prev == NULL.");
    
  if (ep == &linkedFileListHeader)
    Fatal ("removeFromLinkedListOfFiles(): cannot remove head of list.");

  if (ep->ef_next == NULL) {
    /* It's the last element of the linked list of file headers. */
    ep->ef_prev->ef_next = NULL;
    return;
  }

  /* It's an inbetween element of the list of linked headers. */
  ep->ef_prev->ef_next = ep->ef_next;
  ep->ef_next->ef_prev = ep->ef_prev;
}

/*
 * CleanUpFiles
 * ************
 * This routine goes through the list of files and then removes and closes
 * all files which are marked as this.
 */
void
CleanUpFiles()
{
  EX_FILES_P p;
  int i;
  int intr;
  char *fn;
  FILE *fp;

  p = linkedFileListHeader.ef_next;
  fflush(stderr);

  while (p != NULL) {
    for (i=1; i<=2; i++) {
      fprintf (stderr, "CleanUpFiles(): sn %d, ", p->ef_sn);
      if (i==1) {
	intr = p->ef_intr_1;
	fn = p->ef_fn;
	fp = p->ef_filep;
	fprintf (stderr, "/1/, ");
      } else {
	intr = p->ef_intr_2;
	fn = p->ef_fn_sec;
	fp = p->ef_filep_2;
	fprintf (stderr, "/2/, ");
      }
      switch (intr) {
        case FIT_IGNORE:
	  if (Strlen(fn) == 0)
	    fprintf (stderr, "ignored (no file)\n");
	  else
	    fprintf (stderr, "ignore \"%s\"\n", fn);
	  break;
	case FIT_CLOSE_ONLY:
	  fprintf (stderr, "close \"%s\"\n", fn);
	  fclose(fp);
	  break;
	case FIT_CLOSE_AND_REMOVE:
	  fprintf (stderr, "close and remove \"%s\"\n", fn);
	  fclose(fp);
	  UnLinkSoftError(fn);
	  break;
	case FIT_REMOVE:
	  fprintf (stderr, "remove \"%s\"\n", fn);
	  UnLinkSoftError(fn);
	  break;
	default:
	  Fatal ("CleanUpFiles(): default.");
      }
    } /* for */
    p = p->ef_next;
  }
  fprintf (stderr, "CleanUpFiles(): done\n");
}

/*
 * UnLink
 * ******
 * Unlink a named file. Generate a fatal error if unlink failed.
 *
 * fn: file name of file to unlink.
 */
void
UnLink(fn)
    char *fn;
{  
  if (Strlen(fn) == 0)
    Fatal ("UnLink(): empty file name.");
  if (Strcmp(fn, "-") == 0)
    Fatal ("UnLink(): file name is stdin or stdout.");
  if (FileBusinessVerbose)
    fprintf (stderr, "UnLink(): file \"%s\"\n", fn);
  if (unlink(fn) == -1)
    Fatal2 ("UnLink(): unlink of \"%s\" failed.", fn);
}

/*
 * UnLinkSoftError
 * ***************
 * Unlink a named file. Generate a warning if unlink failed.
 *
 * fn: file name of file to unlink.
 */
void
UnLinkSoftError(fn)
    char *fn;
{  
  if (Strlen(fn) == 0)
    Fatal ("UnLinkSoftError(): empty file name.");
  if (Strcmp(fn, "-") == 0)
    fprintf (stderr, "UnLinkSoftError(): file name is \"-\"\n");
  if (FileBusinessVerbose)
    fprintf (stderr, "UnLinkSoftError(): file \"%s\"\n", fn);
  if (unlink(fn) == -1)
    fprintf (stderr, "UnLinkSoftError(): unlink of \"%s\" failed.\n", fn);
}


/*
 * UnLinkIfFileExists
 * ******************
 * Unlink a named file if it exists. Do nothing if file
 * does not exist. But generate a fatal error if file exists but
 * unlink failed.
 *
 * fn: file name of file to unlink (if it exists).
 */
void
UnLinkIfFileExists(fn)
    char *fn;
{  
  if (! access(fn, F_OK))
    UnLink(fn);
}

/*
 * ReturnLengthOfFile
 * ******************
 * This procedure determines the length of some file.
 * The file is opened, an fseek to the end occurs, ftell
 * is used to determine the length and the file is closed.
 *
 * fn: file name
 * RET: length of file
 */
int
ReturnLengthOfFile(fn)
     char *fn;
{
  EX_FILES exf;
  int ret;

  if (Strlen(fn) == 0)
    Fatal ("ReturnLengthOfFile(): empty file name");
  FExOpen(&exf, EFT_READ, EFQ_NO_STDIN, "fn", "");
  FExSeek(&exf, 0, FSEEK_END);
  ret = FExTell(&exf);
  FExClose(&exf);
  return(ret);
}