#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <dos.h>
#include <fcntl.h>
#include <stat.h>
#include <exec/types.h>
#include <libraries/dosextens.h>
#include <devices/tpt.h>

#include <dlg/dlg.h>
#include <dlg/user.h>
#include <dlg/msg.h>
#include <dlg/file.h>
#include <dlg/resman.h>

#include <link/file.h>
#include <link/io.h>
#include <link/lang.h>
#include <link/util.h>

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

#include <pragmas/dlg.h>

#include <private/Version.h>
#define  ObjRev "1"
const UBYTE version[]="\0$VER: FilePurge " BUILDVER "." ObjRev " ©" COPYRIGHT " by Digerati Dreams "__AMIGADATE__;

void  main         (int, char **);
void  Check_User   (char *);
void  RenumberDir  (char *, char *);
void  Check_Batch  (char *);
void  Check_Areas  (long *);
void  Check_Area   (struct Msg_Area *);
void  GetPointers  (long area, LONG *low, LONG *high);
long  MakeAreaArray(long *, char *);
int   cmp          (int *, int *);
void  Usage        (char *);
void _CXBRK        (void);


#define NumAreas 100000
long   *AreaArray;
long    numareas;

long    user;
long    numusers;
char   *userlist  = NULL;

long    days      = 0;
SHORT   downloads = 0;
SHORT   Level     = 255;
char    DelMode   = 1;

long    TotalSize = 0;
long    TotalLeft = 0;
SHORT   NumFiles  = 0;
SHORT   NumLeft   = 0;


BPTR             sout;
struct Library  *DLGBase = NULL;
char           **SA;


void main(int argc,char **argv)

{ULONG  size;
 char  *alist = NULL;

 sout = Output();
 if (!(DLGBase = OpenLibrary(DLGNAME, DLGVERSION)))  exit(5);
 if (!(SA = getlang(NULL)))                          exit(5);

 days = AmigaTime();

 while(--argc > 0)
      {char *s;

       s = *++argv;
       if (*s++ == '-')
          {while(*s)
                {switch(*s++)
                       {case 'h':
                        case 'H':  Usage(NULL);
                                   break;

                        case 'a':
                        case 'A':  if (!--argc)
                                      {alist = "LOCAL";
                                       break;
                                      }
                                   alist = *++argv;
                                   break;

                        case 'O':
                        case 'o':  if (!--argc)  break;
                                   days = atol(*++argv);
                                   days = AmigaTime() - (86400 * days);
                                   break;

                        case 'D':
                        case 'd':  if (!--argc)  break;
                                   downloads = atoi(*++argv);
                                   break;

                        case 'L':
                        case 'l':  if (!--argc)  break;
                                   Level = atoi(*++argv);
                                   break;

                        case 'n':
                        case 'N':  DelMode = 0;
                                   break;
                       }
                }
          }
        else
           Usage("Bad Syntax");
      }


 if (!alist)
    {/* Load users into an array */
     if (FileSize("DLGConfig:Misc/Users.bbs", &size)==-1)
        {AFPrintf(NULL, sout, SA[470]);  
        _CXBRK();
        }

     numusers = size/36L;

     if (!(userlist = malloc(size)))
        {AFPrintf(NULL, sout, SA[471]);
        _CXBRK();
        }
     GetFirstStruct("DLGConfig:Misc/Users.bbs", userlist, size);

     for(user = 0; user < numusers; user++)
        {signal(SIGINT, SIG_DFL);
         Chk_Abort();
         signal(SIGINT, SIG_IGN);

         Check_User (userlist + (user * 36L));
         Check_Batch(userlist + (user * 36L));
        }
    }
  else
    {
      if(! (AreaArray = calloc(NumAreas,sizeof(long))))
         _CXBRK();

      numareas = MakeAreaArray(AreaArray, alist);
      Check_Areas(AreaArray);
    }

 AFPrintf(NULL, sout, SA[473]);
 AFPrintf(NULL, sout, SA[474],  NumFiles);
 AFPrintf(NULL, sout, SA[475], TotalSize);
 AFPrintf(NULL, sout, SA[476],   NumLeft);
 AFPrintf(NULL, sout, SA[477], TotalLeft);

_CXBRK();
}


