/*
 * CHECKFILES.C
 *
 * This module contains routines to read in the disk directory, search
 * the file database for each filename found to see if it matches, etc.
 *
 */

#ifndef LATTICE_50
#include "system.h"
#endif
#include "bbsindex.h"

/*
 *             addunknown()
 *             ------------
 *             Adds a new entry to the list of unknown files found. If necessary,
 *             more memory will be allocated to hold the new entry.
 */
int addunknown(name, size, date, dirnum)
char *name;
long size;
int date, dirnum;
{
       static DIRENTRY *dirbase, *dir;

       if (numdirentries >= MAXDIRENT) {
               scripterror("directory table full. Skipping rest of files\n");
               return (FALSE);
       }
       if ((numdirentries % DIRFRAG) == 0)     /* Allocate new block */
               dirbase = mymalloc(DIRENTRYSIZE * DIRFRAG);

       dir = &dirbase[numdirentries % DIRFRAG];

       strcpy(dir->name, name);
       dir->size       = size;
       dir->date       = date;
       dir->dirnum     = dirnum;

       direntries[numdirentries++] = dir;
       return (TRUE);
}

/*
 *             find()
 *             ------
 *             Does a binary search of the file records looking for the specified
 *             filename. Returns pointer to the file record if found, NULL if not
 *             found.
 */
UDHEAD *find(name)
char *name;
{
       long low = 0, high = numrecs -1;
       long mid;
       int cmp;

       while (high >= low) {
               mid = (low + high) / 2;
               cmp = stricmp(ptrblock[mid]->disk_name, name);
               if (!cmp && ptrblock[mid]->type == 0)           /* Found match */
                       return (ptrblock[mid]);
               if (cmp > 0)                                            /* Too far ahead, go backwards */
                       high = mid-1;
               else                                                            /* Too far below, go forwards */
                       low  = mid+1;
       }
       return (NULL);
}

/*
 *             scandir()
 *             ---------
 *             Scans directory, adding the files found either to the 'unknown'
 *             list or updating the pointers in the main file database appropriately.
 */
int scandir(dirname, dirnum)
char *dirname;
int dirnum;
{
       UDHEAD *f;
       struct tm *filedate;
       int bbspcdate;

       dirlock = Lock(dirname, ACCESS_READ);
       if (dirlock == NULL) {
               scripterror("can't find directory ");
               print2(dirname, ", skipping\n");
               return (TRUE);
       }

       if (!Examine(dirlock, fib)) {
               scripterror("can't examine directory ");
               print2(dirname, ", skipping\n");
               UnLock(dirlock);
               dirlock = NULL;
               return (TRUE);
       }

       while (ExNext(dirlock, fib)) {
               chkabort();
               if (fib->fib_DirEntryType > 0)  /* Skip over directories */
                       continue;
               if (f = find(fib->fib_FileName)) {
                       f->online = 1;
                       f->valid = (fib->fib_Size == f->length);
                       f->dirnum = dirnum;
               } else {
                       /*
                        *              Convert AmigaDOS date (# days since 1/1/78, # mins
                        *              since midnight) into ANSI date (# seconds since 1/1/70)
                        *              then convert that into BBS-PC! format.
                        */
                       long ansidate;
                       ansidate = (fib->fib_Date.ds_Days + (365 * 8) + 2) * 86400;
                       filedate = gmtime(&ansidate);
                       bbspcdate = (((filedate->tm_year * 13) +
                                                 (filedate->tm_mon+1)) * 32) +
                                                  filedate->tm_mday;
                       if (!addunknown(fib->fib_FileName, fib->fib_Size, bbspcdate,
                                                                                                                                       dirnum)) {
                               UnLock(dirlock);
                               dirlock = NULL;
                               return (FALSE);
                       }

               }
       }
       if (IoErr() != ERROR_NO_MORE_ENTRIES) {
               scripterror("error reading directory ");
               print2(dirname, ", skipping\n");
       }
       UnLock(dirlock);
       dirlock = NULL;
       return (TRUE);
}

