/*
 *    updatedb.c        © by Martin Steppler
 *
 *    last change: 01.10.92
 */

/* includes */

#include <stdio.h>
#include <string.h>
#include <exec/exec.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>


/* defines */

#define WrStdOut(str) Write(StdOut, str, (LONG)strlen(str))
#define TMPNAME "RAM:UPDATEDB.TMP"
#define DBNAME "FindDB:find.codes"
#define CFGNAME "FindDB:find.config"
#define EOS '\0'

/* globals */

struct DirList {
   char DirName[400];
   int Length;
   struct DirList *NextEntry;
};

struct DirList *BottomEntry=NULL, *CurrentEntry=NULL;

/* prototypes */

LONG  main(int, char **);
struct DirList *MakeFirstEntry(VOID);
BOOL  AddEntry(char *DName);
VOID  DeleteCurrentEntry(VOID);
VOID  DeleteAllEntries(VOID);

/* functions */

struct DirList *MakeFirstEntry(VOID)
{
   struct DirList *FirstEntry;

   if(FirstEntry=(struct DirList *)AllocMem(sizeof(struct DirList),0)) {
      /* initialize entry */
      FirstEntry->Length    = 0;
      FirstEntry->NextEntry = NULL;
   }
   return(FirstEntry);
}

BOOL AddEntry(char *DName)
{
   struct DirList *NewEntry;
   int len = CurrentEntry->Length-1;

   if(NewEntry = MakeFirstEntry()) {
      /* store dirname */
      strcpy(NewEntry->DirName, CurrentEntry->DirName);
      /* not a device name ? */
      if(CurrentEntry->DirName[len] != ':') strcat(NewEntry->DirName, "/");
      strcat(NewEntry->DirName, DName);
      NewEntry->Length = strlen(NewEntry->DirName);
      /* append entry */
      BottomEntry->NextEntry = NewEntry;
      /* new bottom */
      BottomEntry = NewEntry;
      return(TRUE);
   }
   return(FALSE);
}

VOID DeleteCurrentEntry(VOID)
{
   struct DirList *NewCurrentEntry = CurrentEntry->NextEntry;

   FreeMem(CurrentEntry, sizeof(struct DirList));

   CurrentEntry = NewCurrentEntry;
}

VOID DeleteAllEntries(VOID)
{
   while(CurrentEntry) DeleteCurrentEntry();
}

/* here we go ... */

