TFL.C

51.5 KB 5ee9abfae3117e5e…
/*  TFL.C

    My very own file list generator for TAG BBS.
    Reads TFL.CFG and generates lists based on info contained within.

*/

#include <alloc.h>
#include <ctype.h>
#include <dir.h>
#include <dos.h>
#include <mem.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* note: Martin's C structs are junk! these work however */
#include "tagrec.h"

#define CMD_NOTFOUND    -1
#define CMD_FBOARDS     0
#define CMD_MFILEPATH   1
#define CMD_DFILEPATH   2
#define CMD_LISTHEADER  3
#define CMD_LISTFOOTER  4
#define CMD_AREAHEADER  5
#define CMD_AREAFOOTER  6
#define CMD_FORMAT      7
#define CMD_LIST        8
#define CMD_ENDLIST     9
#define CMD_UNVALIDATED 10
#define CMD_DSL         11
#define CMD_AR          12
#define CMD_BOARDS      13
#define CMD_CDROM       14
#define CMD_FIXSIZE     15
#define CMD_FIXDATE     16
#define CMD_OFFLINE     17
#define CMD_DOWNLOADS   18
#define CMD_ULAGE       19
#define CMD_FILEAGE     20

#define NO              0
#define YES             1

char *keywords[] =
{
    /* list of all keywords to be found in the config file */

    "FBOARDS",          /* global only */
    "MFILEPATH",        /* global only */
    "DFILEPATH",        /* global only */
    "LISTHEADER",       /* global or local */
    "LISTFOOTER",       /* global or local */
    "AREAHEADER",       /* global or local */
    "AREAFOOTER",       /* global or local */
    "FORMAT",           /* global or local */
    "LIST",
    "ENDLIST",
    "UNVALIDATED",      /* local - allow unvalidated files */
    "DSL",              /* local - DSL's allowed */
    "AR",               /* local - AR flags allowed */
    "BOARDS",           /* local - list of board numbers */
    "CDROM",            /* local - allow CD-ROM files */
    "FIXSIZE",          /* local - fix file sizes */
    "FIXDATE",          /* local - set date to file date */
    "OFFLINE",          /* local - allow offline files */
    "DOWNLOADS",        /* local - minimum downloads numbers */
    "ULAGE",            /* local - ul file age */
    "FILEAGE",          /* local - real file age */
    ""
};

char            inputline[256],
                *ptr,
                fboardname[80]  = "",
                listname[80]    = "",
                mfilepath[80]   = "",
                dfilepath[80]   = "",
                boardname[80],          /* bbs name for this area */
                boardfname[12],         /* name of the .dir file [@]........ */
                ulpath[80],
                dlpath[80],
                dirfname[80],           /* full name of .dir file */
                filename[16],           /* just name of the file */
                fname[80],              /* full name of file */
                description[80],
                owner[40],
                filedate[16],
                uldate[16];

long            ulage,
                fileage,
                ulsize,
                filesize,
                ulblocks,
                fileblocks,
                today;

int             g_listheadercnt = 0,
                g_listfootercnt = 0,
                g_areaheadercnt = 0,
                g_areafootercnt = 0,
                g_formatcnt     = 0;
char            *g_listheader[32],
                *g_listfooter[32],
                *g_areaheader[32],
                *g_areafooter[32],
                *g_format[10];

int             l_listheadercnt = 0,
                l_listfootercnt = 0,
                l_areaheadercnt = 0,
                l_areafootercnt = 0,
                l_formatcnt     = 0;
char            *l_listheader[32],
                *l_listfooter[32],
                *l_areaheader[32],
                *l_areafooter[32],
                *l_format[10];
int             l_unvalidated   = NO,
                l_cdrom         = NO,
                l_fixsize       = NO,
                l_fixdate       = NO,
                l_offline       = NO,
                l_downloads     = 0,
                l_ulage         = 0,
                l_fileage       = 0;
unsigned char   l_bflags[128],      /* support upto 1024 file areas */
                l_dslflags[32],     /* 256 dsl's */
                l_arflags[4];       /* 24 ar's */
int             l_bflagset,
                l_dslflagset,
                l_arflagset;

int             missing,
                linenum,
                filenum,
                boardnum,
                areanum,
                tareas,
                *num;
char            **list;

FILE            *fboardfile     = NULL,
                *cfgfile        = NULL,
                *listfile,
                *dirfile;

UlRec           fboarddata;
int             okay;
UlfRec          dirdata,
                dirheader;
struct ffblk    ffblk;
long            tsize,
                ksize,
                fsize;

/* function prototypes */
void    xerror(char *str, ...);
char    *fixline(char *str);
void    setrflags(unsigned char *rflags, char *str);
void    setbflags(unsigned char *bflags, char *str);
void    setslflags(unsigned char *slflags, char *str);
void    setbit(unsigned char *flags, int bitnum);
int     checkbit(unsigned char *flags, int bitnum);
int     findword(char *word, char *list[]);
char    *strpastoc(char *c, char *pas);
void    fsendf(FILE *f, char *str);

