/***************************************************************************
 * Program: Whereis (searches for a filename on your [hard]-disk)          *
 * Version: 1.18 for AMIGA-Computers. Last-Update: Thursday 15-02-1990     *
 * Whereis.c by R. Bless 1989/90. All Rights reserved! Program (exe) is    *
 * free distributable. Compilable under Aztec 3.6a.                        *
 * ------------------------------ NOTE :---------------------------------- *
 * No redistribution of a changed source and/or executable without         *
 * permission of the author!!! Please ask me first. See address below.     *
 * ----------------------------------------------------------------------- *
 * I'm fed up with all 'whereis' or 'wo' utilities that I had, because the *
 * one is always case sensitive and can't be aborted, the other is         *
 * horribly slow and can't handle wildcards >:-(! So here is my product    *
 * which uses no recursive procedures.                                     *
 * Written from 17-10-89 to 24-10-89 (first version then addups)           *
 * (C)opyright by Roland Bless (Byteable Software Products)                *
 ***************************************************************************/
/*
Any comments or bug-reports to:
+----------------------------------------------------------------------------+
|R o l a n d   B l e s s |UUCP: rob@spirit.UUCP (FRG only!)                  |
|                        |  or  rob%spirit@impch.imp.com (worldwide)         |
|Moersenbroicher Weg 151 |FIDO: 2:242/20 Roland Bless | BTX:0211623818-0001  |
|FRG- 4000 Duesseldorf 30|---------------------------------------------------|
|voice +49 211 623817    |  // AMIGA is multi,| "They build machines that    |
|                        |\X/ AMIGA is more!  |  they can't control..." STING|
|----------------------------------------------------------------------------|
|BANG:...uunet!impch!altger!doitcr!tmpmbx!mfehr!krefcom!spirit!rob           |
+---------s-p-i-r-i-t-s---i-n---t-h-e---m-a-t-e-r-i-a-l---w-o-r-l-d----------+

   Please don't think that I'm not familiar with gotos or breaks and
   such things of not structured languages, but I won't go back to
   BASIC-Level with a jump here or there and you don't know where you are...
   In a mood I just decided to leave these things out... Maybe I should
   write in Pascal or Modula? :-)                                            */
/*
   future adds
       -s|u|D date search
       -l list format  */

#include <stdio.h>
#include <ctype.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>

#include "date.h"                  /* my own date function */

#define MAXPATH 256                /* Maximum pathname length  */
#define MEMTYPE 0L                 /* Memtype: Fast or Chipmem */
#define true 1                     /* Right boolean values, because */
#define false 0                    /* typedef short BOOL, but TRUE= 1L! */
#define ON true
#define OFF false

struct ListType
{
  struct FileInfoBlock FIBlock;
  struct FileLock      *FLock;
  struct ListType  *prev;
};

            /**** external functions & variables ****/

extern int Enable_Abort;

void *Lock();
void *AllocMem();
void *ParentDir();
extern void date();
struct Process *FindTask();
struct MsgPort *CreatePort();
long IsInteractive(), SetSignal();
struct FileHandle *Input(), *Output();
struct FileLock *CurrentDir();

                     /***** globals *****/

char MPath [256], c, casedep;            /* Global PathName (MainPath) */
WORD  options= 0; /* options switches */
WORD MPathPos= 0;
struct StandardPacket *packet;
struct MsgPort *replyport, *conid;
struct Process *myprocess;

char *prgname;
enum { CASESENSITIVE=1, DIRSONLY=2, FILESONLY=4, LONGPATH=8 , SIZE=16,
       TIME=32, NOSUBDIRS=64, JUMPDIR=128 };
      /* switches go here */

enum { NOMEM, NODIR, DESTROYEDENTRY, TOOMANYARGS, WRONGNAME, ILLEGALOPTION,
       NOPATTERN, NOINPUT, STRANGE, INFO, USAGE };
     /*** ErrorNumbers for ErrorHandle ***/

