
#define	VERSION	"1.4 "
/**
 ***** Whence
 *
 ***** 	Searches all directories in AmigaDos PATH for the given
 *	command name, and reports all instances found.
 *
 *	Directories of the same name are ignored.
 *
 *	The first instance printed would be the one AmigaDos executes.
 *
 *	Handles from 1.2 upwards. 2.04 Internals/residents
 *	are reported.
 *
 *	Redundant paths are NOT reported (in contrast with AmigaDos 'which')
 *
 *	Path, File size and date is printed.
 *
 *	Algorithms depend entirely on observed format of
 *	Path and Assign commands executed under AmigaDos 2.04 , i.e
 *	it is assumed that they follow previous releases.
 *
 *
 *
 *
 *****	Written by Gary Duncan
 *
 *	Bug reports etc via e-mail to gduncan@werple.net.au 
 *
 *
 *****	Freely distributable for non-commercial purposes.
 *	Please keep this header intact.
 *
 *
 *	Compiles under SAS 6.2
 *
 *	Formatted with 'indent -gnu' ; a big time-saving program.
 *
 ***** Function List :-

	brk
	main
	chk_cdir
	put_path
	get_fib
	get_cwd
	add_C_assign
	search_resident
	dss2str
	usage
	hexit


Version	Date 	Changes
~~~~~~~ ~~~~	~~~~~~

1.0 	29Oct93 Original release
1.1	24Jan94 used __AMIGADATE__
1.2	28Nov94 uses manual __AMIGADATE__ (buggy) 
1.3	27Apr95 reactivated __AMIGADATE__ , corrected exit state 
1.4	30Sep95 No code changed ( comments corrected)  





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

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <dos.h>
#include <ctype.h>
#include <exec/execbase.h>
#include <exec/types.h>
#include <string.h>
#include <time.h>
#include <exec/memory.h>
#include <libraries/dosextens.h>
#include <proto/dos.h>
#include <clib/intuition_protos.h>
#include <clib/utility_protos.h>
#include <proto/exec.h>
#include <devices/serial.h>
#include <devices/timer.h>
#include <exec/io.h>
#include <utility/date.h>


#include "typedefs.h"
#include "whence_protos.h"


#define REDUNDANT ((char *) -1)

/*
 * string for AmigaDOS Version Command
 */
char A_vers[] = "\0$VER: whence\t" VERSION __AMIGADATE__;


LIBRARY *UtilityBase = NULL;
LIBRARY *BattClockBase;

FILE *fp_P = NULL;
FILE *fp_A = NULL;
FILE *fp_R = NULL;
FILE *fp_CD = NULL;

static char file_P[20];
static char file_A[20];
static char file_R[20];
static char file_CD[20];

FILEINFOBLOCK *fib = NULL;

char *paths[64];		/* assume max of 64 dirs in Path */
int path_cnt = 0;

BOOLEAN c_flag = FALSE;
int break_flag;

int
brk (void)
{
  break_flag++;
  return 0;
}


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

int
main (int argc, char **argv)

