/* Filelist generator for MAX's BBS version 1.54                    */
/* For SAS/C 6.55 - the BEST compiler. Too bad it's discontinued    */
/* Anyone may use this for anything they want if they want to       */
/* This was written after looking at the binary file FILE.DATA,     */
/* I haven't actually seen any documentation on MAX's BBS, so don't */
/* blame me if anything goes wrong :-)                              */
/* V1.0 .. oh. I just discovered that i DO have some docs for MAX!  */
/* V1.1 Small improvements                                          */
/* V1.2 ditto                                                       */
/* V1.3 Oh my god, who put all those bugs in my program ?           */
/* V1.4 Wrapping routine for desc. improved slightly                */

#include <proto/dos.h>          /* C= */
#include <proto/exec.h>
#include <utility/tagitem.h>
#include <exec/memory.h>
#include <stdio.h>              /* SAS printf */
#include <stdlib.h>             /*     qsort + atoi herfra */
#include <string.h>             /*     strengbehandling */
#include <ctype.h>              /*     isdigit */

#define SEKTIONER 100           /* Maksimum sektioner */

struct maxfileentry {
  ULONG dato;                   /* Dato for fil anskaffelse - dage siden 01/01/78 */
  ULONG pad1;
  ULONG siz;                    /* Filstørrelse */
  ULONG dlcount;                /* Antal gange downloadet */
  UBYTE flags;
  UBYTE area;                   /* Area filen hører til i */
  UBYTE name[41];               /* Filnavn */
  UBYTE desc[60];               /* Fil desc. */
  UBYTE uploader[41];           /* Navn på uploader */
  UBYTE unknown[42];            /* ?? evt. en sysop-kommentar, jeg er ikke sikker */
};

struct maxfilelist {
  UBYTE max[62];                /* MAX's text */
  ULONG filetotal;              /* Antal filer på BBS */
  ULONG filesarea[SEKTIONER];   /* Antal filer for hver sektion */
  UBYTE the_rest;               /* Faktisk: struct maxfileentry files[?????] */
};

char * __asm formatfile(register __a0 struct maxfileentry *argument, register __a1 char *datestr, register __d0 BOOL new);

BOOL hentfil(char *filnavnet, APTR *ptrptr, ULONG *sizptr)
{
struct FileInfoBlock *fib;
BPTR filhandle;

  if (filhandle=Open(filnavnet,MODE_OLDFILE))
  {
    if (fib=AllocDosObjectTags(DOS_FIB,TAG_END))
    {
      if (ExamineFH(filhandle,fib))
      {
        if (*ptrptr=AllocVec(fib->fib_Size,MEMF_ANY))
        {
          if (fib->fib_Size==Read(filhandle,*ptrptr,fib->fib_Size))
          {
            *sizptr=fib->fib_Size;
            FreeDosObject(DOS_FIB,fib);
            Close(filhandle);
            return(TRUE);
          } else printf("Coulden't read file %s\n",filnavnet);
          FreeVec(*ptrptr);
        } else printf("No memory\n");
      FreeDosObject(DOS_FIB,fib);
      } else printf("AmigaDos won't cooperate!\n");
    } else printf("REALLY no memory\n");
    Close(filhandle);
  } else printf("Coulden't open file %s\n",filnavnet);
  return(FALSE);
}

int comp(struct maxfileentry **fila, struct maxfileentry **filb)
{
  return(stricmp((*fila)->name,(*filb)->name));
}

const char KarlEinar[]="$VER: MakeMaxGuide version 1.6 by Claus Dyring Jensen\n\0";