int             isleap (unsigned yr);
static unsigned months_to_days (unsigned month);
static long     years_to_days (unsigned yr);
long            ymd_to_scalar (unsigned yr, unsigned mo, unsigned day);
void            scalar_to_ymd (long scalar, unsigned *pyr, unsigned *pmo, unsigned *pday);

int main(int args, char *arglist[])
{
    int             inlist,
                    cmd,
                    c,
                    i,
                    j,
                    k,
                    len,
                    x,
                    bsize,
                    globaldone,
                    skip,
                    pass,
                    fixed;
    unsigned int    ulage2,
                    fileage2;
    long            pos,
                    areapos;
    struct date     d;
    FILE            *tempfile;
    
    /* print welcome message */
    printf("TFL v1.3 (c)1994 Aphelion Software\n");
    printf("This program is released to the public domain\n");

    /* call syntax:  TFL <config file> */
    if (args != 2)
        xerror("Syntax: TFL <config file>");

    /* open the config file */
    if ((cfgfile = fopen(arglist[1], "r")) == NULL)
        xerror("Config file not found.");

    /* begin main loop */
    inlist = NO;
    linenum = 0;
    printf("\nReading global information.\n");
    globaldone = NO;
    getdate(&d);
    today = ymd_to_scalar(d.da_year, d.da_mon, d.da_day);
    do
    {
        /* read a line in. */
        if (fgets(inputline, 255, cfgfile) == NULL)
            break;

        /* remove excess stuff from inline */
        linenum++;
        if (fixline(inputline))
        {
            /* something in the line. process it */
            if ((ptr = strtok(inputline, " ")) == NULL)
                xerror("<%d> Invalid data in config file.", linenum);

            /* execute the command */
            cmd = findword(ptr, keywords);
            switch (cmd)
            {
                case CMD_FBOARDS:
                    /* open fboard */
                    if (inlist)
                        xerror("<%d> FBoards is a global only command.", linenum);
                    if (*fboardname)
                        xerror("<%d> FBOARD.DAT specified more than once.", linenum);
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    strcpy(fboardname, ptr);
                    if ((fboardfile = fopen(fboardname, "rb")) == NULL)
                        xerror("<%d> Unable to open FBOARDS.DAT.", linenum);
                    printf("│ FBOARDS.DAT = %s\n", fboardname);
                    break;

                case CMD_MFILEPATH:
                    if (inlist)
                        xerror("<%d> MFILE path is a global only command.", linenum);
                    if (*mfilepath)
                        xerror("<%d> MFILE path specified more than once.", linenum);
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    strcpy(mfilepath, ptr);
                    printf("│ MFILE path = %s\n", mfilepath);
                    break;

                case CMD_DFILEPATH:
                    if (inlist)
                        xerror("<%d> DFilespath is a global only command.", linenum);
                    if (*dfilepath)
                        xerror("<%d> DFiles path specified more than once.", linenum);
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    strcpy(dfilepath, ptr);
                    printf("│ DFILE path = %s\n", dfilepath);
                    break;

                case CMD_LISTHEADER:
                    if (!inlist)
                    {
                        num = &g_listheadercnt;
                        list = &g_listheader[*num];
                    }
                    else
                    {
                        num = &l_listheadercnt;
                        list = &l_listheader[*num];
                    }
                    if (*num >= 32)
                        xerror("<%d> Too many formats specified.", linenum);
                    ptr = strtok(NULL, "\"");
                    ptr = strtok(NULL, "\"");
                    *list = strdup(ptr);
                    (*num)++;
                    break;

                case CMD_LISTFOOTER:
                    if (!inlist)
                    {
                        num = &g_listfootercnt;
                        list = &g_listfooter[*num];
                    }
                    else
                    {
                        num = &l_listfootercnt;
                        list = &l_listfooter[*num];
                    }
                    if (*num >= 32)
                        xerror("<%d> Too many formats specified.", linenum);
                    ptr = strtok(NULL, "\"");
                    ptr = strtok(NULL, "\"");
                    *list = strdup(ptr);
                    (*num)++;
                    break;

                case CMD_AREAHEADER:
                    if (!inlist)
                    {
                        num = &g_areaheadercnt;
                        list = &g_areaheader[*num];
                    }
                    else
                    {
                        num = &l_areaheadercnt;
                        list = &l_areaheader[*num];
                    }
                    if (*num >= 32)
                        xerror("<%d> Too many formats specified.", linenum);
                    ptr = strtok(NULL, "\"");
                    ptr = strtok(NULL, "\"");
                    *list = strdup(ptr);
                    (*num)++;
                    break;

                case CMD_AREAFOOTER:
                    if (!inlist)
                    {
                        num = &g_areafootercnt;
                        list = &g_areafooter[*num];
                    }
                    else
                    {
                        num = &l_areafootercnt;
                        list = &l_areafooter[*num];
                    }
                    if (*num >= 32)
                        xerror("<%d> Too many formats specified.", linenum);
                    ptr = strtok(NULL, "\"");
                    ptr = strtok(NULL, "\"");
                    *list = strdup(ptr);
                    (*num)++;
                    break;

                case CMD_FORMAT:
                    if (!inlist)
                    {
                        num = &g_formatcnt;
                        list = &g_format[*num];
                    }
                    else
                    {
                        num = &l_formatcnt;
                        list = &l_format[*num];
                    }
                    if (*num >= 32)
                        xerror("<%d> Too many formats specified.", linenum);
                    ptr = strtok(NULL, "\"");
                    ptr = strtok(NULL, "\"");
                    *list = strdup(ptr);
                    (*num)++;
                    break;

                case CMD_LIST:
                    if (inlist)
                        xerror("<%d> No ENDLIST found in config file.", linenum);
                    inlist = YES;
                    if (!globaldone)
                    {
                        globaldone = YES;
                        printf("└ done global info read\n", filenum);
                    }
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    strcpy(listname, ptr);
                    
                    /* clear all local data */
                    memset((void *)l_bflags,    0xff, 128);
                    memset((void *)l_dslflags,  0xff, 32);
                    memset((void *)l_arflags,   0xff, 4);
                    l_bflagset      = NO;
                    l_dslflagset    = NO;
                    l_arflagset     = NO;
                    for (i = 0; i < 10; i++)
                    {
                        if (i < l_listheadercnt)
                            free(l_listheader[i]);
                        if (i < l_listfootercnt)
                            free(l_listfooter[i]);
                        if (i < l_areaheadercnt)
                            free(l_areaheader[i]);
                        if (i < l_areafootercnt)
                            free(l_areafooter[i]);
                        if (i < l_formatcnt)
                            free(l_format[i]);
                    }
                    l_listheadercnt = 0;
                    l_listfootercnt = 0;
                    l_areaheadercnt = 0;
                    l_areafootercnt = 0;
                    l_formatcnt     = 0;
                    l_unvalidated   = NO;
                    l_cdrom         = NO;
                    l_fixsize       = NO;
                    l_fixdate       = NO;
                    l_offline       = NO;
                    l_downloads     = -1,
                    l_ulage         = -1,
                    l_fileage       = -1;

                    printf("\nReading information for %s.\n", listname);
                    break;

                case CMD_ENDLIST:
                    if (!inlist)
                        xerror("<%d> Found ENDLIST with no matching LIST.", linenum);
                    
                    printf("│ Creating %s.\n", listname);
                    if ((listfile = fopen(listname, "w+")) == NULL)
                        xerror("Unable to create %s.", listname);

                    if (!l_listheadercnt)
                    {
                        for (i = 0; i < g_listheadercnt; i++)
                            l_listheader[i] = strdup(g_listheader[i]);
                        l_listheadercnt = g_listheadercnt;
                    }
                    if (!l_listfootercnt)
                    {
                        for (i = 0; i < g_listfootercnt; i++)
                            l_listfooter[i] = strdup(g_listfooter[i]);
                        l_listfootercnt = g_listfootercnt;
                    }
                    if (!l_areaheadercnt)
                    {
                        for (i = 0; i < g_areaheadercnt; i++)
                            l_areaheader[i] = strdup(g_areaheader[i]);
                        l_areaheadercnt = g_areaheadercnt;
                    }
                    if (!l_areafootercnt)
                    {
                        for (i = 0; i < g_areafootercnt; i++)
                            l_areafooter[i] = strdup(g_areafooter[i]);
                        l_areafootercnt = g_areafootercnt;
                    }
                    if (!l_formatcnt)
                    {
                        for (i = 0; i < g_formatcnt; i++)
                            l_format[i] = strdup(g_format[i]);
                        l_formatcnt = g_formatcnt;
                    }

                    /* -------- print the list header -------- */
                    for (i = 0; i < l_listheadercnt; i++)
                        fsendf(listfile, l_listheader[i]);

                    rewind(fboardfile);
                    boardnum = 0;
                    tareas = 0;
                    while (fread(&fboarddata, sizeof fboarddata, 1, fboardfile))
                    {
                        okay = YES;
                        /* is this board okay to use? */
                        if (!checkbit(&l_dslflags, fboarddata.DSL)) 
                            okay = NO;
                        if (!l_cdrom && (fboarddata.Flags.flag1 & ULRF_IsCdRom))
                            okay = NO;
                        if (!checkbit(&l_arflags, fboarddata.ArLvl - '@'))
                            okay = NO;
                        if (!checkbit(&l_bflags, boardnum))
                            okay = NO;
                        
                        if (okay)
                        {
                            strpastoc(boardname, fboarddata.Name);
                            strpastoc(boardfname, fboarddata.Filename);
                            strpastoc(ulpath, fboarddata.UlPathName);
                            strpastoc(dlpath, fboarddata.DlPathname);
                            if (*boardfname == '`')
                            {
                                if (!*mfilepath)
                                    xerror("No MFILE path specified.");
                                sprintf(dirfname, "%s%s.DIR", 
                                    mfilepath, &boardfname[1]);
                            }
                            else if (*boardfname == '@')
                            {
                                if (!*dfilepath)
                                    xerror("No DFILE path specified.");
                                sprintf(dirfname, "%s%s.DIR",
                                    dfilepath, &boardfname[1]);
                            }
                            else
                                sprintf(dirfname, "%s%s.DIR", 
                                    dlpath, boardfname);
                            
                            if ((dirfile = fopen(dirfname, "r+b")) == NULL)
                            {
                                printf("│ Unable to open %s. Skipping.\n", dirfname);
                                okay = NO;
                            }
                            else
                            {
                                printf("│ (%4d) %s\n", boardnum, boardname);

                                filenum = 0;
                                for (pass = 0; pass < 2; pass++)
                                {
                                    rewind(dirfile);

                                    if (pass == 1)
                                    {
                                        /* -------- print the area header -------- */
                                        tareas++;
                                        for (i = 0; i < l_areaheadercnt; i++)
                                            fsendf(listfile, l_areaheader[i]);
                                    }

                                    tsize = 0;
                                    fread(&dirheader, sizeof dirheader, 1, dirfile);
                                    pos = ftell(dirfile);
                                    skip = YES;
                                    while (fread(&dirdata, sizeof dirdata, 1, dirfile))
                                    {
                                        okay = YES;
                                        strpastoc(filename, dirdata.Filename);
                                        ptr = filename;
                                        while (*ptr)
                                            if (isspace(*ptr))
                                                strcpy(ptr, ptr + 1);
                                            else
                                                ptr++;
                                        strpastoc(description, dirdata.Description);
                                        strpastoc(owner, dirdata.Owner);
                                        sprintf(fname, "%s%s", ulpath, filename);

                                        if (fboarddata.Flags.flag1 & ULRF_IsCdRom)
                                            missing = NO;
                                        else
                                            missing = findfirst(fname, &ffblk, 0);
                                    
                                        strpastoc(uldate, dirdata.Date);
                                        ulage = ymd_to_scalar(
                                            atoi(&uldate[6]) + 1900, 
                                            atoi(&uldate[0]), 
                                            atoi(&uldate[3]));
                                        ulage2 = dirdata.DateN;
                                        ulblocks = (long)dirdata.Blocks;
                                        ulsize = ulblocks * 128L;

                                        if (fboarddata.Flags.flag1 & ULRF_IsCdRom)
                                        {
                                            strcpy(filedate, uldate);
                                            fileage = ulage;
                                            fileage2 = (unsigned int)
                                                (fileage - ymd_to_scalar(1985, 1, 1));
                                            fileblocks = ulblocks;
                                            filesize = ulsize;
                                        }
                                        else if (!missing)
                                        {
                                            sprintf(filedate, "%02d/%02d/%02d",
                                                (ffblk.ff_fdate >> 5) & 0x000f,
                                                (ffblk.ff_fdate >> 0) & 0x001f,
                                                (((ffblk.ff_fdate >> 9) & 0x007f) + 1980) % 100);
                                            fileage = ymd_to_scalar(
                                                (((ffblk.ff_fdate >> 9) & 0x007f) + 1980),
                                                (ffblk.ff_fdate >> 5) & 0x000f,
                                                (ffblk.ff_fdate >> 0) & 0x001f);
                                            fileage2 = (unsigned int)
                                                (fileage - ymd_to_scalar(1985, 1, 1));
                                            fileblocks = ffblk.ff_fsize / 128;
                                            filesize = ffblk.ff_fsize;
                                        }
                                        else
                                        {
                                            strcpy(filedate, "        ");
                                            fileage = 0;
                                            fileage2 = 0;
                                            fileblocks = 0;
                                            filesize = 0;
                                        }

                                        /* correct here */
                                        if (pass == 0)
                                        {
                                            fixed = NO;
                                            if ((ulblocks != fileblocks) && l_fixsize)
                                            {
                                                printf("│         %-14s %ld -> %ld blocks\n",
                                                    filename,  ulblocks, fileblocks);
                                                dirdata.Blocks = fileblocks;
                                                fixed = YES;
                                                ulsize = filesize;
                                                ulblocks = fileblocks;
                                            }
                                            if (((fileage2 != dirdata.DateN) ||
                                                (ulage != fileage)) &&
                                                l_fixdate)
                                            {
                                                printf("│         %-14s %s -> %s\n",
                                                    filename,  uldate, filedate);
                                                dirdata.DateN = fileage2;
                                                for (i = 0; i < 8; i++)
                                                    dirdata.Date[i + 1] = filedate[i];
                                                fixed = YES;
                                                ulage = fileage;
                                                ulage2 = fileage2;
                                                strcpy(uldate, filedate);
                                            }
                                            if (fixed)
                                            {
                                                fseek(dirfile, pos, SEEK_SET);
                                                fwrite(&dirdata, sizeof dirdata, 1, dirfile);
                                            }
                                        }

                                        /* okay to write file? */
                                        if (!l_unvalidated && (dirdata.Flag.flag1 & FRS_NotValidated))
                                            okay = NO;
                                        if (!l_offline && missing)
                                            okay = NO;
                                        if ((l_fileage >= 0) &&
                                            ((today - fileage) > l_fileage))
                                            okay = NO;
                                        if ((l_ulage >= 0) &&
                                            ((today - ulage) > l_ulage))
                                            okay = NO;
                                        if ((l_downloads >= 0) &&
                                            (dirdata.Nacc < l_downloads))
                                            okay = NO;

                                        if (okay) 
                                        {
                                            if (pass == 1)
                                            {
                                                filenum++;
                                                tsize += ulsize;
                                                for (j = 0; j < l_formatcnt; j++)
                                                    fsendf(listfile, l_format[j]);
                                            }
                                            else
                                            {
                                                skip = NO;
                                            }
                                        }
                                        pos = ftell(dirfile);
                                    }
                                    if ((pass == 0) && skip)
                                        break;
                                }
                                printf("│         %d file(s)\n", filenum);
                                if (filenum)
                                {
                                    /* -------- print the area footer -------- */
                                    for (i = 0; i < l_areafootercnt; i++)
                                        fsendf(listfile, l_areafooter[i]);
                                }
                                fclose(dirfile);
                            }
                        }
                        boardnum++;
                    }
                    /* -------- print the list footer -------- */
                    for (i = 0; i < l_listfootercnt; i++)
                        fsendf(listfile, l_listfooter[i]);
                    
                    fclose(listfile);
                    printf("└ %s completed.\n", listname);

                    inlist = NO;
                    break;

                case CMD_UNVALIDATED:
                    if (!inlist)
                        xerror("<%d> Unvalidated is a local only command.", linenum);
                    
                    l_unvalidated = YES;
                    printf("│ Unvalidated files allowed.\n");
                    break;

                case CMD_FIXSIZE:
                    if (!inlist)
                        xerror("<%d> Fixsize is a local only command.", linenum);
                    
                    l_fixsize = YES;
                    printf("│ File size correction active.\n");
                    break;
                
                case CMD_FIXDATE:
                    if (!inlist)
                        xerror("<%d> Fixdate is a local only command.", linenum);
                    
                    l_fixdate = YES;
                    printf("│ File date correction active.\n");
                    break;

                case CMD_OFFLINE:
                    if (!inlist)
                        xerror("<%d> Offline is a local only command.", linenum);
                    
                    l_offline = YES;
                    printf("│ Offline files allowed.\n");
                    break;
    
                case CMD_DOWNLOADS:
                    if (!inlist)
                        xerror("<%d> Downloads is a local only command.", linenum);
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    l_downloads = atoi(ptr);
                    printf("│ Minimum downloads = %d\n", l_downloads);
                    break;
                
                case CMD_ULAGE:
                    if (!inlist)
                        xerror("<%d> ULAGE is a local only command.", linenum);
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    l_ulage = atoi(ptr);
                    printf("│ Minimum upload age = %d\n", l_ulage);
                    break;

                case CMD_FILEAGE:
                    if (!inlist)
                        xerror("<%d> FILEAGE is a local only command.", linenum);
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    l_fileage = atoi(ptr);
                    printf("│ Minimum file age = %d\n", l_fileage);
                    break;

                case CMD_DSL:
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    if (!inlist)
                        xerror("<%d> Unvalidated is a local only command.", linenum);
                    
                    if (!l_dslflagset)
                    {
                        l_dslflagset = YES;
                        memset((void *)l_dslflags, 0, 32);
                    }
                    setslflags(l_dslflags, ptr);
                    printf("│ Reading DSL's.\n");
                    break;

                case CMD_AR:
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    if (!inlist)
                        xerror("<%d> AR is a local only command.", linenum);

                    if (!l_arflagset)
                    {
                        l_arflagset = YES;
                        memset((void *)l_arflags, 0, 4);
                    }
                    setrflags(l_arflags, ptr);
                    printf("│ Reading AR flags.\n");
                    break;

                case CMD_BOARDS:
                    if ((ptr = strtok(NULL, "= ")) == NULL)
                        xerror("<%d> Incomplete data in config file.", linenum);
                    if (!inlist)
                        xerror("<%d> AR is a local only command.", linenum);
                    
                    if (!l_bflagset)
                    {
                        l_bflagset = YES;
                        memset((void *)l_bflags, 0, 128);
                    }
                    setbflags(l_bflags, ptr);
                    printf("│ Reading board numbers.\n");
                    break;

                case CMD_CDROM:
                    if (!inlist)
                        xerror("<%d> CDROM is a local only command.", linenum);

                    l_cdrom = YES;
                    printf("│ CD-ROM allowed.\n");
                    break;

                case CMD_NOTFOUND:
                    xerror("<%d> Unknown keyword in config file", linenum);
                    break;
            }
        }
    } while (YES);
    fcloseall();
}