{
  static char buf[256];
  static char line_buf[256];
  LIBRARY *lp;
  char *fname = argv[1];
  char *str;
  char **PP;
  /*
   * set up ^C processing
   */
  if (onbreak (&brk))
    {
      printf ("onbreak fail\n");
      exit (1);
    }

  if (argc != 2)
    {
      fprintf (stderr, "Error: need a command name\n");
      usage ();
      hexit (1);
    }
  if (*argv[1] == '-')
    {
      usage ();
      hexit (1);
    }
  /*
   * get a FIB
   */
  fib = AllocMem ((long) sizeof (*fib), MEMF_CLEAR);
  if (fib == NULL)
    {
      fprintf (stderr, "unable to allocate space for fileinfo block\n");
      hexit (1);
    }

  /*
   * if below 2.04 version, we won't look for Residents (or itinerants :)
   */
  if (lp = OpenLibrary ("exec.library", 37L))
    {
      CloseLibrary (lp);

      /*
       * build Resident File
       */
      sprintf (file_R, "T:wr_%08d", file_R);
      sprintf (buf, "RESIDENT >%s", file_R);
      Execute (buf, 0L, 0L);

      if ((fp_R = fopen (file_R, "r")) == NULL)
	{
	  fprintf (stderr, "Can't fopen %s\n", file_R);
	  hexit (1);
	}
    }

  /*
   * build file containing CD
   */
  sprintf (file_CD, "T:wcd_%08d", file_CD);
  sprintf (buf, "CD >%s", file_CD);
  Execute (buf, 0L, 0L);

  /*
   * build file containing PATHS
   */
  sprintf (file_P, "T:wp_%08d", file_P);
  sprintf (buf, "PATH >%s", file_P);
  Execute (buf, 0L, 0L);

  /*
   * build file containing ASSIGNS
   */
  sprintf (file_A, "T:wa_%08d", file_A);
  sprintf (buf, "ASSIGN >%s", file_A);
  Execute (buf, 0L, 0L);

  /*
   * open the files for further manipulation , later
   */

  if ((fp_P = fopen (file_P, "r")) == NULL)
    {
      fprintf (stderr, "Can't fopen %s\n", file_P);
      hexit (1);
    }
  if ((fp_A = fopen (file_A, "r")) == NULL)
    {
      fprintf (stderr, "Can't fopen %s\n", file_A);
      hexit (1);
    }
  if ((fp_CD = fopen (file_CD, "r")) == NULL)
    {
      fprintf (stderr, "Can't fopen %s\n", file_CD);
      hexit (1);
    }
  /*
   ***************************************************************
   */

  /*
   * if Resident file open, search and report on it first
   * - that's the rules under 2.04
   */
  if (fp_R)
    search_resident (fname);

  /*
   * put Path strings into an *array for convenience
   *
   */

  path_cnt = 0;
  while ((str = fgets (line_buf, sizeof (line_buf), fp_P)) != NULL)
    {
      char *p;

      /*
       * handle "Current directory"
       */
      if (path_cnt == 0)
	{
	  str = get_cwd ();
	}
      p = &str[strlen (str) - 1];
      *p = '\0';		/* remove NL */

      /*
       * C: is last. Drop it now, handle later
       */
      if (strcmp (str, "C:") == 0)
	{
	  continue;
	}

      /*
       * copy it to a malloc'd bit of memory
       */
      put_path (str);
    }

  /*
   * now handle case where current dir is in Path list
   */
  chk_cdir ();


  /*
   * now handle C: (last). If 2.04 may be added dirs (+ xxx)
   */
  add_C_assign ();

  /*
   * now scan recreated path list
   */

  for (PP = paths; *PP; ++PP)
    {
      char buf[256];

      if (*PP == REDUNDANT)
	{
	  continue;
	}

      strcpy (buf, *PP);
      if (buf[strlen (buf) - 1] != ':')
	{
	  strcat (buf, "/");
	}

      strcat (buf, fname);

      chkabort ();
      if (break_flag)
	{
	  fprintf (stderr, "^C\n");
	  hexit (1);
	}
      if (get_fib (buf) == NULL)
	{
#if 0
	  printf ("Not a File (%s)\n", buf);
#endif
	  continue;
	}

      /*
       * make sure its a file not dir
       */
      if (fib->fib_DirEntryType < 0)
	{
	  /*
	   * print pathname , size , date
	   */
	  printf ("%-25s %7d bytes : %s\n", buf, fib->fib_Size,
		  dss2str (&fib->fib_Date));
	}
    }
  hexit (0);
}

