
/*
 * EXECOM.C
 *
 * Matthew Dillon, 28 Apr 1986
 *
 *    This code is particular hacked up and needs re-writing.
 *
 */

#include "shell.h"


struct COMMAND {
   int (*func)();
   short minargs;
   short stat;
   short val;
   char *name;
};

struct MLIST {
   struct MLIST *next;
};

#define F_EXACT   0
#define F_ABBR    1

extern char *breakout();

extern int do_run(), do_number();
extern int do_quit(), do_set_var(), do_unset_var();
extern int do_echo(), do_source(), do_mv();
extern int do_cd(), do_rm(), do_mkdir(), do_history();
extern int do_mem(), do_cat(), do_dir();
extern int do_foreach(), do_return(), do_if(), do_label(), do_goto();
extern int do_failat(), do_checkbrk(), do_inc();
extern int do_input(), do_ver();

static struct MLIST *Mlist;

static struct COMMAND Command[] = {
   do_run      , 0,  0,          0 ,   "\001",
   do_number   , 0,  0,          0 ,   "\001",
   do_quit     , 0,  0,          0 ,   "quit",
   do_set_var  , 0,  0, LEVEL_SET  ,   "set",
   do_unset_var, 0,  0, LEVEL_SET  ,   "unset",
   do_set_var  , 0,  0, LEVEL_ALIAS,   "alias",
   do_unset_var, 0,  0, LEVEL_ALIAS,   "unalias",
   do_echo     , 0,  0,          0 ,   "echo",
   do_source   , 0,  0,          0 ,   "source",
   do_mv       , 2,  0,          0 ,   "mv",
   do_cd       , 0,  0,          0 ,   "cd",
   do_rm       , 0,  0,          0 ,   "rm",
   do_mkdir    , 0,  0,          0 ,   "mkdir",
   do_history  , 0,  0,          0 ,   "history",
   do_mem      , 0,  0,          0 ,   "mem",
   do_cat      , 0,  0,          0 ,   "cat",
   do_dir      , 0,  0,          0 ,   "dir",
   do_dir      , 0,  0,         -1 ,   "devinfo",
   do_foreach  , 3,  0,          0 ,   "foreach",
   do_return   , 0,  0,          0 ,   "return",
   do_if       , 1,  1,          0 ,   "if",
   do_if       , 0,  1,          1 ,   "else",
   do_if       , 0,  1,          2 ,   "endif",
   do_label    , 1,  1,          0 ,   "label",
   do_goto     , 1,  0,          0 ,   "goto",
   do_failat   , 1,  0,          0 ,   "failat",
   do_checkbrk , 0,  0,          0 ,   "checkbreak",
   do_inc      , 1,  0,          1 ,   "inc",
   do_inc      , 1,  0,          -1,   "dec",
   do_input    , 1,  0,          0,    "input",
   do_ver      , 0,  0,          0,    "version",
   NULL        , 0,  0,          0 ,   NULL };

static char *
mpush(amount)
{
   struct MLIST *ml;

   ml = (struct MLIST *)malloc (amount + sizeof(*Mlist));
   ml->next = Mlist;
   Mlist = ml;
   return ((char *)Mlist + sizeof(*Mlist));
}

static char *
mpop()
{
   char *old = NULL;

   if (Mlist == NULL) {
      puts ("Internal MLIST error");
      fflush (stdout);
   } else {
      old = (char *)Mlist + sizeof(*Mlist);
      Free (Mlist);
      Mlist = Mlist->next;
   }
   return (old);
}

mrm()
{
   while (Mlist) {
      Free (Mlist);
      Mlist = Mlist->next;
   }
}

exec_command(base)
char *base;
{
   char *str;
   int i;
   if (!H_stack)
      add_history(base);
   strcpy (str = mpush(strlen(base) + 1), base);
   i = e_command(str);
   if (mpop() != str) {
      puts ("POP ERROR");
      fflush (stdout);
   }
   return (1);
}

