/* :ts=3
** MakeCDDirList - scans (sub)directories and prints filelists for MakeCD
** 
** Refer to the MakeCD documentation for file list format.
** 
** written & © Ralph Reuchlein <amiga@rripley.de> 1999
*/

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include <exec/types.h>
#include <proto/dos.h>

const UBYTE ver_string[] = "$VER: MakeCD_DirList 1.0 (26.12.99)"
                           "Copyright © 1999 by Ralph Reuchlein <amiga@rripley.de>";


/* ReadArgs defines ----------------------------------------------- */
#define ARGS_TEMPLATE   "STARTDIRECTORY=DIR/A,"\
                        "PREFIX=PRE,"\
                        "PROGRESSBAR=BAR/S"

enum {
   OPT_DIR = 0,
   OPT_PRE,
   OPT_BAR,

   OPT_COUNT
};
LONG args[OPT_COUNT];

#define ARG_DIR   ((STRPTR)args[OPT_DIR])
#define ARG_PRE   ((STRPTR)args[OPT_PRE])
#define ARG_BAR   (args[OPT_BAR]==-1)

#define  LINKFILENAME      ".amiga_makecd_update"

#define  LINK_ERROR        0
#define  LINK_GOOD         1
#define  LINK_BAD          2


/* Prototypes ----------------------------------------------------- */
void  error(STRPTR,...);
LONG  examineDir(STRPTR);
int   resolveLink(STRPTR);
void  convertProtection(LONG);



/* Global variables ----------------------------------------------- */

#define FILENAMESIZE 512
static UBYTE fileName[FILENAMESIZE];
static UBYTE linkName[FILENAMESIZE];
UWORD pathstart=0;
BOOL  pathstartdone=FALSE;

ULONG sumFiles=0;
ULONG sumDirs=0;
ULONG sumEmptyDirs=0;
ULONG sumGoodLinks=0;
ULONG sumBadLinks=0;
ULONG sumUnknown=0;
ULONG sumBytes=0;
ULONG sumReadProt=0;
UWORD dirLevel=0;

static struct DateTime dt;
static UBYTE dateString[LEN_DATSTRING];
static UBYTE timeString[LEN_DATSTRING];
static UBYTE protect[16];

FILE *fh=NULL;


/* ---------------------------------------------------------------- */
void main(UWORD argc,STRPTR argv[]) {
   struct RDArgs *rdargs;

   /* process arguments */
   if (rdargs=ReadArgs(ARGS_TEMPLATE,args,NULL)) {
      strcpy(fileName,ARG_DIR);
      AddPart(fileName,LINKFILENAME,FILENAMESIZE-1);
      if (fh=fopen(fileName,"wb")) {
         examineDir(ARG_DIR);
         fclose(fh);
         error("%u dirs (%u empty),\n"
               "%u files (%u read protected, %u bytes),\n"
               "%u good and %u bad links processed (%u unknown).\n",
               sumDirs,sumEmptyDirs,
               sumFiles,sumReadProt,sumBytes,
               sumGoodLinks,sumBadLinks,sumUnknown);
      }
      else {
         error("Couldn't open file '%s' for link list!\n",fileName);
      }
      FreeArgs(rdargs);
   }
   else {
      error("Usage: %s %s\n",argv[0],ARGS_TEMPLATE);
   }
}



/* ---------------------------------------------------------------- */
void error(STRPTR fmt,...) {
   va_list args;
   va_start(args,fmt);
   vfprintf(stderr,fmt,args);
   va_end(args);
   fflush(stderr);
}