void xerror(char *str, ...)
{
    va_list     argptr;
    /* print an error message and abort */
    va_start(argptr, str);
    vprintf(str, argptr);
    va_end(argptr);
    exit(1);
}

char *fixline(char *str)
{
    /* fix a string. */
    char    *ptr;
    int     quote = NO;

    /* remove white spaces off front */
    ptr = str;
    while (isspace(*ptr) && *ptr)
        ptr++;
    strcpy(str, ptr);

    /* remove excess white spaces from middle */
    ptr = str;
    while (*ptr)
    {
        if (*ptr == '\"')
            quote = !quote;

        if (!quote)
        {
            /* not in quote mode. reduce spaces and truncate at ; */
            if (*ptr == ';')
                *ptr = '\0';
            else 
                if (isspace(*ptr))
                    if (isspace(*(ptr + 1)) && *(ptr + 1))
                    {
                        strcpy(ptr, ptr + 1);
                        ptr--;
                    }
        }
        if (*ptr)
            ptr++;
    }
    /* remove spaces off end */
    while (isspace(str[strlen(str) - 1]))
        str[strlen(str) - 1] = '\0';

    if (*str)
        return str;
    else
        return NULL;
}

void setbflags(unsigned char *bflags, char *str)
{
    char    *ptr;
    int     val,
            from,
            to;

    /* set the board flags based on str. can be inform of: 
        #
        #-#
        #-#,#
        etc.
    */
    ptr = strtok(str, ",");
    while (ptr != NULL)
    {
        if (strrchr(ptr, '-'))
        {
            /* a range #-# */
            from = atoi(ptr);
            to = atoi(strrchr(ptr, '-') + 1);
            for (val = from; val <= to; val++)
            {
                if (val >= 1024)
                    xerror("Board number out of range.");
                setbit(bflags, val);
            }
        }
        else
        {
            /* just a number */
            val = atoi(ptr);
            if (val >= 1024)
                xerror("Board number out of range.");
            setbit(bflags, val);
        }
        ptr = strtok(NULL, ",");
    }
}

