home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1990,1991,1992 Chris and John Downey */
- #ifndef lint
- static char *sccsid = "@(#)cmdline.c 2.2 (Chris & John Downey) 8/6/92";
- #endif
-
- /***
-
- * program name:
- xvi
- * function:
- PD version of UNIX "vi" editor, with extensions.
- * module name:
- cmdline.c
- * module function:
- Command-line handling (i.e. :/? commands) - most
- of the actual command functions are in ex_cmds.c.
- * history:
- STEVIE - ST Editor for VI Enthusiasts, Version 3.10
- Originally by Tim Thompson (twitch!tjt)
- Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
- Heavily modified by Chris & John Downey
- Minor, Amiga specific modifications made by Dan Schmelzer.
-
- ***/
-
- #include "xvi.h"
-
- #ifdef MEGAMAX
- overlay "cmdline"
- #endif
-
- /*
- * The next two variables contain the bounds of any range
- * given in a command. If no range was given, both will be NULL.
- * If only a single line was given, u_line will be NULL.
- * The a_line variable is used for those commands which take
- * a third line specifier after the command, e.g. "move", "copy".
- */
- static Line *l_line, *u_line;
- static Line *a_line;
-
- /*
- * Definitions for all ex commands.
- */
-
- #define EX_ENOTFOUND -1 /* command not found */
- #define EX_EAMBIGUOUS -2 /* could be more than one */
- #define EX_ECANTFORCE -3 /* ! given where not appropriate */
- #define EX_EBADARGS -4 /* inappropriate args given */
-
- #define EX_NOCMD 1
- #define EX_SHCMD 2
- #define EX_UNUSED 3 /* unused */
- #define EX_AMPERSAND 4
- #define EX_EXBUFFER 5
- #define EX_LSHIFT 6
- #define EX_EQUALS 7
- #define EX_RSHIFT 8
- #define EX_COMMENT 9
- #define EX_ABBREVIATE 10
- #define EX_APPEND 11
- #define EX_ARGS 12
- #define EX_BUFFER 13
- #define EX_CHDIR 14
- #define EX_CHANGE 15
- #define EX_CLOSE 16
- #define EX_COMPARE 17
- #define EX_COPY 18
- #define EX_DELETE 19
- #define EX_ECHO 20
- #define EX_EDIT 21
- #define EX_EX 22
- #define EX_FILE 23
- #define EX_GLOBAL 24
- #define EX_HELP 25
- #define EX_INSERT 26
- #define EX_JOIN 27
- #define EX_K 28
- #define EX_LIST 29
- #define EX_MAP 30
- #define EX_MARK 31
- #define EX_MOVE 32
- #define EX_NEXT 33
- #define EX_NUMBER 34
- #define EX_OPEN 35
- #define EX_PRESERVE 36
- #define EX_PRINT 37
- #define EX_PUT 38
- #define EX_QUIT 39
- #define EX_READ 40
- #define EX_RECOVER 41
- #define EX_REWIND 42
- #define EX_SET 43
- #define EX_SHELL 44
- #define EX_SOURCE 45
- #define EX_SPLIT 46
- #define EX_SUSPEND 47
- #define EX_SUBSTITUTE 48
- #define EX_TAG 49
- #define EX_UNABBREV 50
- #define EX_UNDO 51
- #define EX_UNMAP 52
- #define EX_V 53
- #define EX_VERSION 54
- #define EX_VISUAL 55
- #define EX_WN 56
- #define EX_WQ 57
- #define EX_WRITE 58
- #define EX_XIT 59
- #define EX_YANK 60
- #define EX_Z 61
- #define EX_GOTO 62
- #define EX_TILDE 63
-
- /*
- * Table of all ex commands, and whether they take an '!'.
- *
- * Note that this table is in strict order, sorted on
- * the ASCII value of the first character of the command.
- *
- * The priority field is necessary to resolve clashes in
- * the first one or two characters; so each group of commands
- * beginning with the same letter should have at least one
- * priority 1, so that there is a sensible default.
- *
- * Commands with argument type ec_rest need no delimiters;
- * they need only be matched. This is really only used for
- * single-character commands like !, " and &.
- */
- static struct ecmd {
- char *ec_name;
- short ec_command;
- short ec_priority;
- unsigned ec_flags;
- /*
- * Flags: EXCLAM means can use !, FILEXP means do filename
- * expansion, INTEXP means do % and # expansion. EXPALL means
- * do INTEXP and FILEXP (they are done in that order).
- *
- * EC_RANGE0 means that the range specifier (if any)
- * may include line 0.
- */
- # define EC_EXCLAM 0x1
- # define EC_FILEXP 0x2
- # define EC_INTEXP 0x4
- # define EC_EXPALL EC_FILEXP|EC_INTEXP
- # define EC_RANGE0 0x8
-
- enum {
- ec_none, /* no arguments after command */
- ec_strings, /* whitespace-separated strings */
- ec_1string, /* like ec_strings but only one */
- ec_line, /* line number or target argument */
- ec_rest, /* rest of line passed entirely */
- ec_nonalnum, /* non-alphanumeric delimiter */
- ec_1lower /* single lower-case letter */
- } ec_arg_type;
- } cmdtable[] = {
- /* name command priority exclam */
-
- /*
- * The zero-length string is used for the :linenumber command.
- */
- "", EX_NOCMD, 1, EC_RANGE0, ec_none,
- "!", EX_SHCMD, 0, EC_INTEXP, ec_rest,
-
- "#", EX_NUMBER, 0, 0, ec_none,
- "&", EX_AMPERSAND, 0, 0, ec_rest,
- "*", EX_EXBUFFER, 0, 0, ec_rest,
- "<", EX_LSHIFT, 0, 0, ec_none,
- "=", EX_EQUALS, 0, 0, ec_none,
- ">", EX_RSHIFT, 0, 0, ec_none,
- "@", EX_EXBUFFER, 0, 0, ec_rest,
- "\"", EX_COMMENT, 0, 0, ec_rest,
-
- "abbreviate", EX_ABBREVIATE, 0, 0, ec_strings,
- "append", EX_APPEND, 1, 0, ec_none,
- "args", EX_ARGS, 0, 0, ec_none,
-
- "buffer", EX_BUFFER, 0, EC_EXPALL, ec_1string,
-
- "cd", EX_CHDIR, 1, EC_EXPALL, ec_1string,
- "change", EX_CHANGE, 2, 0, ec_none,
- "chdir", EX_CHDIR, 1, EC_EXPALL, ec_1string,
- "close", EX_CLOSE, 1, EC_EXCLAM, ec_none,
- "compare", EX_COMPARE, 0, 0, ec_none,
- "copy", EX_COPY, 1, 0, ec_line,
-
- "delete", EX_DELETE, 0, 0, ec_none,
-
- "echo", EX_ECHO, 0, EC_INTEXP, ec_strings,
- "edit", EX_EDIT, 1, EC_EXCLAM|EC_EXPALL, ec_1string,
- "ex", EX_EX, 0, EC_EXPALL, ec_1string,
-
- "file", EX_FILE, 0, EC_EXPALL, ec_1string,
-
- "global", EX_GLOBAL, 0, EC_EXCLAM, ec_nonalnum,
-
- "help", EX_HELP, 0, 0, ec_none,
-
- "insert", EX_INSERT, 0, 0, ec_none,
-
- "join", EX_JOIN, 0, 0, ec_none,
-
- "k", EX_K, 0, 0, ec_1lower,
-
- "list", EX_LIST, 0, 0, ec_none,
-
- "map", EX_MAP, 0, EC_EXCLAM, ec_strings,
- "mark", EX_MARK, 0, 0, ec_1lower,
- "move", EX_MOVE, 1, 0, ec_line,
-
- "next", EX_NEXT, 1, EC_EXCLAM|EC_EXPALL, ec_strings,
- "number", EX_NUMBER, 0, 0, ec_none,
-
- "open", EX_OPEN, 0, 0, ec_none,
-
- "preserve", EX_PRESERVE, 0, 0, ec_none,
- "print", EX_PRINT, 1, 0, ec_none,
- "put", EX_PUT, 0, EC_RANGE0, ec_none,
-
- "quit", EX_QUIT, 0, EC_EXCLAM, ec_none,
-
- "read", EX_READ, 1, EC_EXPALL|EC_RANGE0, ec_1string,
- "recover", EX_RECOVER, 0, 0, ec_none,
- "rewind", EX_REWIND, 0, EC_EXCLAM, ec_none,
-
- "set", EX_SET, 0, 0, ec_strings,
- "shell", EX_SHELL, 0, 0, ec_none,
- "source", EX_SOURCE, 0, EC_EXPALL, ec_1string,
- "split", EX_SPLIT, 0, 0, ec_none,
- "stop", EX_SUSPEND, 0, 0, ec_none,
- "substitute", EX_SUBSTITUTE, 1, 0, ec_nonalnum,
- "suspend", EX_SUSPEND, 0, 0, ec_none,
-
- "t", EX_COPY, 1, 0, ec_line,
- "tag", EX_TAG, 0, EC_EXCLAM, ec_1string,
-
- "unabbreviate", EX_UNABBREV, 0, 0, ec_strings,
- "undo", EX_UNDO, 1, 0, ec_none,
- "unmap", EX_UNMAP, 0, EC_EXCLAM, ec_strings,
-
- "v", EX_V, 1, 0, ec_nonalnum,
- "version", EX_VERSION, 0, 0, ec_none,
- "visual", EX_VISUAL, 0, EC_EXCLAM|EC_EXPALL, ec_1string,
-
- "wn", EX_WN, 0, EC_EXCLAM, ec_none,
- "wq", EX_WQ, 0, EC_EXCLAM|EC_EXPALL, ec_1string,
- "write", EX_WRITE, 1, EC_EXCLAM|EC_EXPALL, ec_1string,
-
- "xit", EX_XIT, 0, 0, ec_none,
-
- "yank", EX_YANK, 0, 0, ec_none,
-
- "z", EX_Z, 0, 0, ec_none,
-
- "|", EX_GOTO, 0, 0, ec_none,
- "~", EX_TILDE, 0, 0, ec_rest,
-
- NULL, 0, 0, 0, ec_none,
- };
-
- /*
- * Internal routine declarations.
- */
- static int decode_command P((char **, bool_t *, struct ecmd **));
- static bool_t get_line P((char **, Line **));
- static bool_t get_range P((char **));
- static void badcmd P((bool_t, char *));
- static char *show_line P((void));
- static char *expand_percents P((char *));
-
- /*
- * These are used for display mode.
- */
- static Line *curline;
- static Line *lastline;
- static bool_t do_line_numbers;
-
- /*
- * Macro to skip over whitespace during command line interpretation.
- */
- #define skipblanks(p) { while (*(p) != '\0' && is_space(*(p))) (p)++; }
-
- /*
- * do_colon() - process a ':' command.
- *
- * The cmdline argument points to a complete command line to be processed
- * (this does not include the ':' itself).
- */
- void
- do_colon(cmdline, interactive)
- char *cmdline; /* optional command string */
- bool_t interactive; /* true if reading from tty */
- {
- char *arg; /* ptr to string arg(s) */
- int argc = 0; /* arg count for ec_strings */
- char **argv = NULL; /* arg vector for ec_strings */
- bool_t exclam; /* true if ! was given */
- int command; /* which command it is */
- struct ecmd *ecp; /* ptr to command entry */
- unsigned savecho; /* previous value of echo */
-
- /*
- * Clear the range variables.
- */
- l_line = NULL;
- u_line = NULL;
-
- /*
- * Parse a range, if present (and update the cmdline pointer).
- */
- if (!get_range(&cmdline)) {
- return;
- }
-
- /*
- * Decode the command.
- */
- skipblanks(cmdline);
- command = decode_command(&cmdline, &exclam, &ecp);
-
- if (command > 0) {
- /*
- * Check that the range specified,
- * if any, is legal for the command.
- */
- if (!(ecp->ec_flags & EC_RANGE0)) {
- if (l_line == curbuf->b_line0 || u_line == curbuf->b_line0) {
- show_error(curwin,
- "Specification of line 0 not allowed");
- return;
- }
- }
-
- switch (ecp->ec_arg_type) {
- case ec_none:
- if (*cmdline != '\0' &&
- (*cmdline != '!' || !(ecp->ec_flags & EC_EXCLAM))) {
- command = EX_EBADARGS;
- }
- break;
-
- case ec_line:
- a_line = NULL;
- skipblanks(cmdline);
- if (!get_line(&cmdline, &a_line) || a_line == NULL) {
- command = EX_EBADARGS;
- }
- break;
-
- case ec_1lower:
- /*
- * One lower-case letter.
- */
- skipblanks(cmdline);
- if (!is_lower(cmdline[0]) || cmdline[1] != '\0') {
- command = EX_EBADARGS;
- } else {
- arg = cmdline;
- }
- break;
-
- case ec_nonalnum:
- case ec_rest:
- case ec_strings:
- case ec_1string:
- arg = cmdline;
- if (ecp->ec_arg_type == ec_strings ||
- ecp->ec_arg_type == ec_1string) {
- if (*arg == '\0') {
- /*
- * No args.
- */
- arg = NULL;
- } else {
- /*
- * Null-terminate the command and skip
- * whitespace to arg or end of line.
- */
- *arg++ = '\0';
- skipblanks(arg);
-
- /*
- * There was trailing whitespace,
- * but no args.
- */
- if (*arg == '\0') {
- arg = NULL;
- }
- }
- } else if (ecp->ec_arg_type == ec_nonalnum && exclam) {
- /*
- * We don't normally touch the arguments for
- * this type, but we have to null-terminate
- * the '!' at least.
- */
- *arg++ = '\0';
- }
-
- if (arg != NULL) {
- /*
- * Perform expansions on the argument string.
- */
- if (ecp->ec_flags & EC_INTEXP) {
- arg = expand_percents(arg);
- }
- if (ecp->ec_flags & EC_FILEXP) {
- arg = fexpand(arg);
- }
-
- if (ecp->ec_arg_type == ec_strings) {
- makeargv(arg, &argc, &argv, " \t");
- }
- }
- }
- }
-
- savecho = echo;
-
- /*
- * Now do the command.
- */
- switch (command) {
- case EX_SHCMD:
- /*
- * If a line range was specified, this must be a pipe command.
- * Otherwise, it's just a simple shell command.
- */
- if (l_line != NULL) {
- specify_pipe_range(curwin, l_line, u_line);
- do_pipe(curwin, arg);
- } else {
- do_shcmd(curwin, arg);
- }
- break;
-
- case EX_ARGS:
- do_args(curwin);
- break;
-
- case EX_BUFFER:
- if (arg != NULL)
- echo &= ~(e_SCROLL | e_REPORT | e_SHOWINFO);
- (void) do_buffer(curwin, arg);
- move_window_to_cursor(curwin);
- update_window(curwin);
- break;
-
- case EX_CHDIR:
- {
- char *error;
-
- if ((error = do_chdir(arg)) != NULL) {
- badcmd(interactive, error);
- } else if (interactive) {
- char *dirp;
-
- if ((dirp = alloc(MAXPATHLEN + 2)) != NULL &&
- getcwd(dirp, MAXPATHLEN + 2) != NULL) {
- show_message(curwin, "%s", dirp);
- }
- if (dirp) {
- free(dirp);
- }
- }
- break;
- }
-
- case EX_CLOSE:
- do_close_window(curwin, exclam);
- break;
-
- case EX_COMMENT: /* This one is easy ... */
- break;
-
- case EX_COMPARE:
- do_compare();
- break;
-
- case EX_COPY:
- do_cdmy('c', l_line, u_line, a_line);
- break;
-
- case EX_DELETE:
- do_cdmy('d', l_line, u_line, (Line *) NULL);
- break;
-
- case EX_ECHO: /* echo arguments on command line */
- {
- int i;
-
- flexclear(&curwin->w_statusline);
- for (i = 0; i < argc; i++) {
- if (!lformat(&curwin->w_statusline, "%s ", argv[i])
- || flexlen(&curwin->w_statusline) >= curwin->w_ncols) {
- break;
- }
- }
- update_sline(curwin);
- break;
- }
-
- case EX_EDIT:
- case EX_VISUAL: /* treat :vi as :edit */
- echo &= ~(e_SCROLL | e_REPORT | e_SHOWINFO);
- (void) do_edit(curwin, exclam, arg);
- move_window_to_cursor(curwin);
- update_buffer(curbuf);
- #if 0
- show_file_info(curwin);
- #endif
- break;
-
- case EX_FILE:
- if (arg != NULL) {
- if (curbuf->b_filename != NULL)
- free(curbuf->b_filename);
- curbuf->b_filename = strsave(arg);
- if (curbuf->b_tempfname != NULL) {
- free(curbuf->b_tempfname);
- curbuf->b_tempfname = NULL;
- }
- }
- show_file_info(curwin);
- break;
-
- case EX_GLOBAL:
- do_global(curwin, l_line, u_line, arg, !exclam);
- break;
-
- case EX_HELP:
- do_help(curwin);
- break;
-
- case EX_MAP:
- xvi_map(argc, argv, exclam, interactive);
- break;
-
- case EX_UNMAP:
- xvi_unmap(argc, argv, exclam, interactive);
- break;
-
- case EX_MARK:
- case EX_K:
- {
- Posn pos;
-
- pos.p_index = 0;
- if (l_line == NULL) {
- pos.p_line = curwin->w_cursor->p_line;
- } else {
- pos.p_line = l_line;
- }
- (void) setmark(arg[0], curbuf, &pos);
- break;
- }
-
- case EX_MOVE:
- do_cdmy('m', l_line, u_line, a_line);
- break;
-
- case EX_NEXT:
- /*
- * do_next() handles turning off the appropriate bits
- * in echo, & also calls move_window_to_cursor() &
- * update_buffer() as required, so we don't have to
- * do any of that here.
- */
- do_next(curwin, argc, argv, exclam);
- break;
-
- case EX_PRESERVE:
- if (do_preserve())
- show_file_info(curwin);
- break;
-
- case EX_LIST:
- case EX_PRINT:
- case EX_NUMBER:
- if (l_line == NULL) {
- curline = curwin->w_cursor->p_line;
- lastline = curline->l_next;
- } else if (u_line == NULL) {
- curline = l_line;
- lastline = l_line->l_next;
- } else {
- curline = l_line;
- lastline = u_line->l_next;
- }
- do_line_numbers = (Pb(P_number) || command == EX_NUMBER);
- disp_init(curwin, show_line, (int) curwin->w_ncols,
- command == EX_LIST || Pb(P_list));
- break;
-
- case EX_PUT:
- {
- Posn where;
-
- if (l_line != NULL) {
- where.p_index = 0;
- where.p_line = l_line;
- } else {
- where.p_index = curwin->w_cursor->p_index;
- where.p_line = curwin->w_cursor->p_line;
- }
- do_put(curwin, &where, FORWARD, '@');
- break;
- }
-
- case EX_QUIT:
- do_quit(curwin, exclam);
- break;
-
- case EX_REWIND:
- do_rewind(curwin, exclam);
- break;
-
- case EX_READ:
- if (arg == NULL) {
- badcmd(interactive, "Unrecognized command");
- break;
- }
- do_read(curwin, arg, (l_line != NULL) ? l_line :
- curwin->w_cursor->p_line);
- break;
-
- case EX_SET:
- do_set(curwin, argc, argv, interactive);
- break;
-
- case EX_SHELL:
- do_shell(curwin);
- break;
-
- case EX_SOURCE:
- if (arg == NULL) {
- badcmd(interactive, "Missing filename");
- } else if (do_source(interactive, arg) && interactive) {
- show_file_info(curwin);
- }
- break;
-
- case EX_SPLIT:
- /*
- * "split".
- */
- do_split_window(curwin);
- break;
-
- case EX_SUBSTITUTE:
- case EX_AMPERSAND:
- case EX_TILDE:
- {
- long nsubs;
- register long (*func) P((Xviwin *, Line *, Line *, char *));
-
- switch (command) {
- case EX_SUBSTITUTE:
- func = do_substitute;
- break;
- case EX_AMPERSAND:
- func = do_ampersand;
- break;
- case EX_TILDE:
- func = do_tilde;
- }
-
- nsubs = (*func)(curwin, l_line, u_line, arg);
- update_buffer(curbuf);
- cursupdate(curwin);
- begin_line(curwin, TRUE);
- if (nsubs >= Pn(P_report)) {
- show_message(curwin, "%ld substitution%c",
- nsubs,
- (nsubs > 1) ? 's' : ' ');
- }
- break;
- }
-
- case EX_SUSPEND:
- do_suspend(curwin);
- break;
-
- case EX_TAG:
- (void) do_tag(curwin, arg, exclam, TRUE, TRUE);
- break;
-
- case EX_V:
- do_global(curwin, l_line, u_line, arg, FALSE);
- break;
-
- case EX_VERSION:
- show_message(curwin, Version);
- #ifdef AMIGA
- amiga_version();
- #endif
- break;
-
- case EX_WN:
- /*
- * This is not a standard "vi" command, but it
- * is provided in PC vi, and it's quite useful.
- */
- if (do_write(curwin, (char *) NULL, (Line *) NULL, (Line *) NULL,
- exclam)) {
- /*
- * See comment for EX_NEXT (above).
- */
- do_next(curwin, 0, argv, exclam);
- #if 0
- move_window_to_cursor(curwin);
- update_buffer(curbuf);
- #endif
- }
- break;
-
- case EX_WQ:
- do_wq(curwin, arg, exclam);
- break;
-
- case EX_WRITE:
- (void) do_write(curwin, arg, l_line, u_line, exclam);
- break;
-
- case EX_XIT:
- do_xit(curwin);
- break;
-
- case EX_YANK:
- do_cdmy('y', l_line, u_line, (Line *) NULL);
- break;
-
- case EX_EXBUFFER:
- yp_stuff_input(curwin, arg[0], FALSE);
- break;
-
- case EX_EQUALS:
- do_equals(curwin, l_line);
- break;
-
- case EX_LSHIFT:
- case EX_RSHIFT:
- if (l_line == NULL) {
- l_line = curwin->w_cursor->p_line;
- }
- if (u_line == NULL) {
- u_line = l_line;
- }
- tabinout((command == EX_LSHIFT) ? '<' : '>', l_line, u_line);
- begin_line(curwin, TRUE);
- update_buffer(curbuf);
- break;
-
- case EX_NOCMD:
- /*
- * If we got a line, but no command, then go to the line.
- */
- if (l_line != NULL) {
- if (l_line == curbuf->b_line0) {
- l_line = l_line->l_next;
- }
- move_cursor(curwin, l_line, 0);
- begin_line(curwin, TRUE);
- }
- break;
-
- case EX_ENOTFOUND:
- badcmd(interactive, "Unrecognized command");
- break;
-
- case EX_EAMBIGUOUS:
- badcmd(interactive, "Ambiguous command");
- break;
-
- case EX_ECANTFORCE:
- badcmd(interactive, "Inappropriate use of !");
- break;
-
- case EX_EBADARGS:
- badcmd(interactive, "Inappropriate arguments given");
- break;
-
- default:
- /*
- * Decoded successfully, but unknown to us. Whoops!
- */
- badcmd(interactive, "Internal error - unimplemented command.");
- break;
-
- case EX_ABBREVIATE:
- case EX_APPEND:
- case EX_CHANGE:
- case EX_EX:
- case EX_GOTO:
- case EX_INSERT:
- case EX_JOIN:
- case EX_OPEN:
- case EX_RECOVER:
- case EX_UNABBREV:
- case EX_UNDO:
- case EX_Z:
- badcmd(interactive, "Unimplemented command.");
- break;
- }
-
- echo = savecho;
-
- if (argc > 0 && argv != NULL) {
- free((char *) argv);
- }
- }
-
- /*
- * Find the correct command for the given string, and return it.
- *
- * Updates string pointer to point to 1st char after command.
- *
- * Updates boolean pointed to by forcep according
- * to whether an '!' was given after the command;
- * if an '!' is given for a command which can't take it,
- * this is an error, and EX_ECANTFORCE is returned.
- * For unknown commands, EX_ENOTFOUND is returned.
- * For ambiguous commands, EX_EAMBIGUOUS is returned.
- *
- * Also updates *cmdp to point at entry in command table.
- */
- static int
- decode_command(str, forcep, cmdp)
- char **str;
- bool_t *forcep;
- struct ecmd **cmdp;
- {
- struct ecmd *cmdptr;
- struct ecmd *last_match = NULL;
- bool_t last_exclam = FALSE;
- int nmatches = 0;
- char *last_delimiter = *str;
-
- for (cmdptr = cmdtable; cmdptr->ec_name != NULL; cmdptr++) {
- char *name = cmdptr->ec_name;
- char *strp;
- bool_t matched;
-
- strp = *str;
-
- while (*strp == *name && *strp != '\0') {
- strp++;
- name++;
- }
-
- matched = (
- /*
- * we've got a full match, or ...
- */
- *strp == '\0'
- ||
- /*
- * ... a whitespace delimiter, or ...
- */
- is_space(*strp)
- ||
- (
- /*
- * ... at least one character has been
- * matched, and ...
- */
- name > cmdptr->ec_name
- &&
- (
- (
- /*
- * ... this command can accept a
- * '!' qualifier, and we've found
- * one ...
- */
- (cmdptr->ec_flags & EC_EXCLAM)
- &&
- *strp == '!'
- )
- ||
- (
- /*
- * ... or it can take a
- * non-alphanumeric delimiter
- * (like '/') ...
- */
- cmdptr->ec_arg_type == ec_nonalnum
- &&
- !is_alnum(*strp)
- )
- ||
- (
- /*
- * ... or it doesn't need any
- * delimiter ...
- */
- cmdptr->ec_arg_type == ec_rest
- )
- ||
- (
- /*
- * ... or we've got a full match,
- * & the command expects a single
- * lower-case letter as an
- * argument, and we've got one ...
- */
- cmdptr->ec_arg_type == ec_1lower
- &&
- *name == '\0'
- &&
- is_lower(*strp)
- )
- ||
- (
- /*
- * ... or we've got a partial match,
- * and the command expects a line
- * specifier as an argument, and the
- * next character is not alphabetic.
- */
- cmdptr->ec_arg_type == ec_line
- &&
- !is_alpha(*strp)
- )
- )
- )
- );
-
- if (!matched)
- continue;
-
- if (last_match == NULL ||
- (last_match != NULL &&
- last_match->ec_priority < cmdptr->ec_priority)) {
- /*
- * No previous match, or this one is better.
- */
- last_match = cmdptr;
- last_exclam = (*strp == '!');
- last_delimiter = strp;
- nmatches = 1;
-
- /*
- * If this is a complete match, we don't
- * need to search the rest of the table.
- */
- if (*name == '\0')
- break;
-
- } else if (last_match != NULL &&
- last_match->ec_priority == cmdptr->ec_priority) {
- /*
- * Another match with the same priority - remember it.
- */
- nmatches++;
- }
- }
-
- /*
- * For us to have found a good match, there must have been
- * exactly one match at a certain priority, and if an '!'
- * was used, it must be allowed by that match.
- */
- if (last_match == NULL) {
- return(EX_ENOTFOUND);
- } else if (nmatches != 1) {
- return(EX_EAMBIGUOUS);
- } else if (last_exclam && ! (last_match->ec_flags & EC_EXCLAM)) {
- return(EX_ECANTFORCE);
- } else {
- *forcep = last_exclam;
- *str = last_delimiter;
- *cmdp = last_match;
- return(last_match->ec_command);
- }
- }
-
- /*
- * get_range - parse a range specifier
- *
- * Ranges are of the form
- *
- * ADDRESS1,ADDRESS2
- * ADDRESS (equivalent to "ADDRESS,ADDRESS")
- * ADDRESS, (equivalent to "ADDRESS,.")
- * ,ADDRESS (equivalent to ".,ADDRESS")
- * , (equivalent to ".")
- * % (equivalent to "1,$")
- *
- * where ADDRESS is
- *
- * /regular expression/ [INCREMENT]
- * ?regular expression? [INCREMENT]
- * $ [INCREMENT]
- * 'x [INCREMENT] (where x denotes a currently defined mark)
- * . [INCREMENT]
- * INCREMENT (equivalent to . INCREMENT)
- * number [INCREMENT]
- *
- * and INCREMENT is
- *
- * + number
- * - number
- * + (equivalent to +1)
- * - (equivalent to -1)
- * ++ (equivalent to +2)
- * -- (equivalent to -2)
- *
- * etc.
- *
- * The pointer *cpp is updated to point to the first character following
- * the range spec. If an initial address is found, but no second, the
- * upper bound is equal to the lower, except if it is followed by ','.
- *
- * Return FALSE if an error occurred, otherwise TRUE.
- */
- static bool_t
- get_range(cpp)
- register char **cpp;
- {
- register char *cp;
- char sepchar;
-
- #define skipp { cp = *cpp; skipblanks(cp); *cpp = cp; }
-
- skipp;
-
- if (*cp == '%') {
- /*
- * "%" is the same as "1,$".
- */
- l_line = curbuf->b_file;
- u_line = curbuf->b_lastline->l_prev;
- ++*cpp;
- return TRUE;
- }
-
- if (!get_line(cpp, &l_line)) {
- return FALSE;
- }
-
- skipp;
-
- sepchar = *cp;
- if (sepchar != ',' && sepchar != ';') {
- u_line = l_line;
- return TRUE;
- }
-
- ++*cpp;
-
- if (l_line == NULL) {
- /*
- * So far, we've got ",".
- *
- * ",address" is equivalent to ".,address".
- */
- l_line = curwin->w_cursor->p_line;
- }
-
- if (sepchar == ';') {
- move_cursor(curwin, (l_line != curbuf->b_line0) ?
- l_line : l_line->l_next, 0);
- }
-
- skipp;
- if (!get_line(cpp, &u_line)) {
- return FALSE;
- }
- if (u_line == NULL) {
- /*
- * "address," is equivalent to "address,.".
- */
- u_line = curwin->w_cursor->p_line;
- }
-
- /*
- * Check for reverse ordering.
- */
- if (earlier(u_line, l_line)) {
- Line *tmp;
-
- tmp = l_line;
- l_line = u_line;
- u_line = tmp;
- }
-
- skipp;
- return TRUE;
- }
-
- static bool_t
- get_line(cpp, lpp)
- char **cpp;
- Line **lpp;
- {
- Line *pos;
- char *cp;
- char c;
- long lnum;
-
- cp = *cpp;
-
- /*
- * Determine the basic form, if present.
- */
- switch (c = *cp++) {
-
- case '/':
- case '?':
- pos = linesearch(curwin, (c == '/') ? FORWARD : BACKWARD,
- &cp);
- if (pos == NULL) {
- return FALSE;
- }
- break;
-
- case '$':
- pos = curbuf->b_lastline->l_prev;
- break;
-
- /*
- * "+n" is equivalent to ".+n".
- */
- case '+':
- case '-':
- cp--;
- /* fall through ... */
- case '.':
- pos = curwin->w_cursor->p_line;
- break;
-
- case '\'': {
- Posn *lp;
-
- lp = getmark(*cp++, curbuf);
- if (lp == NULL) {
- show_error(curwin, "Unknown mark");
- return FALSE;
- }
- pos = lp->p_line;
- break;
- }
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- for (lnum = c - '0'; is_digit(*cp); cp++)
- lnum = lnum * 10 + *cp - '0';
-
- if (lnum == 0) {
- pos = curbuf->b_line0;
- } else {
- pos = gotoline(curbuf, lnum);
- }
- break;
-
- default:
- return TRUE;
- }
-
- skipblanks(cp);
-
- if (*cp == '-' || *cp == '+') {
- char dirchar = *cp++;
-
- skipblanks(cp);
- if (is_digit(*cp)) {
- for (lnum = 0; is_digit(*cp); cp++) {
- lnum = lnum * 10 + *cp - '0';
- }
- } else {
- for (lnum = 1; *cp == dirchar; cp++) {
- lnum++;
- }
- }
-
- if (dirchar == '-')
- lnum = -lnum;
-
- pos = gotoline(curbuf, lineno(curbuf, pos) + lnum);
- }
-
- *cpp = cp;
- *lpp = pos;
- return TRUE;
- }
-
- /*
- * This routine is called for ambiguous, unknown,
- * badly defined or unimplemented commands.
- */
- static void
- badcmd(interactive, str)
- bool_t interactive;
- char *str;
- {
- if (interactive) {
- show_error(curwin, str);
- }
- }
-
- static char *
- show_line()
- {
- Line *lp;
-
- if (curline == lastline) {
- return(NULL);
- }
-
- lp = curline;
- curline = curline->l_next;
-
- if (do_line_numbers) {
- static Flexbuf nu_line;
-
- flexclear(&nu_line);
- (void) lformat(&nu_line, NUM_FMT, lineno(curbuf, lp));
- (void) lformat(&nu_line, "%s", lp->l_text);
- return flexgetstr(&nu_line);
- } else {
- return(lp->l_text);
- }
- }
-
- static char *
- expand_percents(str)
- char *str;
- {
- static Flexbuf newstr;
- register char *from;
- register bool_t escaped;
-
- if (strpbrk(str, "%#") == (char *) NULL) {
- return str;
- }
- flexclear(&newstr);
- escaped = FALSE;
- for (from = str; *from != '\0'; from++) {
- if (!escaped) {
- if (*from == '%' && curbuf->b_filename != NULL) {
- (void) lformat(&newstr, "%s", curbuf->b_filename);
- } else if (*from == '#' && altfilename != NULL) {
- (void) lformat(&newstr, "%s", altfilename);
- } else if (*from == '\\') {
- escaped = TRUE;
- } else {
- (void) flexaddch(&newstr, *from);
- }
- } else {
- if (*from != '%' && *from != '#') {
- (void) flexaddch(&newstr, '\\');
- }
- (void) flexaddch(&newstr, *from);
- escaped = FALSE;
- }
- }
- return flexgetstr(&newstr);
- }
-