/***************************************************************************


Name: 	 	chk_cdir

Purpose:	checks paths[] to see if current dir entry (#1)
		is replicated below, if so mark as redundant that entry.


Entry    :


Returns  :



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

void
chk_cdir ()
{
  int j;

  for (j = 1; j < path_cnt; ++j)
    {
      if (stricmp (paths[0], paths[j]) == 0)
	{
#if 0
	  printf ("%s : REDUNDANT\n", paths[j]);
#endif
	  free (paths[j]);
	  paths[j] = REDUNDANT;
	  return;
	}
    }
}

/***************************************************************************


Name: 	 	put_path

Purpose:	copies given string to malloc'd memory and put
		its pointer in next slot in paths[].


Entry    :


Returns  :



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

void
put_path (char *str)
{
  if (path_cnt == sizeof (paths))
    {
      fprintf (stderr, "path buffer (64) full!\n");
      hexit (1);
    }

  if ((paths[path_cnt] = malloc (strlen (str) + 1)) == NULL)
    {
      fprintf (stderr, "malloc fail\n");
      hexit (1);
    }
  strcpy (paths[path_cnt++], str);
#if 0
  printf ("put_path : %s\n", paths[path_cnt - 1]);
#endif
}

/***************************************************************************


Name: 		get_fib

Purpose:	given a file name, return with a FIB ,
		or NULL if non-existent


Entry    :


Returns  :



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


FILEINFOBLOCK *
get_fib (char *fname)

{
  BPTR flock;


  if (!(flock = Lock (fname, ACCESS_READ)))
    {
      return NULL;
    }

  if (!Examine (flock, fib))
    {
      UnLock (flock);
      return NULL;
    }

  UnLock (flock);
  return fib;
}

/***************************************************************************


  Function :    get_cwd

  Purpose:      returns with path to current dir

  Entry    :

  Returns  :


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


char *
get_cwd ()
{
  static char buf[128];


  if (fgets (buf, sizeof (buf), fp_CD) == NULL)
    {
      fprintf (stderr, "fatal error reading cwd\n");
      hexit (1);
    }
  return buf;

}

/***************************************************************************


  Function :    add_C_assign

  Purpose:     	scans Assign file and adds C: info to path[]

  Entry    :

  Returns  :


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


void
add_C_assign ()
{
  static char buf[128];
  BOOLEAN flag = FALSE;
  char *p;
  int p_c;
  int j;

  if (fseek (fp_A, 0L, SEEK_SET) == -1)
    {
      fprintf (stderr, "\nget_assign seek error\n");
      hexit (1);
    }
  while (1)
    {
      if (fgets (buf, sizeof (buf), fp_A) == NULL)
	{
	  return;
	}
      buf[strlen (buf) - 1] = '\0';	/* zap NL */
      /*
       * point to start of last field (dir)
       */

      p = &buf[strlen (buf) - 1];
      while (*(p - 1) != ' ')
	--p;

      /*
       * when c is found , put its dirs into paths[]
       */
      if (strnicmp (buf, "c ", 2) == 0)
	{
	  flag = TRUE;
	  p_c = path_cnt;	/* hold index */
	  put_path (p);
	}
      else if (flag)
	{
	  if (*(p - 2) == '+')
	    {
	      put_path (p);
	    }
	  else
	    /* end */
	    {
	      /*
	       * now scan paths[] to see if c: was already there (above in
	       * array). If so mark as redundant c: dir
	       */
	      for (j = 0; j < p_c; ++j)
		{
		  if (strcmp (paths[j], paths[p_c]) == 0)
		    {
		      /*
		       * found a matching dir, mark it as a null
		       */
		      free (paths[p_c]);
		      paths[p_c] = REDUNDANT;
		      return;

		    }
		}
	    }
	}
    }
}

