/* -*- Mode: C -*- */

/*
 * wh - show full paths of potential commands
 *
 * This file was placed in the Public Domain by its author on 14
 * March 1999.  Those who modify this file are encouraged to send
 * mail to Rick Campbell <rick@campbellcentral.org> with their Public 
 * Domain modifications so that they can be included in future
 * versions.
 *
 */

static const char Copyright []
= "Placed in the Public Domain on 14 March 1999.";

/* Table of Contents */

/* -*- Mode: C -*- */
/* Table of Contents */
/* Headers, etc. */
/* Prototypes */
/* list */
/* main */
/* scan_directory */
/* usage */

/* Headers, etc. */

#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>

#ifdef WH_ANSI
# define ALLOCATE( SIZE ) malloc (SIZE)
# define RELEASE( POINTER) free (POINTER);
#else /* WH_ANSI */
# define ALLOCATE( SIZE ) alloca (SIZE)
# define RELEASE( POINTER )
#endif /* WH_ANSI */

static char* Program_Name = (char*)NULL;

/* Prototypes */

static
void
list (char*        pathname,
      char**       list_options,
      unsigned int list_options_count);

int
main (int    argument_count,
      char** argument_vector);

static
void
scan_directory (char*        directory_start,
		char*        directory_limit,
		char*        target,
		unsigned int target_length,
		char**       list_options,
		unsigned int list_options_count);

static
void
usage ();

/* list */

static
void
list (char*        pathname,
      char**       list_options,
      unsigned int list_options_count)
{
    pid_t        child = 0;
    char**       exec_arguments = (char**)NULL;
    unsigned int index = 0;
    
    exec_arguments = (char**)(ALLOCATE (list_options_count + 3));
    assert (exec_arguments != ((char**)NULL));
    exec_arguments [0] = "ls";
    for (index = 0; index < list_options_count; ++index)
    {
	exec_arguments [index + 1] = list_options [index];
    }
    exec_arguments [list_options_count + 1] = pathname;
    exec_arguments [list_options_count + 2] = (char*)NULL;
    child = fork ();
    if (child == 0)
    {
	execvp ("ls", exec_arguments);
    }
    else
    {
	int child_status = 0;

	waitpid (child, &child_status, 0);
	assert (WIFEXITED (child_status));
	RELEASE ((void*)exec_arguments);
    }
}

/* main */

int
main (int    argument_count,
      char** argument_vector)
{
    unsigned int index = 0;
    char*        limit = (char*)NULL;
    char**       list_options = (char**)NULL;
    unsigned int list_options_count = 0;
    char*        path_list = (char*)NULL;
    char*        start = (char*)NULL;
    char*        target = (char*)NULL;
    unsigned int target_length = 0;

    Program_Name = argument_vector [0];
    if (argument_count <= 1)
    {
	usage ();
    }
    assert (argument_count > 1);
    list_options_count = argument_count - 2;
    if (list_options_count != 0)
    {
	list_options = ALLOCATE (list_options_count);
	for (index = 0; index < list_options_count; ++index)
	{
	    list_options [index] = argument_vector [index + 1];
	}
    }
    target = argument_vector [argument_count - 1];
    target_length = strlen (target);
    assert (target_length > 0);
    path_list = getenv ("PATH");
    assert (path_list != (char*)NULL);
    start = path_list;
    limit = strchr (start, ':');

    while (1)
    {
	scan_directory (start, limit, target, target_length,
			list_options, list_options_count);
	start = limit + 1;

	limit = strchr (start, ':');
	if (limit == ((char*)NULL))
	{
	    scan_directory (start, (start + strlen (start)),
			    target, target_length,
			    list_options, list_options_count);
	    break;
	}
    }
    RELEASE ((void*)list_options)

    return 0;
}

/* scan_directory */

static
void
scan_directory (char*        directory_start,
		char*        directory_limit,
		char*        target,
		unsigned int target_length,
		char**       list_options,
		unsigned int list_options_count)
{
    unsigned int directory_length = 0;
    unsigned int length = 0;
    char*        path = (char*)NULL;
    struct stat  status;

    directory_length = directory_limit - directory_start;
    length = directory_length + target_length + 1;
    path = ALLOCATE (length + 1);
    assert (path != (char*)NULL);
    strncpy (path, directory_start, directory_length);
    path [directory_length] = '/';
    strncpy (path + directory_length + 1, target, target_length);
    path [length] = '\0';
    if (stat (path, &status) == 0)
    {
	list (path, list_options, list_options_count);
    }
    else if (errno != ENOENT)
    {
	fprintf (stderr, "%s: stat(2) failed for \"%s\".\n", Program_Name,
		 path);
	perror ("stat");
	exit (1);
    }
    RELEASE ((void*)path);
}

/* usage */

static
void
usage ()
{
    fprintf (stderr, "Usage: %s LS_OPTION* program_name\n", Program_Name);
    exit (1);
}
