home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.wwiv.com
/
ftp.wwiv.com.zip
/
ftp.wwiv.com
/
pub
/
BBS
/
FLIST24.ZIP
/
FLIST.C
next >
Wrap
Text File
|
1993-01-10
|
33KB
|
1,037 lines
/*
FLIST -- Generates file lists based on privilege levels and keys
Version 2.4 (1/10/93)
Written by Bob Quinlan of Austin, Texas, USA
Sysop of Red October at 512-834-2593 (1:382/111)
Special thanks to David Powers for suggestions and code.
Copyright 1993 by Bob Quinlan
Compatible with Maximus 2.00 and 2.01
This program generates a list of files that a user with a particular
privilege level and set of keys could access. It is intended to make
the procedure independent of changes to the structure of your file
areas. Instead of specifying which areas should be included you just
specify what sort of user you want the list to apply to.
You can also create lists of new files. Just use /D to specify how many
days of files you want to list.
/Afile Area file. You can use this to specify a path to
AREA.DAT or to refer to another file (although it must
follow the AREA.DAT format). The filename must be
included along with the path. Defaults to AREA.DAT in
the current directory.
/B Include barricaded areas. If this switch is not
included no barricaded areas will be included.
/C Include comments from the FILES.BBS file lists. By
default only files and their descriptions are included.
Using this switch has one side-effect! Normally area
headers are only generated for areas that have files in
them. /C will cause them to be generated for any area
that has comments as well. Do not use /C if you only
want areas that have files in them to appear! (I
recommend not using /C with /D so that you don't show
lots of apparently empty file areas.)
/Ddays Days. Excludes all files more than this many days old.
Use this switch to create lists of new files.
/Ffile Output file. The pathname of the file you want the file
list written to. Defaults to FLIST.TXT in the current
area.
/HAfile Area header file. The contents of this file will be
added to the report for each area header. There are
several sequences you can embed in the file that have
special meanings. All of the codes explained under /HS
are allowed except for %C and %B.
If no area header file is specified a default header
will be used.
/HBfile Beginning header file. The contents of this file will
be added to the beginning of the report. There are
several sequences you can embed in the file that have
special meanings. The codes %D, %T, and %A (explained
under /HS) are allowed.
/HEfile End header file. The contents of this file will be
added to the end of the report. There are several
sequences you can embed in the file that have special
meanings. The codes %F, %S, %D, %T, and %A (explained
under /HS) are allowed.
/HSfile Area summary header file. The contents of this file
will be added to the report after each area. There are
several sequences you can embed in the file that have
special meanings:
%N will be replaced with the area name.
%I will be replaced with the area information.
%C will be replaced with the area file count.
%B will be replaced with the area file size total (bytes).
%F will be replaced with the overall file count.
%S will be replaced with the overall file size total.
%D will be replaced with the report date.
%T will be replaced with the report time.
%A will be replaced with the age of the files
included in the report. Do not use this except
with the /D command line parameter!
Any of these may be followed by an L, R, or C
immediately followed by a number. These will left,
right, or center justify the value in a field of the
specified width.
You can also follow any of the above with an NF or
with an Fx. NF stands for "no fill" and has no
effect (see examples). Fx stands for "fill" and
will cause the entire field to be replaced by an
equivalent number of the character 'x'. This is
useful when drawing boxes.
/Kkeys Keys to use for access to locked areas. Keys are
represented by 1-8 and A-X. An asterisk (*) turns on
all of the keys. Multiple keys can be specified in one
/K argument (such as /K12B).
/N No-wrap. Leave long description lines intact. The
default is to wrap them.
/O Include offline files. By default files that are listed
but not present are not included in the list.
/Ppriv Privilege to use for access to areas. Only the first
letter of the level name is required. Legal values are:
Twit
Disgrace
Limited
Normal
Worthy
Privil
Favored
Extra
Clerk
AsstSysop
Sysop
Hidden
Defaults to Normal.
/T Truncate long description lines. The default is to wrap
them.
/Vcount Volume. 'count' is the number of files each dot stands
for in the progress indicator. Zero will turn off all
progress displays.
/Wcolumn Wrap column. Sets line wrapping to the specified column
relative to the left margin.
Let me show you an example of how to use the %N and %I parameters in an
area header file (defined with the /HA switch). The purpose of those
parameters is to let you build boxes that will conform to the size of
the text produced by %N and %I. Here is an example of a simple header
box using these parameters:
┌──%NF───┬──%IF───┐
│ %NNF │ %INF │
└──%NF───┴──%IF───┘
Note that the characters following the %NF and %IF look like a part of
the regular text, but are actually the characters that will be used to
fill out the parameters to the desired length.
This will expand to something that looks like this:
┌───────┬───────────────────────────────────────────┐
│ MAX │ Maximus BBS files and related utilities │
└───────┴───────────────────────────────────────────┘
Now you can see why the apparently useless NF modifier can be useful. It
pads out the shorter embedded codes so that they visually match those in
the surrounding lines. The following would produce exactly the same
result as the previous example, but it is a lot harder to figure out how
everything will match up:
┌──%NF───┬──%IF───┐
│ %N │ %I │
└──%NF───┴──%IF───┘
Here is an example using fixed width fields. This also right justifies
the area name and left justifies the area information:
┌─%NR9F──┬─%IL50F──┐
│ %NR9NF │ %IL50NF │
└─%NR9F──┴─%IL50F──┘
Which will expand to something that looks like this:
┌───────────┬────────────────────────────────────────────────────┐
│ MAX │ Maximus BBS files and related utilities │
└───────────┴────────────────────────────────────────────────────┘
FLIST returns the following ERRORLEVELS:
0 = success
1 = bad command line parameter
2 = unable to open AREA.DAT
3 = unable to open header file
4 = unable to open output file
5 = not enough memory to run
6 = string overflow processing header
NOTICE: You may use, copy, and distribute this program freely as long
as you insure that both the executable and the documentation (.DOC)
files are included in the distribution package. The source code does
not need to be included. You may modify this program and document, so
long as reasonable credit is given to the original author if a
substantial portion of the original remains intact. The author is not
responsible for any losses which may occur either directly or indirectly
as a result of using this program.
This program uses the Maximus structures written by Scott J. Dudley.
HISTORY:
Version 2.4 (1/10/93) -- Added the /HS option to allow customized area
summary headers in the report. Added new
embedded variables for total and area file
counts and file sizes. Fixed a bug that
occurred when inappropriate replacement codes
were used in a header.
Version 2.3 (1/10/93) -- Added /W parameter to allow line-wrapping to
different lengths. List "offline" as the
size for offline file entries.
Version 2.2 (11/04/92) -- Fixed minor date offsets caused by default
timezone and daylight savings adjustments.
Added the /V switch to limit screen output.
/V0 replaces the /Q switch.
Version 2.1 (8/26/92) -- Added the /Q switch to eliminate the progress
display. Now returns different errorlevels
for each possible error. Fixed problem with
headers before comments.
Version 2.0 (8/23/92) -- Added more error checking.
Version 1.9 (8/22/92) -- Added a progress display. Fixed a memory
corruption bug.
Version 1.8 (8/20/92) -- Added fixed width fields with left, right,
and centered justification for embedded
variables. Changed the previous codes for
embedded variables! Removed built-in help to
reduce memory requirements.
Version 1.7 (8/10/92) -- Added embedded variables to the begin and end
headers. Note: This eliminated the automatic
file age comment that used to be included for
newfile lists.
Version 1.6 (8/10/92) -- Added the /HA option to allow customized area
headers in the report. This is an expansion
of an idea and code by David Powers.
Version 1.5 (8/09/92) -- Added the /HB and /HE options to allow
customized beginning and end text in the
report. Added /? help option to print the
parameter list. Added a clearer explanation
of /C's effect on area headers.
Version 1.4 (8/07/92) -- Added the /C option to include file list
comments. Added code to recognize free
switches so they do not show up in the
descriptions. Fixed error if first entry in
AREA.DAT did not include a file area.
Version 1.3 (8/06/92) -- Read the file list filename from AREA.DAT
instead of assuming FILES.BBS.
Version 1.2 (8/02/92) -- Added days header for new files list.
Version 1.1 (7/30/92) -- Added file age cutoff.
Version 1.0 (6/30/92) -- Original release. Written in Borland C.
Large memory model
*/
#include <ctype.h>
#include <dos.h>
#include <fcntl.h>
#include <io.h>
#include <share.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys\stat.h>
#include <time.h>
#include <mstruct.h> /* Maximus structures by Scott J. Dudley */
#define VERSION "2.4"
#define DATE "1/10/93"
#define MAXWIDTH 79
#define MAXPATH 128
#define MAXLINE 256
#define MAXDESC 4096
#define DEFINDENT 30
#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif
struct _arealist
{
char name[40];
char info[80];
char path[80];
char filesbbs[80];
sword priv;
dword locks;
byte barricade;
struct _arealist *next;
};
typedef struct _arealist __arealist;
typedef struct _area __area;
void process_args(int argc, char *argv[]);
sword get_priv(char priv);
dword get_keys(char *key_list);
__arealist *read_areas(void);
void process_header(char type, char *hfile, char *name, char *info, FILE *out);
void process_entry(char *entry, char *path, char *name, char *info,
int *header, FILE *out);
char areafile[MAXPATH] = {"area.dat"};
char hareafile[MAXPATH] = {""};
char hbeginfile[MAXPATH] = {""};
char hendfile[MAXPATH] = {""};
char hsummaryfile[MAXPATH] = {""};
char outfile[MAXPATH] = {"flist.txt"};
sword priv = NORMAL;
dword keys = 0x00000000;
int barricade = FALSE;
int comment = FALSE;
long days = -1L;
int offline = FALSE;
int wrap = DEFINDENT;
int volume = 1;
int vloop;
long age = -1L;
int total_count = 0;
int area_count;
long total_bytes = 0L;
long area_bytes;
int main(int argc, char *argv[])
{
char filespath[MAXPATH];
int header;
FILE *out;
FILE *files;
__arealist *areas;
__arealist *cur;
__arealist *prev;
char line[MAXDESC];
printf("FLIST %s -- Copyright 1993 by Bob Quinlan (%s)\n\n", VERSION, DATE);
printf("Special thanks to David Powers for suggestions and code.\n\n");
process_args(argc, argv);
areas = read_areas();
if ((out = _fsopen(outfile, "wt", SH_DENYWR)) == NULL)
{
fprintf(stderr, "Unable to open %s\n", outfile);
exit(4);
}
if (hbeginfile[0] != '\0')
{
process_header('b', hbeginfile, NULL, NULL, out);
}
cur = areas;
while (cur != NULL)
{
if ((priv >= (cur->priv)) && (((~keys)&(cur->locks)) == 0) &&
((cur->barricade == 0) || (barricade)))
{
if (volume)
{
vloop = -1;
printf("%s -- %s", cur->name, cur->info);
}
strcpy(filespath, cur->filesbbs);
if (filespath[0] == '\0')
{
strcpy(filespath, cur->path);
strcat(filespath, "files.bbs");
}
if ((files = _fsopen(filespath, "rt", SH_DENYNONE)) != NULL)
{
area_count = 0;
area_bytes = 0L;
header = FALSE;
while (fgets(line, MAXDESC, files) != NULL)
{
process_entry(line, cur->path, cur->name, cur->info,
&header, out);
if (volume)
{
if ((vloop = (vloop+1)%volume) == 0)
{
printf(".");
}
}
}
fclose(files);
if (header)
{
if (hsummaryfile[0] != '\0')
{
process_header('s', hsummaryfile, cur->name,
cur->info, out);
}
}
}
if (volume)
{
printf("\n");
}
}
/* Increment to next link and free previous link */
prev = cur;
cur = cur->next;
free(prev);
}
if (hendfile[0] != '\0')
{
process_header('e', hendfile, NULL, NULL, out);
}
fclose(out);
return 0;
}
void process_args(int argc, char *argv[])
{
int i;
for (i = 1; i < argc; i++)
{
if ((argv[i][0] == '/') || (argv[i][0] == '-'))
{
switch (tolower(argv[i][1]))
{
case 'a': /* area.dat */
strncpy(areafile, argv[i]+2, MAXPATH);
break;
case 'b': /* include barricaded areas */
barricade = TRUE;
break;
case 'c': /* include file list comment lines */
comment = TRUE;
break;
case 'd': /* days (of file date) */
days = atol(argv[i]+2);
age = time(NULL)-(days*24*60*60);
break;
case 'f': /* output file */
strncpy(outfile, argv[i]+2, MAXPATH);
break;
case 'h': /* headers */
switch(tolower(argv[i][2]))
{
case 'a':
strncpy(hareafile, argv[i]+3, MAXPATH);
break;
case 'b':
strncpy(hbeginfile, argv[i]+3, MAXPATH);
break;
case 'e':
strncpy(hendfile, argv[i]+3, MAXPATH);
break;
case 's':
strncpy(hsummaryfile, argv[i]+3, MAXPATH);
break;
default:
fprintf(stderr, "Unknown switch: %s\n", argv[i]);
exit(1);
break;
}
break;
case 'k': /* keys */
keys |= get_keys(argv[i]+2);
break;
case 'n': /* no-wrap */
wrap = -1;
break;
case 'o': /* offline files allowed */
offline = TRUE;
break;
case 'p': /* privilege */
priv = get_priv(argv[i][2]);
if (priv < TWIT)
{
priv = TWIT;
fprintf(stderr, "Invalid privilege: %c\n", argv[i][2]);
}
break;
case 't': /* truncate lines */
wrap = -2;
break;
case 'v': /* volume */
volume = max(atoi(argv[i]+2), 0);
break;
case 'w': /* wrap column */
wrap = min(max(atoi(argv[i]+2), 0),
MAXWIDTH-((MAXWIDTH-DEFINDENT)/2));
break;
default:
fprintf(stderr, "Unknown switch: %s\n", argv[i]);
exit(1);
break;
}
}
else
{
fprintf(stderr, "Not a switch: %s\n", argv[i]);
exit(1);
}
}
}
sword get_priv(char priv)
{
char priv_name[] = {"tdlnwpfecash"};
sword priv_code[] =
{
TWIT,
DISGRACE,
LIMITED,
NORMAL,
WORTHY,
PRIVIL,
FAVORED,
EXTRA,
CLERK,
ASSTSYSOP,
SYSOP,
HIDDEN
};
char *where;
where = strchr(priv_name, tolower(priv));
if (where == NULL)
{
return TWIT-1;
}
return priv_code[(int)(where-priv_name)];
}
dword get_keys(char *key_list)
{
char *key;
dword mask = 0x00000000;
int keylen;
int i;
key = strupr(strdup(key_list));
keylen = strlen(key);
for (i = 0; i < keylen; i++)
{
if (key[i] == '*')
{
mask = 0xFFFFFFFFL;
break;
}
else if ((key[i] >= '1') && (key[i] <= '8'))
{
mask |= 0x00000001L<<(key[i]-'1');
}
else if ((key[i] >= 'A') && (key[i] <= 'X'))
{
mask |= 0x00000100L<<(key[i]-'A');
}
}
free(key);
return mask;
}
__arealist *read_areas(void)
{
FILE *areadat;
__area area;
__arealist *tmp;
__arealist *first;
__arealist *cur = NULL;
dword slen;
dword offset = 0;
if ((areadat = _fsopen(areafile, "rb", SH_DENYNONE)) == NULL)
{
fprintf(stderr, "Unable to open %s\n", areafile);
exit(2);
}
while (fread(&area, sizeof(__area), 1, areadat) > 0)
{
if (offset == 0)
{
slen = (dword)(area.struct_len);
}
if (area.filepath[0] != '\0')
{
if ((tmp = (__arealist *)malloc(sizeof(__arealist))) == NULL)
{
fprintf(stderr, "Out of memory!\n");
exit(5);
}
if (cur == NULL)
{
first = tmp;
}
else
{
cur->next = tmp;
}
cur = tmp;
strcpy(cur->name, (char *)area.name);
strcpy(cur->info, (char *)area.fileinfo);
strcpy(cur->path, (char *)area.filepath);
strcpy(cur->filesbbs, (char *)area.filesbbs);
cur->priv = area.filepriv;
cur->locks = area.filelock;
cur->barricade = (area.filebar[0] != '\0');
}
offset += slen;
if (fseek(areadat, offset, SEEK_SET) != 0)
{
break;
}
}
cur->next = NULL;
fclose(areadat);
return first;
}
void process_header(char type, char *hfile, char *name, char *info, FILE *out)
{
FILE *h;
char buf[MAXLINE];
char tbuf[MAXLINE];
char filler[MAXLINE];
int flen;
char *param;
int plen;
int fwidth;
char *stopper;
char c;
int j, k;
if ((h = _fsopen(hfile, "rt", SH_DENYNONE)) == NULL)
{
fprintf(stderr, "Unable to open %s\n", hfile);
exit(3);
}
/* Read data from the header file */
while (fgets(buf, MAXLINE, h) != NULL)
{
/* Process special replacement characters */
param = buf;
while ((param = strchr(param, '%')) != NULL)
{
filler[0] = '\0';
plen = 2;
/* Check the basic parameter type */
switch (tolower(param[1]))
{
case 'a': /* age */
if (days != -1L)
{
ltoa(days, filler, 10);
}
break;
case 'd': /* date */
_strdate(filler);
break;
case 't': /* time */
_strtime(filler);
break;
}
/* Do not allow totals in beginning headers */
if (type != 'b')
{
switch (tolower(param[1]))
{
case 'f': /* total file count */
itoa(total_count, filler, 10);
break;
case 's': /* file size total */
ltoa(total_bytes, filler, 10);
break;
}
}
/* Only allow area name and information in area and summary headers */
if ((type == 'a') || (type == 's'))
{
switch (tolower(param[1]))
{
case 'n': /* name */
strcpy(filler, name);
break;
case 'i': /* information */
strcpy(filler, info);
break;
}
}
/* Only allow area count and area bytes in summary headers */
if (type == 's')
{
switch (tolower(param[1]))
{
case 'c': /* area file count */
itoa(area_count, filler, 10);
break;
case 'b': /* area file size total (bytes) */
ltoa(area_bytes, filler, 10);
break;
}
}
/* If any replacements were found... */
if (filler[0] != '\0')
{
/* Process width values if present */
c = tolower(param[plen]);
if (strchr("lrc", c) != NULL)
{
++plen;
/* Convert the width value and find where it stops */
fwidth = (int)strtol(param+plen, &stopper, 10);
plen += (int)(stopper-(param+plen));
/* If the field is wider than the filler string... */
if (fwidth >= MAXLINE)
{
fprintf(stderr, "Width %d too wide! Ignored.\n",
fwidth);
}
/* If the field is wider than the current data... */
else if (strlen(filler) < fwidth)
{
switch (c)
{
case 'l': /* left justify */
/* Pad on the right */
for (j = strlen(filler); j < fwidth; j++)
{
filler[j] = ' ';
}
break;
case 'r': /* right justify */
flen = strlen(filler);
/* Shift the string right */
for (j = 1; j <= flen; j++)
{
filler[fwidth-j] = filler[flen-j];
}
/* Pad on the left */
for (j = 0; j < fwidth-flen; j++)
{
filler[j] = ' ';
}
break;
case 'c': /* center */
flen = strlen(filler);
/* Pad on the right */
k = (fwidth-flen+1)/2;
for (j = 1; j <= k; j++)
{
filler[fwidth-j] = ' ';
}
/* Shift the string to center */
for (j = 1; j <= flen; j++)
{
filler[fwidth-k-j] = filler[flen-j];
}
/* Pad on the left */
k = (fwidth-flen)/2;
for (j = 0; j < k; j++)
{
filler[j] = ' ';
}
break;
}
/* Terminate the adjusted string */
filler[fwidth] = '\0';
}
}
/* Process fill character if present */
if (tolower(param[plen]) == 'f')
{
strset(filler, param[++plen]);
++plen;
}
/* Skip "no fill" parameter if present */
else if (strncmpi(param+plen, "nf", 2) == 0)
{
plen += 2;
}
/* Perform the replacment */
if ((strlen(buf)+strlen(filler)-plen) >= MAXLINE)
{
fprintf(stderr, "String overflow!\n");
exit(6);
}
param[0] = '\0';
strcpy(tbuf, buf);
strcat(tbuf, filler);
strcat(tbuf, param+plen);
strcpy(buf, tbuf);
param = param+strlen(filler);
}
else
{
param++;
}
}
/* Output header */
fputs(buf, out);
}
fclose(h);
}
void process_entry(char *entry, char *path, char *name, char *info,
int *header, FILE *out)
{
char filepath[MAXPATH];
int file;
long fsize;
struct ftime ftimep;
struct tm ftm;
int fdata;
char line[MAXDESC];
char fill[MAXWIDTH+1];
char extra[MAXWIDTH+1];
char *descrip;
char *end;
char *ch;
int length;
int nameline;
int i;
strcpy(line, entry);
/* Trim newline */
end = strchr(line, '\n');
if (end != NULL)
{
end[0] = '\0';
}
else
{
end = strchr(line, '\0');
}
/* Check for comment lines */
if (strchr(" -", line[0]) != NULL)
{
if (comment)
{
/* Output area header if not already done */
if (!(*header))
{
if (hareafile[0] != '\0')
{
process_header('a', hareafile, name, info, out);
}
else
{
fprintf(out, "\n\n Area %s -- %s\n\n", name, info);
}
*header = TRUE;
}
/* Output comment line */
fprintf(out, "%s\n", line);
}
}
else
{
total_count++;
area_count++;
/* Parse into filename and description, skipping switches */
descrip = line;
do
{
ch = strpbrk(descrip, " \t");
if (ch == NULL)
{
descrip = end;
}
else
{
ch[0] = '\0';
i = strspn(++ch, " \t");
descrip = ch+i;
}
} while (descrip[0] == '/');
/* Get file size and date */
strcpy(filepath, path);
strcat(filepath, line);
if ((file = sopen(filepath, O_RDONLY, SH_DENYNONE)) == -1)
{
if (offline)
{
fdata = FALSE;
}
else
{
return;
}
}
else
{
fsize = filelength(file);
getftime(file, &ftimep);
close(file);
total_bytes += fsize;
area_bytes += fsize;
fdata = TRUE;
timezone = 0L;
daylight = 0;
memset(&ftm, 0, sizeof(struct tm));
ftm.tm_sec = ftimep.ft_tsec*2;
ftm.tm_min = ftimep.ft_min;
ftm.tm_hour = ftimep.ft_hour;
ftm.tm_mday = ftimep.ft_day;
ftm.tm_mon = ftimep.ft_month-1;
ftm.tm_year = ftimep.ft_year+80;
if (mktime(&ftm) < age)
{
return;
}
}
/* Output area header if not already done */
if (!(*header))
{
if (hareafile[0] != '\0')
{
process_header('a', hareafile, name, info, out);
}
else
{
fprintf(out, "\n\n Area %s -- %s\n\n", name, info);
}
*header = TRUE;
}
/* Output file entry */
if (fdata)
{
fprintf(out, "%-12s %7lu %2.2u-%2.2u-%2.2u", line,
fsize, ftimep.ft_month, ftimep.ft_day,
(ftimep.ft_year+80)%100);
}
else
{
fprintf(out, "%-12s offline ", line);
}
/* Output description */
switch (wrap)
{
case -2: /* truncate */
descrip[MAXWIDTH-DEFINDENT] = '\0';
fprintf(out, " %s\n", descrip);
break;
case -1: /* no-wrap */
fprintf(out, " %s\n", descrip);
break;
default: /* wrap */
length = MAXWIDTH-DEFINDENT;
strcpy(fill, " ");
nameline = TRUE;
/* Set initial length */
while (strlen(descrip) > length)
{
for (i = length; i > length/2; i--)
{
if (strchr(" \t", descrip[i]) != NULL)
{
break;
}
}
/* If there are no breaks... */
if (i == length/2)
{
strncpy(extra, descrip, length);
extra[length] = '\0';
fprintf(out, "%s%s\n", fill, extra);
descrip += length;
}
else
{
descrip[i++] = '\0';
fprintf(out, "%s%s\n", fill, descrip);
descrip += i;
while (strchr(" \t", descrip[0]) != NULL)
{
descrip++;
}
}
if (nameline == TRUE)
{
/* Recalculate length */
length = MAXWIDTH-wrap;
memset(fill, ' ', wrap);
fill[wrap] = '\0';
nameline = FALSE;
}
}
fprintf(out, "%s%s\n", fill, descrip);
break;
}
}
}