char *Msg[]=
{ "FATAL ERROR: Not enough memory available!\n",
  "ERROR: Can't find that directory or device:",
  "ERROR: Destroyed entry! Couldn't examine this '",
  "ERROR: Can't handle so many arguments. See usage!\n",
  "ERROR: Please no path in the filename/pattern! See usage!\n",
  "ERROR: Illegal Option '",
  "ERROR: I've no filepattern to search for!\n",
  "ERROR: Input must be interactive! \n",
  "STRANGE ERROR: Something strange happend...!\n",
  "INFO: whereis V1.18 15-02-1990 by R. Bless.\n"
};  /* Messages for ErrorHandle/Numbers */

                     /******************/

void ErrorHandle(ErrMsgNr)
short ErrMsgNr;

{
  if (ErrMsgNr != USAGE)
     fprintf(stderr, "%s- %s", prgname, Msg[ErrMsgNr]);
  else
  {
     fprintf(stderr, "usage: %s [-acdfijnpst] [dev:path] filename.", prgname);
     fprintf(stderr, " Wildcards *,? allowed.\n");
  }
}


int SwitchRaw(RawMode)
BOOL RawMode;

{
  replyport= (struct MsgPort *) CreatePort(NULL, NULL);
  if (!replyport)
     return 1;

  packet= (struct StandardPacket *)
          AllocMem((long)sizeof(*packet),MEMF_PUBLIC | MEMF_CLEAR);
  if (!packet)
  {
    DeletePort(replyport);
    return 1;
  }

  /* init DOS-Packet */
  packet->sp_Msg.mn_Node.ln_Name= (char *) &(packet->sp_Pkt); /* link packet- */
  packet->sp_Pkt.dp_Link= &(packet->sp_Msg);                 /* to message    */
  packet->sp_Pkt.dp_Port= replyport;                      /* set-up reply port*/
  packet->sp_Pkt.dp_Type= ACTION_SCREEN_MODE;        /* function */

  if (RawMode==ON)
  {
    packet->sp_Pkt.dp_Arg1= DOSTRUE;
    fflush(stdout);
  }
  else
  {
    packet->sp_Pkt.dp_Arg1= DOSFALSE;
  }
  PutMsg(conid,packet);  /* send packet */
  WaitPort(replyport); /* wait for packet to come back */
  GetMsg(replyport);   /* pull message */
  FreeMem(packet, (long) sizeof(*packet));
  DeletePort(replyport);
  return 0;
}


void printdate(ds)
struct DateStamp *ds;
{
  struct date mydate;

  date(&mydate, ds->ds_Days);
  fprintf(stdout," %02d-%02d-%02ld %02ld:%02ld:%02ld",
          mydate.day, mydate.month, mydate.year-1900,
          ds->ds_Minute/60L, ds->ds_Minute%60L, ds->ds_Tick/50L);
}


void Append(s, root)   /* Appends the dirname to MPath (MainPath) */
char *s;
BOOL root;

{
  int p;

  for (p= 0; s[p]!= '\0'; p++)
      MPath[MPathPos+p]= s[p];
  MPath[MPathPos+p++]= root==true ? ':' : '/';
  MPath[MPathPos+p]= '\0';
  MPathPos+=p;
}


void InsertPath(s, root)   /* Inserts a dirname into MPath */
char *s;
struct FileLock *root;

{
  int p,len;

  len= strlen(s)+1;
  for (p= MPathPos; p >= 0; p--)
      MPath[p+len]= MPath[p];
  MPathPos+=len;
  for (p= 0; s[p] != '\0'; p++)
      MPath[p]= s[p];

  MPath[p]= root==NULL ? ':' : '/';
}


void Cut()           /* Removes the last dirname from MPath */
{
  if (MPathPos) --MPathPos;
  while (--MPathPos>=0 && MPath[MPathPos] != '/' && MPath[MPathPos] != ':');
  MPath[++MPathPos]= '\0';
}


struct FileLock *FindRoot(FL)   /* Climbs up to root-directory */
struct FileLock *FL;

{
  struct FileLock *PL;

  PL= FL;
  while (FL= ParentDir(FL))
  {
    UnLock(PL);
    PL=FL;
  }
  return PL;
}


long GetFullPath(path)    /* Get the full pathname */
char *path;