/* ---------------------------------------------------------------- */
LONG examineDir(STRPTR dirname) {
   BOOL  ok=TRUE;
   BPTR  l;
   LONG  entryCnt=0,subCnt;
   int   link;
   UWORD endPos;
   struct FileInfoBlock *fib=AllocDosObject(DOS_FIB,NULL);
   if (fib) {
      /* lock the directory */
      if (l=Lock(dirname,ACCESS_READ)) {
         if (NameFromLock(l,fileName,FILENAMESIZE-1)) {
            /* first determine base path length */
            if (!pathstartdone) {
               endPos=strlen(fileName);
               AddPart(fileName,"x",FILENAMESIZE-1);
               pathstart=(UWORD)((ULONG)FilePart(fileName)-(ULONG)fileName);
               fileName[endPos]=0;
               pathstartdone=TRUE;
            }
            /* examine file */
            if (Examine(l,fib)) {
               STRPTR fname = fib->fib_FileName;
               if (fib->fib_DirEntryType == ST_USERDIR) {
                  if (ARG_BAR) {
                     register int i;
                     dirLevel++;
                     for(i=0; i<dirLevel; i++,error("#")); error("\r");
                  }
                  while (ExNext(l,fib) && ok) {
                     entryCnt++;
                     endPos=strlen(fileName);
                     AddPart(fileName,fname,FILENAMESIZE-1);
                     convertProtection(fib->fib_Protection);
                     /* test for dir */
                     switch (fib->fib_DirEntryType) {
                        case ST_USERDIR:
                           /* determine some important data like timestamp,
                              comment and protection bits */
                           dt.dat_Stamp.ds_Days    = fib->fib_Date.ds_Days;
                           dt.dat_Stamp.ds_Minute  = fib->fib_Date.ds_Minute;
                           dt.dat_Stamp.ds_Tick    = fib->fib_Date.ds_Tick;
                           dt.dat_StrDay           = NULL;
                           dt.dat_StrDate          = dateString;
                           dt.dat_StrTime          = timeString;
                           DateToStr(&dt);
                           /*fprintf(fh,"; %s\n",fileName);*/
                           fprintf(fh,"SETDATE  \x22%s\x22 %s %s\n",&fileName[pathstart],dateString,timeString);
                           if (strlen(fib->fib_Comment)>0) {
                              fprintf(fh,"FILENOTE \x22%s\x22 \x22%s\x22\n",&fileName[pathstart],fib->fib_Comment);
                           }
                           fprintf(fh,"PROTECT  \x22%s\x22 %s\n",&fileName[pathstart],protect);
                           sumDirs++;
                           /* okay, we can examine the directory */
                           subCnt=examineDir(fileName);
                           if (subCnt == 0) {
                              /* is directory is empty, still build it */
                              sumEmptyDirs++;
                              printf("%s::%s%s\n",fileName,(ARG_PRE)?(ARG_PRE):((STRPTR)""),&fileName[pathstart]);
                           }
                           else if (subCnt == -1) {
                              ok=FALSE;
                           }
                           /*printf("<==\n");*/
                           break;
                        case ST_FILE:
                           printf("%s::%s%s\n",fileName,(ARG_PRE)?(ARG_PRE):((STRPTR)""),&fileName[pathstart]);
                           if (strlen(fib->fib_Comment)>0) {
                              fprintf(fh,"FILENOTE \x22%s\x22 \x22%s\x22\n",&fileName[pathstart],fib->fib_Comment);
                           }
                           if (fib->fib_Protection & 0xF0) { /* 0x0F (RWED) is still set */
                              fprintf(fh,"PROTECT  \x22%s\x22 %s\n",&fileName[pathstart],protect);
                           }
                           if (fib->fib_Protection & FIBF_READ) { /* R is _NOT_ set */
                              error("Read protected: %s!\n",fileName);
                              sumReadProt++;
                           }
                           sumFiles++;
                           sumBytes+=fib->fib_Size;
                           break;
                        case ST_SOFTLINK:
                        case ST_LINKDIR:
                        case ST_LINKFILE:
                           link=resolveLink(fileName);
                           if (link!=LINK_ERROR) {
                              switch(link) {
                                 case LINK_GOOD:
                                    sumGoodLinks++;
                                    break;
                                 case LINK_BAD:
                                    fprintf(fh,";");
                                    sumBadLinks++;
                                    break;
                              }
                              fprintf(fh,"MAKELINK \x22%s\x22 \x22%s\x22 %s\n",
                                      &fileName[pathstart],linkName,(fib->fib_DirEntryType==ST_SOFTLINK)?("SOFT"):("HARD"));
                              break;
                           }
                           /* no break here! (if link==LINK_ERROR, print error message) */
                        default:
                           error("UNKNOWN ENTRY: %.70s\n",fileName);
                           sumUnknown++;
                     }
                     fileName[endPos]=0;
                  }
                  if (ARG_BAR) {
                     register int i;
                     dirLevel--;
                     for(i=0; i<dirLevel; i++,error("#")); error(" \r");
                  }
               }
            }
            else {
               error("%s: Couldn't examine file information!\n",fileName); ok=FALSE;
            }
         }
         else {
            error("Couldn't examine full filename!\n"); ok=FALSE;
         }
         UnLock(l);
      }
      else {
         error("%s: Couldn't access to this entry (maybe not found?)!\n",dirname); ok=FALSE;
      }
      FreeDosObject(DOS_FIB,fib);
   }
   else {
      error("%s: Couldn't allocate FileInfoBlock memory!\n",dirname); ok=FALSE;
   }
   if (!ok) return -1;
   return entryCnt;
}



/* ---------------------------------------------------------------- */
int resolveLink(STRPTR lname) {
   int   ok=LINK_ERROR;
   BPTR  l;
   struct DevProc *devproc;
   if (l=Lock(lname,ACCESS_READ)) {
      /* if filesystem can lock the entry, its a valid link */
      if (NameFromLock(l,linkName,FILENAMESIZE-1)) {
         ok=LINK_GOOD;
      }
      UnLock(l);
   }
   else {
      /* if filesystem _cannot_ lock the entry, so its an invalid link
         and we have to ask the filesystem for the path */
      if (devproc=GetDeviceProc(lname,NULL)) {
         if (ReadLink(devproc->dvp_Port,NULL,lname,linkName,FILENAMESIZE-1)) {
            ok=LINK_BAD;
         }
         FreeDeviceProc(devproc);
      }
   }
   return ok;
}



/* ---------------------------------------------------------------- */
void convertProtection(LONG protection) {
   int pos=0;
   if   (protection & FIBF_SCRIPT )   protect[pos++]='s';
   if   (protection & FIBF_PURE   )   protect[pos++]='p';
   if   (protection & FIBF_ARCHIVE)   protect[pos++]='a';
   if (!(protection & FIBF_READ   ))  protect[pos++]='r';
   if (!(protection & FIBF_WRITE  ))  protect[pos++]='w';
   if (!(protection & FIBF_EXECUTE))  protect[pos++]='e';
   if (!(protection & FIBF_DELETE ))  protect[pos++]='d';
   protect[pos]=0;
}
