Metropoli BBS
VIEWER: files.c MODE: TEXT (ASCII)
/***
*files.c - disked file indexing
*
*Copyright (c) 1991-1995, Gregg Jennings.  All wrongs reserved.
*   P O Box 200, Falmouth, MA 02541-0200
*
*Purpose:
*   Indexes all files.
*
*Notice:
*   This progam may be freely used and distributed.  Any distrubution
*   with modifications must retain the above copyright statement and
*   modifications noted.
*   No pulp-publication, in whole or in part, permitted without
*   permission (magazines or books).
*******************************************************************************/

/*
   Versions

   2.5   22-Dec-1994    enums, dDIR was DIR
   2.4   04-Sep-1994    extern reference consolodation; getdir() changes
   2.3   30-Jul-1994    added struct DIR * in getdir()
   2.2   26-Feb-1994    huge clusters/files etc
   2.1   14-Jan-1994    bug fix in initfiles()


   Notes:   The error handling stuff is a bit crude (there has 
            been sleight improvements since last release but it
            could still use some work).
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <conio.h>
#include <dos.h>

#include "disked.h"
#include "console.h"                     /* conout, print */
#include "alloc.h"
#include "diskio.h"
#include "files.h"
#include "error.h"
#include "dirent.h"
#include "direct.h"

enum DOS_DIR_FILENAME
{
   FILENAM = 8,
   FILETYP = 3,
   FILELEN = FILENAM+FILETYP
};

enum DOS_DIR_OFFSETS
{
   ATTRIB = 11,
   CLUSTL = 26,
   CLUSTH = 27
};

enum DOS_DIR_FIRST_CHARS
{
   ERACHR = 0xE5,
   DIRCHR = '.',
   WEIRDC = 5,
   ENDDIR = 0
}; 

enum DOS_DIR_ATTRIBUTES          /* the ones we care about here */
{
   DIRATR = 0x10,                /* (bits) */
   VOLATR = 8
};

/* global data referenced here

   all of the DISKIO stuff

*/

/* global data defined here */

struct files_t __huge *files;
unsigned int n_files,n_dirs;  /* number of files and directories */
int __huge *clusters;         /* pointer to array of cluster entries */
                              /* int limits this to 32000 clusters */
/* static data */

static unsigned int tn_files;
static const char *module = "files";

/* static functions */

static int getdir(unsigned long sector,int parent,int nsecs);
static int getfat12(int nsecs,unsigned int nclusters);
static int getfat16(int nsecs,unsigned int nclusters);
__inline static void namecpy(char *n, char *d);


/***
*initfiles() - read/index all files
*
*  ver   3.0 13-Jan-1994   fixed getfatxx() return signed/unsigned mismatch
*
****/

extern int initfiles(void)
{
int mem,status;

   mem = status = 0;

   if (_osmajor < 3)          /* always off if DOS != 3.x */
   {
      status = REQ_DOS_3;
      goto error;
   }
   if ((clusters=(int _huge *)hugealloc(num_clusters+1,sizeof(int)))==NULL)
   {
      status = NO_MEM;
      goto error;
   }
   mem = 1;

   if (num_clusters <= 4086)                       /* 12 bit FAT */
      status = getfat12(secs_fat,num_clusters);
   else
      status = getfat16(secs_fat,num_clusters);

   if (status == -1)
      goto error;

   n_files = status;

   tn_files = n_files + 2;         /* NOTE extra entries */

   if ((files=(struct files_t _huge *)hugealloc(tn_files,sizeof(struct files_t)))==NULL)
   {
      status = NO_MEM;
      goto error;
   }
   mem = 2;

   n_files = n_dirs = 1;
   strcpy(files[0].name,"\\");         /* root directory name */
   files[0].parent = 0;
   files[0].dir = 1;

   if (getdir((dword)dir_sector,0,dir_sectors) != 0)
   {
      if (Display) 
      { 
         output('\r');
         clreol(); 
      }
      else 
         output(' ');
      return 1;                        /* RETURN */
   }
   output(' ');

error:
   if (mem == 2)
      hugefreep(files);
   if (mem)
      hugefreep(clusters);
   if (status == NO_MEM)
   {
      set_error(NULL,module,status,"initfiles");
      if (mem)
         set_err_arg("files:%lu",(long)         \
         (((long)num_clusters*sizeof(int))+1) + \
         ((n_files+10)*sizeof(struct files_t)));
      else
         set_err_arg("clusters:%lu", \
         (long)((long)num_clusters*sizeof(int))+1);
   }
   return 0;
}