/*
 *             diskcmp()
 *             ---------
 *             Compares the disk filenames of two file headers, and returns true
 *             if they are equal. Used by the sort routine in check_files().
 */
int diskcmp(f1,f2)
UDHEAD **f1, **f2;
{
       return (stricmp((*f1)->disk_name, (*f2)->disk_name));
}

/*
 *             com_checkfiles()
 *             ----------------
 *             This command scans the file directories specified on the command
 *             line (or if none, then using those specified in the CFGINFO.DAT
 *             file). Each file in the catalogue is searched for in the file
 *             catalogue, and those that are found are marked as "Online"; if
 *             the disk filesize matches the catalogue filesize, then their
 *             "valid" flag is also set. Files which are not found are stored
 *             in a seperate array, which can be printed with the FOREIGN command.
 *             After this, any other files listed in the IGNORE list are also marked
 *             as valid.
 *
 *             Note that before the scan, the files are sorted into alphabetical
 *             order, by diskname, to allow a binary search to be done. Afterwards,
 *             if this disrupted a previous sorting order, they are sorted back
 *             to their original state.
 */
void com_checkfiles()
{
       int dirnum;
       IGNORE *ig;
       UDHEAD *f;

       if (checkfiles)
               return;

       CHECKDATABASE();
       qsort(ptrblock, numrecs, sizeof(UDHEAD *), diskcmp);
       if (compos >= comlen) {
               readconfigfile();
               for (dirnum = 0; dirnum < NUM_SECT && dirnames[dirnum][0]; dirnum++) {
                       chkabort();
                       if (!scandir(dirnames[dirnum], dirnum))
                               break;
               }
       } else {
               for (dirnum = 0; dirnum < NUM_SECT; dirnum++) {
                       if (compos >= comlen)
                               break;
                       strcpy(dirnames[dirnum], getstring());
                       chkabort();
                       if (!scandir(dirnames[dirnum], dirnum))
                               break;
               }
       }
       /*
        *              Now mark any "ignored" files as valid.
        */
       for (ig = firstignore; ig; ig = ig->next) {
               if (f = find(ig->name))
                       f->valid = 1;
       }
       chkabort();
       if (sorted)
               qsort(ptrblock, numrecs, sizeof(UDHEAD *), sortcmp);
       checkfiles = TRUE;
}

/*
 *             com_foreign()
 *             -------------
 *             This command prints out the list of foreign files, i.e. files
 *             not in the catalogue, using the format string setup with
 *             FORMAT. Not all fields are valid.
 */
void com_foreign()
{
       /*
        *              Standard file header template for foreign files.
        *              Note file[] is an array, so we can say file->x instead of file.x
        */
       static UDHEAD file[1] = {{
               0,                                              /* Record type (== valid)                               */
               1, 1, 1, 1,                     /* Local, Binary, Valid and Online :-)  */
               0,                                              /* Directory number is set later                */
               "",                                             /* Catalogue filename, filled in later  */
               0,                                              /* Date is set later                                    */
               0, 0, 0,                                /* Section 0, Directory 0, no accesses  */
               0,                                              /* Length is set later                                  */
               "",                                             /* Disk name is set later                               */
               "AmigaDos",                             /* The file owner                                               */
               "Directory = ",                 /* File comment, set later                              */
       }};

       int i;

       if (!checkfiles) {
               scripterror("can't do FOREIGN before CHECKFILES\n");
               Cleanup(10);
       }

       for (i = 0; i < numdirentries; i++) {
               chkabort();
               /* Setup actual file parameters for format() */
               strcpy(file->disk_name, direntries[i]->name);
               strncpy(file->cat_name, direntries[i]->name, CAT_LEN);
               strncpy(file->desc+12, dirnames[direntries[i]->dirnum], DESC_LEN-12);
               file->desc[DESC_LEN] = CHAR_NULL;
               file->length    = direntries[i]->size;
               file->date              = direntries[i]->date;
               file->dirnum    = direntries[i]->dirnum;
               format(out, MAXOUT, formatstring, file, checkfiles);
               putstring(out);
       }
}
