
/*
 * COMMAND.C
 *
 * 'c                single character
 * `string'          string of characters w/ embedded `' allowed!
 *
 * name arg arg      command name. The arguments are interpreted as strings
 *                   for the command.
 *
 * Any string arguments not part of a command are considered to be typed
 * text.
 *
 *
 */

#include "defs.h"

extern char *breakout();

short ComLineMode;

typedef struct {
   char *name;    /* command name                                       */
   short args;    /* # of arguments                                     */
   short sl;      /* 1 if stays within bounds of the current line       */
   int (*func)(); /* function                                           */
} COMM;

extern int do_map(), do_unmap(), do_up(), do_down(), do_left(), do_right(),
            do_return(), do_bs(), do_del(), do_esc(), do_downadd(),
            do_lastcolumn(), do_firstcolumn(), do_edit(), do_tab(),
            do_backtab(), do_save(), do_saveas(), do_deline(), do_insline(),
            do_top(), do_bottom(), do_source(), do_firstnb(),
            do_quit(), do_find(), do_pageup(), do_pagedown(),
            do_split(), do_goto(), do_screentop(), do_screenbottom(),
            do_join(), do_repeat(), do_tabstop(), do_insertmode(),
            do_block(), do_bdelete(), do_bcopy(), do_bmove(), do_bsave();

COMM Comm[] = {
   "esc",           0, 1, do_esc,
   "escimm",        1, 0, do_esc,
   "last",          0, 1, do_lastcolumn,
   "first",         0, 1, do_firstcolumn,
   "downadd",       0, 0, do_downadd,
   "map",           2, 0, do_map,
   "unmap",         1, 0, do_unmap,
   "up",            0, 0, do_up,
   "down",          0, 0, do_down,
   "pageup",        0, 0, do_pageup,
   "pagedown",      0, 0, do_pagedown,
   "left",          0, 1, do_left,
   "right",         0, 1, do_right,
   "return",        0, 1, do_return,    /* special meaning in command line mode */
   "bs",            0, 1, do_bs,
   "back",          0, 1, do_bs,
   "del",           0, 1, do_del,
   "insline",       0, 0, do_insline,
   "deline",        0, 0, do_deline,
   "tab",           0, 1, do_tab,
   "backtab",       0, 1, do_backtab,
   "newfile",       1, 0, do_edit,
   "insfile",       1, 0, do_edit,
   "saveold",       0, 0, do_save,
   "saveas",        1, 0, do_saveas,
   "top",           0, 0, do_top,
   "bottom",        0, 0, do_bottom,
   "source",        1, 0, do_source,
   "firstnb",       0, 1, do_firstnb,
   "quit",          0, 0, do_quit,
   "find",          1, 0, do_find,
   "next",          0, 0, do_find,
   "prev",          0, 0, do_find,
   "split",         0, 0, do_split,
   "join",          0, 0, do_join,
   "repeat",        2, 1, do_repeat,
   "tabstop",       1, 1, do_tabstop,
   "insertmode",    1, 1, do_insertmode,
   "screentop",     0, 0, do_screentop,
   "screenbottom",  0, 0, do_screenbottom,
   "goto",          1, 0, do_goto,
   "block",         0, 0, do_block,
   "unblock",       0, 0, do_block,
   "bcopy",         0, 0, do_bcopy,
   "bmove",         0, 0, do_bmove,
   "bdelete",       0, 0, do_bdelete,
   "bsave",         1, 0, do_bsave,
   NULL, 0, 0, NULL
};


do_command(str)
char *str;
{
   char *arg;
   char quoted;
   short i, j;

   while (arg = breakout(&str, &quoted)) {
      if (quoted) {
         text_write(arg);
         continue;
      }
      for (i = 0; arg[i]; ++i) {
          if (arg[i] >= 'A' && arg[i] <= 'Z')
              arg[i] = arg[i] + 'a' - 'A';
      }
      for (i = 0; Comm[i].name; ++i) {
         if (strcmp(arg, Comm[i].name) == 0) {
            av[0] = Comm[i].name;
            for (j = 1; j <= Comm[i].args; ++j) {
               av[j] = breakout(&str, &quoted);
               if (!av[j]) {
                  title("Bad argument");
                  return(0);
               }
            }
            if (Comm[i].sl || !ComLineMode)
               (*Comm[i].func)();
            goto loop;
         }
      }
      title("Unknown Command");
      return(0);
loop:
   }
   return(1);
}

do_source()
{
   char buf[256];
   long fi;

   if (fi = xopen(av[1], "r", 256)) {
      while (xgets(fi, buf, 255))
         do_command(buf);
      xclose(fi);
   } else {
      if (av[0])
         title("File not found");
   }
}


do_quit()
{
   extern char Quitflag;

   Quitflag = 1;
}

/*
 * repeat X command
 *
 * Since repeat takes up 512+ stack, it should not be nested more than
 * twice.
 *
 * (if X is not a number it can be abbr. with 2 chars)
 *
 * X =  N     -number of repeats
 *      line  -current line # (lines begin at 1)
 *      lbot  -#lines to the bottom, inc. current
 *      cleft -column # (columns begin at 0)
 *              (thus is also chars to the left)
 *      cright-#chars to eol, including current char
 *      tr    -#char positions to get to next tab stop
 *      tl    -#char positions to get to next backtab stop
 */

#define SC(a,b) ((a)<<8|(b))

do_repeat()
{
   char *ptr = av[1];
   char buf1[256];
   char buf2[256];
   long n;

   strcpy(buf1, av[2]);
   switch((ptr[0]<<8)+ptr[1]) {
   case SC('l','i'):
      n = text_lineno();
      break;
   case SC('l','b'):
      n = text_lines() - text_lineno() + 1;
      break;
   case SC('c','l'):
      n = text_colno();
      break;
   case SC('c','r'):
      n = text_cols() - text_colno();
      break;
   case SC('t','r'):
      n = text_tabsize()-(text_colno() % text_tabsize());
      break;
   case SC('t','l'):
      n = text_colno() % text_tabsize();
      if (n == 0)
         n = text_tabsize();
      break;
   default:
      n = atoi(av[1]);
      break;
   }
   while (n > 0) {
      strcpy(buf2, buf1);
      do_command(buf2);
      --n;
   }
}


char *
breakout(ptr, quoted)
char **ptr, *quoted;
{
   char *str = *ptr;
   char *base = str;

   *quoted = 0;
   while (*str == ' ')
      ++str;
   if (!*str)
      return(NULL);
   if (*str == '\'') {
      if (str[1]) {
         *quoted = 1;
         base = str + 1;
         if (str[2])
            ++str;
         *str = '\0';
         *ptr = str;
         return(base);
      }
      return(NULL);
   }
   if (*str == '`') {
      short count = 1;
      base = ++str;
      while (*str && count) {
         if (*str == '`')
            ++count;
         if (*str == '\'')
            --count;
         ++str;
      }
      if (count == 0) {
         --str;
         *quoted = 1;
         *str = '\0';
         *ptr = str + 1;
         return(base);
      }
   }
   base = str;
   while (*str && *str != ' ')
      ++str;
   if (*str) {
      *str = '\0';
      *ptr = str + 1;
      return(base);
   }
   *ptr = str;
   return(base);
}