static
e_command(base)
char *base;
{
   char *com, *start, *avline, *alias;
   char *s1, *s2, *s3, *s4;
   int flag = 0;
   int i, pcount, len, ccno;
loop:
   com = breakout (&base, &flag);
   if (*com == '\0') {
      if (flag & FL_EOL)
         return (1);
      goto loop;
   }
   alias = NULL;
   if ((ccno = find_command(com, F_EXACT)) < 0) {
      switch (flag & FL_MASK) {
      case FL_BANG:
         alias = get_history (com);
         replace_head (alias, base, flag);
         break;
      case FL_DOLLAR:
         alias = get_var (LEVEL_SET, com + 1);
         break;
      default:
         alias = get_var (LEVEL_ALIAS, com);
         break;
      }
      if (alias == NULL) {
         if ((ccno = find_command (com, F_ABBR)) < 0) {
            printf ("%s Command Not Found\n", com);
            return (-1);
         } else {
            goto good_command;
         }
      }

      if (*alias == '%')
         goto good_command;

      start = (!(flag & FL_EOC)) ? base : "";
      while (!(flag & FL_EOC)) {
         flag = FL_OVERIDE;
         breakout (&base, &flag);
      }

      com = mpush (strlen(alias) + strlen(start) + 2);
      strcpy (com, alias);
      strcat (com, " ");
      strcat (com, start);
      i = e_command (com);
      if (mpop() != com)
         puts ("ME BAE ERROR");
      if (i < 0)
         return (-1);
      if (flag & FL_EOL)
         return (1);
      goto loop;
   }
good_command:
   if (ccno < 0 || Command[ccno].stat == 0) {
      if (Disable) {
         while (!(flag & FL_EOC))
            breakout (&base, &flag);
         goto eocc;
      }
   }
   pcount = 0;
   i = pcount = 0;
   av[i] = mpush (strlen(com) + 1);
   ++pcount;
   strcpy (av[i++], com);
   while (!(flag & FL_EOC)) {
      com = breakout (&base, &flag);
      if (*com == '\0')
         continue;
      switch (flag & FL_MASK) {
      case FL_DOLLAR:
         av[i] = get_var (LEVEL_SET, com + 1);
         if (av[i] == NULL)
            av[i] = com;
         av[i] = strcpy(mpush (strlen(av[i]) + 1), av[i]);
         ++pcount;
         break;
      case FL_QUOTE:
      default:
         av[i] = com;
         break;
      }
      if (flag & FL_IDOLLAR) {
         for (s1 = av[i]; *s1 && *s1 != '$'; ++s1);
         if (*s1) {
            *s1 = '\0';
            s1 = get_var (LEVEL_SET, s1 + 1);
            if (s1) {
               register char *scr_str = mpush(strlen(av[i])+strlen(s1)+1);
               ++pcount;
               strcpy (scr_str, av[i]);
               strcat (scr_str, s1);
               av[i] = scr_str;
            }
         }
      }
      if (flag & FL_WILD) {   /*  av[i] has a wild card, expand it */
         int eac;
         char **eav, **ebase;

         eav = ebase = expand (av[i], &eac);   /* returns malloc'd av list */
         if (eav == NULL) {
            puts ("Null expansion");
            goto fail;
         }
         if (i + eac > MAXAV - 2) {
            free_expand (ebase);
            goto avovr;
         }
         for (; eac; --eac, ++eav) {
            av[i++] = strcpy(mpush(strlen(*eav)+1), *eav);
            ++pcount;
         }
         --i;
         free_expand (ebase);
      }
      if (++i > MAXAV - 2) {
avovr:
         puts ("AV overflow");
         goto fail;
      }
   }
   av[i] = NULL;
   ac = i;
   for (len = 0, i = 0; i < ac; ++i)
      len += strlen(av[i]) + 1;
   avline = mpush (len + 1);
   *avline = '\0';
   for (i = 0; i < ac; ++i) {
      strcat (avline, av[i]);
      if (i + 1 < ac)
         strcat (avline, " ");
   }
   if (*alias) {
      for (s2 = alias; *s2 && *s2 != ' '; ++s2);
      if (*s2) {
         *s2 = '\0';
         s3 = strcpy(mpush(strlen(next_word(avline))+1), next_word(avline));
         s4 = strcpy(malloc(strlen(alias)), alias + 1);
         set_var (LEVEL_SET, s4, s3);
         *s2 = ' ';
         s1 = strcpy(mpush(strlen(s2)+1), s2);
         i = e_command (s1);
         unset_var (LEVEL_SET, s4);
         free (s4);
         if (mpop() != s1)
            puts ("AL-LINE ERROR");
         mpop();
      }
   } else {
      i = -1;
      if (ac < Command[ccno].minargs + 1)
         ierror (NULL, 500);
      else
         i = (*Command[ccno].func)(avline, Command[ccno].val);
   }
   if (mpop() != avline) {
      puts ("AVLINE ERROR");
fail:
      i = -1;
   }
   while (pcount--)
      mpop();
   if (i < 0)
      return (i);
eocc:
   if (flag & FL_EOL)
      return (1);
   goto loop;
}

