/*
 * Libraries and headers for PDC release 3.3 (C) 1989 Lionel Hummel.
 * PDC Software Distribution (C) 1989 Lionel Hummel and Paul Petersen.
 * PDC I/O Library (C) 1987 by J.A. Lydiatt.
 *
 * This code is freely redistributable upon the conditions that this
 * notice remains intact and that modified versions of this file not
 * be included as part of the PDC Software Distribution without the
 * express consent of the copyright holders.  No warrantee of any
 * kind is provided with this code.  For further information, contact:
 *
 *  PDC Software Distribution    Internet:                     BIX:
 *  P.O. Box 4006             or hummel@cs.uiuc.edu            lhummel
 *  Urbana, IL  61801-8801       petersen@uicsrd.csrd.uiuc.edu
 */

/*    system.c
 *
 *    system() - Executes a program within the context of the current process
 *    _PathAccess() - Attempts access of file along a trial path
 *
 *    Note: The following two routines are a non-optimal solution to the
 *          need to find the full path name given only a directory lock.
 *
 *    _SearchPath() - Attempts to locate a file along the current CLI path
 */

/*
  History:

  - 901218 Ray Burr
    Modifed to handle redirection and use ScanAmigaArgs().  Now uses first
    argument instead of the full path for the command name on the call
    to System0() (like commands typed from the shell).

  - 910709 Ray Burr
    Added code to return 1 when called with NULL as the argument.
    Reformatted code using indent.

*/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/alerts.h>
#include <libraries/dosextens.h>
#include <functions.h>

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXPATH 1024

#define DO_REDIR 1

static char *_SearchPath ();
static char *_PathAccess ();

int
system (const char *line)
{
  char *buffer = 0;
  char *command = 0;
  char *args;
  char *ptr;
  char *line_end;
  char scan_for;
  long segment_list = 0;
  long return_code = -1;
#ifdef DO_REDIR
  char *in_filename = 0;
  char *out_filename = 0;
  BPTR in_file = 0;
  BPTR out_file = 0;
  BPTR real_CIS;
  BPTR real_COS;
  struct Process *pr;
#endif

  if (line == NULL)
    return 1;

  {
    int len = strlen (line);

    buffer = (char *) malloc (len + 6);	/* Buffer length plus slop */
    if (!(ptr = buffer))
      {
	Alert (AG_NoMemory, NULL);
	return (-1);
      }

    line_end = (char *)(line + len);
  }

  ScanAmigaArg (&line, &ptr, line_end);

  *ptr++ = '\0';

#ifdef DO_REDIR
  while (*line == '<' || *line == '>')
    {
      if (*line == '<' && !in_filename)
	{
	  ++line;
	  in_filename = ptr;
	  if (ScanAmigaArg (&line, &ptr, line_end))
	    *ptr++ = '\0';
	  else
	    in_filename = 0;
	}
      else if (*line == '>' && !out_filename)
	{
	  ++line;
	  out_filename = ptr;
	  if (ScanAmigaArg (&line, &ptr, line_end))
	    *ptr++ = '\0';
	  else
	    out_filename = 0;
	}
      else
	break;
    }
#endif

  args = ptr = (char *) (((ULONG) ptr + 3) & ~3);	/* Longword align */

  while (isspace (*line))
    line++;
  while (*ptr = *line)
    ptr++, line++;
  *ptr++ = '\n';
  *ptr++ = '\0';

  command = _SearchPath (buffer);
  if (!command)
    goto done;

  segment_list = LoadSeg (command);
  if (!segment_list)
    goto done;

#ifdef DO_REDIR
  pr = (struct Process *) FindTask (NULL);

  if (in_filename)
    {
      in_file = Open (in_filename, MODE_OLDFILE);
      if (!in_file)
	goto done;
    }

  if (out_filename)
    {
      out_file = Open (out_filename, MODE_NEWFILE);
      if (!out_file)
	goto done;
    }

  real_CIS = pr->pr_CIS;
  real_COS = pr->pr_COS;

  if (in_file)
    pr->pr_CIS = in_file;

  if (out_file)
    pr->pr_COS = out_file;
#endif

  return_code = System0 (buffer, segment_list, args);

#ifdef DO_REDIR
  pr->pr_CIS = real_CIS;
  pr->pr_COS = real_COS;
#endif

done:

#ifdef DO_REDIR
  if (out_file)
    Close (out_file);
  if (in_file)
    Close (in_file);
#endif
  if (segment_list)
    UnLoadSeg (segment_list);
  if (command)
    free (command);
  if (buffer)
    free (buffer);

  return return_code;
}