void Check_User(char *name)

{struct USER_DATA     UserDat;
 struct File_Header   Header;

 char  filename[80];
 SHORT killedflag = 0;
 ULONG size;
 long  uploaded;
 long  low,high;

 DeScore(name);
 Capitalize(name);
 AFPrintf(NULL, sout, SA[478],name);
 UnderScore(name);

 GetHiLowFPointers((USHORT)PVTAREA,name,&low,&high,"FilePurge");
 if (high < 1)
    {AFPrintf(NULL, sout, SA[479]);
     return;
    }

 ASPrintf(NULL, filename,"USER:%s/user.data", name);
 if (GetFirstStruct(filename,(char *)&UserDat,sizeof(UserDat))==-1)
    {AFPrintf(NULL, sout, SA[480]);
     return;
    }

 if (UserDat.User_Level > Level)
    {AFPrintf(NULL, sout, SA[481],Level);
     return;
    }
 AFPrintf(NULL, sout, SA[482]);

 for(; low <= high; low++)
    {AFPrintf(NULL, sout, SA[483],low);

     ASPrintf(NULL, filename,"USER:%s/%ld.fd",name,low);
     if (GetFirstStruct(filename,(char *)&Header,sizeof(Header))==-1)
        {killedflag++;
         AFPrintf(NULL, sout, SA[484]);
         continue;
        }
     AFPrintf(NULL, sout, SA[485],Header.Filename);

     ASPrintf(NULL, filename,"USER:%s/%s",name,Header.Filename);
     UnderScore(filename);

     size = 0;
     FileSize(filename, &size);

     StringToDate(Header.Date, &uploaded);
     if (uploaded > days)
        {TotalLeft += size;
         NumLeft++;
         AFPrintf(NULL, sout, SA[486]);
         continue;
        }

     if (!Header.Times_Downloaded)
        {TotalLeft += size;
         NumLeft++;
         AFPrintf(NULL, sout, SA[487]);
         continue;
        }

     TotalSize += size;
     NumFiles++;

     if (KillFile(PVTAREA, name, low, "FilePurge", 0, NULL, DelMode, &UserDat, "XXX"))
        {killedflag++;
         AFPrintf(NULL, sout, SA[488]);
        }
    }

 if (killedflag)
    {ASPrintf(NULL, filename, "USER:%s", name);
     RenumberDir(filename,name);
    }
 
 return;
}


void RenumberDir(char *dir,char *name)

{long   Low,High,from,to;
 char   comment[80],fromname[20],toname[20];
 struct File_Header Header;

 CD(dir);
 GetHiLowFPointers((USHORT)PVTAREA,name,&Low,&High,"FilePurge");
 AFPrintf(NULL, sout, SA[489]);

 /* Scan up to first blank fd */
 to = 0;

 do {to++;
     ASPrintf(NULL, toname, "%ld.fd", to);

     if (to>=High && Exists(toname))
        {AFPrintf(NULL, sout, SA[490]);
         return;
        }
    }  while(Exists(toname));

 /* Renumber the remaining file descriptions and re-set comments */
 from = to;

 while(TRUE)
      {from++;

       if (from>High)
          {if (!PutHiLowFPointers((USHORT)PVTAREA,name,1L,to-1L,"FilePurge"))
                AFPrintf(NULL, sout, SA[491]);
              else
                AFPrintf(NULL, sout, SA[490]);
           break;
          }

       ASPrintf(NULL, fromname,"%ld.fd",from);
       if (Exists(fromname))
          {ASPrintf(NULL, toname,"%ld.fd",to);
           if (Exists(toname)) DeleteFile(toname);  

           if (!Rename(fromname,toname))
         {AFPrintf(NULL, sout, SA[492]);
               return;
              }

           GetFirstStruct(toname,(char *)&Header,sizeof(Header));
           ASPrintf(NULL, comment,"USER:%s/%ld.fd",name,to);
           UnderScore(comment);
           SetComment(Header.Filename,comment);
           to++;
          }
      }

 return;
}