void setrflags(unsigned char *rflags, char *str)
{
    char    *ptr;
    int     val,
            from,
            to;

    /* set the r flags based on str. can be inform of: 
        #
        #-#
        #-#,#
        etc.
        where # is in range @-Z
    */
    ptr = strtok(str, ",");
    while (ptr != NULL)
    {
        if (strrchr(ptr, '-'))
        {
            /* a range #-# */
            from = toupper(*ptr) - '@';
            to = toupper(*(ptr + 2)) - '@';
            if ((from < 0) || (from > 26) || (to < 0) || (to > 26) ||
                (from > to))
                xerror("Invalid flag specified.");
            for (val = from; val <= to; val++)
                setbit(rflags, val);
        }
        else
        {
            /* just a number */
            val = *ptr - '@';
            if ((val < 0) || (val > 26))
                xerror("Invalid flag specified.");
            else
                setbit(rflags, val);
        }
        ptr = strtok(NULL, ",");
    }
}
                        
void setslflags(unsigned char *slflags, char *str)
{
    char    *ptr;
    int     val,
            from,
            to;

    /* set the secutity level flags based on str. can be inform of: 
        #
        #-#
        #-#,#
        etc.
    */
    ptr = strtok(str, ",");
    while (ptr != NULL)
    {
        if (strrchr(ptr, '-'))
        {
            /* a range #-# */
            from = atoi(ptr);
            to = atoi(strrchr(ptr, '-') + 1);
            for (val = from; val <= to; val++)
            {
                if (val >= 256)
                    xerror("Secutiry level out of range.");
                setbit(slflags, val);
            }
        }
        else
        {
            /* just a number */
            val = atoi(ptr);
            if (val >= 256)
                xerror("Security level out of range.");
            setbit(slflags, val);
        }
        ptr = strtok(NULL, ",");
    }
}