struct PathNode
{
  BPTR NextPath;
  BPTR PathLock;
};

static char *
_SearchPath (target)
     char *target;
{
  struct CommandLineInterface *this_cli;
  BPTR pl;			/* BPTR to struct FileLock */
  BPTR nextpl;			/* BPTR to struct FileLock */
  struct FileInfoBlock *fib;
  char *fullpath = NULL;
  char *buffer;
  struct PathNode *p_chain;
  unsigned int count;

/* Check current directory. */
  fullpath = _PathAccess ("", target);
  if (fullpath)
    return (fullpath);

/* Then check search path. */
  this_cli = ((struct CommandLineInterface *)
	      BADDR (((struct Process *) FindTask (NULL))->pr_CLI));
  if (this_cli == NULL)
    {
      return (NULL);
    }

  p_chain = (struct PathNode *) BADDR (this_cli->cli_CommandDir);

  buffer = malloc (MAXPATH + 2); /* Path + nul + initial slosh byte */
  if (buffer == NULL)
    {
      Alert (AG_NoMemory, NULL);
      return (NULL);
    }

  fib = malloc (sizeof (struct FileInfoBlock));
  if (!fib)
    {
      Alert (AG_NoMemory, NULL);
      return (NULL);
    }

  while (p_chain)
    {
      pl = DupLock (p_chain->PathLock);
      if (!pl)
	{
	  free (buffer);
	  free (fib);
	  return NULL;
	}

      buffer[MAXPATH + 1] = '\0';
      fullpath = &buffer[MAXPATH + 1];

      while (pl)
	{
	  if (Examine (pl, fib))
	    {
	      if (nextpl = ParentDir (pl))
		*--fullpath = '/';
	      else
		*--fullpath = ':';
	      count = strlen (fib->fib_FileName);
	      fullpath -= count;
	      if (fullpath <= buffer)
		{
		  UnLock (pl);
		  if (nextpl)
		    UnLock (nextpl);
		  goto next_path;
		}
	      memcpy (fullpath, fib->fib_FileName, count);
	      UnLock (pl);
	      pl = nextpl;
	    }
	  else
	    {
	      UnLock (pl);
	      goto next_path;
	    }
	}

      fullpath = _PathAccess (fullpath, target);
      if (fullpath)
	{
	  UnLock (nextpl);
	  free (buffer);
	  free (fib);
	  return (fullpath);
	}

    next_path:
      p_chain = (struct PathNode *) BADDR (p_chain->NextPath);
    }

/* If that fails, default to C: */
  fullpath = _PathAccess ("C:", target);

  free (buffer);
  free (fib);

  return (fullpath);		/* Gets NULL from _PathAccess if failed */
}


static char *
_PathAccess (path, target)
     char *path;
     char *target;
{
  char *buf;
  BPTR fl;

  buf = malloc (MAXPATH + 1);
  if (buf == NULL)
    {
      Alert (AG_NoMemory, NULL);
      return (NULL);
    }

  strcpy (buf, path);
  strcat (buf, target);
  if (fl = Lock (buf, ACCESS_READ))
    {
      UnLock (fl);
      return (buf);
    }

  free (buf);
  return (NULL);
}