static char *
breakout(base, flag)
register int *flag;
char **base;
{
   register char *str, *scr;
   register int oflag = *flag;

loop:
   *flag = 0;
   str = *base;
   while (*str == ' ' || *str == 9)
      ++str;
   switch (*str) {
   case '\0':
      *flag = FL_EOC | FL_EOL;
      *base = str;
      return (str);
   case '*':
   case '?':
      *flag = FL_WILD;
      break;
   case ';':
      *flag = FL_EOC;
      *str = '\0';
      *base = str + 1;
      return (str);
   case '\"':
      *flag = FL_QUOTE;
      break;
   case '$':
      *flag = FL_DOLLAR;
      break;
   case '%':
      *flag = FL_PERCENT;
      break;
   case '!':
      *flag = FL_BANG;
      break;
   default:
      break;
   }
   scr = str;
   for (;;) {
      switch (*scr) {
      case '$':
         if (scr != str)
            *flag |= FL_IDOLLAR;
         ++scr;
         break;
      case '*':
      case '?':
         *flag |= FL_WILD;
         ++scr;
         break;
      case ' ':
      case 9:
         if (!(oflag & FL_OVERIDE))
            *scr = '\0';
         *base = scr + 1;
         return (str);
      case '\"':                    /* Quote */
         del_char(scr);
         while (*scr && *scr != '\"') {
            if (*scr == '\\') {
               del_char(scr);
               if (*scr)
                  ++scr;
            } else {
               ++scr;
            }
         }
         if (*scr == '\"')
            del_char(scr);
         break;
      case '\0':
         *flag |= FL_EOL | FL_EOC;
         *base = scr;
         return (str);
      case ';':
         *flag |= FL_EOC;
         *base = scr + 1;
         *scr = '\0';
         return (str);
      case '^':
         ++scr;
         if (*scr) {
            *(scr - 1) = *scr & 0x1f;
            del_char (scr);
         }
         break;
      case '\\':
         del_char(scr);
         if (*scr)
            ++scr;
         break;
      default:
         ++scr;
      }
   }
}

del_char(str)
register char *str;
{
   for (; *str; ++str)
      str[0] = str[1];
}

static
find_command(str, arg)
char *str;
{
   int i;
   int len = strlen(str);

   if (*str >= '0'  &&  *str <= '9')
      return (1);
   for (i = 0; Command[i].func; ++i) {
      if (strncmp (str, Command[i].name, len) == 0) {
         if (arg == F_ABBR)
            return (i);
         if (strcmp (str, Command[i].name) == 0)
            return (i);
         return (-1);
      }
   }
   if (arg == F_ABBR)
      return (0);
   return (-1);
}