int main(int argc, char *argv[])
{
UBYTE *conmem;
struct maxfilelist *srcmem;
APTR *infmem;
ULONG srcsiz, consiz, infsiz;
BPTR filhandle;
struct maxfileentry **sekarray[100];
struct maxfileentry **sekarray2[100];
struct maxfileentry **sekarrayptr;
struct maxfileentry *filptr;
int i,j;
char *s;
int files[100];
struct DateTime datotid;
struct DateStamp dato;
char tidstreng[LEN_DATSTRING];
BOOL inkluder[100];
char filnavn[100];
BOOL optionused;
ULONG maxage=0;
char *bbsname="Filelist generated by MakeMaxGuide";
BOOL info=FALSE;
BOOL simplelist=FALSE;
ULONG totalareabytes;

  /* INIT THE DATESTAMP */
  DateStamp(&dato);
  /* INIT EXCLUDE ARRAY */
  for(i=0;i<100;i++)
    inkluder[i]=FALSE;
  /* CHECK FOR SENSIBLE NUMBER OF ARGS */
  if (argc<4 || argc/2*2!=argc)
  {
    printf("Usage: %s outfilename file.data maxxbbs.config s sections [a AGELIMIT] [n NAME] [i INFOPAGE] [m MODE]\n\n",argv[0]);
    printf("You must supply full paths for the files.\nProper postfix will be added to outfilename.\n");
    printf("SECTIONS is a string of sections to be included. Fx 20212223\n");
    printf("AGELIMIT excludes files older than AGELIMIT days.\n");
    printf("NAME is a string containing the name of your bbs.\n");
    printf("INFOPAGE is the filename of an ascii text that may be included.\n");
    printf("MODE can be \"Amigaguide\" or \"Textfile\" and defaults to Amigaguide.\n");
    return(10);
  }
  /* CHECK FOR OPTIONS */
  for(j=4;j<argc;j+=2)
  {
    optionused=FALSE;
    /* OPT: INCLUDE AREAS */
    if (tolower(argv[j][0])=='s')
      if ((strlen(argv[j+1])&1)==0)
      {
        for(i=0;i<strlen(argv[j+1])/2;i++)
          if (isdigit(argv[j+1][i*2]) && isdigit(argv[j+1][i*2+1]))
            inkluder[(argv[j+1][i*2]-'0')*10+argv[j+1][i*2+1]-'0']=TRUE;
          else
          {
            printf("Inclusion string contains non-digits\n");
            return(10);
          }
          optionused=TRUE;
      }
      else
      {
        printf("Inclusion string should be even length\n");
        return(10);
      }
    /* OPT: HE WANTS AN INFOPAGE TOO */
    if (tolower(argv[j][0])=='i')
      if (hentfil(argv[j+1],(APTR)&infmem,&infsiz))
      {
        info=TRUE;
        optionused=TRUE;
      }
      else
      {
        printf("Couldn't open your infopage %s\n",argv[j+1]);
        return(10);
      }
    /* OPT: THERE IS A NAME FOR THE BBS */
    if (tolower(argv[j][0])=='n')
    {
      bbsname=argv[j+1];
      optionused=TRUE;
    }
    /* OPT: MODE CHANGE */
    if (tolower(argv[j][0])=='m' && (tolower(argv[j+1][0])=='t' || tolower(argv[j+1][0])=='a'))
    {
      if(tolower(argv[j+1][0])=='t')
        simplelist=TRUE;
      optionused=TRUE;
    }
    /* OPT: EXCLUDE FILES MORE THAN X DAYS OLD */
    if (tolower(argv[j][0])=='a')
    {
      maxage=atoi(argv[j+1]);
      optionused=TRUE;
    }
    if (!optionused)
    {
      printf("Option not recognized:%s %s\n?REDO FROM START\nREADY.\n",argv[j],argv[j+1]);
      return(10);
    }
  }
  /* ADD POSTFIX TO FILENAME */
  strcpy(filnavn,argv[1]);
  if(simplelist)
    strcat(filnavn,".txt");
  else
    strcat(filnavn,".guide");
  /* GET FILES NEEDED AND OPEN GUIDE FILE FOR WRITING */
  if (hentfil(argv[2],(APTR)&srcmem,&srcsiz))
  {
    if (hentfil(argv[3],(APTR)&conmem,&consiz))
    {
      if (filhandle=Open(filnavn,MODE_NEWFILE))
      {
        /* I THOUGHT THIS WAS A TOTAL OF ALL FILES, BUT IT SEEMS MAX CAN'T COUNT :) */
        /* SO I'LL JUST HAVE TO FIX THE VALUE MYSELF.. DAMN */
        srcmem->filetotal=(srcsiz-466)/202;
        if (sekarrayptr=(struct maxfileentry **)AllocVec(srcmem->filetotal*sizeof(struct maxfileentry **),MEMF_ANY))
        {
          /* INITIALIZE DATESTAMP */
          datotid.dat_Stamp.ds_Days=dato.ds_Days;
          datotid.dat_Stamp.ds_Minute=0;
          datotid.dat_Stamp.ds_Tick=0;
          datotid.dat_Format=FORMAT_DOS;
          datotid.dat_Flags=0;
          datotid.dat_StrDay=NULL;
          datotid.dat_StrDate=tidstreng;
          datotid.dat_StrTime=NULL;
          DateToStr(&datotid);
          /* COUNT FILES IN ALL AREAS (0-99) */
          for(i=0;i<100;i++)
            files[i]=0;
          filptr=(struct maxfileentry *)&srcmem->the_rest;
          for(i=0;i<srcmem->filetotal;i++)
          {
            if (!filptr->flags&~4)
              if (!maxage || dato.ds_Days-filptr->dato<maxage)
                files[filptr->area]++;
            filptr++;
          }
          /* OUTPUT INITIAL JUNK */
          if(!simplelist)
          {
            FPrintf(filhandle,"@DATABASE \"%s\"\n",bbsname);
            FPrintf(filhandle,"@NODE MAIN \"%s\"\n"\
            "                    Filelist generation date: %s\n"\
            "                            Table of Contents\n",bbsname,tidstreng);
          }
          /* OUTPUT INFOPAGE REFERENCE IF ANY */
          if(info && !simplelist)
          {
            FPrintf(filhandle,"                      @{\" BBS information page          \" LINK infosektion}\n\n");
          }
          /* OUTPUT TITLEPAGE SECTION REFRENCES */
          if(!simplelist)
          {
            for(i=0;i<100;i++)
              if(inkluder[i] && files[i])
                FPrintf(filhandle,"                      @{\" %-30.30s\" LINK section%ld}\n",conmem+2496+i*62,(LONG)i);
            FPrintf(filhandle,"@ENDNODE\n\n");
          }
          /* MAKE ROOM IN THE ARRAY FOR EACH AREA */
          sekarray[0]=sekarrayptr;
          sekarray2[0]=sekarrayptr;
          for(i=1;i<100;i++)
          {
            sekarray[i]=sekarray[i-1]+files[i-1];
            sekarray2[i]=sekarray[i];
          }
          /* PUT ALL INTERESTING FILES IN THE ARRAY */
          filptr=(struct maxfileentry *)&srcmem->the_rest;
          for(i=0;i<srcmem->filetotal;i++)
          {
            if (!filptr->flags&~4)
              if (!maxage || dato.ds_Days-filptr->dato<maxage)
                *(sekarray[filptr->area]++)=filptr;
            filptr++;
          }
          /* SORT THE ARRAY BY FILENAMES */
          for(i=0;i<100;i++)
            if(files[i]>0)
              qsort(sekarray2[i],files[i],sizeof(APTR),&comp);
          /* INITIALIZE DATESTAMP */
          datotid.dat_Stamp.ds_Minute=0;
          datotid.dat_Stamp.ds_Tick=0;
          datotid.dat_Format=FORMAT_DOS;
          datotid.dat_Flags=0;
          datotid.dat_StrDay=NULL;
          datotid.dat_StrDate=tidstreng;
          datotid.dat_StrTime=NULL;
          /* INCLUDE INFOPAGE HERE IF WANTED */
          if (info)
          {
            if(!simplelist)
              FPrintf(filhandle,"\n@NODE \"infosektion\" \"%s\"\n",bbsname);
            Flush(filhandle);
            Write(filhandle,infmem,infsiz);
            if(!simplelist)
              FPrintf(filhandle,"\n@ENDNODE\n");
          }
          /* FORMAT AND PRINT ALL THE FILES */
          for(i=0;i<100;i++)
          {
            totalareabytes=0;
            if(inkluder[i] && files[i])
            {
              /* PRINT FILES IN ONE SECTION */
              if(simplelist)
                FPrintf(filhandle,"\n---------------------------------------------------------------------------\n                        \"%s\""\
                "\n---------------------------------------------------------------------------\n\n",conmem+2496+i*62);
              else
                FPrintf(filhandle,"\n@NODE \"Section%ld\" \"%s\"\n",(LONG)i,conmem+2496+i*62);
              for(j=0;j<files[i];j++)
              {
                datotid.dat_Stamp.ds_Days=(*(sekarray2[i]+j))->dato;
                DateToStr(&datotid);
                s=formatfile(*(sekarray2[i]+j),tidstreng,dato.ds_Days-datotid.dat_Stamp.ds_Days<30);
                FPuts(filhandle,s);
                totalareabytes+=(*(sekarray2[i]+j))->siz;
              }
              FPrintf(filhandle,"\nTotally %ld bytes in %ld files\n",totalareabytes,files[i]);
              if(!simplelist) FPrintf(filhandle,"@ENDNODE\n");
            }
          }
          /* ALL OVER.. FREE WHAT WAS USED */
          FreeVec(sekarrayptr);
        }
        Close(filhandle);
      FreeVec(conmem);
      } else printf("Couldn't open output file. Now beam me up, Scotty.\n");
    }
  FreeVec(srcmem);
  }
  if(info)
    FreeVec(infmem);
  return(0);
}