{
  struct FileLock *FL, *PL;
  struct FileInfoBlock *FIB;
  BOOL Error= false;

  FIB= (struct FileInfoBlock *)
       AllocMem ((long) sizeof(struct FileInfoBlock), MEMTYPE);

  if (!FIB)
  {
    ErrorHandle(NOMEM);
    Error= true;
  }
  else
  if (!(FL= (struct FileLock *) Lock(path, (ULONG) ACCESS_READ)))
  {
    ErrorHandle(NODIR);
    fprintf(stderr," %s\n", path);
    Error= true;
  }

  while (FL && !Error)
  {
    if (!Examine(FL, FIB))
    {
      ErrorHandle(DESTROYEDENTRY);
      fprintf(stderr,"%s'. Very strange!\n", path);
      Error= true;
    }
    else
    {
      InsertPath(FIB->fib_FileName, PL= ParentDir(FL));
      UnLock (FL);
      FL= PL;
    }
  }
  if (FIB) FreeMem (FIB,(long) sizeof(struct FileInfoBlock));
  if (FL) UnLock (FL);
  return Error;
}


BOOL Match(s, fpat)
char *s,*fpat;

{
  register short p,sp;

  for (sp= p= 0; fpat[p]!='\0';)
  {
    if (fpat[p] == '*')
    {
      while (fpat[p]=='*' || fpat[p]=='?')
      {
        p++;         /* skip following wildcards, until letter or \0 */
      }
      if (fpat[p]!='\0')        /* letter/pattern detected! */
         while (s[sp]!='\0' && (s[sp]&casedep) != (fpat[p]&casedep))
               sp++;
      else
         while (s[sp]!='\0')    /* ingore all chars till end */
               sp++;
    }
    else
    if (fpat[p] == '?')
    {
      p++;                    /* Just ignore that char */
      if (s[sp]!='\0') sp++;
    }
    else
    if ((s[sp]&casedep) == (fpat[p]&casedep))
    {
      sp++;p++;
    }
    else
        return false;
  }
  return (s[sp]=='\0' ? true : false);
}

/* Now comes the main-stuff!!! */

BOOL Search (path, fpat)       /* not recursive! */
char *path, *fpat;

