/*---------------------------------------------------------------------------*
*
* BinkleyTerm
*
* (C) Copyright 1987-96, Bit Bucket Software Co.
* For license and contact information see /doc/orig_260/license.260.
*
* This version was modified by the BinkleyTerm XE Team.
* For contact information see /doc/team.lst and /doc/join.us.
* For a complete list of changes see /doc/xe_user.doc and /doc/xe_hist.doc.
*
* Filename : $Source: /cvsroot/btxe/btnt/sources/cache.cpp,v $
* Revision : $Revision: 1.1 $
* Tagname : $Name: $
* Last updated: $Date: 2005/01/01 15:18:12 $
* State : $State: Exp $
* Orig. Author: MMP
*
* Description : Outbound Cache
*
*---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
* CACHE DATA STRUCTURE
*
* The ent[] array in DIRECTORY is a hash table with single chained lists
* of directory entries with the same hash value. If KEEPORDER is defined,
* an additional chain is maintained using DIRECTORY.list and DIRENTRY.dirnext.
* This chain is used to chain the directory entries in the order they were
* originally read. HPFS users will expect the outbound to be sorted like
* they are used to.
*
* dirlist
* |
* V +-----------+ +-----------+ +-----------+
* +-----------------++>|DIRENT next|->|DIRENT next|->|DIRENT next|->NULL
* |DIRECTORY ent[0]|' +-----------+ +-----------+ +-----------+
* | ent[1]|->...
* | : | +-----------+ +-----------+ +-----------+
* | ent[HASHSIZE-1]|->|DIRENT next|->|DIRENT next|->|DIRENT next|->NULL
* | next flow|, +-----------+ +-----------+ +-----------+
* +-----------------+| +-------------+ +-------------+
* | +>|FLOWFILE next|->|FLOWFILE next|->NULL
* | | files | | files |
* | +-------------+ +-------------+
* | | | +------------+
* | | }>|FLOWENT next|->...
* | | | dir |
* | | +------------+
* | | :
* | |
* | | +------------+ +------------+
* | +>|FLOWENT next|->|FLOWENT next|->NULL
* | | dir | | dir |
* | +------------+ +------------+
* V | |
* +-----------------+<-----------------|---------------+
* |DIRECTORY ent[0]|->... |
* | ent[1]|->... |
* | : | |
* | ent[HASHSIZE-1]|->... |
* | next flow|->... |
* +-----------------+ |
* | |
* V |
* +-----------------+<-----------------+
* |DIRECTORY ent[0]|->...
* | ent[1]|->...
* | : |
* | ent[HASHSIZE-1]|->...
* | next flow|->...
* +-----------------+
* |
* V
* NULL
*
* The DIRECTORY structures are considered static; once a directory makes it
* into the list, it is never removed again. The other structures are dynamic
* in the sense, that they are deallocated and reused. They are not actually
* freed, but inserted into free lists. In this way, we avoid heap manager
* overhead, and reduce fragmentation. Structures are allocated in blocks of
* BLOCKSIZE (50), and when the system is up and running, the memory usage is
* likely to have stabilized, and the heap manager is no longer used. However,
* if memory is exhausted, allocations can fail. Therefore, all allocations
* are made by getmem() which turns the cache off, if an allocation error
* occurs.
*
* The structures are dimensioned to DOS' 8.3 char filenames, and the cache
* simply can't contain those names. Long names are handled specially in
* cachestat() so the statistics will be correct, but dirread() will not
* return them. This is no big problem, as Binkley is not looking for long
* names, when scanning directories.
*-----------------------------------------------------------------------------
*/
#include "includes.h"
#ifdef CACHE
static DIRECTORY *dirlist = NULL; /* List of known directories */
static DIRENTRY *defl = NULL; /* Directory Entry Free List */
static FLOWFILE *fffl = NULL; /* Flow File Free List */
static FLOWENT *fefl = NULL; /* Flow Entry Free List */
#define _C_DEBUG
/*-----------------------------------------------------------------------------
* void *getmem(size_t size)
*
* size Number of bytes to allocate
*
* Returns Pointer to new memory on success; NULL on error
*
* This is a simple wrapper around to malloc() used for two purposes:
* 1. Turn the cache off, if a memory allocation occurs
* 2. Keep track of the memory usage for statistics
*-----------------------------------------------------------------------------
*/
static void *
getmem (size_t size)
{
void *p;
if ((p = malloc (size)) == NULL)
{
cachehold = 0;
status_line (MSG_TXT (M_NOMEM_FOR_OUTB_CACHE));
}
else
{
cachemem += size;
memset (p, 0, size); /* For dumping freelists in cachedump() */
}
return (p);
}
/*-----------------------------------------------------------------------------
* void cacheflush(void)
*
* Flushes the cache for all directories. This will force dirload() to actually
* reload the directory. cacheflush() does NOT remove the cached flow files,
* because they can be reused after the directory has been loaded.
*-----------------------------------------------------------------------------
*/
static void
dirflush (DIRECTORY * dir)
{
DIRENTRY *ent;
int i;
if (dir->loaded)
{
for (i = 0; i < HASHSIZE; i++)
while ((ent = dir->ent[i]) != NULL)
dir->ent[i] = ent->next, ent->next = defl, defl = ent;
#ifdef KEEPORDER
dir->list = NULL;
#endif
dir->loaded = 0;
}
}
void
cacheflush (void)
{
DIRECTORY *dir;
#ifdef C_DEBUG
cachedump ("cache.0");
#endif
for (dir = dirlist; dir; dir = dir->next)
dirflush (dir);
#ifdef C_DEBUG
cachedump ("cache.1");
status_line ("Mcacheflush() done");
#endif
}
/*-----------------------------------------------------------------------------
* char *basename(char *path)
*
* path Pathname
*
* Returns Basename within path
*
* Returns a pointer to the basename within a pathname; that is a pointer to
* the first character after the last drive or directory separator. If the
* path does not contain any drive or directory seprators, path itself is
* returned.
*-----------------------------------------------------------------------------
*/
static char *
basename (char *path)
{
char *a, *b;
for (a = "\\:/"; *a; a++)
if ((b = strrchr (path, *a)) != NULL)
path = b + 1;
return (path);
}
/*-----------------------------------------------------------------------------
* DIRECTORY *getdir(char *name)
*
* name Name of file
*
* Returns Diretory structure on success; NULL on error
*
* This function is used for looking up known directories and add new ones. If
* getdir() is able to find <name> in the known directory list, it simply
* returns a pointer to the directory. If getdir() cannot find the directory,
* it is added to the list, and a new (empty) directory structure is returned.
* This addition may fail because of memory problems (getmem), in which case
* NULL is returned.
*-----------------------------------------------------------------------------
*/
static DIRECTORY *
getdir (char *name)
{
char drive; /* Drive letter */
long len; /* Lenght of path without drive */
DIRECTORY *dir; /* The one to return */
DIRECTORY **dpp; /* Used for rearanging list */
int hit; /* Counter for debugging and statistics */
/* Extract drive letter. The drive letter might no be present, in which
* we use the current drive. The logic about the current drive, ensures
* the the current drive is only obtained once; the operating system is
* only bothered with a single call. */
if (isalpha (name[0]) && name[1] == ':')
{
drive = (char) toupper (*name);
name += 2;
}
else
{
static char curdrive = '\0';
if (!curdrive)
{
char cwd[PATHLEN];
getcwd (cwd, sizeof (cwd));
curdrive = (char) toupper (*cwd);
}
drive = curdrive;
}
/* Get length of path. We use the length and memicmp() instead of
* stricmp(). This has two advantanges: 1) it allows us to skip a
* lot of string comparisons 2) we don't need to strip off the
* filename in the name.
*/
len = (long) (basename (name) - name);
/* Check if directory is listed. If we can't find, we continue below
* and creates a new entry for it. However, if we can find it, the
* directory is move in front of the list. This speeds up the next
* call to getdir() if we are searching for a file in the same
* directory, which is most likely. Then we return the directory.
*/
hit = 0;
dpp = &dirlist;
for (dir = *dpp; dir; dpp = &dir->next, dir = *dpp, hit++)
if (drive == dir->drive && len == dir->len && !memicmp (dir->name, name,
(size_t) len))
{
#ifdef C_DEBUG
status_line ("MDIRECTORY %c:%.*s hit at %d", drive, len, name, hit);
#endif
*dpp = dir->next;
dir->next = dirlist;
dirlist = dir;
return (dir);
}
/* Allocate new directory node */
if ((dir = (DIRECTORY *) getmem ((size_t) (sizeof (DIRECTORY) + len))) == NULL)
return (NULL);
/* Initalize new node, and insert in list */
#ifdef C_DEBUG
status_line ("MDIRECTORY %c:%.*s not hit", drive, len, name);
#endif
dir->flow = NULL;
dir->loaded = 0;
memset (dir->ent, 0, sizeof (dir->ent)); /* Reset hash table */
#ifdef KEEPORDER
dir->list = NULL;
#endif
dir->drive = drive;
dir->len = (int) len;
memcpy (dir->name, name, (size_t) len);
dir->name[(int) len] = '\0';
/* Insert in directory list */
dir->next = dirlist;
dirlist = dir;
/* Return new directory node */
return (dir);
}
/*-----------------------------------------------------------------------------
* int strhash(char *str)
*
* str String
*
* Returns Hash value
*
* Computes a hash value in the range 0..HASHSIZE-1 for the string str. The
* same hash key is generated for both lower and upper case strings.
*-----------------------------------------------------------------------------
*/
#define NBITS (sizeof(unsigned)<<3) /* bits in an int (2*8 or 4*8) */
#define N75 ((unsigned)(((NBITS)*3)>>2))
#define N12 ((unsigned)((NBITS)>>3))
#define HB (~((unsigned)(~0)>>N12))
/*
----------------------------------- 16 bits --------------------------
nbits = 16 == 2*8
n75 = 12 == ((2*8)*3)/4 == 12
N12 = 2 == (2*8)/8
HB = ~(1111 1111 1111 1111 >> n12)
= ~(0011 1111 1111 1111)
= 0xC000
----------------------------------- 32 bits --------------------------
nbits = 32 == 4*8
n75 = 24 == ((4*8)*3)/4 == 24
n12 = 4 == (4*8)/8
HB = ~(1111 1111 1111 1111 1111 1111 1111 1111 >> N12)
= ~(0000 1111 1111 1111 1111 1111 1111 1111)
= 0xF0000000
----------------------------------------------------------------------
*/
static int
strhash (char *s)
{
unsigned g, h = 0;
while (*s)
{
h = (h << N12) + toupper (*s);
s++;
if ((g = (h & HB)) != 0)
h = (h ^ (g >> N75)) & ~HB;
}
return ((int) (h % HASHSIZE));
}
/*-----------------------------------------------------------------------------
* int isdosname(char *name)
*
* name Basename
*
* Returns Non-zero if 8.3, zero if not
*
* Checks if a filename, without path, is a valid DOS 8.3 name.
*-----------------------------------------------------------------------------
*/
static int
isdosname (char *name)
{
char *e;
e = strchr (name, '.');
if (e)
return ((e - name) < 9 && strlen (e) < 5 && (e > name) && *name);
return (strlen (name) < 9 && *name);
}
/*-----------------------------------------------------------------------------
* int dirload(DIRECTORY *dir)
*
* dir Directory
* lazy Flag. Use cache if available?
*
* Returns 0 on success; non-zero on memory problems.
*
* Loads a directory from the disk into the cache. If the directory is already
* cached, this is a no-operation. Use cacheflush() first to force a reload of
* directories. dirload() may fail because of memory problems, in which case a
* non-zero value if returned.
*-----------------------------------------------------------------------------
*/
static int
dirload (DIRECTORY * dir)
{
DTA dta;
DIRENTRY *ent;
FLOWFILE *flow;
FLOWFILE **flowp;
FLOWENT *fent;
#ifdef KEEPORDER
DIRENTRY *last;
#endif
char path[PATHLEN];
int j, cnt;
int hash; /* Hash key for filename */
/* Check if directory is already loaded */
if (dir->loaded)
{
#ifdef C_DEBUG
status_line ("M%c:%s already loaded", dir->drive, dir->name);
#endif
return (0);
}
/* Setup search path */
sprintf (path, "%c:%s*.*", dir->drive, dir->name);
/* Traverse directory. */
cnt = j = 0;
#ifdef KEEPORDER
dir->list = last = NULL;
#endif
while (!dfind (&dta, path, j))
{
j = 1;
if (isdosname (dta.name) && !(dta.attr & FA_VOLID))
{
/* Allocate DIRENTRY structure from the free list. If the free list
* is empty, we expand it first. */
if (defl == NULL)
{
int i;
#ifdef C_DEBUG
status_line ("MExpanding freelist with DIRENTRY");
#endif
if ((defl = getmem (BLOCKSIZE * sizeof (DIRENTRY))) == NULL)
return (1);
for (i = 0; i < BLOCKSIZE - 1; i++)
defl[i].next = defl + i + 1;
defl[i].next = NULL;
}
ent = defl;
defl = defl->next;
/* Initialize directory entry. The name is lower cased, because this
* will simplify comparisons later when traversing the directory. */
ent->time = dta.time;
ent->attr = dta.attr;
ent->size = dta.size;
strcpy (ent->name, dta.name);
strlwr (ent->name); /* Better now, than when comparing */
/* Insert in hash table of directory */
hash = strhash (ent->name);
ent->next = dir->ent[hash];
dir->ent[hash] = ent;
/* Insert in single chained list */
#ifdef KEEPORDER
ent->dirnext = NULL;
if (last)
last->dirnext = ent;
else
dir->list = ent;
last = ent;
#endif
cnt++;
}
}
#ifdef C_DEBUG
status_line ("Mdirload(%s) found %d files", path, cnt);
/* Dump hash table */
for (hash = 0; hash < HASHSIZE; hash++)
{
for (ent = dir->ent[hash]; ent; ent = ent->next)
status_line ("M dir[\"%s\"].ent[%d], %s", dir->name, hash, ent->name);
}
/* Dump single chained list */
for (ent = dir->list; ent; ent = ent->next)
{
status_line ("M %s%s", dir->name, ent->name);
}
#endif
/* Clean up */
if (j)
dfind (&dta, NULL, 2);
/* Set time of loading and return */
dir->loaded = unix_time (NULL);
/* Invalidate cached flow files */
flowp = &dir->flow, j = 0;
while ((flow = *flowp) != NULL)
{
#ifdef C_DEBUG
status_line ("Mdirload(): check %s", flow->name);
#endif
ent = dir->ent[flow->hash];
while (ent && strcmp (ent->name, flow->name))
ent = ent->next;
if (ent == NULL ||
ent->time != flow->time ||
ent->size != flow->size ||
ent->attr != flow->attr)
{
#ifdef C_DEBUG
status_line ("M%s%s has %s", dir->name, flow->name, ent ? "changed" :
"disappeared");
#endif
while ((fent = flow->files) != NULL)
{
flow->files = fent->next;
fent->next = fefl;
fefl = fent;
}
*flowp = flow->next;
flow->next = fffl;
fffl = flow;
j++;
}
else
{
flowp = &flow->next;
}
}
#ifdef C_DEBUG
if (j)
{
status_line ("M%d flow files invalidated. Dumping cache", j);
cachedump ("cache.inv");
}
#endif
return (0);
}
/*-----------------------------------------------------------------------------
* void dirinit(DIRBUF *buf, char *fspec)
*
* buf Directory buffer
* fspec File specification
*
* Initializes reading of a directory. After the setup, the directory entries
* can actually be read using dirread().
*
* dirinit() and dirread() implements the optional caching of directories. If
* caching is turned off, they are simple wrappers around dfind(). If caching
* is turned on, dirinit() sets up a list of directory entries by loading the
* directory, or simple initializes the pointer to an already loaded directory
* list.
*-----------------------------------------------------------------------------
*/
void
dirinit (DIRBUF * buf, char *fspec)
{
DIRECTORY *dir;
int len;
char *src, *srcext, *dest;
if (!cachehold ||
(dir = getdir (fspec)) == NULL ||
(!dir->loaded && dirload (dir)))
{
/* Better be safe and clear the DTA structure. Originally, it was
* initialized to {0} everywhere, eventhough i can se no reason for
* that */
buf->cache = 0;
memset (&buf->u.slow.dta, 0, sizeof (DTA));
buf->u.slow.more = buf->u.slow.cleanup = !dfind (&buf->u.slow.dta,
fspec, 0);
return;
}
/* Initialize cached traversal */
buf->cache = 1;
buf->u.fast.dir = dir;
#ifdef KEEPORDER
buf->u.fast.next = dir->list;
#else
buf->u.fast.hash = 0;
buf->u.fast.next = dir->ent[0];
#endif
/* Format mask. dirread() depends on 11 character with space padding,
* '*' expanded to '?'s, and upper case letters.
*/
src = basename (fspec), srcext = strchr (src, '.'), len = strlen (src);
strcpy (buf->u.fast.mask, " "); /* 11 spaces */
if (srcext == NULL)
srcext = "???";
else
srcext++;
len = 0, dest = buf->u.fast.mask;
while (*src && *src != '.' && len < 8)
{
if (*src == '*')
*dest++ = '?';
else
*dest++ = *src++;
len++;
}
len = 0, dest = buf->u.fast.mask + 8;
while (*srcext && len < 3)
{
if (*srcext == '*')
*dest++ = '?';
else
*dest++ = *srcext++;
len++;
}
strupr (buf->u.fast.mask);
}
/*-----------------------------------------------------------------------------
* int fmatch(char *name, char *mask)
*
* name Filename without path
* mask Filemask without path
*
* Returns Non-zero if match; zero if mismatch
*
* Compares the name with the mask, and returns the result. This routine
* assumes that the name is no longer than 12 characters.
*-----------------------------------------------------------------------------
*/
static int
fmatch (char *name, char *mask)
{
char nbuf[12];
char *e;
int len;
len = strlen (name);
e = strchr (name, '.');
strcpy (nbuf, " "); /* 11 spaces */
if (e)
{
if (e > name + 8)
return (0);
memcpy (nbuf, name, (size_t) (e - name));
e++, len = strlen (e);
memcpy (nbuf + 8, e, len);
}
else
{
memcpy (nbuf, name, len);
}
strupr (nbuf);
/* Match filename */
name = nbuf;
while (*name && *mask)
{
if (*mask != '?' && *mask != *name)
return (0);
name++, mask++;
}
return (*name == '\0' && *mask == '\0');
}
/*-----------------------------------------------------------------------------
* int dirread(DIRBUF *buf)
*
* buf Directory buffer
*
* Returns 0 on success; non-zero on error
*
* Reads the next entry from the directory buffer. If there are no more
* available entries, a non-zero value is returned. If zero is returned,
* the entry is returned in the DIRBUF fields time, attr, size, and name
* fields.
*-----------------------------------------------------------------------------
*/
int
dirread (DIRBUF * buf)
{
int res;
if (!buf->cache)
{
res = !buf->u.slow.more;
if (!res)
{
buf->attr = buf->u.slow.dta.attr;
buf->size = buf->u.slow.dta.size;
buf->time = buf->u.slow.dta.time;
strcpy (buf->name, buf->u.slow.dta.name);
buf->u.slow.more = !dfind (&buf->u.slow.dta, NULL, 1);
}
}
else
{
#ifdef KEEPORDER
DIRENTRY *ent;
res = 1;
while ((ent = buf->u.fast.next) != NULL)
{
buf->u.fast.next = ent->dirnext;
if (fmatch (ent->name, buf->u.fast.mask))
{
buf->attr = ent->attr;
buf->size = ent->size;
buf->time = ent->time;
strcpy (buf->name, ent->name);
res = 0;
break;
}
}
#else
DIRENTRY *ent;
DIRECTORY *dir;
int hash;
hash = buf->u.fast.hash;
dir = buf->u.fast.dir;
res = 1;
while (res && hash < HASHSIZE)
{
while ((ent = buf->u.fast.next) != NULL)
{
buf->u.fast.next = ent->next;
if (fmatch (ent->name, buf->u.fast.mask))
{
buf->attr = ent->attr;
buf->size = ent->size;
buf->time = ent->time;
strcpy (buf->name, ent->name);
res = 0;
break;
}
}
if (res)
{
if (hash++ < HASHSIZE)
buf->u.fast.next = dir->ent[hash];
else
buf->u.fast.next = NULL;
}
}
buf->u.fast.hash = hash;
#endif
}
return (res);
}
/*-----------------------------------------------------------------------------
* void dirquit(DIRBUF *buf, int flush)
*
* buf Directory buffer
* flush Flag. Flush directory?
*
* Returns -
*
* Deallocate any memory neccesary when stopping reading of a directory.
*-----------------------------------------------------------------------------
*/
void
dirquit (DIRBUF * buf, int flush)
{
if (!buf->cache)
{
if (buf->u.slow.cleanup)
dfind (&buf->u.slow.dta, NULL, 2);
}
else
{
if (flush)
dirflush (buf->u.fast.dir);
}
}
/*-----------------------------------------------------------------------------
* time_t dos2unix(unsigned long dostime)
*
* time DOS file time stamp
*
* Returns Unix style time stamp
*
* Converts the timestamp to a unix style julian
*-----------------------------------------------------------------------------
*/
static time_t
dos2unix (unsigned long dostime)
{
struct tm t;
t.tm_sec = (int) (dostime & 0x1FUL) * 2;
dostime >>= 5UL;
t.tm_min = (int) (dostime & 0x3FUL);
dostime >>= 6UL;
t.tm_hour = (int) (dostime & 0x1FUL);
dostime >>= 5UL;
t.tm_mday = (int) (dostime & 0x1FUL);
dostime >>= 5UL;
t.tm_mon = (int) (dostime & 0x0FUL) - 1;
dostime >>= 4UL;
t.tm_year = (int) (dostime + 80); /* TJW 971020 bugfix */
t.tm_wday = t.tm_yday = t.tm_isdst = -1;
return (unix_mktime (&t));
}
#endif /* CACHE */
/*-----------------------------------------------------------------------------
* int cachestat(char *path, struct stat *buf, DIRECTORY *dirp, DIRENTRY **entp,
* int freqflag, long freqfilesize);
*
* path Pathname of file
* buf stat buffer
* dirp Where to store directory, or NULL
* dp Where to store pointer to directory entry, or NULL
* freqfilesize if != 0 : size of file to be frequested
*
* Returns 0 on success; non-zero on error
*
* stat() replacement that takes advantage of the information stored in the
* cached directories. If cachestat() can find the entry in the cache, only
* the st_size, st_mtime, and the directory attribute of the st_mode field
* are guaranteed to be be valid.
*
* If (dp!=NULL), a pointer to the found cached entry is returned in *dp, if
* found, otherwise *dp is set to NULL. cachestat() may run out of memory, in
* which case cachehold will be reset.
*-----------------------------------------------------------------------------
*/
int
cachestat (char *path, struct stat *buf,
#ifdef CACHE
DIRECTORY ** dirp, DIRENTRY ** entp,
#endif
int freqflag, long freqfilesize)
{
#ifdef CACHE
DIRECTORY *dir;
DIRENTRY *ent;
char *base;
int hit;
/* Return NULL's */
if (dirp)
*dirp = NULL;
if (entp)
*entp = NULL;
#endif /* CACHE */
if (freqflag)
{ /* keep this short, this is a freq */
buf->st_size = freqfilesize;
#ifdef __WATCOMC__ /* TS 960901 seems only Watcom does it like this */
buf->st_attr = 0;
#else /* anybody else except Watcom */
buf->st_mode = 0;
#endif
buf->st_mtime = unix_time (NULL);
buf->st_ctime = buf->st_mtime;
buf->st_atime = buf->st_mtime;
return (0);
}
#ifdef CACHE
/* Get directory structure */
if (!cachehold || (dir = getdir (path)) == NULL)
goto fallback;
if (dirp)
*dirp = dir;
/* Load directory if neccesary */
if (!dir->loaded && dirload (dir))
goto fallback;
/* Get basename, and check if it is not an 8.3 name (can't cache them) */
base = basename (path);
if (!isdosname (base))
goto fallback;
/* Search directory for a match */
for (hit = 0, ent = dir->ent[strhash (base)]; ent; ent = ent->next, hit++)
{
if (!stricmp (base, ent->name))
{
if (entp)
*entp = ent;
buf->st_size = ent->size;
#ifdef __WATCOMC__ /* TS 960901 seems only Watcom does it like this */
buf->st_attr = 0;
#else /* anybody else except Watcom */
buf->st_mode = 0;
#endif
buf->st_mtime = dos2unix (ent->time);
buf->st_ctime = buf->st_mtime;
buf->st_atime = buf->st_mtime;
if (ent->attr & FA_SUBDIR)
#ifdef __WATCOMC__ /* TS 960901 seems only Watcom does it like this */
buf->st_attr |= S_IFDIR;
#else /* anybody else except Watcom */
buf->st_mode |= S_IFDIR;
#endif
#ifdef C_DEBUG
status_line ("M%s%s hit at %d", dir->name, ent->name, hit);
#endif
return (0);
}
}
/* It wasn't there - report that */
#ifdef C_DEBUG
status_line ("M%s not found in cache", path);
#endif
errno = ENOENT;
return (-1);
fallback:
#endif /* CACHE */
return (unix_stat (path, buf));
}
#ifdef CACHE
/*-----------------------------------------------------------------------------
* int isarc(char *name)
*
* name Filename, upper case
*
* Returns 1 if ArcMail bundle; 0 if not
*
* Checks if the filename designates an ArcMail bundle or not
*-----------------------------------------------------------------------------
*/
static int
isarc (char *name)
{
static char week[] = "MOTUWETHFRSASU";
int i;
if (strlen (name) != 12)
return (0);
for (i = 0; i < 8; i++)
if (!isxdigit (name[i]))
return (0);
if (name[i++] != '.')
return (0);
if (!isdigit (name[11]))
return (0);
for (i = 0; i < 14; i += 2)
if (name[9] == week[i] && name[10] == week[i + 1])
break;
return (i < 14);
}
#endif /* CACHE */
/*-----------------------------------------------------------------------------
* long netsize(MAILP p,char *extension)
*
* p Mail node
*
* Returns Bytes waiting
*
* This is a replacement or reimplementation of netsize found in mailovly.c,
* that takes advantage of the cache.
*-----------------------------------------------------------------------------
*/
long
netsize (MAILP p, char *ext, unsigned short AttachType)
{
struct stat stbuf;
char net_path[PATHLEN];
char *ptr;
FILE *fp;
char *q;
#ifdef CACHE
char *base = NULL;
int cachethis;
DIRECTORY *flowdir;
DIRECTORY *dir = NULL;
DIRENTRY *ent;
FLOWFILE *flow = NULL;
FLOWENT *fent;
#endif /* CACHE */
unsigned long holdtime;
unsigned short holdcnt;
long holdsize;
int freqflag;
long freqfilesize;
if (!p)
return (0);
/* Append the ARCmail file name to the path line */
sprintf (net_path, "%s%s.%s", HoldAreaNameMunge (&(p->mail_addr)),
Hex_Addr_Str (&(p->mail_addr)), ext);
#ifdef CACHE
/* Set cache flag */
cachethis = (cachehold > 1);
#endif /* CACHE */
/* TJW 960809 Is this a REQ file ? */
freqflag = !strnicmp (ext, "req", 3);
/* Reset counters */
holdtime = 4294967295UL;
holdcnt = 0;
holdsize = 0;
#ifdef CACHE
/* Try to locate flow file in cache */
if (!freqflag && cachethis)
{ /* !freqflag for debugging */
base = basename (net_path);
if ((dir = getdir (net_path)) != NULL)
for (flow = dir->flow; flow; flow = flow->next)
if (!stricmp (base, flow->name))
{
#ifdef C_DEBUG
status_line ("Mfound %s in cache", flow->name);
#endif
for (fent = flow->files; fent; fent = fent->next)
{
flowdir = fent->dir;
if (!flowdir)
{
cachethis = 0;
holdsize = 0;
holdcnt = 0;
goto fallback;
}
if (!flowdir->loaded && dirload (flowdir))
{
cachethis = 0;
holdsize = 0;
holdcnt = 0;
goto fallback;
}
ent = flowdir->ent[fent->hash];
while (ent && strcmp (ent->name, fent->name))
ent = ent->next;
if (ent)
{
/* CFS 970504 */
if (!AttachType || (AttachType & get_filetype (ent->name)))
{
holdcnt++;
holdsize += ent->size;
}
if (isarc (ent->name))
{
if ((unsigned long) ent->time < holdtime)
holdtime = ent->time;
}
}
}
if (holdtime != 4294967295UL)
{
holdtime = dos2unix (holdtime);
if (holdtime < (unsigned long) p->oldest)
p->oldest = holdtime;
}
p->numfiles += holdcnt;
return (holdsize);
}
}
fallback:
#endif /* CACHE */
/* Open file */
if ((fp = share_fopen (net_path, read_binary, DENY_NONE)) == NULL)
return (0);
#ifdef CACHE
/* Initialize FLOWFILE structure */
if (!freqflag && cachethis)
{ /* !freqflag for debugging */
/* Get information on the flow file */
if (cachestat (net_path, &stbuf, NULL, &ent, 0, 0))
return (0);
/* Allocate FLOWFILE structure from the free list. If the free list
* is empty, we expand it first.
*/
if (fffl == NULL)
{
if ((fffl = getmem (BLOCKSIZE * sizeof (FLOWFILE))) != NULL)
{
int i;
for (i = 0; i < BLOCKSIZE - 1; i++)
fffl[i].next = fffl + i + 1;
fffl[i].next = NULL;
}
}
if (fffl)
{
flow = fffl;
fffl = flow->next;
strntcpy (flow->name, base, 13);
strlwr (flow->name);
flow->hash = strhash (flow->name);
flow->time = ent->time;
flow->size = ent->size;
flow->attr = ent->attr;
flow->files = NULL;
/* Insert into directory structure */
flow->next = dir->flow;
dir->flow = flow;
#ifdef C_DEBUG
status_line ("MRegistered %s%s", dir->name, dir->flow->name);
#endif
}
else
{
cachethis = 0;
}
}
#endif /* CACHE */
while (!feof (fp))
{
/*
* Make sure of a nice zero there if we're at an undetected EOF.
* Then try to read a line from the file. MMP - also make sure to
* NUL-terminate the line if it is too long.
*/
net_path[0] = net_path[sizeof (net_path) - 1] = '\0';
fgets (net_path, sizeof (net_path) - 1, fp);
freqfilesize = 0; /* init size */
if (freqflag)
{
/*
* Get file sizes out of request file:
* NNNNnnnn.REQ:
* FILENAME.123 !PASSWORD $FILESIZEBYTES
* ...
*/
q = ptr = net_path;
if (!*ptr)
continue; /* nothing there */
else if (*ptr == '+') /* function request ? */
ptr++;
q = skip_to_blank (ptr);
if (*q) /* if there is anything left */
*q++ = '\0'; /* 0-terminate filename */
while (*q && *q != '$')
q = skip_to_word (q);
if (*q == '$') /* do we have size information ? */
freqfilesize = atol (++q); /* get size */
#ifdef C_DEBUG
status_line ("MFreq %s size %ld", ptr, freqfilesize);
#endif
}
else
{
/*
* Clean up anything we don't want to see (blanks, tabs, CR, etc)
*/
for (q = ptr = net_path; *q; q++)
if (*q <= ' ')
*q = '\0';
/*
* File disposition commands should be skipped over to get to
* actual filenames.
*/
if ((*ptr == TRUNC_AFTER) ||
(*ptr == DELETE_AFTER) ||
(*ptr == SHOW_DELETE_AFTER) ||
(*ptr == NOTHING_AFTER))
ptr++;
/*
* Now -- if what's left starts with a semicolon, it's a comment.
* If it starts with a tilde, the file has already been sent. If
* what we see is a zero, there's nothing on the line. In any one
* of these cases, we should skip this line.
*/
if ((*ptr == '\0') ||
(*ptr == ';') ||
(*ptr == FILE_ALREADY_SENT))
continue;
}
/*
* Get the file size by doing a stat call. If it's not there
* then we obviously need not add any size in. If the file is
* arcmail, figure it into the "oldest" calculation.
*/
if (!AttachType || (AttachType & get_filetype (ptr)))
{
#ifdef CACHE
if (cachestat (ptr, &stbuf, &flowdir, NULL, freqflag, freqfilesize))
#else
if (cachestat (ptr, &stbuf, freqflag, freqfilesize))
#endif
/* file exist? */
continue;
else
{
if (!freqflag) /* REQs don't count as hold file, but */
holdcnt++;
holdsize += stbuf.st_size; /* this also includes FREQ traffic now */
#ifdef CACHE
/* Register file */
if (!freqflag && cachethis)
{ /* !freqflag for debugging */
if (!isdosname (base = basename (ptr)))
{
status_line ("MNot DOS 8.3 name: %s", ptr);
/* The file is not a DOS 8.3 name. Our structures are not
* dimensioned for anything else, so we cant cache this
* particular file. Clear the dos flag, and deallocate
*/
while ((fent = flow->files) != NULL)
{
flow->files = fent->next;
fent->next = fefl;
fefl = fent;
}
dir->flow = flow->next;
flow->next = fffl;
fffl = flow;
cachethis = 0;
}
else
{
/* Expand free list */
if (fefl == NULL && (fefl = getmem (BLOCKSIZE * sizeof
(FLOWENT))) != NULL)
{
int i;
for (i = 0; i < BLOCKSIZE - 1; i++)
fefl[i].next = fefl + i + 1;
fefl[i].next = NULL;
}
if (fefl)
{
/* Allocate node */
fent = fefl;
fefl = fent->next;
/* Register file */
fent->dir = flowdir;
strcpy (fent->name, base);
strlwr (fent->name);
fent->hash = strhash (fent->name);
/* Insert in chain */
fent->next = flow->files;
flow->files = fent;
}
}
}
#endif /* CACHE */
if (!freqflag && is_arcmail (ptr, 1 - strlen (ptr)))
{
if ((unsigned long) stbuf.st_mtime < (unsigned long) holdtime)
holdtime = stbuf.st_mtime;
}
}
}
}
fclose (fp);
/* Summarize */
p->numfiles += holdcnt;
p->oldest = min ((unsigned long) holdtime, p->oldest);
return (holdsize);
}
#ifdef CACHE
/*-----------------------------------------------------------------------------
* int cachedump(char *path)
*
* path Pathname of output file
*
* Returns 0 on success; non-zero on i/o error
*
* Dumps the contents of the cache to the file specified
*-----------------------------------------------------------------------------
*/
#ifdef C_DEBUG /* DEBUGGING */
static char *
timestr (time_t t)
{
static char buf[20];
strftime (buf, 20, "%y-%m-%d %H:%M:%S", unix_localtime (&t));
return (buf);
}
static char *
ptrstr (void *p)
{
static char buf[20];
if (p)
sprintf (buf, "*%p", p);
else
strcpy (buf, "NULL");
return (buf);
}
int
cachedump (char *path)
{
FILE *fp;
DIRECTORY *dir;
DIRENTRY *ent;
FLOWFILE *flow;
FLOWENT *fent;
int hash;
if ((fp = fopen (path, write_ascii)) == NULL)
return (errno);
for (dir = dirlist; dir; dir = dir->next)
{
fprintf (fp, "DIRECTORY at %s\n", ptrstr (dir));
fprintf (fp, " next %s\n", ptrstr (dir->next));
fprintf (fp, " flow %s\n", ptrstr (dir->flow));
#ifdef KEEPORDER
fprintf (fp, " list %s\n", ptrstr (dir->list));
#endif
for (hash = 0; hash < HASHSIZE; hash++)
if (dir->ent[hash])
fprintf (fp, " ent[%02d] %s\n", hash, ptrstr (dir->ent[hash]));
fprintf (fp, " loaded 0x%08X (%s)\n", dir->loaded, timestr (dir->loaded));
fprintf (fp, " len %d\n", dir->len);
fprintf (fp, " drive '%c'\n", dir->drive);
fprintf (fp, " name \"%s\"\n", dir->name);
fprintf (fp, "\n");
for (flow = dir->flow; flow; flow = flow->next)
{
fprintf (fp, " FLOWFILE at %s\n", ptrstr (flow));
fprintf (fp, " next %s\n", ptrstr (flow->next));
fprintf (fp, " time 0x%08X (%s)\n", flow->time, timestr
(dos2unix (flow->time)));
fprintf (fp, " size %ld\n", flow->size);
fprintf (fp, " attr 0x%02X\n", flow->attr);
fprintf (fp, " name \"%s\"\n", flow->name);
fprintf (fp, " hash %d\n", flow->hash);
fprintf (fp, " files %s\n", ptrstr (flow->files));
for (fent = flow->files; fent; fent = fent->next)
{
fprintf (fp, " FLOWENT at %s\n", ptrstr (fent));
fprintf (fp, " next %s\n", ptrstr (fent->next));
fprintf (fp, " dir %s (\"%c:%s\")\n", ptrstr
(fent->dir), fent->dir->drive, fent->dir->name);
fprintf (fp, " name \"%s\"\n", fent->name);
fprintf (fp, " hash %d\n", fent->hash);
}
fprintf (fp, "\n");
}
for (hash = 0; hash < HASHSIZE; hash++)
if (dir->ent[hash])
{
fprintf (fp, " Hash chain ent[%d] starting at %s\n", hash, ptrstr
(dir->ent[hash]));
for (ent = dir->ent[hash]; ent; ent = ent->next)
{
fprintf (fp, " DIRENTRY at %s\n", ptrstr (ent));
fprintf (fp, " next %s\n", ptrstr (ent->next));
#ifdef KEEPORDER
fprintf (fp, " dirnext %s\n", ptrstr (ent->dirnext));
#endif
fprintf (fp, " time 0x%08X (%s)\n", ent->time,
timestr (dos2unix (ent->time)));
fprintf (fp, " size %ld\n", ent->size);
fprintf (fp, " attr 0x%02X\n", ent->attr);
fprintf (fp, " name \"%s\"\n", ent->name);
}
}
fprintf (fp, "\n");
}
fprintf (fp, "\n");
fprintf (fp, "DIRENTRY freelist starting at %s\n", ptrstr (defl));
for (ent = defl; ent; ent = ent->next)
{
fprintf (fp, " DIRENTRY at %s\n", ptrstr (ent));
fprintf (fp, " next %s\n", ptrstr (ent->next));
#ifdef KEEPORDER
fprintf (fp, " dirnext %s\n", ptrstr (ent->dirnext));
#endif
fprintf (fp, " time 0x%08X (%s)\n", ent->time, timestr
(dos2unix (ent->time)));
fprintf (fp, " size %ld\n", ent->size);
fprintf (fp, " attr 0x%02X\n", ent->attr);
fprintf (fp, " name \"%s\"\n", ent->name);
}
fprintf (fp, "\n");
fprintf (fp, "FLOWFILE freelist starting at %s\n", ptrstr (fffl));
for (flow = fffl; flow; flow = flow->next)
{
fprintf (fp, " FLOWFILE at %s\n", ptrstr (flow));
fprintf (fp, " next %s\n", ptrstr (flow->next));
fprintf (fp, " time 0x%08X (%s)\n", flow->time, timestr
(dos2unix (flow->time)));
fprintf (fp, " size %ld\n", flow->size);
fprintf (fp, " attr 0x%02X\n", flow->attr);
fprintf (fp, " name \"%s\"\n", flow->name);
fprintf (fp, " hash %d\n", flow->hash);
fprintf (fp, " files %s\n", ptrstr (flow->files));
}
fprintf (fp, "\n");
fprintf (fp, "FLOWENT freelist starting at %s\n", ptrstr (fefl));
for (fent = fefl; fent; fent = fent->next)
{
fprintf (fp, " FLOWENT at %s\n", ptrstr (fent));
fprintf (fp, " next %s\n", ptrstr (fent->next));
if (fent->dir)
fprintf (fp, " dir %s (\"%c:%s\")\n", ptrstr (fent->dir),
fent->dir->drive, fent->dir->name);
else
fprintf (fp, " dir %s\n", ptrstr (fent->dir));
fprintf (fp, " name \"%s\"\n", fent->name);
fprintf (fp, " hash %d\n", fent->hash);
}
if (fclose (fp))
return (errno);
return (0);
}
#endif /* DEBUGGING */
#endif /* CACHE */
/* $Id: cache.cpp,v 1.1 2005/01/01 15:18:12 vildanov Exp $ */