void Check_Batch(char *name)

{__aligned struct FileInfoBlock fib;
 BPTR             lock;
 char             filename[80];

 ASPrintf(NULL, filename,"USER:%s/Batch.File",name);
 UnderScore(filename);

 lock = Lock(filename, ACCESS_READ);
 if (lock)
    {Examine(lock, &fib);
     UnLock(lock);

     if (!(fib.fib_Size))
         if (DeleteFile(filename))
            {AFPrintf(NULL, sout, SA[493], filename);
             NumFiles++;
            }
    }

 return;
}


void  Check_Areas(long *array)
{long            area;
 long            found;
 struct Msg_Area Area;
 int             fp;

 for(area=0; area < numareas; area++)
    {found = FALSE;

     if ((fp=open("File:Area.bbs", O_RDONLY))==EOF)
        {AFPrintf(NULL, sout, SA[494]);
        _CXBRK();
        }

     while(read(fp,&Area,sizeof(Area))==sizeof(Area))
          {signal(SIGINT, SIG_DFL);
           Chk_Abort();
           signal(SIGINT, SIG_IGN);

           if (Area.Number==AreaArray[area])
              {found = TRUE;
               Check_Area(&Area);
               break;
              }
          }

     close(fp);
     if (!found)  AFPrintf(NULL, sout, SA[495], AreaArray[area]);
    }

 return;
}


void Check_Area(struct Msg_Area *Area)

{__aligned struct FileInfoBlock fib;
 BPTR             lock;

 long               Low;
 long               High;
 long               uploaded;
 USHORT             area;

 struct USER_DATA   UserDat;
 struct File_Header Header;
 BOOL               killedflag = FALSE;

 char               renumber[80];
 char               path    [80];
 char               filename[128];

 area = Area->Number;
 AFPrintf(NULL, sout, SA[496], area);

 GetPath(path, area, Area, NULL);
 strcpy(filename, path);
 filename[strlen(filename)-1] = 0;
 lock = Lock(filename, ACCESS_READ);
 if (lock)
    {Examine(lock, &fib);
     UnLock(lock);

     if (fib.fib_Protection & FIBF_DELETE)
        {AFPrintf(NULL, sout, SA[499]);
         return;
        }
    }

 if (LockArea(area,"FilePurge", "Area Is Being Purged", 1, FILELOCK) !=RMNOERR)
    {AFPrintf(NULL, sout, SA[500],area);
     return;
    }

 GetPointers(area, &Low, &High);
 AFPrintf(NULL, sout, SA[497], Low, High);

 if (High < 1)
    {FreeArea(area, "FilePurge", FILELOCK|WRITELOCK);
     AFPrintf(NULL, sout, SA[498]);
     return;
    }
  else
     AFPrintf(NULL, sout, "\n");

 for(; Low <= High; Low++)
    {AFPrintf(NULL, sout, SA[483], Low);

     ASPrintf(NULL, filename, "File:%d/%d.fd", area, Low);
     if (GetFirstStruct(filename,(char *)&Header,sizeof(Header))==-1)
        {AFPrintf(NULL, sout, SA[484]);
         continue;
        }

     AFPrintf(NULL, sout, SA[485], Header.Filename);
     GetPath(path, area, NULL, Header.Filename);
     ASPrintf(NULL, filename, "%s%s", path, Header.Filename);

     lock = Lock(filename, ACCESS_READ);
     if (lock)
        {Examine(lock, &fib);
         UnLock(lock);

         if (fib.fib_Protection & FIBF_DELETE)
            {AFPrintf(NULL, sout, SA[501]);
             continue;
            }
        }
      else
        {AFPrintf(NULL, sout, SA[484]);
         continue;
        }

     StringToDate(Header.Date, &uploaded);
     if (uploaded > days)
        {TotalLeft += fib.fib_Size;
         NumLeft++;
         AFPrintf(NULL, sout, SA[486]);
         continue;
        }

     if (Header.Times_Downloaded >= downloads)
        {TotalLeft += fib.fib_Size;
         NumLeft++;
         AFPrintf(NULL, sout, SA[502]);
         continue;
        }

     Forbid();
     FreeArea(area, "FilePurge", FILELOCK);
     if (KillFile(area, "FilePurge", Low, "FilePurge", 0, Area, DelMode, &UserDat, "XXX"))
        {TotalSize += fib.fib_Size;
         NumFiles++;
         killedflag++;
         AFPrintf(NULL, sout, SA[488]);
        }
     if (LockArea(area,"FilePurge", "Area Is Being Purged", 1, FILELOCK) !=RMNOERR)
        {AFPrintf(NULL, sout, SA[500],area);
         return;
        }
     Permit();
    }

 FreeArea(area, "FilePurge", FILELOCK);
 if (killedflag)
    {ASPrintf(NULL, renumber, "DLG:Renumber -a %d -f", area);
     OverlayProgram(renumber);
    }

 return;
}


