|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T e
Length: 36212 (0x8d74) Types: TextFile Names: »exf.c«
└─⟦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«
/* 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); }