LONG
main(int argc, char **argv)
{
   struct DosLibrary *DosBase = NULL;
   struct FileInfoBlock *infoBlock = NULL;
   BPTR StdOut=0, lock, TmpFile=0;
   char buffer[400], *hunk = NULL, comline[100], newdir[100];
   int verbose = FALSE;       /* verbose mode is switched off */
   int argsfromconfig = FALSE; /* use find.config as command line */
   int i = 0; /* noargs */
   int num_dirs=0, num_files=0, len, where=0, do_not_resolve = FALSE;
   int num_hard_dirs = 0, num_hard_files = 0, num_soft_dirs = 0, num_soft_files = 0;

   /* open standard output */
   StdOut = (BPTR)Open("*", MODE_OLDFILE);

   if(argc >= 2) {
      /* there are args! */
      i++;
      /* display template */
      if(!strcmp(argv[1], "?") || !strcmp(argv[1], "-?") || !strcmp(argv[1], "h") ||
         !strcmp(argv[1], "-h")) {
         WrStdOut("USAGE: \33[33mupdatedb\33[31m [-v] [<device> {<device>}]\n");
         WrStdOut("          -v     verbose mode\n\n");
         WrStdOut("       V2.1 © 1989-92 Larry Phillips, Martin Steppler\n\n");
         WrStdOut("       Examples:\n");
         WrStdOut("       'updatedb dh0: dh1:'   scan dh0: and dh1:\n");
         WrStdOut("       'updatedb -v wb_2.x:'  scan wb_2.x and print file/dirnames\n");
         WrStdOut("       'updatedb'             use FindDB:find.config as command line\n\n");
         WrStdOut("The list of file/dirnames is saved as FindDB:find.codes.\n");
         WrStdOut("See find.doc for further informations!\n");

         Close(StdOut);
         StdOut = 0;
         goto abort;
      }
      /* switch verbose mode on, skip first arg */
      else if(!strcmp(argv[1], "-v")) {
         verbose = TRUE; i++;
      }
   }

   /* open dos.library */
   if(DosBase = (struct DosLibrary *)OpenLibrary("dos.library",37)) {
      /* open temporary file */
      if(TmpFile = (BPTR)Open(TMPNAME, MODE_READWRITE)) {
         /* alloc mem for infoBlock */
         if(infoBlock=(struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),0)) {
            WrStdOut("\33[33mUpdateDB V2.1\33[31m © 1989 - 1992 by Larry Phillips & Martin Steppler\n\n");
            /* no args submitted -> parse FindDB:find.config */
            if(argc==1) {
               if(lock = (BPTR)Open(CFGNAME, MODE_OLDFILE)) {
                  if((len=Read(lock, comline, 100)) != -1) {
                     Close(lock);
                     argsfromconfig = TRUE;
                     if(comline[len-1] != ':') comline[len-1] = EOS;
                     comline[len] = EOS;
                  }
                  else {
                     sprintf(buffer, "Unable to read from config file. DOS Error Code: %d\n", IoErr());
                     WrStdOut(buffer);
                     Close(lock);
                     goto abort;
                  }
               }
               else {
                  sprintf(buffer, "Unable to open 'FindDB:find.config'. DOS Error Code: %d\n", IoErr());
                  WrStdOut(buffer);
                  goto abort;
               }
            }
            /* now scan all devices */
            for(;;) {
               if(argsfromconfig) {
                  len=0;
                  for(;;) {
                     newdir[len] = comline[where];
                     if(newdir[len] == ' ') {
                        newdir[len] = EOS; where++;
                        break;
                     }
                     else if(!newdir[len]) {
                        argsfromconfig = FALSE;
                        break;
                     }
                     len++; where++;
                  }
                  len=0;
               }
               else {
                  if(i>=argc) break;
                  len=1;
               }

               if(len) sprintf(buffer, "Scanning \33[33m%s\33[31m\n", argv[i]);
               else    sprintf(buffer, "Scanning \33[33m%s\33[31m\n", newdir);
               WrStdOut(buffer);
               /* initialize data */
               CurrentEntry = BottomEntry = MakeFirstEntry();
               if(len) strcpy(CurrentEntry->DirName, argv[i]);
               else    strcpy(CurrentEntry->DirName, newdir);
               CurrentEntry->Length = strlen(CurrentEntry->DirName);
               do {
                  if(verbose) {
                     sprintf(buffer, "\33[33m%s\33[31m\n", CurrentEntry->DirName);
                     WrStdOut(buffer);
                  }
                  if(lock = Lock(CurrentEntry->DirName, ACCESS_READ)) {
                     if(Examine(lock, infoBlock)) {
                        /* save name of current dir */
                        strcpy(buffer, CurrentEntry->DirName);
                        len = CurrentEntry->Length;
                        /* not a device name ? */
                        if(buffer[len-1] != ':') {
                           strcat(buffer, "/"); len++;
                        }
                        /* +1 --> send null byte, too */
                        if(Write(TmpFile, buffer, len+1) != -1) {
                           /* increase dirs counter */
                           num_dirs++;
                           /* examine all entries of the current dir */
                           while(ExNext(lock, infoBlock)) {
                              if (infoBlock->fib_EntryType == ST_LINKDIR) {
                                 /* an hardlink to a dir */
                                 num_hard_dirs++;
                                 num_files--;
                                 /* do not resolve hard linked directories */
                                 do_not_resolve = TRUE;
                              } else if (infoBlock->fib_EntryType == ST_LINKFILE) {
                                 /* an hardlink to a file */
                                 num_hard_files++;
                                 num_files--;
                              } else if (infoBlock->fib_EntryType == ST_SOFTLINK) {
                                 /* a softlink */
                                 struct DevProc *devproc;
                                 BPTR oldlock;

                                 /* cd to softlink's dir */
                                 /* otherwise ReadLink() might fail */
                                 oldlock = CurrentDir(lock);

                                 if (!(devproc = GetDeviceProc(infoBlock->fib_FileName, NULL))) {
                                     sprintf(buffer, "GetDeviceProc() failed.\n");
                                     WrStdOut(buffer);
                                     CurrentDir(oldlock);
                                     goto abort;
                                 }

                                 if (!ReadLink(devproc->dvp_Port, devproc->dvp_Lock, infoBlock->fib_FileName, buffer, 300)) {
                                     sprintf(buffer, "ReadLink() failed. DOS Error Code: %d\n", IoErr());
                                     WrStdOut(buffer);
                                     FreeDeviceProc(devproc);
                                     CurrentDir(oldlock);
                                     goto abort;
                                 } else {
                                     struct FileInfoBlock file_info_block;
                                     BPTR soft_lock;

                                     FreeDeviceProc(devproc);
                                     soft_lock = Lock(buffer, ACCESS_READ);
                                     Examine(soft_lock, &file_info_block);

                                     num_files--;
                                     /* do not resolve soft links */
                                     do_not_resolve = TRUE;
                                     if (file_info_block.fib_DirEntryType > 0) {
                                        num_soft_dirs++;
                                     } else
                                        num_soft_files++;

                                     UnLock(soft_lock);
                                     CurrentDir(oldlock);
                                 }
                              }

                              /* a directory ? */
                              if(infoBlock->fib_DirEntryType > 0 && do_not_resolve == FALSE) {
                                 /* then add to the dir list */
                                 if(!AddEntry(infoBlock->fib_FileName)) {
                                    WrStdOut("Out of memory!\n");
                                    UnLock(lock);
                                    goto abort;
                                 }
                              }
                              /* no, it's a file or a link (soft or hard) */
                              else {
                                 if(verbose) {
                                    sprintf(buffer, "   %s\n", infoBlock->fib_FileName);
                                    WrStdOut(buffer);
                                 }
                                 /* send filename + null byte */
                                 if(Write(TmpFile, infoBlock->fib_FileName, strlen(infoBlock->fib_FileName)+1) == -1) {
                                    sprintf(buffer, "Unable to write to temporary file. DOS Error Code: %d\n", IoErr());
                                    WrStdOut(buffer);
                                    UnLock(lock);
                                    goto abort;
                                 }
                                 /* increase num_files counter */
                                 num_files++;
                                 do_not_resolve = FALSE;
                              }
                           }
                           if((len = IoErr()) != ERROR_NO_MORE_ENTRIES) {
                              sprintf(buffer, "ExNext() failed. DOS Error Code: %d\n", len);
                              WrStdOut(buffer);
                              UnLock(lock);
                              goto abort;
                           }
                        }
                        else {
                           sprintf(buffer, "Unable to write to temporary file. DOS Error Code: %d\n", IoErr());
                           WrStdOut(buffer);
                           UnLock(lock);
                           goto abort;
                        }
                     }
                     else {
                        sprintf(buffer, "Examine() failed. DOS Error Code: %d\n", IoErr());
                        WrStdOut(buffer);
                        UnLock(lock);
                        goto abort;
                     }
                  }
                  else {
                     sprintf(buffer, "Lock() failed. DOS Error Code: %d\n", IoErr());
                     WrStdOut(buffer);
                     goto abort;
                  }
                  /* scanned the whole dir */
                  if (lock)
                     UnLock(lock);
                  DeleteCurrentEntry();

                  /* user hit Ctrl-c? */
                  if (SetSignal(0,0) & SIGBREAKF_CTRL_C) {
                     sprintf(buffer, "Ctrl-C --> Aborting ...\n");
                     WrStdOut(buffer);
                     goto abort;
                  }
               } while(CurrentEntry);
               i++;
            }
            /* everything worked fine - so far =-) */
            /* now copy tmpfile to FindDB:find.codes */
            if(lock = (BPTR)Open(DBNAME, MODE_NEWFILE)) {
               Close(TmpFile);
               TmpFile=Lock(TMPNAME, ACCESS_READ);
               if(Examine(TmpFile, infoBlock)) {
                  UnLock(TmpFile);
                  TmpFile = (BPTR)Open(TMPNAME, MODE_OLDFILE);
                  len = infoBlock->fib_Size;
                  if(hunk = (char *)AllocMem(len+1, 0)) {
                     if((len=Read(TmpFile, hunk, len)) != -1) {
                        if(Write(lock, hunk, len) == -1) {
                           sprintf(buffer, "Unable to write to 'FindDB:find.codes'. DOS Error Code: %d\n", IoErr());
                           WrStdOut(buffer);
                           Close(lock);
                           goto abort;
                        }
                     }
                     else {
                        sprintf(buffer, "Unable to read from temporary file. DOS Error Code: %d\n", IoErr());
                        WrStdOut(buffer);
                        Close(lock);
                        goto abort;
                     }
                  }
                  else {
                     WrStdOut("Out of memory!\n");
                     Close(lock);
                     goto abort;
                  }
               }
               else {
                  sprintf(buffer, "Unable to examine temporary file. DOS Error Code: %d\n", IoErr());
                  WrStdOut(buffer);
                  Close(lock);
                  UnLock(TmpFile);
                  TmpFile = 0;
                  goto abort;
               }
            }
            else {
               sprintf(buffer, "Unable to open 'FindDB:find.codes'. DOS Error Code: %d\n", IoErr());
               WrStdOut(buffer);
               goto abort;
            }
            /* game over, man */
            Close(lock);
            sprintf(buffer, "Update completed. No errors occurred.\n\n           \33[33mDirs:\33[31m %4d \33[33mFiles:\33[31m %4d\n", num_dirs, num_files);
            WrStdOut(buffer);
            sprintf(buffer, "\33[33mHardlinked Dirs:\33[31m %4d \33[33mFiles:\33[31m %4d\n", num_hard_dirs, num_hard_files);
            WrStdOut(buffer);
            sprintf(buffer, "\33[33mSoftlinked Dirs:\33[31m %4d \33[33mFiles:\33[31m %4d\n", num_soft_dirs, num_soft_files);
            WrStdOut(buffer);
         }
      }
      else WrStdOut("Unable to open temporary file!\n");
   }
   else WrStdOut("Unable to open dos.library (V37)\n");

abort:
   if(hunk)            FreeMem(hunk, len);
   if(CurrentEntry)    DeleteAllEntries();
   if(infoBlock)       FreeMem(infoBlock, sizeof(struct FileInfoBlock));
   if(TmpFile)         { Close(TmpFile); DeleteFile(TMPNAME); }
   if(DosBase)         CloseLibrary((struct Library *)DosBase);
   if(StdOut)          Close(StdOut);
   return(0);
}
