;/* execute me to compile
dcc ShowRev.c -proto -mi -r -mRR
;gcc showrev.c -Ia31: -Wall -O2 -o showrev
quit
*/

/*
 * ShowRev
 *
 * copyright 1994 by Christof Damian
 *
 * $Id: showrev.c,v 1.15 1994/03/15 19:43:17 damian Exp damian $
 *
 * $Source: Würg:home/damian/cc/RCS/showrev.c,v $
 *
 * $Revision: 1.15 $
 *
 * $Author: damian $
 *
 * $Log: showrev.c,v $
 * Revision 1.15  1994/03/15  19:43:17  damian
 * changed LIB/DEV handling
 * fixed "file not found"-msg bug
 * cleanup
 *
 * Revision 1.14  1994/03/06  19:43:22  damian
 * Added LIB/DEV keyword
 * Changed Version-String-Search
 *
 * Revision 1.13  1994/03/03  18:28:49  damian
 * added UID,GID & cleanup of protection bits & filenote
 *
 * [old logs deleted]
 */

/*** C= includes ***/

#include <exec/devices.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/exall.h>
#include <dos/stdio.h>
#include <utility/tagitem.h>

/*** std includes ***/

#include <strings.h>
#include <ctype.h>

/*** library bases & protopragmas ***/

extern struct DosLibrary *DOSBase;
#define DOSBase_DECLARED

#include <proto/exec_protos.h>
#include <proto/dos_protos.h>

/*** global #defines ***/

#define Prototype GLOBAL
#define Local     STATIC
#define CONST

struct PBLE
   {
   BPTR Next;
   ULONG DirLock;
   };

/*** prototypes of this file ***/

Local VOID Failing(STRPTR text);
Local VOID Warning(STRPTR text);
Local VOID CheckLibrary(STRPTR name);
Local VOID CheckDevice(STRPTR name);
Local BOOL CheckResidentList(STRPTR name,BOOL system);
Local BOOL CheckVersion(STRPTR dir,STRPTR file);
Local BOOL CheckVersionMulti(STRPTR dir,STRPTR file);
Local BOOL DoThePathThang(struct CommandLineInterface *CLI,STRPTR filename);
Prototype int main(int argc_notused,char **argv_notused);

#ifndef __COMMODORE_DATE__
#  ifdef __AMIGADATE__
#     define __COMMODORE_DATE__ __AMIGADATE__
#  else
#     define __COMMODORE_DATE__ __DATE__
#  endif
#endif

#define BASENAME     "ShowRev"
#define MAXPATHLEN   512
#define MAXLINELEN   512

/*** for C= Version or ShowRev ***/

CONST STATIC char VER[] = "\0$VER: " BASENAME " 1.15 (" __COMMODORE_DATE__ ")";
CONST STATIC char Id[] = "$Id: showrev.c,v 1.15 1994/03/15 19:43:17 damian Exp damian $";

CONST STATIC char ArgString[] = "NAME/A,ASSIGN/K,LIB=LIBRARY/S,DEV=DEVICE/S,ALL/S,VERBOSE/S";
STATIC struct
   {
   STRPTR Filename;
   STRPTR Assign;
   ULONG Library;
   ULONG Device;
   ULONG CheckAll;
   ULONG Verbose;
   } ArgStruct = { NULL, NULL, FALSE, FALSE, FALSE, FALSE };

int ReturnCode = RETURN_OK;   /* main() return code */

/*
 * VOID Failing(STRPTR text)
 *
 * prints *text to Output() and sets ReturnCode can do with OS1.1
 *
 */

VOID Failing(STRPTR text)
{
CONST STATIC char header[] = BASENAME ": ";
CONST STATIC char footer[] = ".\n";

   Write(Output(),header,sizeof(header)-1);
   Write(Output(),text,strlen(text));
   Write(Output(),footer,sizeof(footer)-1);

   if (ReturnCode < RETURN_FAIL)
      ReturnCode = RETURN_FAIL;
}

/*
 * VOID Warning(STRPTR text)
 *
 * prints *text to Output() and sets ReturnCode can do with OS1.1
 *
 */

VOID Warning(STRPTR text)
{
   Failing(text);
   if (ReturnCode < RETURN_WARN)
      ReturnCode = RETURN_WARN;
}


/*
 *
 */

