/* Copyright (c) 2001 by SoftIntegration, Inc. All Rights Reserved */
/***********************************************************
* command name: which
*
* Note: (1) tells you which one you are using
*       (2) Usage: which [-av] names
*       (3) -a option will find all command, including 
*           environmental variables and include file
*           ex: which -a ls PATH stdio 
*       (4) -v send out search message if command is not found
*
* History: created by Harry H. Cheng, 4/7/1994
**********************************************************/
#define CHWHICHCOMMAND
#define COMMANDONLY
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>        /* define X_OK and R_OK */
#include <sys/stat.h>  
#include <string.h>        
#include <stdbool.h>        
#include <chshell.h>        

int main(int argc, char **argv) {
  char usage[] = "Usage: which [-av] names \n"
                 "       -a option will find all commands, including\n"
                 "          function files, environmental variables, and header files.\n"
                 "          ex: which -a ls PATH stdio hypot\n"
                 "       -v send out search message if command is not found.\n";

  char *s, *arg, *alius1, *alius2, *token, *token2;
  char fullname[1024], name[1024];
  int i, found; 
  struct stat sbuf;
  int a_option = false;   /* default get the first match only */
  int v_option = false; 
  /* local function */
  void backslash2slash(char *p) {
    while(p = strchr(p,'\\'))
      *p = '/';
  }

  if(argc == 1){
    printf(usage);
    return (-1);
  }

  argc--; argv++;
  while(argc >0 && **argv =='-')
  {
      for(s = argv[0]+1; *s&&*s!=' '; s++)  /* empty space is not valid option */
        switch(*s)
        {
          case 'a': 
            a_option = true;   /* get all possible matches */
            break;
          case 'v': 
            v_option = true;   /* print message */
            break;
          default:
            fprintf(stderr,"Warning: invalid option %c\n", *s);
            fprintf(stderr,usage);
            break;
        }
      argc--; argv++;
  }  

  while(argc >0)            /* process list of argument */
  {
    found = false;          /* reset for next command search */
    arg = *argv;
    argc--; argv++;
    if(strcmp(arg, "-a") == 0) /* optional arg ?, ex. "which ls -a PATH" */
    {
      a_option = true;      /* get all possible matches */
      continue;
    }

    if(strcmp(arg, "-v") == 0) /* option -v */
    {
      v_option = true;
      continue;
    }

    alius1 = alias(arg); /* get alias */
    if(alius1 != NULL)      /* alias ? */
    {
      found = true;
      printf("%s is aliased to %s\n", arg, alius1);
      token = strchr(alius1, ' ');
      if(token == NULL)     /* nested aliasing */
      {
        alius2 = alias(alius1);
        if(alius2 != NULL)
          printf("%s is aliased to %s\n", alius1, alius2);
      }
#ifdef COMMANDONLY
      if(!a_option)
        continue;           /* I got it */
#endif
    }

    i = iskey(arg);         /* keyword ? */
    if(i)                   /* keyword ? */
    {
      found = true;
      if(i==1) 
        printf("%s is a generic function\n", arg);
      else
        printf("%s is a keyword\n", arg);
#ifdef COMMANDONLY
      if(!a_option)
        continue;           /* I got it */
#endif
    }

    i = isvar(arg);
    if(i && i != CH_EXTERNFUN)    /* defined variable ? */
    {
      found = true;
      if(i == CH_SYSTEMCONST)
        printf("%s is a system constant\n", arg);
      else if(i == CH_SYSTEMVAR)
        printf("%s is a system variable\n", arg);
      else if(i ==  CH_EXTERNVAR)
        printf("%s is a variable\n", arg);
    //else if(i ==  CH_EXTERNFUN)
    //  printf("%s() is a function\n", arg);
#ifdef COMMANDONLY
      if(!a_option)
        continue;           /* I got it */
#endif
    }

    found = false; /* reset */
    /* check if it is a command ? */
    /* search through command path */ 
    foreach(token; _path; NULL; ";")
    {
      strcpy(name, token);
      strcat(name, "/");
      strcat(name, arg);
      if(!access(name, X_OK)) /* executable ? */
      {
        if(!stat(name, &sbuf) && (sbuf.st_mode & S_IFMT) != S_IFDIR) {
          found = true;
          backslash2slash(name); /* for Win95 */
          printf("%s\n", name);
          if(!a_option)
            break;            /* I got it */
        }
      }
      fullname[0] = '\0';
      /* append extension of command file */
      if(_pathext != NULL) /* there are command extensions */
      {
        foreach(token2; _pathext)
        {
          strcpy(fullname, name);
          strcat(fullname, token2);
          if(!access(fullname, X_OK)) /* executable ? */
          { 
            if(!stat(fullname, &sbuf) && (sbuf.st_mode & S_IFMT) != S_IFDIR) {
              found = true;
              backslash2slash(fullname); /* for Win95 */
              printf("%s\n", fullname);
#ifdef COMMANDONLY
              if(!a_option)
                break;           /* I got it */
#endif
            }
          }
        }
      }
      if(found && !a_option)
        break;               /* I got it */
    }
#ifdef COMMANDONLY
    if(found && !a_option)
      continue;             /* I got it */

    if(a_option)  /* get all possible match */
    {
#endif
      /* check if it is function file name ? */
      /* search through function file path */ 
      foreach(token; _fpath; NULL; ";")
      {
        strcpy(name, token);
        strcat(name, "/");
        strcat(name, arg);
        /* append extension of function file */
        foreach(token2; _fpathext)
        {
          strcpy(fullname, name);
          strcat(fullname, token2);
          if(!access(fullname, R_OK)) /* readable ? */
          { 
            /* need to check the function definition to match name here */
            found = true;
            printf("%s\n", fullname);
            if(!a_option)
              break;           /* I got it */
          }
        }
#ifdef COMMANDONLY
        if(found && !a_option)
          break;               /* I got it */
#endif
      }

      /* check if is defined as environment variable */
      if(isenv(arg)) 
      {
        found = true;
        printf("%s\n", getenv(arg));
      }

      /* check if include file ? */
      /* search through include file path */ 
      foreach(token; _ipath; NULL; ";")
      {
        strcpy(name, token);
        strcat(name, "/");
        strcat(name, arg);
        /* append .h extension */
        strcat(name, ".h");
        if(!access(name, R_OK)) /* readable ? */
        { 
          found = true;
          printf("%s\n", name);
        }
      }
#ifdef COMMANDONLY
    }
#endif

    if(!found && v_option) /* crying out with nonthing found */
    {
      char *apath;
      apath = (char *)malloc(strlen(_path)+1);
      strcpy(apath,_path);
      backslash2slash(apath); /* for Win95 */
      printf("\n'%s' not found in the following command paths:\n", arg);
      foreach(token; apath; NULL; ";") {
        printf("\t%s\n", token);
      }
      free(apath);
      if(a_option) { /* get all possible match */
        printf("\n'%s' not found in the following function paths:\n", arg);
        foreach(token; _fpath; NULL; ";") {
          printf("\t%s\n", token);
        }
        printf("\n");
        printf("\n'%s' not found in the following include header file paths:\n", arg);
        foreach(token; _ipath; NULL; ";") {
          printf("\t%s\n", token);
        }
        printf("\n\n");
      }
    }
  }
  fflush(stdout); /* this is needed when Ch is executed inside the output pane in ChIDE */
  return (0);
}
