diff options
Diffstat (limited to 'usr.bin/more/ncommand.c')
-rw-r--r-- | usr.bin/more/ncommand.c | 1505 |
1 files changed, 0 insertions, 1505 deletions
diff --git a/usr.bin/more/ncommand.c b/usr.bin/more/ncommand.c deleted file mode 100644 index b46d9ee..0000000 --- a/usr.bin/more/ncommand.c +++ /dev/null @@ -1,1505 +0,0 @@ -/*- - * Copyright (c) 1999 Timmy M. Vanderhoek - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - -/* - * These functions handle evaluation of primitive commands. In general, - * commands either come from macro.h as it expands user input, or - * directly from a .morerc file (in which case only a limited set of - * commands is valid. - * - * Commands are matched by command() against a command table. The rest - * of the command line string passed to command() is then passed to a - * function corresponding to the given command. The specific command - * function evaluates the remainder of the command string with the help - * of getstr() and getint(), both of which also handle variable expansion - * into a single word. Specific command functions should not try grokking - * the command string by themselves. - * - * A command and its arguments are terminated by either a NUL or a ';'. - * This is recognized by both getstr() and getint(). Specific command - * functions return a pointer to the end of the command (and its arguments) - * thus allowing command() to accept commands that are chained together - * by semicolons. If a specific command fails, it returns NULL preventing - * any proceeding commands (chained together with ';') from being parsed. - * This can be considered as a feature. - * - * All variable-access functions and variable state are internal to - * ncommand.c. The sole exceptions are setvar() and setvari(). - */ - -#include <sys/param.h> - -#include <assert.h> -#include <ctype.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - -#include "less.h" -#include "pathnames.h" - -static int getint __P((long *numb, const char **line)); - -static getstr_free(); -static void **getstr_raisectxt(); - -/* The internal command table. */ - -static const char *cscroll(), *cquit(), *cerror(), *ceval(), *cset(), - *cflush(), *cmacro(), *caskfile(), *cusercom(), - *ctags(), *chscroll(), *cgomisc(), *cgoend(), *csearch(), - *cstat(), *cdeftog(), *ccondition(), *chelp(), *cfile(), - *cfile_list(), *cedit(), *cmark(), *creadrc(); - -/* An enum identifying each command */ -enum cident { - DEFTOG, /* Initialize toggle values */ - EVAL, /* Evaluate a subexpression */ - SET, /* Set a variable */ - MACRO, /* Create a new macro */ - ERROR, /* Print a notification message */ - CONDITION, /* Condition evaluation of (almost) _all_ commands */ - CONDITION_N, /* CONDITION with an inverse truth table */ - CONDITION_TOGGLE,/* Switch to the reverse sense of the last condition */ - USERCOM, /* Get the user to type in a direct command */ - READRC, /* Read-in a named rc file */ - QUIT, /* Quit */ - HELP, /* Help */ - FLUSH, /* Flush file buffer and friends */ - REPAINT, /* Redraw the screen (useful if it got trashed) */ - FORW_SCROLL, /* Scroll forward N lines */ - BACK_SCROLL, /* Scroll backward N lines */ - FORW, /* Jump or scroll forward N lines */ - BACK, /* Jump or scroll backwards N lines */ - LSCROLL, /* Scroll horizontally leftwards */ - RSCROLL, /* Scroll horizontally to the right */ - GOLINE, /* Goto line number N */ - GOPERCENT, /* Goto percent N of the file */ - GOEND, /* Goto the end of the file */ - EDIT, /* Edit the current file, using getenv(EDITOR) */ - ASKFILE, /* Ask for a different, new file */ - CFILE, /* Page/view the N'th next or prev file */ - FILE_LIST, /* List the files that CFILE moves around in */ - STAT, /* List detailed file statistics in prompt */ - MAGICASKSEARCH, /* Ask for a regexp search string */ - SEARCH, /* Search for a regexp */ - RESEARCH, /* Search for the next N'th occurrence */ - SETMARK, /* Set a bookmark to the current position */ - GOMARK, /* Goto a previously set bookmark */ - ASKFTAG, /* Ask for a tag to goto */ - NEXTFTAG, /* Move forward N in the tag queue */ - PREVFTAG, /* Move backwards N in the tag queue */ -}; - -static struct ctable { - const char *cname; - enum cident cident; - const char * (*cfunc)(enum cident, const char *args); -} ctable[] = { - { "deftog", DEFTOG, cdeftog }, - { "eval", EVAL, ceval }, - { "set", SET, cset }, - { "macro", MACRO, cmacro }, - { "error", ERROR, cerror }, - { "condition", CONDITION, ccondition }, - { "condition_!", CONDITION_N, ccondition }, - { "condition_toggle", CONDITION_TOGGLE, ccondition }, - { "condition_else", CONDITION_TOGGLE, ccondition }, - { "usercom", USERCOM, cusercom }, - { "readrc", READRC, creadrc }, - { "quit", QUIT, cquit }, - { "help", HELP, chelp }, - { "flush", FLUSH, cflush }, - { "repaint", REPAINT, cflush }, - { "forw_scroll", FORW_SCROLL, cscroll }, - { "back_scroll", BACK_SCROLL, cscroll }, - { "forw", FORW, cscroll }, - { "back", BACK, cscroll }, - { "rscroll", RSCROLL, chscroll }, - { "lscroll", LSCROLL, chscroll }, - { "goline", GOLINE, cgomisc }, - { "gopercent", GOPERCENT, cgomisc }, - { "goend", GOEND, cgoend }, - { "edit", EDIT, cedit }, - { "askfile", ASKFILE, caskfile }, - { "file", CFILE, cfile }, - { "file_list", FILE_LIST, cfile_list }, - { "stat", STAT, cstat }, - { "magicasksearch", MAGICASKSEARCH, csearch }, - { "search", SEARCH, csearch }, - { "research", RESEARCH, csearch }, - { "setmark", SETMARK, cmark }, - { "gomark", GOMARK, cmark }, - { "asktag", ASKFTAG, ctags }, - { "nexttag", NEXTFTAG, ctags }, - { "prevtag", PREVFTAG, ctags }, -}; - - -/* I believe this is just for cosmetic purposes. */ -#define CMD_EXEC lower_left(); flush() - - -/* - * Prototypes are for people who can't program. - */ - - -/* - * The main command string evaluator. Returns -1 if an error occurred - * in the command or in executing the command, returns 0 otherwise. If an - * error occurs while evaluating a command line containing multiple commands, - * commands after the error are not processed. Multiple commands may be - * separated by ';' or '\n'. (Multiple commands may also be separated by - * a ' ', but this is really a bug...) - */ -int -command(line) - const char *line; -{ - struct ctable *i; - -donextcommand: - - while (isspace(*line) || *line == ';' || *line == '\n') line++; - if (!*line) - return 0; - - for (i = ctable; i != ctable + sizeof(ctable) / sizeof(struct ctable); - i++) { - if (!strncmp(i->cname, line, strlen(i->cname)) && - (line[strlen(i->cname)] == ' ' || - line[strlen(i->cname)] == ';' || - line[strlen(i->cname)] == '\0')) { - /* Found a match! */ - void **ctxt; - CMD_EXEC; - ctxt = getstr_raisectxt(); - line = i->cfunc (i->cident, line + strlen(i->cname)); - getstr_free(ctxt); - if (!line) - return -1; /* error evaluating command */ - goto donextcommand; - } - } - - SETERRSTR(E_BOGCOM, "invalid command: ``%s''", line); - (void) command("condition true"); - return -1; -} - - -/***************************************************************************** - * - * Functions to help specific command functions to parse their arguments. - * - * The three functions here, getstr(), getint(), and gettog() could in theory - * have vastly different concepts of what a number is, and what a string is, - * etc., but in practice they don't. - */ - -static char *readvar(); - -#define NCTXTS 128 -void *getstr_ctxts[NCTXTS]; /* could easily be made dynamic... */ -void **getstr_curctxt = getstr_ctxts; - -/* - * Read a single argument string from a command string. This understands - * $variables, "double quotes", 'single quotes', and backslash escapes - * for \\, \$, \n, \e, \t, and \". A string may be delimited by double - * quotes or spaces, not both (duh). It may be worthwhile to add - * another quotation style in which arithmetic expressions are expanded. - * Currently an arithmetic expression is expanded iff it is the only - * component of the string. - * - * Returns a pointer to the beginning of the string or NULL if it was unable to - * read a string. The line is modified to point somewhere between the end of - * the command argument just read-in and the beginning of the next command - * argument (if any). The returned pointer will be free()'d by calling - * getstr_free(). - */ -static char * -getstr(line) - const char **line; /* Where to look for the return string */ -{ - int doquotes = 0; /* Doing a double-quote string */ - char *retr; - - if (getstr_curctxt - getstr_ctxts == NCTXTS) { - SETERRSTR(E_COMPLIM, - "compile-time limit exceeded: command contexts"); - return NULL; /* wouldn't be able to register return pointer */ - } - - while (isspace(**line)) (*line)++; - if (**line == '\'') { - /* Read until closing quote or '\0'. */ - char *nextw = retr = malloc(1); - const char *c = ++(*line); - int l; - for (; *c; c++) { - if (*c == '\'') { - if (c[-1] == '\\') { - nextw[-1] = '\''; - continue; - } else { - *nextw = '\0'; - *line = c + 1; - *getstr_curctxt = retr; - getstr_curctxt++; - return retr; - } - } - l = nextw - retr; - /* XXX How many realloc()'s can you make per second? */ - if (!(retr = reallocf(retr, c - *line + 250))) { - SETERR (E_MALLOC); - return NULL; - } - nextw = retr + l; - *nextw = *c; - nextw++; - } - SETERR(E_CANTPARSE); - return NULL; - } - if (**line == '"') { - doquotes = 1; - (*line)++; - } - if (**line == '(') { - /* An arithmetic expression instead of a string... Well, I - * guess this is valid. See comment leading this function. */ - long n; - if (getint(&n, line)) - return NULL; - retr = NULL; - asprintf(&retr, "%ld", n); - if (!retr) - SETERR (E_MALLOC); - *getstr_curctxt = retr; - getstr_curctxt++; - return retr; - } - - if (!FMALLOC(1, retr)) - return NULL; - *retr = '\0'; - for (;;) { - char *c, hack[2]; - - switch (**line) { - case '\\': - switch (*(*line + 1)) { - case '\\': case '$': case '\'': - case ' ': case ';': - hack[0] = *(*line + 1); - hack[1] = '\0'; - c = hack; - (*line) += 2; - break; - case 't': - c = "\t"; - (*line) += 2; - break; - case 'n': - c = "\n"; - (*line) += 2; - break; - case 'e': - c = "\e"; - (*line) += 2; - break; - case '"': - if (doquotes) { - c = "\""; - (*line) += 2; - break; - } else - ; /* fallthrough */ - default: - c = "\\"; - (*line)++; - break; - } - break; - case '$': - (*line)++; - if (!(c = readvar(line))) { - free (retr); - return NULL; - } - break; - case ' ': case '\t': case ';': - if (!doquotes) { - doquotes = 1; - case '"': - if (doquotes) { - /* The end of the string */ - (*line)++; - case '\0': - *getstr_curctxt = retr; - getstr_curctxt++; - return retr; - } - } - /* fallthrough */ - default: - hack[0] = **line; - hack[1] = '\0'; - c = hack; - (*line)++; - break; - } - - retr = reallocf(retr, strlen(retr) + strlen(c) + 1); - if (!retr) { - SETERR (E_MALLOC); - return NULL; - } - strcat(retr, c); - } -} - -/* - * Returns a new context that should be passed to getstr_free() so that - * getstr_free() only free()'s memory from that particular context. - */ -static void ** -getstr_raisectxt() -{ - return getstr_curctxt; -} - -/* - * Calls free() on all memory from context or higher. - */ -static -getstr_free(context) - void **context; -{ - while (getstr_curctxt != context) { - getstr_curctxt--; - free (*getstr_curctxt); - } -} - -/* - * Reads an integer value from a command string. Typed numbers must be - * in base10. If a '(' is found as the first character of the integer value, - * then getint() will read until a closing ')' unless interupted by an - * end-of-command marker (error). The parentheses are expected to contain a - * simple arithmetic statement involving only one '*', '/', etc. operation. The - * rightmost digit or the closing parenthesis should be followed by either a - * space or an end-of-command marker. - * - * Returns 0 on success, -1 on failure. The line will be modified to just - * after the last piece of text parsed. - * - * XXX We may add support for negative numbers, someday... - */ -static int -getint(numb, line) - long *numb; /* The read-in number is returned through this */ - const char **line; /* The command line from which to read numb */ -{ - long n; - int j; - char *p; - const char *t; - - while (isspace(**line)) (*line)++; - - switch (**line) { - case '(': - (*line)++; - if (getint(numb, line)) - return -1; - while (isspace(**line)) (*line)++; - j = **line; - (*line)++; - if (j == ')') - return 0; - if (**line == '=' && (j == '!' || j == '=') - || j == '&' && **line == '&' || j == '|' && **line == '|') - j = (j << 8) + *((*line)++); - if (getint(&n, line)) - return -1; - while (isspace(**line)) (*line)++; - if (**line != ')') { - SETERRSTR (E_BADMATH, - "missing arithmetic close parenthesis"); - return -1; - } else - (*line)++; - switch (j) { - case ('!' << 8) + '=': - *numb = *numb != n; - return 0; - case ('=' << 8) + '=': - *numb = *numb == n; - return 0; - case ('&' << 8) + '&': - *numb = *numb && n; - return 0; - case ('|' << 8) + '|': - *numb = *numb || n; - return 0; - case '+': - *numb += n; - return 0; - case '-': - *numb -= n; - return 0; - case '*': - *numb *= n; - return 0; - case '/': - if (n == 0) - *numb = 1; - else - *numb /= n; - return 0; - default: - SETERRSTR (E_BADMATH, - "bad arithmetic operator: ``%c''", j); - return -1; - } - case '$': - t = (*line)++; - if (!(p = readvar(line))) - return -1; - if (!isdigit(*p)) { - SETERRSTR (E_BADMATH, - "non-number found (``%s'') " - "after expanding variable at ``%s''", p, t); - return -1; - } - *numb = atol(p); - return 0; - case '9': case '0': case '8': case '1': case '7': case '2': case '6': - case '3': case '5': case '4': - *numb = atol(*line); - while (isdigit(**line)) (*line)++; - return 0; - case '"': case '\'': - /* Uh-oh. It's really a string. We'll go through getstr() - * and hope for the best, but this isn't looking good. */ - if (!(p = getstr(line))) - return -1; - *numb = atol(p); - return 0; - default: - SETERRSTR (E_BADMATH, - "non-number found, number expected, before parsing ``%s''", - *line); - return -1; - } -} - -/* - * Read an argument from the command string and match that argument against - * a series of legitimate values. For example, - * - * command <<opt0|opt1|opt2>> - * - * This command by be given to the command() processor as a variant of either - * "command opt1" or "command 4", both of which will cause this function to - * return the value 1. This function returns -1 on failure. - * - * Note that an option (eg. "opt1") must _not_ start with a digit!! - */ -static int -gettog(const char **line, int nopts, ...) -{ - char *str; - int n; - va_list opts; - - if (!(str = getstr(line))) - return -1; - - if (isdigit(*str)) { - n = atol(str) % nopts; - return n; - } - - va_start(opts, nopts); - for (n=0; n < nopts; n++) { - if (!strcasecmp(str, va_arg(opts, const char *))) { - va_end(opts); - return n; - } - } - va_end(opts); - SETERR (E_NOTOG); /* XXX would be nice to list valid toggles... */ - return -1; -} - -/* - * A companion function for gettog(). Example, - * - * optnumb = gettog(&args, 3, "opt1", "opt2", "opt3"); - * settog("_lastoptnumb", optnumb, "opt1", "opt2", "opt3"); - * - * And the variable named _lastoptnumb_s will be set to one of "opt1", "opt2", - * or "opt3" as per the value of optnumb. The variable _lastoptnumb_n will - * also be set to a corresponding value. The optnumb argument had better - * be within the correct range (between 0 and 2 in the above example)!! - */ -settog(const char *varname, int optval, int nargs, ...) -{ - va_list opts; - char *s; - int optval_orig = optval; - - assert(optval < nargs); assert(optval >= 0); - if (!(s = malloc(strlen(varname) + 3))) - return; - strcpy (s, varname); - va_start(opts, nargs); - for (; optval; optval--) va_arg(opts, const char *); - s[strlen(varname)] = '_'; - s[strlen(varname) + 1] = 's'; - s[strlen(varname) + 2] = '\0'; - (void) setvar(s, va_arg(opts, const char *)); - s[strlen(varname) + 1] = 'n'; - (void) setvari(s, (long) optval_orig); - clear_error(); - va_end(opts); - free(s); -} - -/* - * Read {text} and return the string associated with the variable named - * <<text>>. Returns NULL on failure. - */ -static char * -readvar(line) - char **line; -{ - int vlength; - char *vstart; - static char *getvar(); - - if (**line != '{') { - SETERR (E_BADVAR); - return NULL; - } - (*line)++; - for (vlength = 0, vstart = *line; **line && - (isprint(**line) && **line != '{' && **line != '}'); (*line)++) - vlength++; - if (**line != '}' || vlength == 0) { - SETERRSTR (E_BADVAR, - "bad character ``%c'' in variable ``%.*s''", **line, - vlength, vstart); - return NULL; - } - (*line)++; - return getvar(vstart, vlength); -} - - -/***************************************************************************** - * - * Track variables. - * - */ - -static struct vble { - struct vble *next; - char *name; - char *value; -} *vble_l; /* linked-list of existing variables */ - -/* - * Return a pointer to the string that variable var represents. Returns - * NULL if a match could not be found and sets erreur. The returned - * pointer is valid until any calls to setvar() or until a call to - * getstr_free() frees the current context. - */ -static const char * -getvar(var, len) - char *var; - int len; /* strncmp(var, varmatch, len); is used to match variables */ -{ - struct vble *i; - char tcap[3]; - char *s, *retr; - char *gettermcap(); - - if (sscanf (var, "_termcap_%2s", tcap) == 1 && - len == sizeof "_termcap_XX" - 1) { - /* Magic termcap variable */ - if ((s = gettermcap(tcap))) { - if (getstr_curctxt - getstr_ctxts == NCTXTS) { - SETERRSTR(E_COMPLIM, - "compile-time limit exceeded: " - "command contexts"); - return NULL; - } - if (!FMALLOC(strlen(s) + 1, retr)) - return NULL; - strcpy (retr, s); - *getstr_curctxt = retr; - getstr_curctxt++; - return retr; - } else { - return ""; - } - } - - for (i = vble_l; i; i = i->next) { - if (!strncasecmp (i->name, var, len)) - return i->value; - } - - SETERRSTR (E_BADVAR, "variable ``%.*s'' not set", len, var); - return NULL; -} - -/* - * Set variable var to val. Both var and val may be destroyed by the - * caller after setvar(). - * - * Returns -1 on failure, 0 on success. - */ -int -setvar(var, val) - char *var; /* variable to set */ - char *val; /* value to set variable to */ -{ - struct vble *i, *last; - char *var_n, *val_n; - char *c; - - for (c = var; *c && (isprint(*c) && *c != '{' && *c != '}'); c++) ; - if (*c) { - SETERRSTR (E_BADVAR, - "bad character ``%c'' in variable ``%s''", *c, var); - return -1; - } - - for (i = vble_l; i; last = i, i = i->next) { - if (!strcasecmp (i->name, var)) { - if (!FMALLOC(strlen(val) + 1, val_n)) - return -1; - free(i->value); - i->value = val_n; - strcpy(i->value, val); - return 0; - } - } - - /* Need to add another variable to the list vble_l */ - if (!FMALLOC(strlen(var) + 1, var_n)) - return -1; - if (!FMALLOC(strlen(val) + 1, val_n)) { - free(var_n); - return -1; - } - if (!vble_l) { - if (!FMALLOC(sizeof(struct vble), vble_l)) { - free(var_n), free(val_n); - return -1; - } - i = vble_l; - } else { - if (!FMALLOC(sizeof(struct vble), last->next)) { - free(var_n), free(val_n); - return -1; - } - i = last->next; - } - i->next = NULL; - i->name = var_n; strcpy(i->name, var); - i->value = val_n; strcpy(i->value, val); - return 0; -} - -/* - * Set or reset, as appropriate, variable var to val. - */ -int -setvari(var, val) - const char *var; - long val; -{ - char n[21]; /* XXX */ - snprintf(n, sizeof(n), "%ld", val); - n[20] = '\0'; - setvar(var, n); -} - - -/***************************************************************************** - * - * Specific command functions. These aren't actually individual functions, - * since using a gigantic switch statement is faster to type, but they - * pretend to be individual functions. - * - */ - -int condition_eval = 1; /* false if we just parse commands, but do nothing */ - -#define ARGSTR(v) do { \ - if (!((v) = getstr(&args))) return NULL; \ - } while (0) -#define ARGNUM(v) do { \ - if (getint(&(v), &args)) return NULL; \ - } while (0) -/* semi-gratuitous use of GNU cpp extension */ -#define ARGTOG(v, n, togs...) do { \ - if (((v) = gettog(&args, n, togs)) == -1) \ - return NULL; \ - } while (0) -#define ENDPARSE do { \ - if (!condition_eval) return args; \ - } while (0) - -/* - * deftog - * - * Set all toggle options to their default values, provided the toggle option - * is registered with this function. This command is meant to be used at the - * beginning of the startup command list. - */ -static const char * -cdeftog(cident, args) - enum cident cident; - const char *args; -{ - extern int horiz_off, wraplines; - - ENDPARSE; - settog("_statprompt", 1, 2, "on", "off"); - settog("_ls_direction", 0, 2, "forw", "back"); - settog("_ls_sense", 0, 2, "noinvert", "invert"); - setvari("_curhscroll", (long) horiz_off); - settog("_wraplines", wraplines, 2, "off", "on"); - setvar("_ls_regexp", ""); - /* - * not present: _file_direction - */ - return args; -} - -/* - * eval <<string>> - * - * Passes string back into the command evaluator. - */ -static const char * -ceval(cident, args) - enum cident cident; - const char *args; -{ - const char *com; - - ARGSTR(com); /* The command line to evaluate */ - ENDPARSE; - - /* It's not clear what to do with the command() return code */ - (void) command(com); - - return args; -} - -/* - * set <<variablename>> <<variablestring>> - * - * Sets variable variablename to string variablestring. - */ -static const char * -cset(cident, args) - enum cident cident; - const char *args; -{ - const char *str, *var; - - ARGSTR(var); /* name of variable to set */ - ARGSTR(str); /* value to set variable to */ - ENDPARSE; - - if (*var == '_') { - SETERRSTR (E_BADVAR, - "variables beginning with '_' are reserved"); - return NULL; - } - if (setvar(var, str)) - return NULL; - - return args; -} - -/* - * macro <<default_number>> <<keys>> <<command>> - * - * Associates the macro keys with command. - */ -static const char * -cmacro(cident, args) - enum cident cident; - const char *args; -{ - const char *keys, *com; - long num; - - ARGNUM(num); /* the default number N for this macro */ - ARGSTR(keys); /* string of keys representing a macro */ - ARGSTR(com); /* command line to associate with macro */ - ENDPARSE; - - if (setmacro(keys, com)) - return NULL; - if (setmacnumb(keys, num)) - return NULL; - - return args; -} - -/* - * error <<string>> - * - * Prints a notification message. - */ -static const char * -cerror(cident, args) - enum cident cident; - const char *args; -{ - char *s; - - ARGSTR(s); /* error message */ - ENDPARSE; - error(s); - return args; -} - -/* - * condition <<boolean>> - * condition_! <<boolean>> - * - * If boolean is false, causes all commands except for other condition - * commands to be ignored. The <<boolean>> may be specified as a number - * (in which case even numbers are true, odd numbers are false), or one - * of "on", "off", "true", and "false". - */ -static const char * -ccondition(cident, args) - enum cident cident; - const char *args; -{ - /* ENDPARSE; */ - - if (cident == CONDITION_TOGGLE) { - condition_eval = !condition_eval; - return args; - } - - switch (gettog(&args, 4, "off", "on", "false", "true")) { - case 0: case 2: - condition_eval = 0; - break; - case 1: case 3: - condition_eval = 1; - break; - case -1: - return NULL; - } - if (cident == CONDITION_N) - condition_eval = !condition_eval; - return args; -} - -/* - * usercom - * - * Accept a direct command from the user's terminal. - */ -static const char * -cusercom(cident, args) - enum cident cident; - const char *args; -{ - char buf[125]; /* XXX should avoid static buffer... */ - - ENDPARSE; - if (getinput("Command: ", buf, sizeof(buf))) - return args; /* abort the command */ - if (command(buf)) - return NULL; - - return args; -} - -/* - * readrc <<filename>> - * - * Read-in rc commands from the named file. - */ -static const char * -creadrc(cident, args) - enum cident cident; - const char *args; -{ - const char *file; - FILE *fd; - - ARGSTR(file); - ENDPARSE; - - if (!*file) - return args; - /* - * Should perhaps warn user if file perms or ownership look suspicious. - */ - fd = fopen(file, "r"); - if (!fd) { - SETERRSTR (E_NULL, "could not open file ``%s''", file); - return NULL; - } - readrc(fd); - fclose(fd); - - return args; -} - -/* - * quit - * - * Performs as advertised. - */ -static const char * -cquit(cident, args) - enum cident cident; - const char *args; -{ - ENDPARSE; - quit(); - return NULL; /* oh boy... */ -} - -/* - * help - * - * Doesn't do much. - */ -static const char * -chelp(cident, args) - enum cident cident; - const char *args; -{ - extern int ac, curr_ac; - extern char **av; - - ENDPARSE; - if (ac > 0 && !strcmp(_PATH_HELPFILE, av[curr_ac])) { - SETERRSTR(E_NULL, "already viewing help"); - return NULL; - } - help(); - return args; -} - -/* - * flush - * repaint - * - * Flushes the file buffer, provided we are not reading from a pipe. - * Frees any other memory that I can get my hands on from here. - */ -static const char * -cflush(cident, args) - enum cident cident; - const char *args; -{ - extern int ispipe; - - ENDPARSE; - if (cident == FLUSH && !ispipe) { - ch_init(0, 0); /* XXX should this be ch_init(ctags,0) */ - clr_linenum(); - } - repaint(); - - return args; -} - -/* - * forw_scroll <<n>> - * back_scroll <<n>> - * forw <<n>> - * back <<n>> - * - * Move forward number n lines. The _scroll variants force a scroll, the - * others may scroll or may just redraw the screen at the appropriate location, - * whichever is faster. - */ -static const char * -cscroll(cident, args) - enum cident cident; - const char *args; -{ - long n; - char *retr; - - ARGNUM(n); /* number of lines to move by */ - ENDPARSE; - - switch (cident) { - case FORW_SCROLL: - forward(n, 0); - break; - case BACK_SCROLL: - backward(n, 0); - break; - case FORW: - forward(n, 1); - break; - case BACK: - backward(n, 1); - break; - } - - return args; -} - -/* - * rscroll <<n>> - * lscroll <<n>> - * - * Scroll left or right by n lines. - */ -static const char * -chscroll(cident, args) - enum cident cident; - const char *args; -{ - long n; - char *retr; - extern int horiz_off, wraplines; - - ARGNUM(n); /* Number of columns to scroll by */ - ENDPARSE; - - if (n == 0) - return args; - - switch (cident) { - case RSCROLL: - if (wraplines) { - wraplines = 0; - assert (horiz_off == 0); - } else { - horiz_off += n; - if (horiz_off < 0) - horiz_off = INT_MAX; /* disaster control */ - } - break; - case LSCROLL: - if (horiz_off != 0) { - horiz_off -= n; - if (horiz_off < 0) - horiz_off = 0; - } else - wraplines = 1; - break; - } - repaint(); /* screen_trashed = 1 */ - setvari("_curhscroll", (long) horiz_off); - settog("_wraplines", wraplines, 2, "off", "on"); - - return args; -} - -/* - * goline <<line>> - * gopercent <<percent>> - * - * Goto the line numbered <<line>>, if possible. Goto <<percent>> percent of - * the file. Whole-numbered percents only, of course. - */ -static const char * -cgomisc(cident, args) - enum cident cident; - const char *args; -{ - long n; - - ARGNUM(n); /* number N */ - ENDPARSE; - - switch (cident) { - case GOLINE: - jump_back(n); - break; - case GOPERCENT: - if (n > 100) - n = 100; - jump_percent(n); - break; - } - return args; -} - -/* - * goend - * - * Goto the end of the file. Future variation should include the GNU less(1)- - * style follow a-la tail(1). - */ -static const char * -cgoend(cident, args) - enum cident cident; - const char *args; -{ - ENDPARSE; - jump_forw(); - return args; -} - -/* - * edit - * - * Edits the current file with a word editor. This command is just begging - * to be extended to allow the user to specify an editor. Additionally, this - * would require some kind of getenv command or similar change. - */ -static const char * -cedit(cident, args) - enum cident cident; - const char *args; -{ - extern ispipe; - - ENDPARSE; - if (ispipe) { - SETERRSTR(E_NULL, "cannot edit standard input"); - return NULL; - } - editfile(); - /* - * XXX less-than brilliant things happen if the user while editing - * deletes a large section at the end of the file where we think we - * are currently viewing... - */ - ch_init(0, 0); /* Clear the internal file buffer */ - clr_linenum(); - return args; -} - -/* - * askfile - * - * Loads a new file. Queries the user for the name of the new file. - */ -static const char * -caskfile(cident, args) - enum cident cident; - const char *args; -{ - char buf[MAXPATHLEN + 1]; - - ENDPARSE; - if (getinput("Examine: ", buf, sizeof(buf))) - return args; /* abort */ - /* Abort is different from a "" answer in that "" causes the current - * file to be re-opened. */ - /* XXX should modify this() or edit() to handle lists of file, ie. - * the type of lists that I get if I try to glob("*") */ - (void)edit(glob(buf), FORCE_OPEN); - - return args; -} - -/* - * file <<next|previous>> <<N>> - * - * Loads the N'th next or previous file, typically from the list of files - * given on the command line. - */ -static const char * -cfile(cident, args) - enum cident cident; - const char *args; -{ - enum { FORW=0, BACK=1 } direction; - long N; - - ARGTOG(direction, 10, "next", "previous", "forward", "backward", - "forwards", "backwards", "next", "prev", "forw", "back"); - ARGNUM(N); - ENDPARSE; - direction %= 2; - - /* next_file() and prev_file() call error() directly (bad) */ - switch (direction) { - case FORW: - next_file(N); - break; - case BACK: - prev_file(N); - break; - } - settog("_file_direction", direction, 2, "next", "previous"); - return args; -} - -/* - * file_list - * - * Lists the files the "file next" and "file prev" are moving around in. - */ -static const char * -cfile_list(cident, args) - enum cident cident; - const char *args; -{ - ENDPARSE; - showlist(); - repaint(); /* screen_trashed = 1; */ - return args; -} - -/* - * stat <<on|off>> - * - * Display the detailed statistics as part of the prompt. The toggle option - * variable is called _statprompt (giving ${_statprompt_s} and - * ${_statprompt_n}). - */ -static const char * -cstat(cident, args) - enum cident cident; - const char *args; -{ - int onoff; - - ARGTOG(onoff, 2, "on", "off"); - ENDPARSE; - statprompt(onoff); - settog("_statprompt", onoff, 2, "on", "off"); - return args; -} - -/* - * magicasksearch <<forw|back>> <<n>> - * search <<forw|back>> <<n>> <<<noinvert|invert>> <searchstring>> - * research <<forw|back>> <<n>> - * - * Arguments specifying an option (ie. <<forw|back>> and <<noinvert|invert>> - * may be specified either as text (eg. "forw"), or as a number, in which case - * even numbers specify the former setting and odd numbers the latter setting. - * - * The magicasksearch will ask the user for a regexp and intuit whether they - * want to invert the sense of matching or not: if the first character of the - * regexp is a '!', it is removed and the sense is inverted. If the regexp - * entered is null, then we will use ${_ls_regexp} (error if not set). - * - * The toggle options are called _ls_direction and _ls_sense. In addition, - * ${_ls_regexp} is set to the regexp used. These variables are only set - * when the search and magicsearch commands are used. - */ -static const char * -csearch(cident, args) - enum cident cident; - const char *args; -{ - char buf[100], *str; - enum { FORW=0, BACK=1 } direction; - static enum { NOINVERT=0, INVERT=1 } sense; - long N; - int abrt = 0; - - ARGTOG(direction, 6, "forw", "back", "forward", "backward", - "forwards", "backwards"); - ARGNUM(N); - if (cident == SEARCH) { - ARGTOG(sense, 2, "noinvert", "invert"); - ARGSTR(str); - } - ENDPARSE; - direction %= 2; - - /* Get the search string, one way or another */ - switch (cident) { - case MAGICASKSEARCH: - biggetinputhack(); /* It's magic, boys */ - if (direction == FORW) - abrt = getinput("Search: /", buf, 2); - else - abrt = getinput("Search: ?", buf, 2); - switch (*buf) { - case '!': - /* Magic */ - if (direction == FORW) - abrt = getinput("Search: !/", buf, sizeof(buf)); - else - abrt = getinput("Search: !?", buf, sizeof(buf)); - sense = INVERT; - break; - default: - /* No magic */ - ungetcc(*buf); - if (direction == FORW) - abrt = getinput("Search: /", buf, sizeof(buf)); - else - abrt = getinput("Search: ?", buf, sizeof(buf)); - case '\0': - sense = NOINVERT; - break; - } - str = buf; - break; - case SEARCH: - break; - case RESEARCH: - str = NULL; - break; - } - - if (abrt) - return args; - - if (cident == SEARCH || cident == MAGICASKSEARCH) { - settog("_ls_direction", direction, 2, "forw", "back"); - settog("_ls_sense", sense, 2, "noinvert", "invert"); - if (*str) - setvar("_ls_regexp", str); - } - /* - * XXX Currently search() contains magic to deal with (*str=='\0'). - * This magic should be moved into this function so that we can work - * as described in the function comment header. - */ - search(!direction, str, N, !sense); - return args; -} - -/* - * setmark <<character>> - * gomark <<character>> - * - * Set a marker at the current position, or goto a previously set marker. - * Character may be a-z, or '?' to ask the user to enter a character. The - * special mark '\'' may not be set, but may be the target of a goto. - */ -static const char * -cmark(cident, args) - enum cident cident; - const char *args; -{ - char smark[2]; - const char *mark; - - ARGSTR(mark); - ENDPARSE; - - /* gomark() and setmark() will further check mark's validity */ - if (!*mark || mark[1]) { - SETERRSTR(E_NULL, "bad mark character"); - return NULL; - } - - if (*mark == '?') { - biggetinputhack(); /* so getinput() returns after one char */ - switch (cident) { - case GOMARK: - getinput("goto mark: ", smark, sizeof smark); - break; - case SETMARK: - getinput("set mark: ", smark, sizeof smark); - break; - } - if (!*smark) - return args; - mark = smark; - } - - switch (cident) { - case GOMARK: - gomark(*mark); - break; - case SETMARK: - setmark(*mark); - break; - } - return args; -} - -/* - * asktag - * nexttag <<number>> - * prevtag <<number>> - * - * Asks the user for a tag, or moves around the tag queue. - */ -static const char * -ctags(cident, args) - enum cident cident; - const char *args; -{ - extern char *tagfile; /* XXX No reason for this to be a global... */ - long n; - - if (cident != ASKFTAG) - ARGNUM(n); - ENDPARSE; - - if (cident == ASKFTAG) { - char buf[100]; /* XXX should do something else... */ - getinput("Tag: ", buf, sizeof(buf)); - if (!*buf) - return args; - findtag(buf); - } else { - switch (cident) { - case NEXTFTAG: - nexttag(n); - break; - case PREVFTAG: - prevtag(n); - break; - } - } - - /* Load the tagfile and position ourselves. */ - if (tagfile == NULL) - return NULL; - if (edit(tagfile, NO_FORCE_OPEN)) - tagsearch(); - return args; /* tag stuff still calls error() on its own */ -} |