VOID CheckLibrary(STRPTR name)
{
struct Library *library;

   if (ArgStruct.Verbose)
      Printf("checking OpenLibrary(\"%s\",0).\n",name);

   if ((library = OpenLibrary(name,0)))
      {
      Printf("OpenLibrary(\"%s\",0) results in Version %ld.%ld.\n",
         name,library->lib_Version,library->lib_Revision);

      CloseLibrary(library);
      }
   else
      Printf("OpenLibrary(\"%s\",0) failed.\n",name);
}

/*
 *
 */

VOID CheckDevice(STRPTR name)
{
struct IORequest iorequest;

   if (ArgStruct.Verbose)
      Printf("checking OpenDevice(\"%s\",0,iorequest,0).\n",name);

   if (OpenDevice(name,0,&iorequest,0)==0)
      {
      Printf("OpenDevice(\"%s\",0,iorequest,0) results in Version %ld.%ld.\n",
         iorequest.io_Device->dd_Library.lib_Version,
         iorequest.io_Device->dd_Library.lib_Revision);

      CloseDevice(&iorequest);
      }
   else
      Printf("OpenDevice(\"%s\",0,iorequest,0) failed.\n",name);
}

/*
 * BOOL CheckResidentList(STRPTR name,BOOL system)
 *
 * searches the system/normal residentlist for *name
 *
 * returns TRUE if found
 */

BOOL CheckResidentList(STRPTR name,BOOL system)
{
struct Segment *segment;
LONG uc = 0;
BOOL found = FALSE;

   if (ArgStruct.Verbose)
      Printf("searching %ls in %ls resident list\n",name, system ? "system" : "normal");

   Forbid();
   if ((segment = FindSegment(name,NULL,FALSE)))
      {
      found = TRUE;
      uc = segment->seg_UC;
      };
   Permit();

   if (found)
      {
      Printf("%ls found in %ls resident list (",name, system ? "system" : "normal");
      switch (uc)
         {
         case CMD_SYSTEM:     PutStr("system"); break;
         case CMD_INTERNAL:   PutStr("internal"); break;
         case CMD_DISABLED:   PutStr("disabled"); break;
         default:             Printf("usercount = %ld",uc); break;
         };
      PutStr(")\n");
      };

   return found;
}

/*
 * BOOL CheckVersion(STRPTR dir,STRPTR file)
 *
 * tries to open the file *file in dir *dir, and searches for one or more
 * Commodore Version, RCS ident or DUUCP-uident strings and prints them
 *
 * returns TRUE if file is found
 */