/***
*getdir - read in ALL files on the drive
*
*
*  ver   2.1   04-Sep-1994    twirl chars; struct DIR, namecpy()
****/

static int getdir(unsigned long sector,int parent,int nsecs)
{
unsigned int i,l;
unsigned char *buffer;
int status;
dDIR *dir;

   status = 0;  
   
   if (!Display)
   {
      static int ind;
      print("%c\b","\\|/-"[++ind%4]);     /* for slow machines */
      /*if (ind == 5) ind = 0;*/          /*  get rid of %4 and add this */
   }
   if ((buffer=(unsigned char *)alloc(sec_size,sizeof(char)))==NULL)
   {
      status = NO_MEM;
      goto error;
   }
   
   while (nsecs-- >0)
   {
      if (diskio(DISK_READ,sector++,buffer) != DISK_OK)
         goto error;
      dir = (dDIR *)buffer;

      for (i = 0; i < sec_size/sizeof(dDIR); i++,dir++)
      {
         if (dir->name[0] == ERACHR || dir->name[0] == DIRCHR)
             continue;
         if (dir->name[0] == ENDDIR)
         {
            freep(buffer);
            return n_files;                    /* so return */
         }
         l = dir->start;
         if (dir->attr.volume)
         {
            namecpy(volume,(char *)dir->name);
            continue;
         }
         if (l == 0)                   /* skip 0 length files */
            continue;

         namecpy(files[n_files].name,(char *)dir->name);
         files[n_files].parent=parent;
         ++n_files;

         if (n_files == tn_files)      /* trying to write past end? */
         {
            status = NUM_FILES;
            goto error;
         }
         if (l > num_clusters)
         {
            status = INV_ST_CLUS;
            goto error;
         }
         if (dir->attr.subdir)               /* is a directory? */
         {
            register int k;
            register int nf = n_files-1;
            if (Display)
            {
               print("\r%s",files[nf].name);
               clreol();
            }
            ++n_dirs;
            files[nf].dir = 1;

            /* get subdirectory */

            if (!getdir(clustertosector(l),nf,secs_cluster))
               goto error;

            while ((k = clusters[l]) > 0)
            {
               clusters[l] = nf;
               if (!getdir(clustertosector(k),nf,secs_cluster))
                  goto error;
               l = k;
            }
            clusters[l] = nf;
         }
         else
         {           
            register int k;
            register int nf = n_files-1;
            while ((k = clusters[l]) > 0)
            {
               if ((unsigned int)k == l)
               {
                  status = FAT_PHASE;
                  goto error;
               }
               clusters[l] = nf;
               l = k;
            }
            clusters[l] = nf;
         }
      }
   }
   freep(buffer);
   return n_files;

error:
   freep(buffer);
   if (status)
   {
      set_error(err_msg[status],module,status,"getdir");
      if (status == INV_ST_CLUS)
         set_err_arg(" \"%s\" (%04x)",gfile(n_files-1),l);
      else if (status == NUM_FILES)
         set_err_arg(" (%d)",n_files);
   }
   return 0;
}

/* convert "AAAA    XXX" to "AAAA.XXX\0" */

__inline static void namecpy(char *n, char *d)
{
int j;

   for (j = 0; j < FILELEN; j++,d++)
   {
      if (*d == ' ')
         continue;
      if (j == FILENAM)
         *n++ = '.';
      *n++ = *d;
   }
   *n = '\0';
}