/***************************************************************************


  Function :    search_resident

  Purpose:      searches for resident commands

  Entry    :

  Returns  :


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

void
search_resident (char *name)
{
  char buf[80];

  /*
   * skip first line
   */
  if (fgets (buf, sizeof (buf), fp_R) == NULL)
    {
      return;
    }
  /*
   * skip second line
   */
  if (fgets (buf, sizeof (buf), fp_R) == NULL)
    {
      return;
    }

  while (1)
    {
      char *p = buf;

      if (fgets (buf, sizeof (buf), fp_R) == NULL)
	{
	  return;
	}

      while (*++p != ' ');

      *p = '\0';
      if (strlen (name) == strlen (buf))
	{
	  if (stricmp (name, buf) == 0)
	    {
	      while (*++p == ' ');

	      if (isdigit (*p))
		printf ("RES %s\n", name);
	      else
		printf ("INTERNAL %s\n", name);

	      return;
	    }
	}
    }
}

/***************************************************************************


  Function :  	dss2str

  Purpose:	generates a date string

  Entry    :    Datestamp


  Returns  :    ptr to date string



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


char *
dss2str (DATESTAMP * ds)

{
  static DM days_month[] =
  {
    31, "Jan",
    28, "Feb",
    31, "Mar",
    30, "Apr",
    31, "May",
    30, "Jun",
    31, "Jul",
    31, "Aug",
    30, "Sep",
    31, "Oct",
    30, "Nov",
    31, "Dec",
  };
  static char buf[40];
  struct tm Time;

  int j;
  long time, ago;

  /*
   * convert to a secs
   */
  time = ds->ds_Days * (24 * 60 * 60);
  time += ds->ds_Minute * 60;
  time += ds->ds_Tick / TICKS_PER_SECOND;

  Time.tm_sec = time % 60;
  time /= 60;
  Time.tm_min = time % 60;
  time /= 60;
  Time.tm_hour = time % 24;
  time /= 24;
  Time.tm_wday = time % 7;
  Time.tm_year = 78 + (time / (4 * 365 + 1)) * 4;
  time %= 4 * 365 + 1;


  while (time)
    {
      ago = 365;
      if ((Time.tm_year & 3) == 0)	/* leap year */
	ago++;
      if (time < ago)
	break;
      time -= ago;
      Time.tm_year++;
    }
  Time.tm_yday = ++time;

  for (j = 0; j < 12; j++)
    {
      ago = days_month[j].days;
      if (j == 1 && (Time.tm_year & 3) == 0)	/* Feb , and leap year ? */
	ago++;
      if (time <= ago)
	break;
      time -= ago;
    }
  Time.tm_mon = j;
  Time.tm_mday = time;

  /* date format : 23-dec-88 22:33:01  */

  sprintf (buf, "%02d-%s-%2d %02d:%02d:%02d",
	   Time.tm_mday,
	   days_month[Time.tm_mon].months,
	   Time.tm_year,
	   Time.tm_hour,
	   Time.tm_min,
	   Time.tm_sec
    );

  return (buf);
}

/***************************************************************************


  Function :    usage

  Purpose:      prints usage

  Entry    :

  Returns  :


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

void
usage ()
{
  static char *rats[] =
  {
    "usage: whence <command>",
    ""				/* empty string ;TERM */
  };
  int j = 0;
  char *ptr;



  fprintf (stderr,
/****/ "Whence: prints paths to given command in AmigaDos search path\n\n\
Version %s [%s] written by gduncan@philips.oz.au\n", VERSION, __DATE__);

  while (*(ptr = rats[j++]))
    fprintf (stderr, "%s\n", ptr);
}

/***************************************************************************


  Function :    hexit

  Purpose:      cleans up and exits

  Entry    :

  Returns  :


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

void
hexit (int k)
{
  int j;

  fclose (fp_R);
  unlink (file_R);

  fclose (fp_CD);
  unlink (file_CD);

  fclose (fp_P);
  unlink (file_P);

  fclose (fp_A);
  unlink (file_A);
  if (fib)
    FreeMem (fib, (long) sizeof (*fib));

  /*
   * release path array memory
   */
  for (j = 0; j < path_cnt; ++j)
    {
      char *p = paths[j];

      if (p && (p != REDUNDANT))
	free (p);

    }

  exit (k);
}

/*--*/
€