BOOL CheckVersion(STRPTR dir,STRPTR file)
{
char buffer[MAXPATHLEN];
BPTR fh;

   strcpy(buffer,dir);
   AddPart(buffer,file,MAXPATHLEN);

   if (ArgStruct.Verbose)
      Printf("searching %ls in %ls\n",file,buffer);

   if ((fh = Open(buffer,MODE_OLDFILE)))
      {
      {
      char realname[MAXPATHLEN];

      if (!NameFromFH(fh,realname,sizeof(realname)))
         strcpy(realname,buffer);

      Printf("found %ls:\n   ", realname);
      }

      {
      struct FileInfoBlock *fib = AllocVec(sizeof(struct FileInfoBlock),MEMF_ANY);
      if (fib)
         {
         if (ExamineFH(fh,fib))
            {
            char strday[LEN_DATSTRING];
            char strdate[LEN_DATSTRING];
            char strtime[LEN_DATSTRING];
            struct DateTime datetime;

            datetime.dat_Format  = FORMAT_DOS;
            datetime.dat_Flags   = 0;
            datetime.dat_StrDay  = strday;
            datetime.dat_StrDate = strdate;
            datetime.dat_StrTime = strtime;
            CopyMem(&fib->fib_Date,&datetime.dat_Stamp,sizeof(struct DateStamp));

            if (DateToStr(&datetime))
               Printf("%ls %ls %ls ", strday,strdate,strtime);

            Printf("%lc%lc%lc%lc%lc%lc%lc ",
               (fib->fib_Protection & 64) ? 's' : '-',
               (fib->fib_Protection & 32) ? 'p' : '-',
               (fib->fib_Protection & 16) ? 'a' : '-',
               (fib->fib_Protection & 8) ? '-' : 'r',
               (fib->fib_Protection & 4) ? '-' : 'w',
               (fib->fib_Protection & 2) ? '-' : 'e',
               (fib->fib_Protection & 1) ? '-' : 'd');

            if (DOSBase->dl_lib.lib_Version >= 39) /* at least AmigaOS 3.0 required */
               Printf("(UID=%ld,GID=%ld) ", fib->fib_OwnerUID, fib->fib_OwnerGID);

            Printf("%ld bytes \"%ls\"\n", fib->fib_Size, fib->fib_Comment);
            };

         FreeVec(fib);
         };
      }

      WriteChar('\n');

      /* examine binary block */
      {
      BOOL one = FALSE;    /* indicates a least one version string found */
      char temp[MAXLINELEN+1];
      ULONG len;
      LONG c;

      while ((c = FGetC(fh))!=-1)
         {
         if (c=='$' || c=='@')
            {
            temp[0] = c;

            if ((c = FGetC(fh))!=-1)
               {
               if (c=='V' || c=='(' || c=='I')
                  {
                  temp[1] = c;
                  len = 2;

                  while ((c = FGetC(fh))!=-1)
                     {
                     temp[len++] = c;

                     if (len==5 && temp[0]=='$')
                        if (strncmp(&temp[1],"Id: ",4)==0)
                           {
                           while ((c = FGetC(fh))!=-1)
                              {
                              temp[len++] = c;
                              if (c=='$' || len==MAXLINELEN)
                                 break;
                              };
                           temp[len] = '\0';

                           Printf("   ident   : %ls\n", temp);
                           one = TRUE;
                           };

                     if (len==4 && temp[0]=='@')
                        if (strncmp(&temp[1],"($)",3)==0)
                           {
                           while ((c = FGetC(fh))!=-1)
                              {
                              temp[len++] = c;
                              if (c=='\0' || len==MAXLINELEN)
                                 break;
                              };
                           temp[len] = '\0';

                           Printf("   uident  : %ls\n", temp);
                           one = TRUE;
                           };

                     if (len==6 && temp[0]=='$')
                        if (strncmp(&temp[1],"VER: ",5)==0)
                           {
                           while ((c = FGetC(fh))!=-1)
                              {
                              temp[len++] = c;
                              if (c=='\0' || c=='\n' || len==MAXLINELEN)
                                 break;
                              };
                           if (c=='\n')
                              len--;
                           temp[len] = '\0';

                           Printf("   Version : %ls\n", temp);
                           one = TRUE;
                           };

                     if (len>=6)
                        {
                        UnGetC(fh,-1);
                        break;
                        };
                     };
                  }
               else
                  UnGetC(fh,-1);
               };
            };
         };

      if (!one)
         Printf("   no version string found\n");

      }

      if (!Close(fh))
         Warning("closing file error");

      WriteChar('\n');

      return TRUE;
      };

   return FALSE;
}

/*
 * BOOL CheckVersionMulti(STRPTR dir,STRPTR file)
 *
 * checks if *dir ends in ':' and checks for multiassigns if it is.
 * calls CheckVersion() for every found directory
 *
 * returns TRUE if at least one CheckVersion() returns TRUE
 */

BOOL CheckVersionMulti(STRPTR dir,STRPTR file)
{
   if (strlen(dir)>0)
      {
      if (dir[strlen(dir)-1]==':')
         {
         BOOL found = FALSE;
         struct DevProc *devproc = NULL;

         if ((devproc = GetDeviceProc(dir,NULL)))
            if (devproc->dvp_Lock)
               {
               devproc = NULL;

               if (ArgStruct.Verbose)
                  Printf("checking multiassigns for %ls\n",dir);

               while ((devproc = GetDeviceProc(dir,devproc)))
                  {
                  if (devproc->dvp_Lock)
                     {
                     char buffer[MAXPATHLEN];
                     if (NameFromLock(devproc->dvp_Lock,buffer,sizeof(buffer)))
                        {
                        if (CheckVersion(buffer,file))
                           {
                           found = TRUE;
                           if (!ArgStruct.CheckAll)
                              return TRUE;
                           };
                        }
                     else
                        Warning("NameFromLock() failed");
                     };
                  };

               if (ArgStruct.Verbose)
                  PutStr("\n");
               }
            else
               if (CheckVersion(dir,file))
                  found = TRUE;

         return found;
         };
      };

   return CheckVersion(dir,file);
}
/*
 * BOOL DoThePathThang(struct CommandLineInterface *CLI,STRPTR filename,STRPTR assign)
 *
 *
 */