/*
   How FATs work:

   function: getfat12(number_of_sectors, number_of_clusters)

   reads all FAT sectors into a buffer and then convert the buffer
   into the clusters[] array.

   12 bit:  used on drives with up to 4086 clusters

   start from fatbuf[3], taking 3 unsigned chars at a time:

   ___         take the 0 from 40 (last 4 bits) and put it in
   || \        front of the 03 which results in 003
   03 40 00
      \__||    take the 4 from 40 (first 4 bits) and put it in
                    back of the 00 which results in 004

        But Remember: int's are stored in memory Least Significant
                      Byte first.

   000         unused
   ff7         bad
   ff8-fff     last used cluster of the file

        What MS-DOS Programmer's Reference says:

        Start with the starting cluster number.  Multiply cluster
        number just used by 1.5.  The whole part of the product is
        an offset into the FAT, pointing to the entry that maps the
        cluster just used.  That entry contains the cluster number
        of the next cluster of the file.  Get the entry.  If the
        last cluster used was an even number AND the entry
        with 0xfff to keep the low-order 12 bits otherwise shift
        it to the right 4 times.


   16 bit:  drives > 4086 clusters

   start from fatbuf[4], take an integer at a time (2 bytes)

   03 00       results in 0003

   0000        unused
   fff7        bad
   fff8-ffff   last used cluster of the file
*/

static int getfat12(int nsecs,unsigned nclusters)
{
register unsigned int h,n;
unsigned int l;
int status,i;
int nf;
unsigned char *buffer;
unsigned char *bufptr;
unsigned int *po;
unsigned int *pe;
int stat;

   stat = status = nf = 0;
   h = l = 0;

   if ((buffer = (unsigned char *)alloc(sec_size*nsecs,sizeof(char))) == NULL)
   {
      status = NO_MEM;
      goto error;
   }
   /* read logical sectors 1 to nsecs */
   stat = 1;
   for (i = 1, bufptr = buffer; i <= nsecs; i++, bufptr += sec_size)
      if (diskio(DISK_READ,(long)i,bufptr) != DISK_OK)
         goto error;

   for (h = 2, n = 3; h <= nclusters; n+=3)
   {
      pe = (unsigned int *)(buffer+n);
      po = (unsigned int *)(buffer+n+1);
      l = *pe&0xfff;
      if (l >= 0xff8)                     /* end of file */
      {
         clusters[h] = -1;
         ++nf;
      }
      else if (l==0xff7)                  /* bad */
         clusters[h] = -2;
      else
         clusters[h] = l;
      if (++h > nclusters)
         break;
      if (l > nclusters && l < 0xff7)
      {
         status = INV_CLUS;
         goto error;
      }
      l = *po>>4;
      if (l >= 0xff8)
      {
         ++nf;
         clusters[h] = -1;
      }
      else if (l == 0xff7)
         clusters[h] = -2;
      else
         clusters[h] = l;
      if (++h > nclusters)
         break;
      if (l > nclusters && l < 0xff7)
      {
         status = INV_CLUS;
         goto error;
      }
   }
   freep(buffer);
   return nf;

error:
   if (stat)
      freep(buffer);
   if (status)
   {
      set_error(err_msg[status],module,status,"getfat12");
      if (status == NO_MEM)
         set_err_arg("%u",sec_size*nsecs);
      else if (status == INV_CLUS)
         set_err_arg("entry: %d value: %x",h,l);
   }
   return ERROR;
}

/*
   ver 2.0 14-Nov-1993 fixed bug where not all sectors read (oops!)
                       (do..while tested: sector < nsecs)
*/