void GetPointers(long area, LONG *low, LONG *high)

{BPTR fh;
 char filename[80];

 ASPrintf(NULL, filename, "FILE:%d/Pointers.file", area);

 if (!(fh = Open(filename, MODE_OLDFILE)))
    {*high = 0;
     *low  = 1;
    }
  else
    {if (!FGets(fh, filename, 20))
        *low = 1;
       else
         StrToLong(filename, low);

     if (!FGets(fh, filename, 20))
        *high = 1;
       else
         StrToLong(filename, high);

     Close(fh);
    }
}


long MakeAreaArray(long *array, char *alist)

{long            area;
 struct Msg_Area Area;
 int             fp;
 char           *s;

 unsigned char LocalFlag = 0;

 s = alist;

 for(area=0; area < NumAreas; area++)
    {while(*s==32) s++;
     if (!*s) break;

     if (!Strnicmp(s,"LOCAL",5))
        {if (LocalFlag)
            {AFPrintf(NULL, sout, SA[503]);
             area--;
            }
          else
            {LocalFlag = 1;
             if ((fp = open("File:Area.bbs", O_RDONLY)) == EOF)
                {AFPrintf(NULL, sout, SA[494]);
                 break;
                }

             while(((read(fp, &Area, sizeof(Area))) == sizeof(Area) && (area < NumAreas)))
                  {array[area] = Area.Number;
                   area++;
                  }

             --area;
             close(fp);
            }
        }
      else
        array[area] = atoi(s);

     while((*s)&&(*s!=32)) s++;
    }

 if (area == NumAreas)
     AFPrintf(NULL, sout, SA[504], NumAreas);

 qsort(array, area, 2, cmp);
 return(area);
}


int cmp(int *i, int *j)

{return((*i)-(*j));
}


void Usage(char *str)

{if (str)  AFPrintf(NULL, sout, SA[505],str);

 AFPrintf(NULL, sout, SA[506]);

 AFPrintf(NULL, sout, SA[507]);
 AFPrintf(NULL, sout, SA[508]);
 AFPrintf(NULL, sout, SA[509]);
 AFPrintf(NULL, sout, SA[510]);
 AFPrintf(NULL, sout, SA[511]);

 exit(0);
}


void _CXBRK(void)

{if (userlist)  free(userlist);
 CloseLibrary(DLGBase);
 exit(0);
}