{
  struct FileLock      *FL= NULL;
  struct FileInfoBlock *FIB= NULL;
  struct ListType  *LChainP= NULL, *OldLChainP= NULL;

  BOOL Error= false;
  char cbuf;
  int p;

  casedep= (options & CASESENSITIVE) ? 0xFF : 0x5F; /* Mask for match() */

  if (!fpat)
  {
    ErrorHandle(WRONGNAME);
    Error= true;
  }
  else
  if (path)
  {
     if (options & LONGPATH)
        Error= GetFullPath(path); /* Full-Pathname */
     else
     {                            /* "short pathname" */
       int p;

       for (p= 0; path[p]!= '\0'; p++)
            MPath[MPathPos+p]= path[p];

       if (p>0)
       {
         p--;
         if (MPath[MPathPos+p]!='/' && MPath[MPathPos+p]!=':')
         {
           p++; MPath[MPathPos+p]= '/';
         }
         p++;
       }
       else
         MPathPos= 0;

       MPath[MPathPos+p]= '\0';
       MPathPos+= p;
     }
  }

  if (!Error)
  {
    LChainP= (struct ListType *)
             AllocMem ((long) sizeof(struct ListType), MEMTYPE);

    if (!LChainP)
    {
      ErrorHandle(NOMEM);
      Error= true;
    }
    else
    {
      FIB= &LChainP->FIBlock;

      if (!(FL= (struct FileLock *) Lock(MPath, (ULONG) ACCESS_READ)))
      {
        ErrorHandle(NODIR);
        fprintf(stderr," %s\n", MPath);
        Error= true;
      }
      else
      {
        if (!path)
           FL= FindRoot(FL);

        if (!Examine(FL, FIB))
        {
          ErrorHandle(DESTROYEDENTRY);
          fprintf(stderr,"%s'. Very strange!\n", path);
          Error= true;
        }
        else
          if (!path) Append(FIB->fib_FileName, true);

        if (!Error)
         while (FL)
         {
           while (FL && ExNext(FL, FIB) && !Error)
           {
             if (Match(FIB->fib_FileName, fpat))  /*Compare filename&pattern*/
             {
               BOOL found= false;

               if (FIB->fib_DirEntryType >= 0L)      /** => it is a dirname **/
               {
                 if (!(options & FILESONLY))
                 {
                   fprintf(stdout, "%s%s", MPath, FIB->fib_FileName);

                   if (options & SIZE)
                      fprintf(stdout, " (dir) ");

                   if (options & TIME)
                      printdate(&(FIB->fib_Date));
                   fprintf(stdout, "\n");
                   if (options & JUMPDIR)
                      found= true;
                 }
               }
               else                                 /** => it is a filename **/
               if (!(options & DIRSONLY))
               {
                 fprintf(stdout, "%s%s", MPath, FIB->fib_FileName);

                 if (options & SIZE)
                     fprintf(stdout, " %ld",FIB->fib_Size);
                 if (options & TIME)
                    printdate(&(FIB->fib_Date));
                 fprintf(stdout, "\n");
                 if (options & JUMPDIR)
                    found= true;
               }
               if (found)
               {
                 if (SwitchRaw(ON))
                    Error= true;
                 else
                 {
                   int c;

                   fprintf(stderr,"Change to that dir? [yNq]:");
                   c= getchar();
                   fprintf(stderr,"%c\n", c);
                   SwitchRaw(OFF);

                   if (toupper(c)=='Y')
                   {
                     struct FileLock *CDL;
                     struct CommandLineInterface *CLI;
                     char *i;
                     int p;

                     if (FIB->fib_DirEntryType >= 0L)
                        Append(FIB->fib_FileName, false);
                     /* if Directory found append it to mpath, cause we'll: */

                     /* change the current dir */

                     CDL=(struct FileLock *) Lock(MPath, (ULONG) ACCESS_READ);
                     if (!CDL)
                     {
                       ErrorHandle(STRANGE);
                       Error= true;
                     }
                     CDL= CurrentDir(CDL);
                     UnLock(CDL);
                     /* The new-lock must be retained and can't be unlocked,
                        the old one must be unlocked */

                     CLI=(struct CommandLineInterface *)
                          BADDR(myprocess->pr_CLI);
                     i= (char *) ((CLI->cli_SetName) << 2);

                     /* Copy the new name to CLI */
                     for (p=0; MPath[p]!='\0'; p++)
                         *(i+p+1)= MPath[p];
                     if (MPath[p-1]=='/')
                     {
                       *i= (char) (p-1);
                     }
                     else
                       *i= (char) p;

                     if (FIB->fib_DirEntryType >= 0L)
                        Cut();

                     Error= true;
                   }
                   if (toupper(c)=='Q')
                   {
                     Error= true;
                     fprintf(stdout,"Quit!\n");
                   }
                 }
               }
             }

             if (FIB->fib_DirEntryType >= 0L) /* Dirname? Climb down the tree */
             {
               if (!(options & NOSUBDIRS))
               {
                 Append(FIB->fib_FileName, false);
                 LChainP->FLock= FL;
                 LChainP->prev = OldLChainP;
                 OldLChainP= LChainP;
                 cbuf= MPath[MPathPos-1]; MPath[MPathPos-1]= '\0';
                 FL=(struct FileLock *) Lock(MPath, (ULONG) ACCESS_READ);
                 MPath[MPathPos-1]= cbuf;
                 if (!FL)
                 {
                   ErrorHandle(STRANGE);
                   Error= true;
                 }
                 else
                 {
                   LChainP= (struct ListType *) AllocMem
                         ((long) sizeof(struct ListType), MEMTYPE);

                   if (!LChainP)
                   {
                     ErrorHandle(NOMEM);
                     Error= true;
                     UnLock(FL);           /* Free last Lock */
                     LChainP= OldLChainP;  /* To free the whole list */
                     FL= OldLChainP->FLock; /* Get actual Lock */
                     OldLChainP= OldLChainP->prev;
                   }
                   else
                   {
                     FIB= &LChainP->FIBlock;
                     Examine(FL, FIB);
                   }
                 }
               }
             }
           }            /* Climb-up-the-tree section*/
           if (SetSignal(NULL, NULL) & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D))
           {
             SetSignal(NULL, (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D));
             if (!Error)
             {
                fprintf(stderr, "** BREAK **\n"); /* Just print one break */
                Error= true;
             }
           }
           if (LChainP)    /* Is there still an entry? */
           {
             UnLock(FL);   /* Free actual Lock */
             Cut();
             FreeMem (LChainP, (long) sizeof(struct ListType));
             if (OldLChainP)        /* Get next entry to free,climb up list */
             {
               FL = OldLChainP->FLock;
               FIB= &OldLChainP->FIBlock;
               LChainP= OldLChainP;
               OldLChainP= OldLChainP->prev;
             }
             else                  /* Last entry reached */
             {
               FL= NULL;
               LChainP= NULL;
             }
           }
           else
             FL= NULL;  /* No more entries? -> leave while loop */
         }
      }
    }
  }
  if (LChainP) FreeMem (LChainP,(long) sizeof(struct ListType));
  if (OldLChainP) FreeMem (OldLChainP,(long) sizeof(struct ListType));
  if (FL) UnLock (FL);

  return Error;
}

                 /******* main(), what else? *******/