static int getfat16(int nsecs,unsigned nclusters)
{
register unsigned int h = 0;
unsigned int *pi;
int n;
unsigned int l = 0;
int status;
int nf;
unsigned int sector;
unsigned char *buffer;
int stat;

   stat = status = nf = 0;

   /* allocate space for the FAT to be read */
   /* NOTE: sectors are read one at a time due due to 16 bit FATs
      being usually large.
   */
   if ((buffer=(unsigned char *)alloc(sec_size,sizeof(char)))==NULL)
   {
      status = NO_MEM;
      goto error;
   }
   stat = 1;
   sector = reserved_secs;
   h = 2;
   do
   {
      if (diskio(DISK_READ,(long)sector,buffer) != DISK_OK)
      {
         goto error;
      }
      if (sector == reserved_secs)
         n = 4;                        /* skip first 4 bytes in first sector */
      else
         n = 0;

      for (;n<(int)sec_size;n+=2)
      {
         pi = (unsigned int *)(buffer+n);
         l = *pi;
         if (l >= 0xfff8)              /* end of entries */
         {
            ++nf;                      /* increment file count */
            clusters[h] = -1;          /* mark file end */
         }
         else if (l == 0xfff7)         /* cluster marked bad */
            clusters[h] = -2;          /* flag it */
         else
            clusters[h] = l;           /* else assume good cluster entry */
         if (++h > nclusters)
            break;
         if (l > nclusters && l < 0xfff7)  /* check for out-of-range number */
         {
            status = INV_CLUS;
            goto error;
         }
      }
      ++sector;
   } while (sector < (unsigned)nsecs + reserved_secs);
   freep(buffer);
   return nf;

error:
   if (stat)
      freep(buffer);
   if (status)
   {
      set_error(err_msg[status],module,status,"getfat16");
      if (status == NO_MEM)
         set_err_arg("%u",sec_size);
      else if (status == INV_CLUS)
         set_err_arg("entry: %d value: %x",h,l);
   }
   return ERROR;
}

/* print file name */

extern int pfile(char *file, unsigned int i)
{
    while (i != 0)
       i = pfile(files[i].name,files[i].parent);
    print("\\%s",file);
    return files[i].parent;
}

/* get file name matched to cluster number */

#define F_LEV 10     /* directory name depth level */

extern char *gfile(unsigned int index)
{
static char buf[67];
int flev[F_LEV],i;

   buf[0] = '\0';
   i=0;
   memset(flev,0,sizeof(int)*F_LEV);

   while (index != 0)
   {
      if (i > F_LEV)
      {
         i = 0;
         strcpy(buf,"max lev error");
         break;
      }
      flev[i++] = index;
      index = files[index].parent;
   }
   while (i)
   {
      strcat(buf,"\\");
      strcat(buf,files[flev[i-1]].name);
      --i;
   }
   return buf;
}

/*
 * Findfile:  Returns the first cluster number of the passed filename
 * if it is in the files structure.
 *

                     *** BUG ***

      sometimes finds improperly; "temp" finds first
      string whether its "\foo\temp" or "\win\system\temp"


 *   ver  0.0  9/91
 */

extern unsigned int findfile(char *file)
{
register char *token;
register unsigned int i,j;

   token = strtok(file,"\\");
   for (i = 0; i < n_files; i++)
   {
      if (stricmp(token,files[i].name) == 0)
      {
         token = strtok(NULL,"\\");
         if (token == NULL)
         {
            for (j = 2; j < num_clusters; j++)
               if (clusters[j] > 0 && (unsigned)clusters[j] == i)
                  return j;
            break;
         }
      }
   }
   return 0;
}

extern unsigned int get_avail_clusters(void)
{
union REGS regs;
unsigned c,j;

   if (!Files)
   {
      regs.x.ax = 0x3600;
      regs.x.dx = disk;
      intdos(&regs,&regs);
      return regs.x.bx;
   }
   else
      for (c = 0,j = 2; j <= num_clusters; j++)
         if (clusters[j] == 0)
             ++c;
   return c;
}

#ifdef MAP_FATS

/* display FAT -- not done yet */

extern void mapfat12(int sec)
{
register unsigned int h,n;
int i;
unsigned char *buffer = sec_buf;
unsigned int *po;
unsigned int *pe;

   if (sec == sec)
      n = 3;

   send('\n');

   for (h=2,n=3;n<sec_size-1;h+=8)
   {
      printf("\n%03d-",h);
      for (i=0;i<8;i++,n+=3)
      {
         pe=(unsigned int *)(buffer+n);
         po=(unsigned int *)(buffer+n+1);
         printf("%03x ",(unsigned int)*pe&0xfff);
         printf("%03x ",(unsigned int)*po>>4);
      }
   }
}

#endif
[ RETURN TO DIRECTORY ]