void setbit(unsigned char *flags, int bitnum)
{
    int byte, 
        bit;

    byte = (bitnum >> 3);
    bit  = (bitnum & 0x07);
    flags[byte] |= (0x01 << bit);
}

int checkbit(unsigned char *flags, int bitnum)
{
    int byte, 
        bit;

    byte = (bitnum >> 3);
    bit  = (bitnum & 0x07);
    return (flags[byte] & (0x01 << bit));
}

int findword(char *word, char *list[])
{
    int     cmd;
    char    *ptr;

    cmd = 0;
    while (strcmpi(word, list[cmd]) && *list[cmd])
        cmd++;
    if (!*list[cmd])
        return CMD_NOTFOUND;
    return cmd;
}

char *strpastoc(char *d, char *s)
{
    register int i;
    register char *p;

    i = 1;
    p = d;
    while (i <= s[0])
    {
        if (i < s[0] && (s[i] == 0x03 || s[i] == '^') &&
            (isdigit(s[i+1]) || (s[i+1] >= 0 && s[i+1] <= 9)))
            ++i;
        else
            *p++ = s[i];
        ++i;
    }
    *p = '\0';
    return(d);
}
                                
void fsendf(FILE *f, char *str)
{
    /*    
        %A : area name (26 characters)
        %B : file blocks
        %C : date of upload (8 characters- ##/##/##)
        %D : date on file (8 characters- ##/##/##)
        %E : file extension only (3 characters)
        %F : full file name (12 characters- period unjustified)
        %G : file name only (12 characters)
        %H : file description (? characters- last field/no wrap)
        %I : file description (? characters- last field/wrapped)
        %J : name of uploader (32 characters)
        %K : number of files in the area (4 characters- ####)
        %L : path here files live (60 characters)
        %M : area size ( 7 characters- "##,###k")
        %N : area size (10 characters- ##,###,###)
        %O : file size ( 7 characters- ##,###k,OFFLINE,UNVAL'D)
        %P : file size (10 characters- ##,###,###,OFFLINE,UNVALIDT'D)
        %Q : time of file (5 characters- ##:##/military)
        %R : times downloaded (5 characters- #####)
        %S : number of areas in list    (4 characters)
        %T : Area number                (5 characters)
    */
    char            buff[256],
                    *iptr,
                    *optr,
                    temp[256],
                    temp2[256];
    int             x,
                    len,
                    thislen,
                    i,
                    j,
                    k,
                    c;

    iptr = str;
    optr = buff;
    *optr = '\0';
    while (*iptr)
    {
        if (*iptr == '%')
        {
            switch (toupper(iptr[1]))
            {
                case 'A':
                    /* %A : area name (26 characters) */
                    sprintf(temp, "%-26s", boardname);
                    strcat(optr, temp);
                    optr += 26;
                    iptr += 2;
                    break;

                case 'B':
                    /* %B : file blocks # 128 blocks? (#####) */
                    sprintf(temp, "%5.5ld", ulblocks);
                    strcat(optr, temp);
                    optr += strlen(temp);
                    iptr += 2;
                    break;

                case 'C':
                    /* %C : date of upload (8 characters- ##/##/##) */
                    strcpy(temp, uldate);
                    strcat(optr, temp);
                    optr += 8;
                    iptr += 2;
                    break;

                case 'D':
                    /* %D : date on file (8 characters- ##/##/##) */
                    strcpy(temp, filedate);
                    strcat(optr, temp);
                    optr += 8;
                    iptr += 2;
                    break;

                case 'E':
                    /* %E : file extension only (3 characters) */
                    strcpy(temp, filename);
                    ptr = strrchr(temp, '.');
                    if (ptr)
                        sprintf(temp2, "%-3s", ptr + 1);
                    strcat(optr, temp2);
                    optr += 3;
                    iptr += 2;
                    break;

                case 'F':
                    /* %F : full file name (12 characters- period unjustified) */
                    sprintf(temp, "%-12s", filename);
                    strcat(optr, temp);
                    optr += 12;
                    iptr += 2;
                    break;

                case 'G':
                    /* %G : file name only (8 characters) */
                    strcpy(temp, filename);
                    ptr = strrchr(temp, '.');
                    if (ptr)
                        *ptr = '\0';
                    sprintf(temp2, "%-8s", temp);
                    strcat(optr, temp2);
                    optr += 8;
                    iptr += 2;
                    break;

                case 'H':
                    /* %H : file description (? characters- last field/no wrap) */
                    strcpy(temp, description);
                    strcat(optr, temp);
                    optr += strlen(temp);
                    iptr += 2;
                    break;
                
                case 'I':
                    /* %I : file description (? characters- last field/wrapped) */
                    x = strlen(buff);
                    strcpy(temp, description);
                    strcat(optr, temp);
                    optr += strlen(temp);
                    iptr += 2;
                    thislen = strlen(buff);
                    while (thislen > 79)
                    {
                        /* find wrap point */
                        for (i = 78; i > x; i--)
                            if (isspace(buff[i]))
                            {
                                /* break it here */
                                buff[i] = '\0';
                                fprintf(f, "%s\n", buff);
                                for (j = 0; j < x; j++)
                                    buff[j] = ' ';
                                for (k = i + 1;
                                    buff[k] && isspace(buff[k]);
                                    k++);
                                strcpy(&buff[x], &buff[k]);
                                break;
                            }
                        if (i == x)
                        {
                            c = buff[78];
                            buff[78] = '\0';
                            fprintf(f, "%s\n", buff);
                            buff[78] = c;
                            for (j = 0; j < x; j++)
                                buff[j] = ' ';
                            strcpy(&buff[x], &buff[78]);
                        }
                        thislen = strlen(buff);
                    }
                    break;

                case 'J':
                    /* %J : name of uploader (36 characters) */
                    strcpy(temp, owner);
                    sprintf(temp2, "%-36s", temp);
                    strcat(optr, temp2);
                    optr += 36;
                    iptr += 2;
                    break;

                case 'K':
                    /* %K : number of files in the area (4 characters- ####) */
                    sprintf(temp, "%4d", filenum);
                    strcat(optr, temp);
                    optr += 4;
                    iptr += 2;
                    break;

                case 'L':
                    /* %L : path here files live (60 characters) */
                    strpastoc(temp, fboarddata.UlPathName);
                    sprintf(temp2, "%-60s", temp);
                    strcat(optr, temp2);
                    optr += 60;
                    iptr += 2;
                    break;

                case 'M':
                    /* %M : area size ( 7 characters- "##,###k") */
                    ksize = tsize / 1024;
                    if (ksize > 999)
                        sprintf(temp, "%2ld,%03ldk",
                            ksize / 1000L,
                            ksize % 1000L);
                    else
                        sprintf(temp, "%6ldk", ksize);
                    strcat(optr, temp);
                    optr += 7;
                    iptr += 2;
                    break;

                case 'N':
                    /* %N : area size (10 characters- ##,###,###)*/
                    if (tsize > 999999L)
                        sprintf(temp, "%2ld,%03ld,%03ld",
                            tsize / 1000000L,
                            (tsize % 1000000L) / 1000L,
                            tsize % 1000L);
                    else if (tsize > 999)
                        sprintf(temp, "   %3ld,%03ld",
                            tsize / 1000L,
                            tsize % 1000L);
                    else
                        sprintf(temp, "       %3ld", tsize);
                    strcat(optr, temp);
                    optr += 10;
                    iptr += 2;
                    break;

                case 'O':
                    /* %O : file size ( 7 characters- ##,###k,OFFLINE,UNVAL'D) */
                    if (dirdata.Flag.flag1 & FRS_NotValidated)
                        strcpy(temp, "UNVAL'D");
                    else if (ulsize)
                    {
                        ksize = ulsize / 1024L;
                        if (ksize > 999)
                            sprintf(temp, "%2ld,%03ldk",
                                ksize / 1000L,
                                ksize % 1000L);
                        else
                            sprintf(temp, "%6ldk", ksize);
                    }
                    else
                        strcpy(temp, "OFFLINE");
                    strcat(optr, temp);
                    optr += 7;
                    iptr += 2;
                    break;

                case 'P':
                    /* P : file size (10 characters- ##,###,###,OFFLINE,UNVALIDT'D) */
                    if (dirdata.Flag.flag1 & FRS_NotValidated)
                        strcpy(temp, "UNVALIDT'D");
                    else if (ulsize)
                    {
                        if (ulsize > 999999L)
                            sprintf(temp, "%2ld,%03ld,%03ld",
                                ulsize / 1000000L,
                                (ulsize % 1000000L) / 1000L,
                                ulsize % 1000L);
                        else if (ulsize > 999)
                            sprintf(temp, "   %3ld,%03ld",
                                ulsize / 1000L,
                                ulsize % 1000L);
                        else
                            sprintf(temp, "       %3ld", ulsize);
                    }
                    else 
                        strcpy(temp, "   OFFLINE");
                    strcat(optr, temp);
                    optr += 10;
                    iptr += 2;
                    break;

                case 'Q':
                    /* %Q : time of file (5 characters- ##:##/military) */
                    if ((fboarddata.Flags.flag1 & ULRF_IsCdRom) || missing)
                        strcpy(temp, "     ");
                    else 
                        sprintf(temp, "%02d:%02d",
                            (ffblk.ff_ftime >> 11) & 0x001f,
                            (ffblk.ff_ftime >> 5) & 0x003f);
                    strcat(optr, temp);
                    optr += 5;
                    iptr += 2;
                    break;

                case 'R':
                    /* %R : times downloaded (5 characters- #####) */
                    sprintf(temp, "%5d", dirdata.Nacc);
                    strcat(optr, temp);
                    optr += 5;
                    iptr += 2;
                    break;

                case 'S':
                    /* %S : number of areas in list    (5 characters) */
                    sprintf(temp, "%5d", tareas);
                    strcat(optr, temp);
                    optr += 5;
                    iptr += 2;
                    break;

                case 'T':
                    /* %T : Area number                (5 characters) */
                    sprintf(temp, "%5d", boardnum);
                    strcat(optr, temp);
                    optr += 5;
                    iptr += 2;
                    break;

                default:
                    *optr = *iptr;
                    optr++;
                    iptr++;
                    *optr = '\0';
                    break;
            }
        }
        else
        {
            *optr = *iptr;
            optr++;
            iptr++;
            *optr = '\0';
        }
    }
    fprintf(f, "%s\n", buff);
}