BOOL DoThePathThang(struct CommandLineInterface *CLI,STRPTR filename)
{
STRPTR filepart = FilePart(filename);
BOOL found = FALSE;

   if (ArgStruct.Library)
      CheckLibrary(filename);

   if (ArgStruct.Device)
      CheckDevice(filename);

   if (filepart!=filename)   /* is this path qualified ? */
      {
      if (ArgStruct.Library)
         CheckLibrary(filepart);

      if (ArgStruct.Device)
         CheckDevice(filepart);
      };

   if (ArgStruct.Library || ArgStruct.Device)
      WriteChar('\n');

   if (filepart!=filename)   /* is this path qualified ? */
      {
      if (CheckVersion("",filename))   /* yes! so check it directly (worx only with 37 and up) */
         {
         found = TRUE;
         if (!ArgStruct.CheckAll)
            return TRUE;
         };

      filename = filepart;   /* now its unqualified */
      };

   if (ArgStruct.Assign)
      if (CheckVersionMulti(ArgStruct.Assign,filename))
         {
         found = TRUE;
         if (!ArgStruct.CheckAll)
            return TRUE;
         };

   if (ArgStruct.Library)
      if (CheckVersionMulti("LIBS:",filename))
         {
         found = TRUE;
         if (!ArgStruct.CheckAll)
            return TRUE;
         };

   if (ArgStruct.Device)
      if (CheckVersionMulti("DEVS:",filename))
         {
         found = TRUE;
         if (!ArgStruct.CheckAll)
            return TRUE;
         };

   {
   BOOL resident = FALSE;
   if ((resident = CheckResidentList(filename,FALSE))) /* user list */
      {
      found = TRUE;
      if (!ArgStruct.CheckAll)
         return TRUE;
      };

   if ((resident = CheckResidentList(filename,TRUE))) /* system list */
      {
      found = TRUE;
      if (!ArgStruct.CheckAll)
         return TRUE;
      };
   if (resident)
      WriteChar('\n');
   }

   if (CheckVersion("",filename))
      {
      found = TRUE;
      if (!ArgStruct.CheckAll)
         return TRUE;
      };

   {
   struct PBLE *pble = BADDR(CLI->cli_CommandDir); /* PATH-list */
   while (pble)
      {
      char buffer[MAXPATHLEN];

      if (NameFromLock(pble->DirLock,buffer,sizeof(buffer)))
         {
         if (CheckVersionMulti(buffer,filename))
            {
            found = TRUE;
            if (!ArgStruct.CheckAll)
               return TRUE;
            };
         }
      else
         Warning("NameFromLock() failed");

      pble = BADDR(pble->Next);  /* check next path */
      };
   }

   found = found || CheckVersionMulti("C:",filename);

   return found;
}

/*
 * ShowRev
 *
 * int main(int argc_notused,char **argv_notused)
 *
 */

int main(int argc_notused,char **argv_notused)
{
   if (DOSBase->dl_lib.lib_Version >= 37) /* at least AmigaOS 2.04 required */
      {
      struct RDArgs *rdargs = ReadArgs(ArgString,(LONG *)&ArgStruct,NULL);
      if (rdargs)
         {
         struct Task *task = FindTask(NULL);

         if (task->tc_Node.ln_Type==NT_PROCESS) /* should be TRUE for a CLI-command */
            {
            struct CommandLineInterface *CLI = BADDR(((struct Process *)task)->pr_CLI);

            if (CLI) /* should be true for a CLI-command too */
               {
               Printf("%ls\n%ls\n\n",&VER[1],Id);

               if (ArgStruct.Library && ArgStruct.Device)
                  {
                  Warning("LIBRARY and DEVICE keyword given, ignoring DEVICE");
                  ArgStruct.Device = FALSE;
                  };

               if (!DoThePathThang(CLI, ArgStruct.Filename))
                  Warning("no version found, file not in path");
               }
            else
               Failing("not a CLI-process");
            }
         else
            Failing("not a DOS-process");

         FreeArgs(rdargs);
         }
      else
         {
         PrintFault(IoErr(),BASENAME);
         ReturnCode = RETURN_FAIL;
         };
      }
   else
      Failing("needs dos.library v37 or higher");

   return ReturnCode;
}

