
/*
 * COMMAND.C
 *
 *	(C)Copyright 1987 by Matthew Dillon, All Rights Reserved
 *
 * )c		     single character (typing)
 * 'c                single character (typing)
 * `string'          string of characters w/ embedded `' allowed!
 * (string)		same thing w/ embedded () allowed!
 *
 * name arg arg      command name. The arguments are interpreted as strings
 *		     for the command.
 *
 * $scanf	     macro insert variable (currently only $scanf)
 *
 * Any string arguments not part of a command are considered to be typed
 * text.
 */

#include "defs.h"
#include <stdio.h>

extern char *breakout();

typedef struct {
   char *name;	    /* command name	  */
   ubyte args;
   ubyte sl;
   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_page(),	    do_savetabs(),
	    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(),     do_wleft(),     do_wright(),    do_remeol(),
	    do_savemap(),   do_toggle(),    do_if(),	    do_tlate(),
	    do_bsource(),   do_findr(),     do_findstr(),   do_newwindow(),
	    do_windowparm(),do_resize(),    do_margin(),    do_wordwrap(),
	    do_reformat(),  do_execute(),   do_chfilename(),do_scrollup(),
	    do_scrolldown(),do_recall(),    do_scanf(),     do_iconify(),
	    do_tomouse(),   do_refs();


/*
 *  WLEFT/WRIGHT will check command line mode themselves, and thus can
 *  be marked sl=1 even though they can change the line number.
 *
 *  No more than 255 commands may exist unless you change the type of hindex[]
 *
 *  Command names MUST be sorted by their first character
 */

unsigned char hindex[26];   /*	alpha hash into table	*/

	/*	  args flags	*/

COMM Comm[] = {
    "back",          0, 1, do_bs,
    "backtab",       0, 1, do_backtab,
    "bcopy",         0, 0, do_bcopy,
    "bdelete",       0, 0, do_bdelete,
    "block",         0, 0, do_block,    /* checks com name for mode */
    "bmove",         0, 0, do_bmove,
    "bottom",        0, 0, do_bottom,
    "bs",            0, 1, do_bs,
    "bsave",         1, 0, do_bsave,
    "bsource",       0, 0, do_bsource,
    "chfilename",    1, 0, do_chfilename,
    "del",           0, 1, do_del,
    "deline",        0, 0, do_deline,
    "down",          0, 0, do_down,
    "downadd",       0, 0, do_downadd,
    "esc",           0, 1, do_esc,
    "escimm",        1, 2, do_esc,
    "execute",       1, 0, do_execute,
    "find",          1, 0, do_find,     /* checks com name for mode */
    "findr",         2, 0, do_findr,    /* checks com name for mode */
    "findstr",       1, 1, do_findstr,  /* checks com name for mode */
    "first",         0, 1, do_firstcolumn,
    "firstnb",       0, 1, do_firstnb,
    "goto",          1, 0, do_goto,
    "height",        1, 1, do_windowparm,
    "iconify",       0, 0, do_iconify,
    "if",            2, 0, do_if,
    "ifelse",        3, 0, do_if,
    "insertmode",    1, 1, do_insertmode,
    "insfile",       1, 0, do_edit,
    "insline",       0, 0, do_insline,
    "join",          0, 0, do_join,
    "last",          0, 1, do_lastcolumn,
    "left",          0, 1, do_left,
    "leftedge",      1, 1, do_windowparm,
    "map",           2, 0, do_map,
    "margin",        1, 1, do_margin,
    "newfile",       1, 0, do_edit,     /* checks com name for mode */
    "newwindow",     0, 0, do_newwindow,
    "next",          0, 0, do_find,
    "nextr",         0, 0, do_findr,
    "pagedown",      0, 0, do_page,
    "pageset",       1, 0, do_page,
    "pageup",        0, 0, do_page,
    "prev",          0, 0, do_find,
    "prevr",         0, 0, do_findr,
    "quit",          0, 0, do_quit,
    "recall",        0, 1, do_recall,
    "ref",           0, 0, do_refs,
    "reformat",      0, 0, do_reformat,
    "remeol",        0, 1, do_remeol,
    "repeat",        2, 1, do_repeat,
    "repstr",        1, 1, do_findstr,
    "resettoggle",   1, 1, do_toggle,
    "resize",        2, 0, do_resize,
    "return",        0, 1, do_return,   /* special meaning in command line mode */
    "right",         0, 1, do_right,
    "saveas",        1, 0, do_saveas,
    "savemap",       1, 0, do_savemap,  /* checks com name for mode */
    "saveold",       0, 0, do_save,
    "savesmap",      1, 0, do_savemap,
    "savetabs",      1, 0, do_savetabs,
    "scanf",         1, 1, do_scanf,
    "screenbottom",  0, 0, do_screenbottom,
    "screentop",     0, 0, do_screentop,
    "scrollup",      0, 0, do_scrollup,
    "scrolldown",    0, 0, do_scrolldown,
    "settoggle",     1, 1, do_toggle,
    "source",        1, 0, do_source,
    "split",         0, 0, do_split,
    "tab",           0, 1, do_tab,
    "tabstop",       1, 1, do_tabstop,
    "tlate",         1, 0, do_tlate,
    "tmpheight",     1, 1, do_windowparm,
    "tmpwidth",      1, 1, do_windowparm,
    "toggle",        1, 1, do_toggle,
    "tomouse",       0, 0, do_tomouse,
    "top",           0, 0, do_top,
    "topedge",       1, 1, do_windowparm,
    "unblock",       0, 0, do_block,
    "unmap",         1, 0, do_unmap,
    "up",            0, 0, do_up,
    "while",         2, 0, do_if,
    "width",         1, 1, do_windowparm,
    "wleft",         0, 1, do_wleft,
    "wordwrap",      1, 1, do_wordwrap,
    "wright",        0, 1, do_wright,
    NULL, 0, 0, NULL
};

init_command()
{
    register short hi;
    register COMM *comm;

    hi = sizeof(Comm)/sizeof(Comm[0]) - 2;
    comm = Comm + hi;

    while (hi >= 0) {
	hindex[comm->name[0] - 'a'] = hi;
	--hi;
	--comm;
    }
}

do_command(str)
char *str;
{
    register char *arg;
    char quoted;
    register short i, j;
    static int level;

    if (++level > 20) {
	title("Recursion Too Deep!");
	--level;
	return(0);
    }
    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] += 'a' - 'A';
	}
	if (arg[0] >= 'a' && arg[0] <= 'z') {
	    register COMM *comm = &Comm[hindex[arg[0]-'a']];
	    for (; comm->name && comm->name[0] == arg[0]; ++comm) {
		if (strcmp(arg, comm->name) == 0) {
		    av[0] = (ubyte *)comm->name;
		    for (j = 1; j <= comm->args; ++j) {
			av[j] = (ubyte *)breakout(&str, &quoted);
			if (!av[j]) {
			    title("Bad argument");
			    --level;
			    return(0);
			}
			if (av[j][0] == '$')
			    av[j] = (ubyte *)String;
		    }
		    if ((comm->sl&1) || !Comlinemode) {
			if (comm->sl & 2) {
			    if (Partial)
				free(Partial);
			    Partial = (char *)malloc(strlen(str)+1);
			    strcpy(Partial, str);
			    str += strlen(str);     /*	skip string */
			}
			(*comm->func)(-1);
		    }
		    if (Abortcommand)
			goto fail;
		    goto loop;
		}
	    }
	}

	/* Command not found, check for macro	*/

	{
	    char *str;
	    int ret;
	    str = keyspectomacro(arg);
	    if (str) {
		str = (char *)strcpy(malloc(strlen(str)+1), str);
		ret = do_command(str);
		free(str);
		if (ret)
		    goto loop;
		goto fail;
	    }
	}

	title("Unknown Command");