main(argc, argv)
int argc;
char **argv;

{
  int i1, i2, findx= 0;
  char *args[2], *patharg=NULL, *filepattern=NULL;


  Enable_Abort= 0;                     /*** No Ctrl-C please!!! ***/
  args[0]= args[1]= NULL;
  prgname= *argv;

  SetSignal(NULL, (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D));   /*** Clear Ctrl-D signal ***/

  if (argc == 1)
  {
    ErrorHandle(USAGE);
    exit(RETURN_OK);
  }

  for (i1= 1; i1 != argc; i1++)
  {
      if (*argv[i1] == '-')
         for (i2= 1; argv[i1][i2] != '\0'; i2++)
         {
           switch (argv[i1][i2])
           {
             case 'a': options|= SIZE | TIME;
                       break;

             case 'c': options|= CASESENSITIVE;
                       break;

             case 'd': options|= DIRSONLY;
                       break;

             case 'f': options|= FILESONLY;
                       break;

             case 'i': ErrorHandle(USAGE);
                       ErrorHandle(INFO);
                       exit(RETURN_OK);
                       break;

             case 'j': if (IsInteractive(Input()))
                       {
                         myprocess= (struct Process *) FindTask(NULL);
                         conid = (struct MsgPort *) myprocess->pr_ConsoleTask;
                                 /* get con-handler */
                         options|= (JUMPDIR | LONGPATH);
                       }
                       else
                       {
                         ErrorHandle(NOINPUT);
                         exit(RETURN_FAIL);
                       }

                       break;

             case 'n': options|= NOSUBDIRS;
                       break;

             case 'p': options|= LONGPATH;
                       break;

             case 's': options|= SIZE;
                       break;

             case 't': options|= TIME;
                       break;


             default : ErrorHandle(ILLEGALOPTION);
                       fprintf(stderr,"%c' !\n", argv[i1][i2]);
                       ErrorHandle(USAGE);
                       exit (RETURN_ERROR);
                       break;
           }
         }
      else
      {
        if (findx > 1)
        {
          ErrorHandle(TOOMANYARGS);
          exit (RETURN_ERROR);
        }
        else
        {
          args[findx]= argv[i1]; findx++;
        }
      }
  }

  if (findx==0)
  {
    ErrorHandle(NOPATTERN);
    ErrorHandle(USAGE);
    exit(RETURN_ERROR);
  }
  for (i1= 0; i1 < 2; i1++)
  {
    if (args[i1] != NULL)
    {
      for (i2= 0; (c= args[i1][i2]) != '\0' && c != ':' && c != '/';i2++);

      if (c != '\0')
         patharg= args[i1];
      else
         filepattern= args[i1];
    }
  }
  if (findx == 2 && !patharg)
  {
     patharg= args[0];
     filepattern= args[1];
  }

  if (Search(patharg, filepattern) == true)
  {
    fprintf(stderr, "%s-- Aborted!\n", prgname);
    exit(RETURN_ERROR);
  }
}
