This is files.c in view mode; [Download] [Up]
/***************************************************************/
/* */
/* FILES.C */
/* */
/* Controls the opening and closing of files, etc. Also */
/* handles caching of lines and reading of lines from */
/* files. */
/* */
/* This file is part of REMIND. */
/* Copyright (C) 1992, 1993, 1994 by David F. Skoll. */
/* */
/***************************************************************/
#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#if defined(__MSDOS__)
#include <io.h>
#endif
#ifdef __MSC__
#include <dos.h>
#endif
#include "types.h"
#include "protos.h"
#include "globals.h"
#include "err.h"
/* Convenient macro for closing files */
#define FCLOSE(fp) (((fp)&&((fp)!=stdin)) ? (fclose(fp),(fp)=NULL) : ((fp)=NULL))
/* Define the structures needed by the file caching system */
typedef struct cache {
struct cache *next;
char *text;
int LineNo;
} CachedLine;
typedef struct cheader {
struct cheader *next;
char *filename;
CachedLine *cache;
} CachedFile;
/* Define the structures needed by the INCLUDE file system */
typedef struct {
char *filename;
int LineNo;
unsigned int IfFlags;
int NumIfs;
long offset;
CachedLine *CLine;
} IncludeStruct;
static CachedFile *CachedFiles = (CachedFile *) NULL;
static CachedLine *CLine = (CachedLine *) NULL;
static FILE *fp;
static IncludeStruct IStack[INCLUDE_NEST];
static int IStackPtr = 0;
PRIVATE int ReadLineFromFile ARGS ((void));
PRIVATE int CacheFile ARGS ((const char *fname));
PRIVATE void DestroyCache ARGS ((CachedFile *cf));
/***************************************************************/
/* */
/* ReadLine */
/* */
/* Read a line from the file or cache. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int ReadLine(void)
#else
int ReadLine()
#endif
{
int r;
/* If we're at the end of a file, pop */
while (!CLine && !fp) {
r = PopFile();
if (r) return r;
}
/* If it's cached, read line from the cache */
if (CLine) {
CurLine = CLine->text;
LineNo = CLine->LineNo;
CLine = CLine->next;
FreshLine = 1;
if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp);
return OK;
}
/* Not cached. Read from the file. */
return ReadLineFromFile();
}
/***************************************************************/
/* */
/* ReadLineFromFile */
/* */
/* Read a line from the file pointed to by fp. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int ReadLineFromFile(void)
#else
static ReadLineFromFile()
#endif
{
int l;
char *ptr;
char *tmp;
CurLine = LineBuffer;
*LineBuffer = (char) 0;
l = 0;
ptr = LineBuffer;
while(fp) {
tmp=fgets(ptr, LINELEN-l, fp);
LineNo++;
if (ferror(fp)) return E_IO_ERR;
if (feof(fp) || !tmp) {
FCLOSE(fp);
}
l = strlen(LineBuffer);
if (l && (LineBuffer[l-1] == '\n')) LineBuffer[--l] = '\0';
if (l && (LineBuffer[l-1] == '\\')) {
l--;
ptr = LineBuffer+l;
if (l >= LINELEN-1) return E_LINE_2_LONG;
continue;
}
FreshLine = 1;
if (DebugFlag & DB_ECHO_LINE) OutputLine(ErrFp);
return OK;
}
return OK;
}
/***************************************************************/
/* */
/* OpenFile */
/* */
/* Open a file for reading. If it's in the cache, set */
/* CLine. Otherwise, open it on disk and set fp. If */
/* ShouldCache is 1, cache the file */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int OpenFile(const char *fname)
#else
int OpenFile(fname)
char *fname;
#endif
{
CachedFile *h = CachedFiles;
int r;
/* If it's in the cache, get it from there. */
while (h) {
if (!strcmp(fname, h->filename)) {
CLine = h->cache;
STRSET(FileName, fname);
LineNo = 0;
if (FileName) return OK; else return E_NO_MEM;
}
h = h->next;
}
/* If it's a dash, then it's stdin */
if (!strcmp(fname, "-")) {
fp = stdin;
} else {
fp = fopen(fname, "r");
}
if (!fp) return E_CANT_OPEN;
CLine = NULL;
if (ShouldCache) {
LineNo = 0;
r = CacheFile(fname);
if (r == OK) {
fp = NULL;
CLine = CachedFiles->cache;
} else {
if (strcmp(fname, "-"))
fp = fopen(fname, "r");
else
fp = stdin;
if (!fp) return E_CANT_OPEN;
}
}
STRSET(FileName, fname);
LineNo = 0;
if (FileName) return OK; else return E_NO_MEM;
}
/***************************************************************/
/* */
/* CacheFile */
/* */
/* Cache a file in memory. If we fail, set ShouldCache to 0 */
/* Returns an indication of success or failure. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE int CacheFile(const char *fname)
#else
static int CacheFile(fname)
char *fname;
#endif
{
int r;
CachedFile *cf;
CachedLine *cl;
char *s;
cl = NULL;
/* Create a file header */
cf = NEW(CachedFile);
cf->cache = NULL;
if (!cf) { ShouldCache = 0; FCLOSE(fp); return E_NO_MEM; }
cf->filename = StrDup(fname);
if (!cf->filename) {
ShouldCache = 0;
FCLOSE(fp);
free(cf);
return E_NO_MEM;
}
/* Read the file */
while(fp) {
r = ReadLineFromFile();
if (r) {
DestroyCache(cf);
ShouldCache = 0;
FCLOSE(fp);
return r;
}
/* Skip blank chars */
s = LineBuffer;
while (isspace(*s)) s++;
if (*s && *s!=';' && *s!='#') {
/* Add the line to the cache */
if (!cl) {
cf->cache = NEW(CachedLine);
if (!cf->cache) {
DestroyCache(cf);
ShouldCache = 0;
FCLOSE(fp);
return E_NO_MEM;
}
cl = cf->cache;
} else {
cl->next = NEW(CachedLine);
if (!cl->next) {
DestroyCache(cf);
ShouldCache = 0;
FCLOSE(fp);
return E_NO_MEM;
}
cl = cl->next;
}
cl->next = NULL;
cl->LineNo = LineNo;
cl->text = StrDup(s);
if (!cl->text) {
DestroyCache(cf);
ShouldCache = 0;
FCLOSE(fp);
return E_NO_MEM;
}
}
}
/* Put the cached file at the head of the queue */
cf->next = CachedFiles;
CachedFiles = cf;
return OK;
}
/***************************************************************/
/* */
/* PopFile - we've reached the end. Pop up to the previous */
/* file, or return E_EOF */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int PopFile(void)
#else
int PopFile()
#endif
{
IncludeStruct *i;
if (!Hush && NumIfs) Eprint("%s", ErrMsg[E_MISS_ENDIF]);
if (!IStackPtr) return E_EOF;
IStackPtr--;
i = &IStack[IStackPtr];
LineNo = i->LineNo;
IfFlags = i->IfFlags;
NumIfs = i->NumIfs;
CLine = i->CLine;
fp = NULL;
STRSET(FileName, i->filename);
if (!CLine && (i->offset != -1L)) {
/* We must open the file, then seek to specified position */
if (strcmp(i->filename, "-"))
fp = fopen(i->filename, "r");
else
fp = stdin;
if (!fp) return E_CANT_OPEN;
if (fp != stdin)
(void) fseek(fp, i->offset, 0); /* Trust that it works... */
}
free(i->filename);
return OK;
}
/***************************************************************/
/* */
/* DoInclude */
/* */
/* The INCLUDE command. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int DoInclude(ParsePtr p)
#else
int DoInclude(p)
ParsePtr p;
#endif
{
char tok[TOKSIZE];
int r, e;
if ( (r=ParseToken(p, tok)) ) return r;
e = VerifyEoln(p);
if (e) Eprint("%s", ErrMsg[e]);
if ( (r=IncludeFile(tok)) ) return r;
NumIfs = 0;
IfFlags = 0;
return OK;
}
/***************************************************************/
/* */
/* IncludeFile */
/* */
/* Process the INCLUDE command - actually do the file */
/* inclusion. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int IncludeFile(const char *fname)
#else
int IncludeFile(fname)
char *fname;
#endif
{
IncludeStruct *i;
int r;
if (IStackPtr+1 >= INCLUDE_NEST) return E_NESTED_INCLUDE;
i = &IStack[IStackPtr];
i->filename = StrDup(FileName);
if (!i->filename) return E_NO_MEM;
i->LineNo = LineNo;
i->NumIfs = NumIfs;
i->IfFlags = IfFlags;
i->CLine = CLine;
i->offset = -1L;
if (fp) {
i->offset = ftell(fp);
FCLOSE(fp);
}
IStackPtr++;
/* Try to open the new file */
if (!OpenFile(fname)) {
return OK;
}
/* Ugh! We failed! */
if ( (r=PopFile()) ) return r;
Eprint("%s: %s", ErrMsg[E_CANT_OPEN], fname);
return E_CANT_OPEN;
}
/***************************************************************/
/* */
/* GetAccessDate - get the access date of a file. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int GetAccessDate(char *file)
#else
int GetAccessDate(file)
char *file;
#endif
{
struct stat statbuf;
struct tm *t1;
if (stat(file, &statbuf)) return -1;
#ifdef __TURBOC__
t1 = localtime( (time_t *) &(statbuf.st_atime) );
#else
t1 = localtime(&(statbuf.st_atime));
#endif
if (t1->tm_year + 1900 < BASE)
return 0;
else
return Julian(t1->tm_year+1900, t1->tm_mon, t1->tm_mday);
}
/***************************************************************/
/* */
/* SetAccessDate */
/* */
/* Used only by DOS to set access date after we close the */
/* file. Not needed for UNIX. */
/* */
/***************************************************************/
#if defined(__MSDOS__)
/*
* WARNING WARNING WARNING WARNING
* In the version of Turbo C which I have, there is a bug in the
* stdio.h file. The following lines correct the bug. YOU MAY
* HAVE TO REMOVE THESE LINES FOR LATER VERSIONS OF TURBOC
*/
#ifdef __TURBOC__
#ifndef fileno
#define fileno(f) ((f)->fd)
#endif
#endif
#ifdef HAVE_PROTOS
PUBLIC int SetAccessDate(char *fname, int jul)
#else
int SetAccessDate(fname, jul)
char *fname;
int jul;
#endif
{
#ifdef __TURBOC__
int y, m, d;
struct ftime ft;
FILE *f;
FromJulian(jul, &y, &m, &d);
ft.ft_tsec = 0;
ft.ft_min = 0;
ft.ft_hour = 12; /* Arbitrarily set time to noon. */
ft.ft_day = (unsigned int) d;
ft.ft_month = (unsigned int) m+1;
ft.ft_year = (unsigned int) (y - 1980);
f = fopen(fname, "r");
if (!f || setftime(fileno(f) , &ft)) {
#else /* Must be MSC */
if (utime(fname, (struct utimbuf *) NULL)) {
#endif
fprintf(ErrFp, ErrMsg[M_CANTSET_ACCESS], fname);
#ifdef __TURBOC__
if (f) FCLOSE(f);
#endif
return -1;
}
#ifdef __TURBOC__
FCLOSE(f);
#endif
return 0;
}
#endif /* __MSDOS__ */
/***************************************************************/
/* */
/* DestroyCache */
/* */
/* Free all the memory used by a cached file. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PRIVATE void DestroyCache(CachedFile *cf)
#else
static void DestroyCache(cf)
CachedFile *cf;
#endif
{
CachedLine *cl, *cnext;
CachedFile *temp;
if (cf->filename) free(cf->filename);
cl = cf->cache;
while (cl) {
if (cl->text) free (cl->text);
cnext = cl->next;
free(cl);
cl = cnext;
}
if (CachedFiles == cf) CachedFiles = cf->next;
else {
temp = CachedFiles;
while(temp) {
if (temp->next == cf) {
temp->next = cf->next;
break;
}
temp = temp->next;
}
}
free(cf);
}
/***************************************************************/
/* */
/* TopLevel */
/* */
/* Returns 1 if current file is top level, 0 otherwise. */
/* */
/***************************************************************/
#ifdef HAVE_PROTOS
PUBLIC int TopLevel(void)
#else
int TopLevel()
#endif
{
return !IStackPtr;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.