fail:
	--level;
	return(0);
loop:
	;
    }
    --level;
    return(1);
}


do_source()
{
    char buf[256];
    long xfi;
    register char *str;

    if (xfi = xfopen(av[1], "r", 512)) {
	while (xfgets(xfi, buf, 256) >= 0) {
	    if (buf[0] == '#')
		continue;
	    for (str = buf; *str; ++str) {
		if (*str == 9)
		    *str = ' ';
	    }
	    do_command(buf);
	}
	xfclose(xfi);
    } else {
	if (av[0])
	    title("File not found");
    }
}


do_quit()
{
    extern char Quitflag;

    Quitflag = 1;
}

do_execute()
{
    Execute(av[1], 0, 0);
}

/*
 * 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()
{
    ubyte *ptr = av[1];
    char buf1[256];
    char buf2[256];
    unsigned long n;

    breakreset();
    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);
	if (do_command(buf2) == 0 || breakcheck()) {
	    Abortcommand = 1;
	    break;
	}
	--n;
    }
}


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

    *quoted = 0;
    while (*str == ' ')
	++str;
    if (!*str)
	return(NULL);
    if (*str == '\'' || *str == ')') {
	if (str[1]) {
	    *quoted = 1;
	    base = str + 1;
	    if (str[2])
		++str;
	    *str = '\0';
	    *ptr = str;
	    return(base);
	}
	return(NULL);
    }
    if (*str == '`' || *str == '(') {
	short count = 1;
	char opc = *str;
	char clc = (opc == '(') ? ')' : '\'';

	base = ++str;
	while (*str && count) {
	    if (*str == opc)
		++count;
	    if (*str == clc)
		--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);
}