int isleap (unsigned yr)
{
   return yr % 400 == 0 || (yr % 4 == 0 && yr % 100 != 0);
}

static unsigned months_to_days (unsigned month)
{
   return (month * 3057 - 3007) / 100;
}

static long years_to_days (unsigned yr)
{
   return yr * 365L + yr / 4 - yr / 100 + yr / 400;
}

long ymd_to_scalar (unsigned yr, unsigned mo, unsigned day)
{
   long scalar;
   scalar = day + months_to_days(mo);
   if ( mo > 2 )                         /* adjust if past February */
      scalar -= isleap(yr) ? 1 : 2;
   yr--;
   scalar += years_to_days(yr);
   return scalar;
}

void scalar_to_ymd (long scalar, unsigned *pyr, unsigned *pmo, unsigned *pday)
{
   unsigned n;                /* compute inverse of years_to_days() */

   for ( n = (unsigned)((scalar * 400L) / 146097); years_to_days(n) < scalar;)
      n++;                          /* 146097 == years_to_days(400) */
   *pyr = n;
   n = (unsigned)(scalar - years_to_days(n-1));
   if ( n > 59 ) {                       /* adjust if past February */
      n += 2;
      if ( isleap(*pyr) )
         n -= n > 62 ? 1 : 2;
   }
   *pmo = (n * 100 + 3007) / 3057;  /* inverse of months_to_days() */
   *pday = n - months_to_days(*pmo);
}