home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / ex / ex.c < prev    next >
C/C++ Source or Header  |  1997-06-29  |  66KB  |  2,371 lines

  1. /*-
  2.  * Copyright (c) 1992, 1993, 1994
  3.  *    The Regents of the University of California.  All rights reserved.
  4.  * Copyright (c) 1992, 1993, 1994, 1995, 1996
  5.  *    Keith Bostic.  All rights reserved.
  6.  *
  7.  * See the LICENSE file for redistribution information.
  8.  */
  9.  
  10. #include "config.h"
  11.  
  12. #ifndef lint
  13. static const char sccsid[] = "@(#)ex.c    10.57 (Berkeley) 10/10/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18. #include <sys/stat.h>
  19. #include <sys/time.h>
  20.  
  21. #include <bitstring.h>
  22. #include <ctype.h>
  23. #include <errno.h>
  24. #include <fcntl.h>
  25. #include <limits.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <unistd.h>
  30.  
  31. #include "../common/common.h"
  32. #include "../vi/vi.h"
  33.  
  34. #if defined(DEBUG) && defined(COMLOG)
  35. static void    ex_comlog __P((SCR *, EXCMD *));
  36. #endif
  37. static EXCMDLIST const *
  38.         ex_comm_search __P((char *, size_t));
  39. static int    ex_discard __P((SCR *));
  40. static int    ex_line __P((SCR *, EXCMD *, MARK *, int *, int *));
  41. static int    ex_load __P((SCR *));
  42. static void    ex_unknown __P((SCR *, char *, size_t));
  43.  
  44. /*
  45.  * ex --
  46.  *    Main ex loop.
  47.  *
  48.  * PUBLIC: int ex __P((SCR **));
  49.  */
  50. int
  51. ex(spp)
  52.     SCR **spp;
  53. {
  54.     EX_PRIVATE *exp;
  55.     GS *gp;
  56.     MSGS *mp;
  57.     SCR *sp;
  58.     TEXT *tp;
  59.     u_int32_t flags;
  60.  
  61.     sp = *spp;
  62.     gp = sp->gp;
  63.     exp = EXP(sp);
  64.  
  65.     /* Start the ex screen. */
  66.     if (ex_init(sp))
  67.         return (1);
  68.  
  69.     /* Flush any saved messages. */
  70.     while ((mp = gp->msgq.lh_first) != NULL) {
  71.         gp->scr_msg(sp, mp->mtype, mp->buf, mp->len);
  72.         LIST_REMOVE(mp, q);
  73.         free(mp->buf);
  74.         free(mp);
  75.     }
  76.  
  77.     /* If reading from a file, errors should have name and line info. */
  78.     if (F_ISSET(gp, G_SCRIPTED)) {
  79.         gp->excmd.if_lno = 1;
  80.         gp->excmd.if_name = "script";
  81.     }
  82.  
  83.     /*
  84.      * !!!
  85.      * Initialize the text flags.  The beautify edit option historically
  86.      * applied to ex command input read from a file.  In addition, the
  87.      * first time a ^H was discarded from the input, there was a message,
  88.      * "^H discarded", that was displayed.  We don't bother.
  89.      */
  90.     LF_INIT(TXT_BACKSLASH | TXT_CNTRLD | TXT_CR);
  91.     for (;; ++gp->excmd.if_lno) {
  92.         /* Display status line and flush. */
  93.         if (F_ISSET(sp, SC_STATUS)) {
  94.             if (!F_ISSET(sp, SC_EX_SILENT))
  95.                 msgq_status(sp, sp->lno, 0);
  96.             F_CLR(sp, SC_STATUS);
  97.         }
  98.         (void)ex_fflush(sp);
  99.  
  100.         /* Set the flags the user can reset. */
  101.         if (O_ISSET(sp, O_BEAUTIFY))
  102.             LF_SET(TXT_BEAUTIFY);
  103.         if (O_ISSET(sp, O_PROMPT))
  104.             LF_SET(TXT_PROMPT);
  105.  
  106.         /* Clear any current interrupts, and get a command. */
  107.         CLR_INTERRUPT(sp);
  108.         if (ex_txt(sp, &sp->tiq, ':', flags))
  109.             return (1);
  110.         if (INTERRUPTED(sp)) {
  111.             (void)ex_puts(sp, "\n");
  112.             (void)ex_fflush(sp);
  113.             continue;
  114.         }
  115.  
  116.         /* Initialize the command structure. */
  117.         CLEAR_EX_PARSER(&gp->excmd);
  118.  
  119.         /*
  120.          * If the user entered a single carriage return, send
  121.          * ex_cmd() a separator -- it discards single newlines.
  122.          */
  123.         tp = sp->tiq.cqh_first;
  124.         if (tp->len == 0) {
  125.             gp->excmd.cp = " ";    /* __TK__ why not |? */
  126.             gp->excmd.clen = 1;
  127.         } else {
  128.             gp->excmd.cp = tp->lb;
  129.             gp->excmd.clen = tp->len;
  130.         }
  131.         F_INIT(&gp->excmd, E_NRSEP);
  132.  
  133.         if (ex_cmd(sp) && F_ISSET(gp, G_SCRIPTED))
  134.             return (1);
  135.  
  136.         if (INTERRUPTED(sp)) {
  137.             CLR_INTERRUPT(sp);
  138.             msgq(sp, M_ERR, "170|Interrupted");
  139.         }
  140.  
  141.         /*
  142.          * If the last command caused a restart, or switched screens
  143.          * or into vi, return.
  144.          */
  145.         if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_SSWITCH | SC_VI)) {
  146.             *spp = sp;
  147.             break;
  148.         }
  149.  
  150.         /* If the last command switched files, we don't care. */
  151.         F_CLR(sp, SC_FSWITCH);
  152.  
  153.         /*
  154.          * If we're exiting this screen, move to the next one.  By
  155.          * definition, this means returning into vi, so return to the
  156.          * main editor loop.  The ordering is careful, don't discard
  157.          * the contents of sp until the end.
  158.          */
  159.         if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
  160.             if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
  161.                 return (1);
  162.             *spp = screen_next(sp);
  163.             return (screen_end(sp));
  164.         }
  165.     }
  166.     return (0);
  167. }
  168.  
  169. /*
  170.  * ex_cmd --
  171.  *    The guts of the ex parser: parse and execute a string containing
  172.  *    ex commands.
  173.  *
  174.  * !!!
  175.  * This code MODIFIES the string that gets passed in, to delete quoting
  176.  * characters, etc.  The string cannot be readonly/text space, nor should
  177.  * you expect to use it again after ex_cmd() returns.
  178.  *
  179.  * !!!
  180.  * For the fun of it, if you want to see if a vi clone got the ex argument
  181.  * parsing right, try:
  182.  *
  183.  *    echo 'foo|bar' > file1; echo 'foo/bar' > file2;
  184.  *    vi
  185.  *    :edit +1|s/|/PIPE/|w file1| e file2|1 | s/\//SLASH/|wq
  186.  *
  187.  * or:    vi
  188.  *    :set|file|append|set|file
  189.  *
  190.  * For extra credit, try them in a startup .exrc file.
  191.  *
  192.  * PUBLIC: int ex_cmd __P((SCR *));
  193.  */
  194. int
  195. ex_cmd(sp)
  196.     SCR *sp;
  197. {
  198.     enum nresult nret;
  199.     EX_PRIVATE *exp;
  200.     EXCMD *ecp;
  201.     GS *gp;
  202.     MARK cur;
  203.     recno_t lno;
  204.     size_t arg1_len, discard, len;
  205.     u_int32_t flags;
  206.     long ltmp;
  207.     int at_found, gv_found;
  208.     int ch, cnt, delim, isaddr, namelen;
  209.     int newscreen, notempty, tmp, vi_address;
  210.     char *arg1, *p, *s, *t;
  211.  
  212.     gp = sp->gp;
  213.     exp = EXP(sp);
  214.  
  215.     /*
  216.      * We always start running the command on the top of the stack.
  217.      * This means that *everything* must be resolved when we leave
  218.      * this function for any reason.
  219.      */
  220. loop:    ecp = gp->ecq.lh_first;
  221.  
  222.     /* If we're reading a command from a file, set up error information. */
  223.     if (ecp->if_name != NULL) {
  224.         gp->if_lno = ecp->if_lno;
  225.         gp->if_name = ecp->if_name;
  226.     }
  227.  
  228.     /*
  229.      * If a move to the end of the file is scheduled for this command,
  230.      * do it now.
  231.      */
  232.     if (F_ISSET(ecp, E_MOVETOEND)) {
  233.         if (db_last(sp, &sp->lno))
  234.             goto rfail;
  235.         sp->cno = 0;
  236.         F_CLR(ecp, E_MOVETOEND);
  237.     }
  238.  
  239.     /* If we found a newline, increment the count now. */
  240.     if (F_ISSET(ecp, E_NEWLINE)) {
  241.         ++gp->if_lno;
  242.         ++ecp->if_lno;
  243.         F_CLR(ecp, E_NEWLINE);
  244.     }
  245.  
  246.     /* (Re)initialize the EXCMD structure, preserving some flags. */
  247.     CLEAR_EX_CMD(ecp);
  248.  
  249.     /* Initialize the argument structures. */
  250.     if (argv_init(sp, ecp))
  251.         goto err;
  252.  
  253.     /* Initialize +cmd, saved command information. */
  254.     arg1 = NULL;
  255.     ecp->save_cmdlen = 0;
  256.  
  257.     /* Skip <blank>s, empty lines.  */
  258.     for (notempty = 0; ecp->clen > 0; ++ecp->cp, --ecp->clen)
  259.         if ((ch = *ecp->cp) == '\n') {
  260.             ++gp->if_lno;
  261.             ++ecp->if_lno;
  262.         } else if (isblank(ch))
  263.             notempty = 1;
  264.         else
  265.             break;
  266.  
  267.     /*
  268.      * !!!
  269.      * Permit extra colons at the start of the line.  Historically,
  270.      * ex/vi allowed a single extra one.  It's simpler not to count.
  271.      * The stripping is done here because, historically, any command
  272.      * could have preceding colons, e.g. ":g/pattern/:p" worked.
  273.      */
  274.     if (ecp->clen != 0 && ch == ':') {
  275.         notempty = 1;
  276.         while (--ecp->clen > 0 && (ch = *++ecp->cp) == ':');
  277.     }
  278.  
  279.     /*
  280.      * Command lines that start with a double-quote are comments.
  281.      *
  282.      * !!!
  283.      * Historically, there was no escape or delimiter for a comment, e.g.
  284.      * :"foo|set was a single comment and nothing was output.  Since nvi
  285.      * permits users to escape <newline> characters into command lines, we
  286.      * have to check for that case.
  287.      */
  288.     if (ecp->clen != 0 && ch == '"') {
  289.         while (--ecp->clen > 0 && *++ecp->cp != '\n');
  290.         if (*ecp->cp == '\n') {
  291.             F_SET(ecp, E_NEWLINE);
  292.             ++ecp->cp;
  293.             --ecp->clen;
  294.         }
  295.         goto loop;
  296.     }
  297.  
  298.     /* Skip whitespace. */
  299.     for (; ecp->clen > 0; ++ecp->cp, --ecp->clen) {
  300.         ch = *ecp->cp;
  301.         if (!isblank(ch))
  302.             break;
  303.     }
  304.  
  305.     /*
  306.      * The last point at which an empty line can mean do nothing.
  307.      *
  308.      * !!!
  309.      * Historically, in ex mode, lines containing only <blank> characters
  310.      * were the same as a single <carriage-return>, i.e. a default command.
  311.      * In vi mode, they were ignored.  In .exrc files this was a serious
  312.      * annoyance, as vi kept trying to treat them as print commands.  We
  313.      * ignore backward compatibility in this case, discarding lines that
  314.      * contain only <blank> characters from .exrc files.
  315.      *
  316.      * !!!
  317.      * This is where you end up when you're done a command, i.e. clen has
  318.      * gone to zero.  Continue if there are more commands to run.
  319.      */
  320.     if (ecp->clen == 0 &&
  321.         (!notempty || F_ISSET(sp, SC_VI) || F_ISSET(ecp, E_BLIGNORE))) {
  322.         if (ex_load(sp))
  323.             goto rfail;
  324.         ecp = gp->ecq.lh_first;
  325.         if (ecp->clen == 0)
  326.             goto rsuccess;
  327.         goto loop;
  328.     }
  329.  
  330.     /*
  331.      * Check to see if this is a command for which we may want to move
  332.      * the cursor back up to the previous line.  (The command :1<CR>
  333.      * wants a <newline> separator, but the command :<CR> wants to erase
  334.      * the command line.)  If the line is empty except for <blank>s,
  335.      * <carriage-return> or <eof>, we'll probably want to move up.  I
  336.      * don't think there's any way to get <blank> characters *after* the
  337.      * command character, but this is the ex parser, and I've been wrong
  338.      * before.
  339.      */
  340.     if (F_ISSET(ecp, E_NRSEP) &&
  341.         ecp->clen != 0 && (ecp->clen != 1 || ecp->cp[0] != '\004'))
  342.         F_CLR(ecp, E_NRSEP);
  343.  
  344.     /* Parse command addresses. */
  345.     if (ex_range(sp, ecp, &tmp))
  346.         goto rfail;
  347.     if (tmp)
  348.         goto err;
  349.  
  350.     /*
  351.      * Skip <blank>s and any more colons (the command :3,5:print
  352.      * worked, historically).
  353.      */
  354.     for (; ecp->clen > 0; ++ecp->cp, --ecp->clen) {
  355.         ch = *ecp->cp;
  356.         if (!isblank(ch) && ch != ':')
  357.             break;
  358.     }
  359.  
  360.     /*
  361.      * If no command, ex does the last specified of p, l, or #, and vi
  362.      * moves to the line.  Otherwise, determine the length of the command
  363.      * name by looking for the first non-alphabetic character.  (There
  364.      * are a few non-alphabetic characters in command names, but they're
  365.      * all single character commands.)  This isn't a great test, because
  366.      * it means that, for the command ":e +cut.c file", we'll report that
  367.      * the command "cut" wasn't known.  However, it makes ":e+35 file" work
  368.      * correctly.
  369.      *
  370.      * !!!
  371.      * Historically, lines with multiple adjacent (or <blank> separated)
  372.      * command separators were very strange.  For example, the command
  373.      * |||<carriage-return>, when the cursor was on line 1, displayed
  374.      * lines 2, 3 and 5 of the file.  In addition, the command "   |  "
  375.      * would only display the line after the next line, instead of the
  376.      * next two lines.  No ideas why.  It worked reasonably when executed
  377.      * from vi mode, and displayed lines 2, 3, and 4, so we do a default
  378.      * command for each separator.
  379.      */
  380. #define    SINGLE_CHAR_COMMANDS    "\004!#&*<=>@~"
  381.     newscreen = 0;
  382.     if (ecp->clen != 0 && ecp->cp[0] != '|' && ecp->cp[0] != '\n') {
  383.         if (strchr(SINGLE_CHAR_COMMANDS, *ecp->cp)) {
  384.             p = ecp->cp;
  385.             ++ecp->cp;
  386.             --ecp->clen;
  387.             namelen = 1;
  388.         } else {
  389.             for (p = ecp->cp;
  390.                 ecp->clen > 0; --ecp->clen, ++ecp->cp)
  391.                 if (!isalpha(*ecp->cp))
  392.                     break;
  393.             if ((namelen = ecp->cp - p) == 0) {
  394.                 msgq(sp, M_ERR, "080|Unknown command name");
  395.                 goto err;
  396.             }
  397.         }
  398.  
  399.         /*
  400.          * !!!
  401.          * Historic vi permitted flags to immediately follow any
  402.          * subset of the 'delete' command, but then did not permit
  403.          * further arguments (flag, buffer, count).  Make it work.
  404.          * Permit further arguments for the few shreds of dignity
  405.          * it offers.
  406.          *
  407.          * Adding commands that start with 'd', and match "delete"
  408.          * up to a l, p, +, - or # character can break this code.
  409.          *
  410.          * !!!
  411.          * Capital letters beginning the command names ex, edit,
  412.          * next, previous, tag and visual (in vi mode) indicate the
  413.          * command should happen in a new screen.
  414.          */
  415.         switch (p[0]) {
  416.         case 'd':
  417.             for (s = p,
  418.                 t = cmds[C_DELETE].name; *s == *t; ++s, ++t);
  419.             if (s[0] == 'l' || s[0] == 'p' || s[0] == '+' ||
  420.                 s[0] == '-' || s[0] == '^' || s[0] == '#') {
  421.                 len = (ecp->cp - p) - (s - p);
  422.                 ecp->cp -= len;
  423.                 ecp->clen += len;
  424.                 ecp->rcmd = cmds[C_DELETE];
  425.                 ecp->rcmd.syntax = "1bca1";
  426.                 ecp->cmd = &ecp->rcmd;
  427.                 goto skip_srch;
  428.             }
  429.             break;
  430.         case 'E': case 'F': case 'N': case 'P': case 'T': case 'V':
  431.             newscreen = 1;
  432.             p[0] = tolower(p[0]);
  433.             break;
  434.         }
  435.  
  436.         /*
  437.          * Search the table for the command.
  438.          *
  439.          * !!!
  440.          * Historic vi permitted the mark to immediately follow the
  441.          * 'k' in the 'k' command.  Make it work.
  442.          *
  443.          * !!!
  444.          * Historic vi permitted any flag to follow the s command, e.g.
  445.          * "s/e/E/|s|sgc3p" was legal.  Make the command "sgc" work.
  446.          * Since the following characters all have to be flags, i.e.
  447.          * alphabetics, we can let the s command routine return errors
  448.          * if it was some illegal command string.  This code will break
  449.          * if an "sg" or similar command is ever added.  The substitute
  450.          * code doesn't care if it's a "cgr" flag or a "#lp" flag that
  451.          * follows the 's', but we limit the choices here to "cgr" so
  452.          * that we get unknown command messages for wrong combinations.
  453.          */
  454.         if ((ecp->cmd = ex_comm_search(p, namelen)) == NULL)
  455.             switch (p[0]) {
  456.             case 'k':
  457.                 if (namelen == 2) {
  458.                     ecp->cp -= namelen - 1;
  459.                     ecp->clen += namelen - 1;
  460.                     ecp->cmd = &cmds[C_K];
  461.                     break;
  462.                 }
  463.                 goto unknown;
  464.             case 's':
  465.                 for (s = p + 1, cnt = namelen; --cnt; ++s)
  466.                     if (s[0] != 'c' &&
  467.                         s[0] != 'g' && s[0] != 'r')
  468.                         break;
  469.                 if (cnt == 0) {
  470.                     ecp->cp -= namelen - 1;
  471.                     ecp->clen += namelen - 1;
  472.                     ecp->rcmd = cmds[C_SUBSTITUTE];
  473.                     ecp->rcmd.fn = ex_subagain;
  474.                     ecp->cmd = &ecp->rcmd;
  475.                     break;
  476.                 }
  477.                 /* FALLTHROUGH */
  478.             default:
  479. unknown:            if (newscreen)
  480.                     p[0] = toupper(p[0]);
  481.                 ex_unknown(sp, p, namelen);
  482.                 goto err;
  483.             }
  484.  
  485.         /*
  486.          * The visual command has a different syntax when called
  487.          * from ex than when called from a vi colon command.  FMH.
  488.          * Make the change now, before we test for the newscreen
  489.          * semantic, so that we're testing the right one.
  490.          */
  491. skip_srch:    if (ecp->cmd == &cmds[C_VISUAL_EX] && F_ISSET(sp, SC_VI))
  492.             ecp->cmd = &cmds[C_VISUAL_VI];
  493.  
  494.         /*
  495.          * !!!
  496.          * Historic vi permitted a capital 'P' at the beginning of
  497.          * any command that started with 'p'.  Probably wanted the
  498.          * P[rint] command for backward compatibility, and the code
  499.          * just made Preserve and Put work by accident.  Nvi uses
  500.          * Previous to mean previous-in-a-new-screen, so be careful.
  501.          */
  502.         if (newscreen && !F_ISSET(ecp->cmd, E_NEWSCREEN) &&
  503.             (ecp->cmd == &cmds[C_PRINT] ||
  504.             ecp->cmd == &cmds[C_PRESERVE]))
  505.             newscreen = 0;
  506.  
  507.         /* Test for a newscreen associated with this command. */
  508.         if (newscreen && !F_ISSET(ecp->cmd, E_NEWSCREEN))
  509.             goto unknown;
  510.  
  511.         /* Secure means no shell access. */
  512.         if (F_ISSET(ecp->cmd, E_SECURE) && O_ISSET(sp, O_SECURE)) {
  513.             ex_emsg(sp, ecp->cmd->name, EXM_SECURE);
  514.             goto err;
  515.         }
  516.  
  517.         /*
  518.          * Multiple < and > characters; another "feature".  Note,
  519.          * The string passed to the underlying function may not be
  520.          * nul terminated in this case.
  521.          */
  522.         if ((ecp->cmd == &cmds[C_SHIFTL] && *p == '<') ||
  523.             (ecp->cmd == &cmds[C_SHIFTR] && *p == '>')) {
  524.             for (ch = *p;
  525.                 ecp->clen > 0; --ecp->clen, ++ecp->cp)
  526.                 if (*ecp->cp != ch)
  527.                     break;
  528.             if (argv_exp0(sp, ecp, p, ecp->cp - p))
  529.                 goto err;
  530.         }
  531.  
  532.         /* Set the format style flags for the next command. */
  533.         if (ecp->cmd == &cmds[C_HASH])
  534.             exp->fdef = E_C_HASH;
  535.         else if (ecp->cmd == &cmds[C_LIST])
  536.             exp->fdef = E_C_LIST;
  537.         else if (ecp->cmd == &cmds[C_PRINT])
  538.             exp->fdef = E_C_PRINT;
  539.         F_CLR(ecp, E_USELASTCMD);
  540.     } else {
  541.         /* Print is the default command. */
  542.         ecp->cmd = &cmds[C_PRINT];
  543.  
  544.         /* Set the saved format flags. */
  545.         F_SET(ecp, exp->fdef);
  546.  
  547.         /*
  548.          * !!!
  549.          * If no address was specified, and it's not a global command,
  550.          * we up the address by one.  (I have no idea why globals are
  551.          * exempted, but it's (ahem) historic practice.)
  552.          */
  553.         if (ecp->addrcnt == 0 && !F_ISSET(sp, SC_EX_GLOBAL)) {
  554.             ecp->addrcnt = 1;
  555.             ecp->addr1.lno = sp->lno + 1;
  556.             ecp->addr1.cno = sp->cno;
  557.         }
  558.  
  559.         F_SET(ecp, E_USELASTCMD);
  560.     }
  561.  
  562.     /*
  563.      * !!!
  564.      * Historically, the number option applied to both ex and vi.  One
  565.      * strangeness was that ex didn't switch display formats until a
  566.      * command was entered, e.g. <CR>'s after the set didn't change to
  567.      * the new format, but :1p would.
  568.      */
  569.     if (O_ISSET(sp, O_NUMBER)) {
  570.         F_SET(ecp, E_OPTNUM);
  571.         FL_SET(ecp->iflags, E_C_HASH);
  572.     } else
  573.         F_CLR(ecp, E_OPTNUM);
  574.  
  575.     /* Check for ex mode legality. */
  576.     if (F_ISSET(sp, SC_EX) && (F_ISSET(ecp->cmd, E_VIONLY) || newscreen)) {
  577.         msgq(sp, M_ERR,
  578.             "082|%s: command not available in ex mode", ecp->cmd->name);
  579.         goto err;
  580.     }
  581.  
  582.     /* Add standard command flags. */
  583.     F_SET(ecp, ecp->cmd->flags);
  584.     if (!newscreen)
  585.         F_CLR(ecp, E_NEWSCREEN);
  586.  
  587.     /*
  588.      * There are three normal termination cases for an ex command.  They
  589.      * are the end of the string (ecp->clen), or unescaped (by <literal
  590.      * next> characters) <newline> or '|' characters.  As we're now past
  591.      * possible addresses, we can determine how long the command is, so we
  592.      * don't have to look for all the possible terminations.  Naturally,
  593.      * there are some exciting special cases:
  594.      *
  595.      * 1: The bang, global, v and the filter versions of the read and
  596.      *    write commands are delimited by <newline>s (they can contain
  597.      *    shell pipes).
  598.      * 2: The ex, edit, next and visual in vi mode commands all take ex
  599.      *    commands as their first arguments.
  600.      * 3: The s command takes an RE as its first argument, and wants it
  601.      *    to be specially delimited.
  602.      *
  603.      * Historically, '|' characters in the first argument of the ex, edit,
  604.      * next, vi visual, and s commands didn't delimit the command.  And,
  605.      * in the filter cases for read and write, and the bang, global and v
  606.      * commands, they did not delimit the command at all.
  607.      *
  608.      * For example, the following commands were legal:
  609.      *
  610.      *    :edit +25|s/abc/ABC/ file.c
  611.      *    :s/|/PIPE/
  612.      *    :read !spell % | columnate
  613.      *    :global/pattern/p|l
  614.      *
  615.      * It's not quite as simple as it sounds, however.  The command:
  616.      *
  617.      *    :s/a/b/|s/c/d|set
  618.      *
  619.      * was also legal, i.e. the historic ex parser (using the word loosely,
  620.      * since "parser" implies some regularity of syntax) delimited the RE's
  621.      * based on its delimiter and not anything so irretrievably vulgar as a
  622.      * command syntax.
  623.      *
  624.      * Anyhow, the following code makes this all work.  First, for the
  625.      * special cases we move past their special argument(s).  Then, we
  626.      * do normal command processing on whatever is left.  Barf-O-Rama.
  627.      */
  628.     discard = 0;        /* Characters discarded from the command. */
  629.     arg1_len = 0;
  630.     ecp->save_cmd = ecp->cp;
  631.     if (ecp->cmd == &cmds[C_EDIT] || ecp->cmd == &cmds[C_EX] ||
  632.         ecp->cmd == &cmds[C_NEXT] || ecp->cmd == &cmds[C_VISUAL_VI]) {
  633.         /*
  634.          * Move to the next non-whitespace character.  A '!'
  635.          * immediately following the command is eaten as a
  636.          * force flag.
  637.          */
  638.         if (ecp->clen > 0 && *ecp->cp == '!') {
  639.             ++ecp->cp;
  640.             --ecp->clen;
  641.             FL_SET(ecp->iflags, E_C_FORCE);
  642.  
  643.             /* Reset, don't reparse. */
  644.             ecp->save_cmd = ecp->cp;
  645.         }
  646.         for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
  647.             if (!isblank(*ecp->cp))
  648.                 break;
  649.         /*
  650.          * QUOTING NOTE:
  651.          *
  652.          * The historic implementation ignored all escape characters
  653.          * so there was no way to put a space or newline into the +cmd
  654.          * field.  We do a simplistic job of fixing it by moving to the
  655.          * first whitespace character that isn't escaped.  The escaping
  656.          * characters are stripped as no longer useful.
  657.          */
  658.         if (ecp->clen > 0 && *ecp->cp == '+') {
  659.             ++ecp->cp;
  660.             --ecp->clen;
  661.             for (arg1 = p = ecp->cp;
  662.                 ecp->clen > 0; --ecp->clen, ++ecp->cp) {
  663.                 ch = *ecp->cp;
  664.                 if (IS_ESCAPE(sp, ecp, ch) &&
  665.                     ecp->clen > 1) {
  666.                     ++discard;
  667.                     --ecp->clen;
  668.                     ch = *++ecp->cp;
  669.                 } else if (isblank(ch))
  670.                     break;
  671.                 *p++ = ch;
  672.             }
  673.             arg1_len = ecp->cp - arg1;
  674.  
  675.             /* Reset, so the first argument isn't reparsed. */
  676.             ecp->save_cmd = ecp->cp;
  677.         }
  678.     } else if (ecp->cmd == &cmds[C_BANG] ||
  679.         ecp->cmd == &cmds[C_GLOBAL] || ecp->cmd == &cmds[C_V]) {
  680.         /*
  681.          * QUOTING NOTE:
  682.          *
  683.          * We use backslashes to escape <newline> characters, although
  684.          * this wasn't historic practice for the bang command.  It was
  685.          * for the global and v commands, and it's common usage when
  686.          * doing text insert during the command.  Escaping characters
  687.          * are stripped as no longer useful.
  688.          */
  689.         for (p = ecp->cp; ecp->clen > 0; --ecp->clen, ++ecp->cp) {
  690.             ch = *ecp->cp;
  691.             if (ch == '\\' && ecp->clen > 1 && ecp->cp[1] == '\n') {
  692.                 ++discard;
  693.                 --ecp->clen;
  694.                 ch = *++ecp->cp;
  695.  
  696.                 ++gp->if_lno;
  697.                 ++ecp->if_lno;
  698.             } else if (ch == '\n')
  699.                 break;
  700.             *p++ = ch;
  701.         }
  702.     } else if (ecp->cmd == &cmds[C_READ] || ecp->cmd == &cmds[C_WRITE]) {
  703.         /*
  704.          * For write commands, if the next character is a <blank>, and
  705.          * the next non-blank character is a '!', it's a filter command
  706.          * and we want to eat everything up to the <newline>.  For read
  707.          * commands, if the next non-blank character is a '!', it's a
  708.          * filter command and we want to eat everything up to the next
  709.          * <newline>.  Otherwise, we're done.
  710.          */
  711.         for (tmp = 0; ecp->clen > 0; --ecp->clen, ++ecp->cp) {
  712.             ch = *ecp->cp;
  713.             if (isblank(ch))
  714.                 tmp = 1;
  715.             else
  716.                 break;
  717.         }
  718.         if (ecp->clen > 0 && ch == '!' &&
  719.             (ecp->cmd == &cmds[C_READ] || tmp))
  720.             for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
  721.                 if (ecp->cp[0] == '\n')
  722.                     break;
  723.     } else if (ecp->cmd == &cmds[C_SUBSTITUTE]) {
  724.         /*
  725.          * Move to the next non-whitespace character, we'll use it as
  726.          * the delimiter.  If the character isn't an alphanumeric or
  727.          * a '|', it's the delimiter, so parse it.  Otherwise, we're
  728.          * into something like ":s g", so use the special s command.
  729.          */
  730.         for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
  731.             if (!isblank(ecp->cp[0]))
  732.                 break;
  733.  
  734.         if (isalnum(ecp->cp[0]) || ecp->cp[0] == '|') {
  735.             ecp->rcmd = cmds[C_SUBSTITUTE];
  736.             ecp->rcmd.fn = ex_subagain;
  737.             ecp->cmd = &ecp->rcmd;
  738.         } else if (ecp->clen > 0) {
  739.             /*
  740.              * QUOTING NOTE:
  741.              *
  742.              * Backslashes quote delimiter characters for RE's.
  743.              * The backslashes are NOT removed since they'll be
  744.              * used by the RE code.  Move to the third delimiter
  745.              * that's not escaped (or the end of the command).
  746.              */
  747.             delim = *ecp->cp;
  748.             ++ecp->cp;
  749.             --ecp->clen;
  750.             for (cnt = 2; ecp->clen > 0 &&
  751.                 cnt != 0; --ecp->clen, ++ecp->cp)
  752.                 if (ecp->cp[0] == '\\' &&
  753.                     ecp->clen > 1) {
  754.                     ++ecp->cp;
  755.                     --ecp->clen;
  756.                 } else if (ecp->cp[0] == delim)
  757.                     --cnt;
  758.         }
  759.     }
  760.  
  761.     /*
  762.      * Use normal quoting and termination rules to find the end of this
  763.      * command.
  764.      *
  765.      * QUOTING NOTE:
  766.      *
  767.      * Historically, vi permitted ^V's to escape <newline>'s in the .exrc
  768.      * file.  It was almost certainly a bug, but that's what bug-for-bug
  769.      * compatibility means, Grasshopper.  Also, ^V's escape the command
  770.      * delimiters.  Literal next quote characters in front of the newlines,
  771.      * '|' characters or literal next characters are stripped as they're
  772.      * no longer useful.
  773.      */
  774.     vi_address = ecp->clen != 0 && ecp->cp[0] != '\n';
  775.     for (p = ecp->cp; ecp->clen > 0; --ecp->clen, ++ecp->cp) {
  776.         ch = ecp->cp[0];
  777.         if (IS_ESCAPE(sp, ecp, ch) && ecp->clen > 1) {
  778.             tmp = ecp->cp[1];
  779.             if (tmp == '\n' || tmp == '|') {
  780.                 if (tmp == '\n') {
  781.                     ++gp->if_lno;
  782.                     ++ecp->if_lno;
  783.                 }
  784.                 ++discard;
  785.                 --ecp->clen;
  786.                 ++ecp->cp;
  787.                 ch = tmp;
  788.             }
  789.         } else if (ch == '\n' || ch == '|') {
  790.             if (ch == '\n')
  791.                 F_SET(ecp, E_NEWLINE);
  792.             --ecp->clen;
  793.             break;
  794.         }
  795.         *p++ = ch;
  796.     }
  797.  
  798.     /*
  799.      * Save off the next command information, go back to the
  800.      * original start of the command.
  801.      */
  802.     p = ecp->cp + 1;
  803.     ecp->cp = ecp->save_cmd;
  804.     ecp->save_cmd = p;
  805.     ecp->save_cmdlen = ecp->clen;
  806.     ecp->clen = ((ecp->save_cmd - ecp->cp) - 1) - discard;
  807.  
  808.     /*
  809.      * QUOTING NOTE:
  810.      *
  811.      * The "set tags" command historically used a backslash, not the
  812.      * user's literal next character, to escape whitespace.  Handle
  813.      * it here instead of complicating the argv_exp3() code.  Note,
  814.      * this isn't a particularly complex trap, and if backslashes were
  815.      * legal in set commands, this would have to be much more complicated.
  816.      */
  817.     if (ecp->cmd == &cmds[C_SET])
  818.         for (p = ecp->cp, len = ecp->clen; len > 0; --len, ++p)
  819.             if (*p == '\\')
  820.                 *p = CH_LITERAL;
  821.  
  822.     /*
  823.      * Set the default addresses.  It's an error to specify an address for
  824.      * a command that doesn't take them.  If two addresses are specified
  825.      * for a command that only takes one, lose the first one.  Two special
  826.      * cases here, some commands take 0 or 2 addresses.  For most of them
  827.      * (the E_ADDR2_ALL flag), 0 defaults to the entire file.  For one
  828.      * (the `!' command, the E_ADDR2_NONE flag), 0 defaults to no lines.
  829.      *
  830.      * Also, if the file is empty, some commands want to use an address of
  831.      * 0, i.e. the entire file is 0 to 0, and the default first address is
  832.      * 0.  Otherwise, an entire file is 1 to N and the default line is 1.
  833.      * Note, we also add the E_ADDR_ZERO flag to the command flags, for the
  834.      * case where the 0 address is only valid if it's a default address.
  835.      *
  836.      * Also, set a flag if we set the default addresses.  Some commands
  837.      * (ex: z) care if the user specified an address or if we just used
  838.      * the current cursor.
  839.      */
  840.     switch (F_ISSET(ecp, E_ADDR1 | E_ADDR2 | E_ADDR2_ALL | E_ADDR2_NONE)) {
  841.     case E_ADDR1:                /* One address: */
  842.         switch (ecp->addrcnt) {
  843.         case 0:                /* Default cursor/empty file. */
  844.             ecp->addrcnt = 1;
  845.             F_SET(ecp, E_ADDR_DEF);
  846.             if (F_ISSET(ecp, E_ADDR_ZERODEF)) {
  847.                 if (db_last(sp, &lno))
  848.                     goto err;
  849.                 if (lno == 0) {
  850.                     ecp->addr1.lno = 0;
  851.                     F_SET(ecp, E_ADDR_ZERO);
  852.                 } else
  853.                     ecp->addr1.lno = sp->lno;
  854.             } else
  855.                 ecp->addr1.lno = sp->lno;
  856.             ecp->addr1.cno = sp->cno;
  857.             break;
  858.         case 1:
  859.             break;
  860.         case 2:                /* Lose the first address. */
  861.             ecp->addrcnt = 1;
  862.             ecp->addr1 = ecp->addr2;
  863.         }
  864.         break;
  865.     case E_ADDR2_NONE:            /* Zero/two addresses: */
  866.         if (ecp->addrcnt == 0)        /* Default to nothing. */
  867.             break;
  868.         goto two_addr;
  869.     case E_ADDR2_ALL:            /* Zero/two addresses: */
  870.         if (ecp->addrcnt == 0) {    /* Default entire/empty file. */
  871.             F_SET(ecp, E_ADDR_DEF);
  872.             ecp->addrcnt = 2;
  873.             if (sp->ep == NULL)
  874.                 ecp->addr2.lno = 0;
  875.             else if (db_last(sp, &ecp->addr2.lno))
  876.                 goto err;
  877.             if (F_ISSET(ecp, E_ADDR_ZERODEF) &&
  878.                 ecp->addr2.lno == 0) {
  879.                 ecp->addr1.lno = 0;
  880.                 F_SET(ecp, E_ADDR_ZERO);
  881.             } else
  882.                 ecp->addr1.lno = 1;
  883.             ecp->addr1.cno = ecp->addr2.cno = 0;
  884.             F_SET(ecp, E_ADDR2_ALL);
  885.             break;
  886.         }
  887.         /* FALLTHROUGH */
  888.     case E_ADDR2:                /* Two addresses: */
  889. two_addr:    switch (ecp->addrcnt) {
  890.         case 0:                /* Default cursor/empty file. */
  891.             ecp->addrcnt = 2;
  892.             F_SET(ecp, E_ADDR_DEF);
  893.             if (sp->lno == 1 &&
  894.                 F_ISSET(ecp, E_ADDR_ZERODEF)) {
  895.                 if (db_last(sp, &lno))
  896.                     goto err;
  897.                 if (lno == 0) {
  898.                     ecp->addr1.lno = ecp->addr2.lno = 0;
  899.                     F_SET(ecp, E_ADDR_ZERO);
  900.                 } else
  901.                     ecp->addr1.lno =
  902.                         ecp->addr2.lno = sp->lno;
  903.             } else
  904.                 ecp->addr1.lno = ecp->addr2.lno = sp->lno;
  905.             ecp->addr1.cno = ecp->addr2.cno = sp->cno;
  906.             break;
  907.         case 1:                /* Default to first address. */
  908.             ecp->addrcnt = 2;
  909.             ecp->addr2 = ecp->addr1;
  910.             break;
  911.         case 2:
  912.             break;
  913.         }
  914.         break;
  915.     default:
  916.         if (ecp->addrcnt)        /* Error. */
  917.             goto usage;
  918.     }
  919.  
  920.     /*
  921.      * !!!
  922.      * The ^D scroll command historically scrolled the value of the scroll
  923.      * option or to EOF.  It was an error if the cursor was already at EOF.
  924.      * (Leading addresses were permitted, but were then ignored.)
  925.      */
  926.     if (ecp->cmd == &cmds[C_SCROLL]) {
  927.         ecp->addrcnt = 2;
  928.         ecp->addr1.lno = sp->lno + 1;
  929.         ecp->addr2.lno = sp->lno + O_VAL(sp, O_SCROLL);
  930.         ecp->addr1.cno = ecp->addr2.cno = sp->cno;
  931.         if (db_last(sp, &lno))
  932.             goto err;
  933.         if (lno != 0 && lno > sp->lno && ecp->addr2.lno > lno)
  934.             ecp->addr2.lno = lno;
  935.     }
  936.  
  937.     ecp->flagoff = 0;
  938.     for (p = ecp->cmd->syntax; *p != '\0'; ++p) {
  939.         /*
  940.          * The force flag is sensitive to leading whitespace, i.e.
  941.          * "next !" is different from "next!".  Handle it before
  942.          * skipping leading <blank>s.
  943.          */
  944.         if (*p == '!') {
  945.             if (ecp->clen > 0 && *ecp->cp == '!') {
  946.                 ++ecp->cp;
  947.                 --ecp->clen;
  948.                 FL_SET(ecp->iflags, E_C_FORCE);
  949.             }
  950.             continue;
  951.         }
  952.  
  953.         /* Skip leading <blank>s. */
  954.         for (; ecp->clen > 0; --ecp->clen, ++ecp->cp)
  955.             if (!isblank(*ecp->cp))
  956.                 break;
  957.         if (ecp->clen == 0)
  958.             break;
  959.  
  960.         switch (*p) {
  961.         case '1':                /* +, -, #, l, p */
  962.             /*
  963.              * !!!
  964.              * Historically, some flags were ignored depending
  965.              * on where they occurred in the command line.  For
  966.              * example, in the command, ":3+++p--#", historic vi
  967.              * acted on the '#' flag, but ignored the '-' flags.
  968.              * It's unambiguous what the flags mean, so we just
  969.              * handle them regardless of the stupidity of their
  970.              * location.
  971.              */
  972.             for (; ecp->clen; --ecp->clen, ++ecp->cp)
  973.                 switch (*ecp->cp) {
  974.                 case '+':
  975.                     ++ecp->flagoff;
  976.                     break;
  977.                 case '-':
  978.                 case '^':
  979.                     --ecp->flagoff;
  980.                     break;
  981.                 case '#':
  982.                     F_CLR(ecp, E_OPTNUM);
  983.                     FL_SET(ecp->iflags, E_C_HASH);
  984.                     exp->fdef |= E_C_HASH;
  985.                     break;
  986.                 case 'l':
  987.                     FL_SET(ecp->iflags, E_C_LIST);
  988.                     exp->fdef |= E_C_LIST;
  989.                     break;
  990.                 case 'p':
  991.                     FL_SET(ecp->iflags, E_C_PRINT);
  992.                     exp->fdef |= E_C_PRINT;
  993.                     break;
  994.                 default:
  995.                     goto end_case1;
  996.                 }
  997. end_case1:        break;
  998.         case '2':                /* -, ., +, ^ */
  999.         case '3':                /* -, ., +, ^, = */
  1000.             for (; ecp->clen; --ecp->clen, ++ecp->cp)
  1001.                 switch (*ecp->cp) {
  1002.                 case '-':
  1003.                     FL_SET(ecp->iflags, E_C_DASH);
  1004.                     break;
  1005.                 case '.':
  1006.                     FL_SET(ecp->iflags, E_C_DOT);
  1007.                     break;
  1008.                 case '+':
  1009.                     FL_SET(ecp->iflags, E_C_PLUS);
  1010.                     break;
  1011.                 case '^':
  1012.                     FL_SET(ecp->iflags, E_C_CARAT);
  1013.                     break;
  1014.                 case '=':
  1015.                     if (*p == '3') {
  1016.                         FL_SET(ecp->iflags, E_C_EQUAL);
  1017.                         break;
  1018.                     }
  1019.                     /* FALLTHROUGH */
  1020.                 default:
  1021.                     goto end_case23;
  1022.                 }
  1023. end_case23:        break;
  1024.         case 'b':                /* buffer */
  1025.             /*
  1026.              * !!!
  1027.              * Historically, "d #" was a delete with a flag, not a
  1028.              * delete into the '#' buffer.  If the current command
  1029.              * permits a flag, don't use one as a buffer.  However,
  1030.              * the 'l' and 'p' flags were legal buffer names in the
  1031.              * historic ex, and were used as buffers, not flags.
  1032.              */
  1033.             if ((ecp->cp[0] == '+' || ecp->cp[0] == '-' ||
  1034.                 ecp->cp[0] == '^' || ecp->cp[0] == '#') &&
  1035.                 strchr(p, '1') != NULL)
  1036.                 break;
  1037.             /*
  1038.              * !!!
  1039.              * Digits can't be buffer names in ex commands, or the
  1040.              * command "d2" would be a delete into buffer '2', and
  1041.              * not a two-line deletion.
  1042.              */
  1043.             if (!isdigit(ecp->cp[0])) {
  1044.                 ecp->buffer = *ecp->cp;
  1045.                 ++ecp->cp;
  1046.                 --ecp->clen;
  1047.                 FL_SET(ecp->iflags, E_C_BUFFER);
  1048.             }
  1049.             break;
  1050.         case 'c':                /* count [01+a] */
  1051.             ++p;
  1052.             /* Validate any signed value. */
  1053.             if (!isdigit(*ecp->cp) && (*p != '+' ||
  1054.                 (*ecp->cp != '+' && *ecp->cp != '-')))
  1055.                 break;
  1056.             /* If a signed value, set appropriate flags. */
  1057.             if (*ecp->cp == '-')
  1058.                 FL_SET(ecp->iflags, E_C_COUNT_NEG);
  1059.             else if (*ecp->cp == '+')
  1060.                 FL_SET(ecp->iflags, E_C_COUNT_POS);
  1061.             if ((nret =
  1062.                 nget_slong(<mp, ecp->cp, &t, 10)) != NUM_OK) {
  1063.                 ex_badaddr(sp, NULL, A_NOTSET, nret);
  1064.                 goto err;
  1065.             }
  1066.             if (ltmp == 0 && *p != '0') {
  1067.                 msgq(sp, M_ERR, "083|Count may not be zero");
  1068.                 goto err;
  1069.             }
  1070.             ecp->clen -= (t - ecp->cp);
  1071.             ecp->cp = t;
  1072.  
  1073.             /*
  1074.              * Counts as address offsets occur in commands taking
  1075.              * two addresses.  Historic vi practice was to use
  1076.              * the count as an offset from the *second* address.
  1077.              *
  1078.              * Set a count flag; some underlying commands (see
  1079.              * join) do different things with counts than with
  1080.              * line addresses.
  1081.              */
  1082.             if (*p == 'a') {
  1083.                 ecp->addr1 = ecp->addr2;
  1084.                 ecp->addr2.lno = ecp->addr1.lno + ltmp - 1;
  1085.             } else
  1086.                 ecp->count = ltmp;
  1087.             FL_SET(ecp->iflags, E_C_COUNT);
  1088.             break;
  1089.         case 'f':                /* file */
  1090.             if (argv_exp2(sp, ecp, ecp->cp, ecp->clen))
  1091.                 goto err;
  1092.             goto arg_cnt_chk;
  1093.         case 'l':                /* line */
  1094.             /*
  1095.              * Get a line specification.
  1096.              *
  1097.              * If the line was a search expression, we may have
  1098.              * changed state during the call, and we're now
  1099.              * searching the file.  Push ourselves onto the state
  1100.              * stack.
  1101.              */
  1102.             if (ex_line(sp, ecp, &cur, &isaddr, &tmp))
  1103.                 goto rfail;
  1104.             if (tmp)
  1105.                 goto err;
  1106.  
  1107.             /* Line specifications are always required. */
  1108.             if (!isaddr) {
  1109.                 msgq_str(sp, M_ERR, ecp->cp,
  1110.                      "084|%s: bad line specification");
  1111.                 goto err;
  1112.             }
  1113.             /*
  1114.              * The target line should exist for these commands,
  1115.              * but 0 is legal for them as well.
  1116.              */
  1117.             if (cur.lno != 0 && !db_exist(sp, cur.lno)) {
  1118.                 ex_badaddr(sp, NULL, A_EOF, NUM_OK);
  1119.                 goto err;
  1120.             }
  1121.             ecp->lineno = cur.lno;
  1122.             break;
  1123.         case 'S':                /* string, file exp. */
  1124.             if (ecp->clen != 0) {
  1125.                 if (argv_exp1(sp, ecp, ecp->cp,
  1126.                     ecp->clen, ecp->cmd == &cmds[C_BANG]))
  1127.                     goto err;
  1128.                 goto addr_verify;
  1129.             }
  1130.             /* FALLTHROUGH */
  1131.         case 's':                /* string */
  1132.             if (argv_exp0(sp, ecp, ecp->cp, ecp->clen))
  1133.                 goto err;
  1134.             goto addr_verify;
  1135.         case 'W':                /* word string */
  1136.             /*
  1137.              * QUOTING NOTE:
  1138.              *
  1139.              * Literal next characters escape the following
  1140.              * character.  Quoting characters are stripped here
  1141.              * since they are no longer useful.
  1142.              *
  1143.              * First there was the word.
  1144.              */
  1145.             for (p = t = ecp->cp;
  1146.                 ecp->clen > 0; --ecp->clen, ++ecp->cp) {
  1147.                 ch = *ecp->cp;
  1148.                 if (IS_ESCAPE(sp,
  1149.                     ecp, ch) && ecp->clen > 1) {
  1150.                     --ecp->clen;
  1151.                     *p++ = *++ecp->cp;
  1152.                 } else if (isblank(ch)) {
  1153.                     ++ecp->cp;
  1154.                     --ecp->clen;
  1155.                     break;
  1156.                 } else
  1157.                     *p++ = ch;
  1158.             }
  1159.             if (argv_exp0(sp, ecp, t, p - t))
  1160.                 goto err;
  1161.  
  1162.             /* Delete intervening whitespace. */
  1163.             for (; ecp->clen > 0;
  1164.                 --ecp->clen, ++ecp->cp) {
  1165.                 ch = *ecp->cp;
  1166.                 if (!isblank(ch))
  1167.                     break;
  1168.             }
  1169.             if (ecp->clen == 0)
  1170.                 goto usage;
  1171.  
  1172.             /* Followed by the string. */
  1173.             for (p = t = ecp->cp; ecp->clen > 0;
  1174.                 --ecp->clen, ++ecp->cp, ++p) {
  1175.                 ch = *ecp->cp;
  1176.                 if (IS_ESCAPE(sp,
  1177.                     ecp, ch) && ecp->clen > 1) {
  1178.                     --ecp->clen;
  1179.                     *p = *++ecp->cp;
  1180.                 } else
  1181.                     *p = ch;
  1182.             }
  1183.             if (argv_exp0(sp, ecp, t, p - t))
  1184.                 goto err;
  1185.             goto addr_verify;
  1186.         case 'w':                /* word */
  1187.             if (argv_exp3(sp, ecp, ecp->cp, ecp->clen))
  1188.                 goto err;
  1189. arg_cnt_chk:        if (*++p != 'N') {        /* N */
  1190.                 /*
  1191.                  * If a number is specified, must either be
  1192.                  * 0 or that number, if optional, and that
  1193.                  * number, if required.
  1194.                  */
  1195.                 tmp = *p - '0';
  1196.                 if ((*++p != 'o' || exp->argsoff != 0) &&
  1197.                     exp->argsoff != tmp)
  1198.                     goto usage;
  1199.             }
  1200.             goto addr_verify;
  1201.         default:
  1202.             msgq(sp, M_ERR,
  1203.                 "085|Internal syntax table error (%s: %s)",
  1204.                 ecp->cmd->name, KEY_NAME(sp, *p));
  1205.         }
  1206.     }
  1207.  
  1208.     /* Skip trailing whitespace. */
  1209.     for (; ecp->clen > 0; --ecp->clen) {
  1210.         ch = *ecp->cp++;
  1211.         if (!isblank(ch))
  1212.             break;
  1213.     }
  1214.  
  1215.     /*
  1216.      * There shouldn't be anything left, and no more required fields,
  1217.      * i.e neither 'l' or 'r' in the syntax string.
  1218.      */
  1219.     if (ecp->clen != 0 || strpbrk(p, "lr")) {
  1220. usage:        msgq(sp, M_ERR, "086|Usage: %s", ecp->cmd->usage);
  1221.         goto err;
  1222.     }
  1223.  
  1224.     /*
  1225.      * Verify that the addresses are legal.  Check the addresses here,
  1226.      * because this is a place where all ex addresses pass through.
  1227.      * (They don't all pass through ex_line(), for instance.)  We're
  1228.      * assuming that any non-existent line doesn't exist because it's
  1229.      * past the end-of-file.  That's a pretty good guess.
  1230.      *
  1231.      * If it's a "default vi command", an address of zero is okay.
  1232.      */
  1233. addr_verify:
  1234.     switch (ecp->addrcnt) {
  1235.     case 2:
  1236.         /*
  1237.          * Historic ex/vi permitted commands with counts to go past
  1238.          * EOF.  So, for example, if the file only had 5 lines, the
  1239.          * ex command "1,6>" would fail, but the command ">300"
  1240.          * would succeed.  Since we don't want to have to make all
  1241.          * of the underlying commands handle random line numbers,
  1242.          * fix it here.
  1243.          */
  1244.         if (ecp->addr2.lno == 0) {
  1245.             if (!F_ISSET(ecp, E_ADDR_ZERO) &&
  1246.                 (F_ISSET(sp, SC_EX) ||
  1247.                 !F_ISSET(ecp, E_USELASTCMD))) {
  1248.                 ex_badaddr(sp, ecp->cmd, A_ZERO, NUM_OK);
  1249.                 goto err;
  1250.             }
  1251.         } else if (!db_exist(sp, ecp->addr2.lno))
  1252.             if (FL_ISSET(ecp->iflags, E_C_COUNT)) {
  1253.                 if (db_last(sp, &lno))
  1254.                     goto err;
  1255.                 ecp->addr2.lno = lno;
  1256.             } else {
  1257.                 ex_badaddr(sp, NULL, A_EOF, NUM_OK);
  1258.                 goto err;
  1259.             }
  1260.         /* FALLTHROUGH */
  1261.     case 1:
  1262.         if (ecp->addr1.lno == 0) {
  1263.             if (!F_ISSET(ecp, E_ADDR_ZERO) &&
  1264.                 (F_ISSET(sp, SC_EX) ||
  1265.                 !F_ISSET(ecp, E_USELASTCMD))) {
  1266.                 ex_badaddr(sp, ecp->cmd, A_ZERO, NUM_OK);
  1267.                 goto err;
  1268.             }
  1269.         } else if (!db_exist(sp, ecp->addr1.lno)) {
  1270.             ex_badaddr(sp, NULL, A_EOF, NUM_OK);
  1271.             goto err;
  1272.         }
  1273.         break;
  1274.     }
  1275.  
  1276.     /*
  1277.      * If doing a default command and there's nothing left on the line,
  1278.      * vi just moves to the line.  For example, ":3" and ":'a,'b" just
  1279.      * move to line 3 and line 'b, respectively, but ":3|" prints line 3.
  1280.      *
  1281.      * !!!
  1282.      * In addition, IF THE LINE CHANGES, move to the first nonblank of
  1283.      * the line.
  1284.      *
  1285.      * !!!
  1286.      * This is done before the absolute mark gets set; historically,
  1287.      * "/a/,/b/" did NOT set vi's absolute mark, but "/a/,/b/d" did.
  1288.      */
  1289.     if ((F_ISSET(sp, SC_VI) || F_ISSET(ecp, E_NOPRDEF)) &&
  1290.         F_ISSET(ecp, E_USELASTCMD) && vi_address == 0) {
  1291.         switch (ecp->addrcnt) {
  1292.         case 2:
  1293.             if (sp->lno !=
  1294.                 (ecp->addr2.lno ? ecp->addr2.lno : 1)) {
  1295.                 sp->lno =
  1296.                     ecp->addr2.lno ? ecp->addr2.lno : 1;
  1297.                 sp->cno = 0;
  1298.                 (void)nonblank(sp, sp->lno, &sp->cno);
  1299.             }
  1300.             break;
  1301.         case 1:
  1302.             if (sp->lno !=
  1303.                 (ecp->addr1.lno ? ecp->addr1.lno : 1)) {
  1304.                 sp->lno =
  1305.                     ecp->addr1.lno ? ecp->addr1.lno : 1;
  1306.                 sp->cno = 0;
  1307.                 (void)nonblank(sp, sp->lno, &sp->cno);
  1308.             }
  1309.             break;
  1310.         }
  1311.         ecp->cp = ecp->save_cmd;
  1312.         ecp->clen = ecp->save_cmdlen;
  1313.         goto loop;
  1314.     }
  1315.  
  1316.     /*
  1317.      * Set the absolute mark -- we have to set it for vi here, in case
  1318.      * it's a compound command, e.g. ":5p|6" should set the absolute
  1319.      * mark for vi.
  1320.      */
  1321.     if (F_ISSET(ecp, E_ABSMARK)) {
  1322.         cur.lno = sp->lno;
  1323.         cur.cno = sp->cno;
  1324.         F_CLR(ecp, E_ABSMARK);
  1325.         if (mark_set(sp, ABSMARK1, &cur, 1))
  1326.             goto err;
  1327.     }
  1328.  
  1329. #if defined(DEBUG) && defined(COMLOG)
  1330.     ex_comlog(sp, ecp);
  1331. #endif
  1332.     /* Increment the command count if not called from vi. */
  1333.     if (F_ISSET(sp, SC_EX))
  1334.         ++sp->ccnt;
  1335.  
  1336.     /*
  1337.      * If file state available, and not doing a global command,
  1338.      * log the start of an action.
  1339.      */
  1340.     if (sp->ep != NULL && !F_ISSET(sp, SC_EX_GLOBAL))
  1341.         (void)log_cursor(sp);
  1342.  
  1343.     /*
  1344.      * !!!
  1345.      * There are two special commands for the purposes of this code: the
  1346.      * default command (<carriage-return>) or the scrolling commands (^D
  1347.      * and <EOF>) as the first non-<blank> characters  in the line.
  1348.      *
  1349.      * If this is the first command in the command line, we received the
  1350.      * command from the ex command loop and we're talking to a tty, and
  1351.      * and there's nothing else on the command line, and it's one of the
  1352.      * special commands, we move back up to the previous line, and erase
  1353.      * the prompt character with the output.  Since ex runs in canonical
  1354.      * mode, we don't have to do anything else, a <newline> has already
  1355.      * been echoed by the tty driver.  It's OK if vi calls us -- we won't
  1356.      * be in ex mode so we'll do nothing.
  1357.      */
  1358.     if (F_ISSET(ecp, E_NRSEP)) {
  1359.         if (sp->ep != NULL &&
  1360.             F_ISSET(sp, SC_EX) && !F_ISSET(gp, G_SCRIPTED) &&
  1361.             (F_ISSET(ecp, E_USELASTCMD) || ecp->cmd == &cmds[C_SCROLL]))
  1362.             gp->scr_ex_adjust(sp, EX_TERM_SCROLL);
  1363.         F_CLR(ecp, E_NRSEP);
  1364.     }
  1365.  
  1366.     /*
  1367.      * Call the underlying function for the ex command.
  1368.      *
  1369.      * XXX
  1370.      * Interrupts behave like errors, for now.
  1371.      */
  1372.     if (ecp->cmd->fn(sp, ecp) || INTERRUPTED(sp)) {
  1373.         if (F_ISSET(gp, G_SCRIPTED))
  1374.             F_SET(sp, SC_EXIT_FORCE);
  1375.         goto err;
  1376.     }
  1377.  
  1378. #ifdef DEBUG
  1379.     /* Make sure no function left global temporary space locked. */
  1380.     if (F_ISSET(gp, G_TMP_INUSE)) {
  1381.         F_CLR(gp, G_TMP_INUSE);
  1382.         msgq(sp, M_ERR, "087|%s: temporary buffer not released",
  1383.             ecp->cmd->name);
  1384.     }
  1385. #endif
  1386.     /*
  1387.      * Ex displayed the number of lines modified immediately after each
  1388.      * command, so the command "1,10d|1,10d" would display:
  1389.      *
  1390.      *    10 lines deleted
  1391.      *    10 lines deleted
  1392.      *    <autoprint line>
  1393.      *
  1394.      * Executing ex commands from vi only reported the final modified
  1395.      * lines message -- that's wrong enough that we don't match it.
  1396.      */
  1397.     if (F_ISSET(sp, SC_EX))
  1398.         mod_rpt(sp);
  1399.  
  1400.     /*
  1401.      * Integrate any offset parsed by the underlying command, and make
  1402.      * sure the referenced line exists.
  1403.      *
  1404.      * XXX
  1405.      * May not match historic practice (which I've never been able to
  1406.      * completely figure out.)  For example, the '=' command from vi
  1407.      * mode often got the offset wrong, and complained it was too large,
  1408.      * but didn't seem to have a problem with the cursor.  If anyone
  1409.      * complains, ask them how it's supposed to work, they might know.
  1410.      */
  1411.     if (sp->ep != NULL && ecp->flagoff) {
  1412.         if (ecp->flagoff < 0) {
  1413.             if (sp->lno <= -ecp->flagoff) {
  1414.                 msgq(sp, M_ERR,
  1415.                     "088|Flag offset to before line 1");
  1416.                 goto err;
  1417.             }
  1418.         } else {
  1419.             if (!NPFITS(MAX_REC_NUMBER, sp->lno, ecp->flagoff)) {
  1420.                 ex_badaddr(sp, NULL, A_NOTSET, NUM_OVER);
  1421.                 goto err;
  1422.             }
  1423.             if (!db_exist(sp, sp->lno + ecp->flagoff)) {
  1424.                 msgq(sp, M_ERR,
  1425.                     "089|Flag offset past end-of-file");
  1426.                 goto err;
  1427.             }
  1428.         }
  1429.         sp->lno += ecp->flagoff;
  1430.     }
  1431.  
  1432.     /*
  1433.      * If the command executed successfully, we may want to display a line
  1434.      * based on the autoprint option or an explicit print flag.  (Make sure
  1435.      * that there's a line to display.)  Also, the autoprint edit option is
  1436.      * turned off for the duration of global commands.
  1437.      */
  1438.     if (F_ISSET(sp, SC_EX) && sp->ep != NULL && sp->lno != 0) {
  1439.         /*
  1440.          * The print commands have already handled the `print' flags.
  1441.          * If so, clear them.
  1442.          */
  1443.         if (FL_ISSET(ecp->iflags, E_CLRFLAG))
  1444.             FL_CLR(ecp->iflags, E_C_HASH | E_C_LIST | E_C_PRINT);
  1445.  
  1446.         /* If hash set only because of the number option, discard it. */
  1447.         if (F_ISSET(ecp, E_OPTNUM))
  1448.             FL_CLR(ecp->iflags, E_C_HASH);
  1449.  
  1450.         /*
  1451.          * If there was an explicit flag to display the new cursor line,
  1452.          * or autoprint is set and a change was made, display the line.
  1453.          * If any print flags were set use them, else default to print.
  1454.          */
  1455.         LF_INIT(FL_ISSET(ecp->iflags, E_C_HASH | E_C_LIST | E_C_PRINT));
  1456.         if (!LF_ISSET(E_C_HASH | E_C_LIST | E_C_PRINT | E_NOAUTO) &&
  1457.             !F_ISSET(sp, SC_EX_GLOBAL) &&
  1458.             O_ISSET(sp, O_AUTOPRINT) && F_ISSET(ecp, E_AUTOPRINT))
  1459.             LF_INIT(E_C_PRINT);
  1460.  
  1461.         if (LF_ISSET(E_C_HASH | E_C_LIST | E_C_PRINT)) {
  1462.             cur.lno = sp->lno;
  1463.             cur.cno = 0;
  1464.             (void)ex_print(sp, ecp, &cur, &cur, flags);
  1465.         }
  1466.     }
  1467.  
  1468.     /*
  1469.      * If the command had an associated "+cmd", it has to be executed
  1470.      * before we finish executing any more of this ex command.  For
  1471.      * example, consider a .exrc file that contains the following lines:
  1472.      *
  1473.      *    :set all
  1474.      *    :edit +25 file.c|s/abc/ABC/|1
  1475.      *    :3,5 print
  1476.      *
  1477.      * This can happen more than once -- the historic vi simply hung or
  1478.      * dropped core, of course.  Prepend the + command back into the
  1479.      * current command and continue.  We may have to add an additional
  1480.      * <literal next> character.  We know that it will fit because we
  1481.      * discarded at least one space and the + character.
  1482.      */
  1483.     if (arg1_len != 0) {
  1484.         /*
  1485.          * If the last character of the + command was a <literal next>
  1486.          * character, it would be treated differently because of the
  1487.          * append.  Quote it, if necessary.
  1488.          */
  1489.         if (IS_ESCAPE(sp, ecp, arg1[arg1_len - 1])) {
  1490.             *--ecp->save_cmd = CH_LITERAL;
  1491.             ++ecp->save_cmdlen;
  1492.         }
  1493.  
  1494.         ecp->save_cmd -= arg1_len;
  1495.         ecp->save_cmdlen += arg1_len;
  1496.         memcpy(ecp->save_cmd, arg1, arg1_len);
  1497.  
  1498.         /*
  1499.          * Any commands executed from a +cmd are executed starting at
  1500.          * the first column of the last line of the file -- NOT the
  1501.          * first nonblank.)  The main file startup code doesn't know
  1502.          * that a +cmd was set, however, so it may have put us at the
  1503.          * top of the file.  (Note, this is safe because we must have
  1504.          * switched files to get here.)
  1505.          */
  1506.         F_SET(ecp, E_MOVETOEND);
  1507.     }
  1508.  
  1509.     /* Update the current command. */
  1510.     ecp->cp = ecp->save_cmd;
  1511.     ecp->clen = ecp->save_cmdlen;
  1512.  
  1513.     /*
  1514.      * !!!
  1515.      * If we've changed screens or underlying files, any pending global or
  1516.      * v command, or @ buffer that has associated addresses, has to be
  1517.      * discarded.  This is historic practice for globals, and necessary for
  1518.      * @ buffers that had associated addresses.
  1519.      *
  1520.      * Otherwise, if we've changed underlying files, it's not a problem,
  1521.      * we continue with the rest of the ex command(s), operating on the
  1522.      * new file.  However, if we switch screens (either by exiting or by
  1523.      * an explicit command), we have no way of knowing where to put output
  1524.      * messages, and, since we don't control screens here, we could screw
  1525.      * up the upper layers, (e.g. we could exit/reenter a screen multiple
  1526.      * times).  So, return and continue after we've got a new screen.
  1527.      */
  1528.     if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | SC_FSWITCH | SC_SSWITCH)) {
  1529.         at_found = gv_found = 0;
  1530.         for (ecp = sp->gp->ecq.lh_first;
  1531.             ecp != NULL; ecp = ecp->q.le_next)
  1532.             switch (ecp->agv_flags) {
  1533.             case 0:
  1534.             case AGV_AT_NORANGE:
  1535.                 break;
  1536.             case AGV_AT:
  1537.                 if (!at_found) {
  1538.                     at_found = 1;
  1539.                     msgq(sp, M_ERR,
  1540.         "090|@ with range running when the file/screen changed");
  1541.                 }
  1542.                 break;
  1543.             case AGV_GLOBAL:
  1544.             case AGV_V:
  1545.                 if (!gv_found) {
  1546.                     gv_found = 1;
  1547.                     msgq(sp, M_ERR,
  1548.         "091|Global/v command running when the file/screen changed");
  1549.                 }
  1550.                 break;
  1551.             default:
  1552.                 abort();
  1553.             }
  1554.         if (at_found || gv_found)
  1555.             goto discard;
  1556.         if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE | SC_SSWITCH))
  1557.             goto rsuccess;
  1558.     }
  1559.  
  1560.     goto loop;
  1561.     /* NOTREACHED */
  1562.  
  1563. err:    /*
  1564.      * On command failure, we discard keys and pending commands remaining,
  1565.      * as well as any keys that were mapped and waiting.  The save_cmdlen
  1566.      * test is not necessarily correct.  If we fail early enough we don't
  1567.      * know if the entire string was a single command or not.  Guess, as
  1568.      * it's useful to know if commands other than the current one are being
  1569.      * discarded.
  1570.      */
  1571.     if (ecp->save_cmdlen == 0)
  1572.         for (; ecp->clen; --ecp->clen) {
  1573.             ch = *ecp->cp++;
  1574.             if (IS_ESCAPE(sp, ecp, ch) && ecp->clen > 1) {
  1575.                 --ecp->clen;
  1576.                 ++ecp->cp;
  1577.             } else if (ch == '\n' || ch == '|') {
  1578.                 if (ecp->clen > 1)
  1579.                     ecp->save_cmdlen = 1;
  1580.                 break;
  1581.             }
  1582.         }
  1583.     if (ecp->save_cmdlen != 0 || gp->ecq.lh_first != &gp->excmd) {
  1584. discard:    msgq(sp, M_BERR,
  1585.             "092|Ex command failed: pending commands discarded");
  1586.         ex_discard(sp);
  1587.     }
  1588.     if (v_event_flush(sp, CH_MAPPED))
  1589.         msgq(sp, M_BERR,
  1590.             "093|Ex command failed: mapped keys discarded");
  1591.  
  1592. rfail:    tmp = 1;
  1593.     if (0)
  1594. rsuccess:    tmp = 0;
  1595.  
  1596.     /* Turn off any file name error information. */
  1597.     gp->if_name = NULL;
  1598.  
  1599.     /* Turn off the global bit. */
  1600.     F_CLR(sp, SC_EX_GLOBAL);
  1601.  
  1602.     return (tmp);
  1603. }
  1604.  
  1605. /*
  1606.  * ex_range --
  1607.  *    Get a line range for ex commands, or perform a vi ex address search.
  1608.  *
  1609.  * PUBLIC: int ex_range __P((SCR *, EXCMD *, int *));
  1610.  */
  1611. int
  1612. ex_range(sp, ecp, errp)
  1613.     SCR *sp;
  1614.     EXCMD *ecp;
  1615.     int *errp;
  1616. {
  1617.     enum { ADDR_FOUND, ADDR_NEED, ADDR_NONE } addr;
  1618.     GS *gp;
  1619.     EX_PRIVATE *exp;
  1620.     MARK m;
  1621.     int isaddr;
  1622.  
  1623.     *errp = 0;
  1624.  
  1625.     /*
  1626.      * Parse comma or semi-colon delimited line specs.
  1627.      *
  1628.      * Semi-colon delimiters update the current address to be the last
  1629.      * address.  For example, the command
  1630.      *
  1631.      *    :3;/pattern/ecp->cp
  1632.      *
  1633.      * will search for pattern from line 3.  In addition, if ecp->cp
  1634.      * is not a valid command, the current line will be left at 3, not
  1635.      * at the original address.
  1636.      *
  1637.      * Extra addresses are discarded, starting with the first.
  1638.      *
  1639.      * !!!
  1640.      * If any addresses are missing, they default to the current line.
  1641.      * This was historically true for both leading and trailing comma
  1642.      * delimited addresses as well as for trailing semicolon delimited
  1643.      * addresses.  For consistency, we make it true for leading semicolon
  1644.      * addresses as well.
  1645.      */
  1646.     gp = sp->gp;
  1647.     exp = EXP(sp);
  1648.     for (addr = ADDR_NONE, ecp->addrcnt = 0; ecp->clen > 0;)
  1649.         switch (*ecp->cp) {
  1650.         case '%':        /* Entire file. */
  1651.             /* Vi ex address searches didn't permit % signs. */
  1652.             if (F_ISSET(ecp, E_VISEARCH))
  1653.                 goto ret;
  1654.  
  1655.             /* It's an error if the file is empty. */
  1656.             if (sp->ep == NULL) {
  1657.                 ex_badaddr(sp, NULL, A_EMPTY, NUM_OK);
  1658.                 *errp = 1;
  1659.                 return (0);
  1660.             }
  1661.             /*
  1662.              * !!!
  1663.              * A percent character addresses all of the lines in
  1664.              * the file.  Historically, it couldn't be followed by
  1665.              * any other address.  We do it as a text substitution
  1666.              * for simplicity.  POSIX 1003.2 is expected to follow
  1667.              * this practice.
  1668.              *
  1669.              * If it's an empty file, the first line is 0, not 1.
  1670.              */
  1671.             if (addr == ADDR_FOUND) {
  1672.                 ex_badaddr(sp, NULL, A_COMBO, NUM_OK);
  1673.                 *errp = 1;
  1674.                 return (0);
  1675.             }
  1676.             if (db_last(sp, &ecp->addr2.lno))
  1677.                 return (1);
  1678.             ecp->addr1.lno = ecp->addr2.lno == 0 ? 0 : 1;
  1679.             ecp->addr1.cno = ecp->addr2.cno = 0;
  1680.             ecp->addrcnt = 2;
  1681.             addr = ADDR_FOUND;
  1682.             ++ecp->cp;
  1683.             --ecp->clen;
  1684.             break;
  1685.         case ',':               /* Comma delimiter. */
  1686.             /* Vi ex address searches didn't permit commas. */
  1687.             if (F_ISSET(ecp, E_VISEARCH))
  1688.                 goto ret;
  1689.             /* FALLTHROUGH */
  1690.         case ';':               /* Semi-colon delimiter. */
  1691.             if (sp->ep == NULL) {
  1692.                 ex_badaddr(sp, NULL, A_EMPTY, NUM_OK);
  1693.                 *errp = 1;
  1694.                 return (0);
  1695.             }
  1696.             if (addr != ADDR_FOUND)
  1697.                 switch (ecp->addrcnt) {
  1698.                 case 0:
  1699.                     ecp->addr1.lno = sp->lno;
  1700.                     ecp->addr1.cno = sp->cno;
  1701.                     ecp->addrcnt = 1;
  1702.                     break;
  1703.                 case 2:
  1704.                     ecp->addr1 = ecp->addr2;
  1705.                     /* FALLTHROUGH */
  1706.                 case 1:
  1707.                     ecp->addr2.lno = sp->lno;
  1708.                     ecp->addr2.cno = sp->cno;
  1709.                     ecp->addrcnt = 2;
  1710.                     break;
  1711.                 }
  1712.             if (*ecp->cp == ';')
  1713.                 switch (ecp->addrcnt) {
  1714.                 case 0:
  1715.                     abort();
  1716.                     /* NOTREACHED */
  1717.                 case 1:
  1718.                     sp->lno = ecp->addr1.lno;
  1719.                     sp->cno = ecp->addr1.cno;
  1720.                     break;
  1721.                 case 2:
  1722.                     sp->lno = ecp->addr2.lno;
  1723.                     sp->cno = ecp->addr2.cno;
  1724.                     break;
  1725.                 }
  1726.             addr = ADDR_NEED;
  1727.             /* FALLTHROUGH */
  1728.         case ' ':        /* Whitespace. */
  1729.         case '\t':        /* Whitespace. */
  1730.             ++ecp->cp;
  1731.             --ecp->clen;
  1732.             break;
  1733.         default:
  1734.             /* Get a line specification. */
  1735.             if (ex_line(sp, ecp, &m, &isaddr, errp))
  1736.                 return (1);
  1737.             if (*errp)
  1738.                 return (0);
  1739.             if (!isaddr)
  1740.                 goto ret;
  1741.             if (addr == ADDR_FOUND) {
  1742.                 ex_badaddr(sp, NULL, A_COMBO, NUM_OK);
  1743.                 *errp = 1;
  1744.                 return (0);
  1745.             }
  1746.             switch (ecp->addrcnt) {
  1747.             case 0:
  1748.                 ecp->addr1 = m;
  1749.                 ecp->addrcnt = 1;
  1750.                 break;
  1751.             case 1:
  1752.                 ecp->addr2 = m;
  1753.                 ecp->addrcnt = 2;
  1754.                 break;
  1755.             case 2:
  1756.                 ecp->addr1 = ecp->addr2;
  1757.                 ecp->addr2 = m;
  1758.                 break;
  1759.             }
  1760.             addr = ADDR_FOUND;
  1761.             break;
  1762.         }
  1763.  
  1764.     /*
  1765.      * !!!
  1766.      * Vi ex address searches are indifferent to order or trailing
  1767.      * semi-colons.
  1768.      */
  1769. ret:    if (F_ISSET(ecp, E_VISEARCH))
  1770.         return (0);
  1771.  
  1772.     if (addr == ADDR_NEED)
  1773.         switch (ecp->addrcnt) {
  1774.         case 0:
  1775.             ecp->addr1.lno = sp->lno;
  1776.             ecp->addr1.cno = sp->cno;
  1777.             ecp->addrcnt = 1;
  1778.             break;
  1779.         case 2:
  1780.             ecp->addr1 = ecp->addr2;
  1781.             /* FALLTHROUGH */
  1782.         case 1:
  1783.             ecp->addr2.lno = sp->lno;
  1784.             ecp->addr2.cno = sp->cno;
  1785.             ecp->addrcnt = 2;
  1786.             break;
  1787.         }
  1788.  
  1789.     if (ecp->addrcnt == 2 && ecp->addr2.lno < ecp->addr1.lno) {
  1790.         msgq(sp, M_ERR,
  1791.             "094|The second address is smaller than the first");
  1792.         *errp = 1;
  1793.     }
  1794.     return (0);
  1795. }
  1796.  
  1797. /*
  1798.  * ex_line --
  1799.  *    Get a single line address specifier.
  1800.  *
  1801.  * The way the "previous context" mark worked was that any "non-relative"
  1802.  * motion set it.  While ex/vi wasn't totally consistent about this, ANY
  1803.  * numeric address, search pattern, '$', or mark reference in an address
  1804.  * was considered non-relative, and set the value.  Which should explain
  1805.  * why we're hacking marks down here.  The problem was that the mark was
  1806.  * only set if the command was called, i.e. we have to set a flag and test
  1807.  * it later.
  1808.  *
  1809.  * XXX
  1810.  * This is probably still not exactly historic practice, although I think
  1811.  * it's fairly close.
  1812.  */
  1813. static int
  1814. ex_line(sp, ecp, mp, isaddrp, errp)
  1815.     SCR *sp;
  1816.     EXCMD *ecp;
  1817.     MARK *mp;
  1818.     int *isaddrp, *errp;
  1819. {
  1820.     enum nresult nret;
  1821.     EX_PRIVATE *exp;
  1822.     GS *gp;
  1823.     long total, val;
  1824.     int isneg;
  1825.     int (*sf) __P((SCR *, MARK *, MARK *, char *, size_t, char **, u_int));
  1826.     char *endp;
  1827.  
  1828.     gp = sp->gp;
  1829.     exp = EXP(sp);
  1830.  
  1831.     *isaddrp = *errp = 0;
  1832.     F_CLR(ecp, E_DELTA);
  1833.  
  1834.     /* No addresses permitted until a file has been read in. */
  1835.     if (sp->ep == NULL && strchr("$0123456789'\\/?.+-^", *ecp->cp)) {
  1836.         ex_badaddr(sp, NULL, A_EMPTY, NUM_OK);
  1837.         *errp = 1;
  1838.         return (0);
  1839.     }
  1840.  
  1841.     switch (*ecp->cp) {
  1842.     case '$':                /* Last line in the file. */
  1843.         *isaddrp = 1;
  1844.         F_SET(ecp, E_ABSMARK);
  1845.  
  1846.         mp->cno = 0;
  1847.         if (db_last(sp, &mp->lno))
  1848.             return (1);
  1849.         ++ecp->cp;
  1850.         --ecp->clen;
  1851.         break;                /* Absolute line number. */
  1852.     case '0': case '1': case '2': case '3': case '4':
  1853.     case '5': case '6': case '7': case '8': case '9':
  1854.         *isaddrp = 1;
  1855.         F_SET(ecp, E_ABSMARK);
  1856.  
  1857.         if ((nret = nget_slong(&val, ecp->cp, &endp, 10)) != NUM_OK) {
  1858.             ex_badaddr(sp, NULL, A_NOTSET, nret);
  1859.             *errp = 1;
  1860.             return (0);
  1861.         }
  1862.         if (!NPFITS(MAX_REC_NUMBER, 0, val)) {
  1863.             ex_badaddr(sp, NULL, A_NOTSET, NUM_OVER);
  1864.             *errp = 1;
  1865.             return (0);
  1866.         }
  1867.         mp->lno = val;
  1868.         mp->cno = 0;
  1869.         ecp->clen -= (endp - ecp->cp);
  1870.         ecp->cp = endp;
  1871.         break;
  1872.     case '\'':                /* Use a mark. */
  1873.         *isaddrp = 1;
  1874.         F_SET(ecp, E_ABSMARK);
  1875.  
  1876.         if (ecp->clen == 1) {
  1877.             msgq(sp, M_ERR, "095|No mark name supplied");
  1878.             *errp = 1;
  1879.             return (0);
  1880.         }
  1881.         if (mark_get(sp, ecp->cp[1], mp, M_ERR)) {
  1882.             *errp = 1;
  1883.             return (0);
  1884.         }
  1885.         ecp->cp += 2;
  1886.         ecp->clen -= 2;
  1887.         break;
  1888.     case '\\':                /* Search: forward/backward. */
  1889.         /*
  1890.          * !!!
  1891.          * I can't find any difference between // and \/ or between
  1892.          * ?? and \?.  Mark Horton doesn't remember there being any
  1893.          * difference.  C'est la vie.
  1894.          */
  1895.         if (ecp->clen < 2 ||
  1896.             ecp->cp[1] != '/' && ecp->cp[1] != '?') {
  1897.             msgq(sp, M_ERR, "096|\\ not followed by / or ?");
  1898.             *errp = 1;
  1899.             return (0);
  1900.         }
  1901.         ++ecp->cp;
  1902.         --ecp->clen;
  1903.         sf = ecp->cp[0] == '/' ? f_search : b_search;
  1904.         goto search;
  1905.     case '/':                /* Search forward. */
  1906.         sf = f_search;
  1907.         goto search;
  1908.     case '?':                /* Search backward. */
  1909.         sf = b_search;
  1910.  
  1911. search:        mp->lno = sp->lno;
  1912.         mp->cno = sp->cno;
  1913.         if (sf(sp, mp, mp, ecp->cp, ecp->clen, &endp,
  1914.             SEARCH_MSG | SEARCH_PARSE | SEARCH_SET |
  1915.             (F_ISSET(ecp, E_SEARCH_WMSG) ? SEARCH_WMSG : 0))) {
  1916.             *errp = 1;
  1917.             return (0);
  1918.         }
  1919.  
  1920.         /* Fix up the command pointers. */
  1921.         ecp->clen -= (endp - ecp->cp);
  1922.         ecp->cp = endp;
  1923.  
  1924.         *isaddrp = 1;
  1925.         F_SET(ecp, E_ABSMARK);
  1926.         break;
  1927.     case '.':                /* Current position. */
  1928.         *isaddrp = 1;
  1929.         mp->cno = sp->cno;
  1930.  
  1931.         /* If an empty file, then '.' is 0, not 1. */
  1932.         if (sp->lno == 1) {
  1933.             if (db_last(sp, &mp->lno))
  1934.                 return (1);
  1935.             if (mp->lno != 0)
  1936.                 mp->lno = 1;
  1937.         } else
  1938.             mp->lno = sp->lno;
  1939.  
  1940.         /*
  1941.          * !!!
  1942.          * Historically, .<number> was the same as .+<number>, i.e.
  1943.          * the '+' could be omitted.  (This feature is found in ed
  1944.          * as well.)
  1945.          */
  1946.         if (ecp->clen > 1 && isdigit(ecp->cp[1]))
  1947.             *ecp->cp = '+';
  1948.         else {
  1949.             ++ecp->cp;
  1950.             --ecp->clen;
  1951.         }
  1952.         break;
  1953.     }
  1954.  
  1955.     /* Skip trailing <blank>s. */
  1956.     for (; ecp->clen > 0 &&
  1957.         isblank(ecp->cp[0]); ++ecp->cp, --ecp->clen);
  1958.  
  1959.     /*
  1960.      * Evaluate any offset.  If no address yet found, the offset
  1961.      * is relative to ".".
  1962.      */
  1963.     total = 0;
  1964.     if (ecp->clen != 0 && (isdigit(ecp->cp[0]) ||
  1965.         ecp->cp[0] == '+' || ecp->cp[0] == '-' ||
  1966.         ecp->cp[0] == '^')) {
  1967.         if (!*isaddrp) {
  1968.             *isaddrp = 1;
  1969.             mp->lno = sp->lno;
  1970.             mp->cno = sp->cno;
  1971.         }
  1972.         /*
  1973.          * Evaluate an offset, defined as:
  1974.          *
  1975.          *        [+-^<blank>]*[<blank>]*[0-9]*
  1976.          *
  1977.          * The rough translation is any number of signs, optionally
  1978.          * followed by numbers, or a number by itself, all <blank>
  1979.          * separated.
  1980.          *
  1981.          * !!!
  1982.          * All address offsets were additive, e.g. "2 2 3p" was the
  1983.          * same as "7p", or, "/ZZZ/ 2" was the same as "/ZZZ/+2".
  1984.          * Note, however, "2 /ZZZ/" was an error.  It was also legal
  1985.          * to insert signs without numbers, so "3 - 2" was legal, and
  1986.          * equal to 4.
  1987.          *
  1988.          * !!!
  1989.          * Offsets were historically permitted for any line address,
  1990.          * e.g. the command "1,2 copy 2 2 2 2" copied lines 1,2 after
  1991.          * line 8.
  1992.          *
  1993.          * !!!
  1994.          * Offsets were historically permitted for search commands,
  1995.          * and handled as addresses: "/pattern/2 2 2" was legal, and
  1996.          * referenced the 6th line after pattern.
  1997.          */
  1998.         F_SET(ecp, E_DELTA);
  1999.         for (;;) {
  2000.             for (; ecp->clen > 0 && isblank(ecp->cp[0]);
  2001.                 ++ecp->cp, --ecp->clen);
  2002.             if (ecp->clen == 0 || !isdigit(ecp->cp[0]) &&
  2003.                 ecp->cp[0] != '+' && ecp->cp[0] != '-' &&
  2004.                 ecp->cp[0] != '^')
  2005.                 break;
  2006.             if (!isdigit(ecp->cp[0]) &&
  2007.                 !isdigit(ecp->cp[1])) {
  2008.                 total += ecp->cp[0] == '+' ? 1 : -1;
  2009.                 --ecp->clen;
  2010.                 ++ecp->cp;
  2011.             } else {
  2012.                 if (ecp->cp[0] == '-' ||
  2013.                     ecp->cp[0] == '^') {
  2014.                     ++ecp->cp;
  2015.                     --ecp->clen;
  2016.                     isneg = 1;
  2017.                 } else
  2018.                     isneg = 0;
  2019.  
  2020.                 /* Get a signed long, add it to the total. */
  2021.                 if ((nret = nget_slong(&val,
  2022.                     ecp->cp, &endp, 10)) != NUM_OK ||
  2023.                     (nret = NADD_SLONG(sp,
  2024.                     total, val)) != NUM_OK) {
  2025.                     ex_badaddr(sp, NULL, A_NOTSET, nret);
  2026.                     *errp = 1;
  2027.                     return (0);
  2028.                 }
  2029.                 total += isneg ? -val : val;
  2030.                 ecp->clen -= (endp - ecp->cp);
  2031.                 ecp->cp = endp;
  2032.             }
  2033.         }
  2034.     }
  2035.  
  2036.     /*
  2037.      * Any value less than 0 is an error.  Make sure that the new value
  2038.      * will fit into a recno_t.
  2039.      */
  2040.     if (*isaddrp && total != 0) {
  2041.         if (total < 0) {
  2042.             if (-total > mp->lno) {
  2043.                 msgq(sp, M_ERR,
  2044.                 "097|Reference to a line number less than 0");
  2045.                 *errp = 1;
  2046.                 return (0);
  2047.             }
  2048.         } else
  2049.             if (!NPFITS(MAX_REC_NUMBER, mp->lno, total)) {
  2050.                 ex_badaddr(sp, NULL, A_NOTSET, NUM_OVER);
  2051.                 *errp = 1;
  2052.                 return (0);
  2053.             }
  2054.         mp->lno += total;
  2055.     }
  2056.     return (0);
  2057. }
  2058.  
  2059.  
  2060. /*
  2061.  * ex_load --
  2062.  *    Load up the next command, which may be an @ buffer or global command.
  2063.  */
  2064. static int
  2065. ex_load(sp)
  2066.     SCR *sp;
  2067. {
  2068.     GS *gp;
  2069.     EXCMD *ecp;
  2070.     RANGE *rp;
  2071.  
  2072.     F_CLR(sp, SC_EX_GLOBAL);
  2073.  
  2074.     /*
  2075.      * Lose any exhausted commands.  We know that the first command
  2076.      * can't be an AGV command, which makes things a bit easier.
  2077.      */
  2078.     for (gp = sp->gp;;) {
  2079.         /*
  2080.          * If we're back to the original structure, leave it around,
  2081.          * but discard any allocated source name, we've returned to
  2082.          * the beginning of the command stack.
  2083.          */
  2084.         if ((ecp = gp->ecq.lh_first) == &gp->excmd) {
  2085.             if (F_ISSET(ecp, E_NAMEDISCARD)) {
  2086.                 free(ecp->if_name);
  2087.                 ecp->if_name = NULL;
  2088.             }
  2089.             return (0);
  2090.         }
  2091.  
  2092.         /*
  2093.          * ecp->clen will be 0 for the first discarded command, but
  2094.          * may not be 0 for subsequent ones, e.g. if the original
  2095.          * command was ":g/xx/@a|s/b/c/", then when we discard the
  2096.          * command pushed on the stack by the @a, we have to resume
  2097.          * the global command which included the substitute command.
  2098.          */
  2099.         if (ecp->clen != 0)
  2100.             return (0);
  2101.  
  2102.         /*
  2103.          * If it's an @, global or v command, we may need to continue
  2104.          * the command on a different line.
  2105.          */
  2106.         if (FL_ISSET(ecp->agv_flags, AGV_ALL)) {
  2107.             /* Discard any exhausted ranges. */
  2108.             while ((rp = ecp->rq.cqh_first) != (void *)&ecp->rq)
  2109.                 if (rp->start > rp->stop) {
  2110.                     CIRCLEQ_REMOVE(&ecp->rq, rp, q);
  2111.                     free(rp);
  2112.                 } else
  2113.                     break;
  2114.  
  2115.             /* If there's another range, continue with it. */
  2116.             if (rp != (void *)&ecp->rq)
  2117.                 break;
  2118.  
  2119.             /* If it's a global/v command, fix up the last line. */
  2120.             if (FL_ISSET(ecp->agv_flags,
  2121.                 AGV_GLOBAL | AGV_V) && ecp->range_lno != OOBLNO)
  2122.                 if (db_exist(sp, ecp->range_lno))
  2123.                     sp->lno = ecp->range_lno;
  2124.                 else {
  2125.                     if (db_last(sp, &sp->lno))
  2126.                         return (1);
  2127.                     if (sp->lno == 0)
  2128.                         sp->lno = 1;
  2129.                 }
  2130.             free(ecp->o_cp);
  2131.         }
  2132.  
  2133.         /* Discard the EXCMD. */
  2134.         LIST_REMOVE(ecp, q);
  2135.         free(ecp);
  2136.     }
  2137.  
  2138.     /*
  2139.      * We only get here if it's an active @, global or v command.  Set
  2140.      * the current line number, and get a new copy of the command for
  2141.      * the parser.  Note, the original pointer almost certainly moved,
  2142.      * so we have play games.
  2143.      */
  2144.     ecp->cp = ecp->o_cp;
  2145.     memcpy(ecp->cp, ecp->cp + ecp->o_clen, ecp->o_clen);
  2146.     ecp->clen = ecp->o_clen;
  2147.     ecp->range_lno = sp->lno = rp->start++;
  2148.  
  2149.     if (FL_ISSET(ecp->agv_flags, AGV_GLOBAL | AGV_V))
  2150.         F_SET(sp, SC_EX_GLOBAL);
  2151.     return (0);
  2152. }
  2153.  
  2154. /*
  2155.  * ex_discard --
  2156.  *    Discard any pending ex commands.
  2157.  */
  2158. static int
  2159. ex_discard(sp)
  2160.     SCR *sp;
  2161. {
  2162.     GS *gp;
  2163.     EXCMD *ecp;
  2164.     RANGE *rp;
  2165.  
  2166.     /*
  2167.      * We know the first command can't be an AGV command, so we don't
  2168.      * process it specially.  We do, however, nail the command itself.
  2169.      */
  2170.     for (gp = sp->gp; (ecp = gp->ecq.lh_first) != &gp->excmd;) {
  2171.         if (FL_ISSET(ecp->agv_flags, AGV_ALL)) {
  2172.             while ((rp = ecp->rq.cqh_first) != (void *)&ecp->rq) {
  2173.                 CIRCLEQ_REMOVE(&ecp->rq, rp, q);
  2174.                 free(rp);
  2175.             }
  2176.             free(ecp->o_cp);
  2177.         }
  2178.         LIST_REMOVE(ecp, q);
  2179.         free(ecp);
  2180.     }
  2181.     gp->ecq.lh_first->clen = 0;
  2182.     return (0);
  2183. }
  2184.  
  2185. /*
  2186.  * ex_unknown --
  2187.  *    Display an unknown command name.
  2188.  */
  2189. static void
  2190. ex_unknown(sp, cmd, len)
  2191.     SCR *sp;
  2192.     char *cmd;
  2193.     size_t len;
  2194. {
  2195.     size_t blen;
  2196.     char *bp;
  2197.  
  2198.     GET_SPACE_GOTO(sp, bp, blen, len + 1);
  2199.     bp[len] = '\0';
  2200.     memcpy(bp, cmd, len);
  2201.     msgq_str(sp, M_ERR, bp, "098|The %s command is unknown");
  2202.     FREE_SPACE(sp, bp, blen);
  2203.  
  2204. alloc_err:
  2205.     return;
  2206. }
  2207.  
  2208. /*
  2209.  * ex_is_abbrev -
  2210.  *    The vi text input routine needs to know if ex thinks this is an
  2211.  *    [un]abbreviate command, so it can turn off abbreviations.  See
  2212.  *    the usual ranting in the vi/v_txt_ev.c:txt_abbrev() routine.
  2213.  *
  2214.  * PUBLIC: int ex_is_abbrev __P((char *, size_t));
  2215.  */
  2216. int
  2217. ex_is_abbrev(name, len)
  2218.     char *name;
  2219.     size_t len;
  2220. {
  2221.     EXCMDLIST const *cp;
  2222.  
  2223.     return ((cp = ex_comm_search(name, len)) != NULL &&
  2224.         (cp == &cmds[C_ABBR] || cp == &cmds[C_UNABBREVIATE]));
  2225. }
  2226.  
  2227. /*
  2228.  * ex_is_unmap -
  2229.  *    The vi text input routine needs to know if ex thinks this is an
  2230.  *    unmap command, so it can turn off input mapping.  See the usual
  2231.  *    ranting in the vi/v_txt_ev.c:txt_unmap() routine.
  2232.  *
  2233.  * PUBLIC: int ex_is_unmap __P((char *, size_t));
  2234.  */
  2235. int
  2236. ex_is_unmap(name, len)
  2237.     char *name;
  2238.     size_t len;
  2239. {
  2240.     EXCMDLIST const *cp;
  2241.  
  2242.     /*
  2243.      * The command the vi input routines are really interested in
  2244.      * is "unmap!", not just unmap.
  2245.      */
  2246.     if (name[len - 1] != '!')
  2247.         return (0);
  2248.     --len;
  2249.     return ((cp = ex_comm_search(name, len)) != NULL &&
  2250.         cp == &cmds[C_UNMAP]);
  2251. }
  2252.  
  2253. /*
  2254.  * ex_comm_search --
  2255.  *    Search for a command name.
  2256.  */
  2257. static EXCMDLIST const *
  2258. ex_comm_search(name, len)
  2259.     char *name;
  2260.     size_t len;
  2261. {
  2262.     EXCMDLIST const *cp;
  2263.  
  2264.     for (cp = cmds; cp->name != NULL; ++cp) {
  2265.         if (cp->name[0] > name[0])
  2266.             return (NULL);
  2267.         if (cp->name[0] != name[0])
  2268.             continue;
  2269.         if (!memcmp(name, cp->name, len))
  2270.             return (cp);
  2271.     }
  2272.     return (NULL);
  2273. }
  2274.  
  2275. /*
  2276.  * ex_badaddr --
  2277.  *    Display a bad address message.
  2278.  *
  2279.  * PUBLIC: void ex_badaddr
  2280.  * PUBLIC:    __P((SCR *, EXCMDLIST const *, enum badaddr, enum nresult));
  2281.  */
  2282. void
  2283. ex_badaddr(sp, cp, ba, nret)
  2284.     SCR *sp;
  2285.     EXCMDLIST const *cp;
  2286.     enum badaddr ba;
  2287.     enum nresult nret;
  2288. {
  2289.     recno_t lno;
  2290.  
  2291.     switch (nret) {
  2292.     case NUM_OK:
  2293.         break;
  2294.     case NUM_ERR:
  2295.         msgq(sp, M_SYSERR, NULL);
  2296.         return;
  2297.     case NUM_OVER:
  2298.         msgq(sp, M_ERR, "099|Address value overflow");
  2299.         return;
  2300.     case NUM_UNDER:
  2301.         msgq(sp, M_ERR, "100|Address value underflow");
  2302.         return;
  2303.     }
  2304.  
  2305.     /*
  2306.      * When encountering an address error, tell the user if there's no
  2307.      * underlying file, that's the real problem.
  2308.      */
  2309.     if (sp->ep == NULL) {
  2310.         ex_emsg(sp, cp->name, EXM_NOFILEYET);
  2311.         return;
  2312.     }
  2313.  
  2314.     switch (ba) {
  2315.     case A_COMBO:
  2316.         msgq(sp, M_ERR, "101|Illegal address combination");
  2317.         break;
  2318.     case A_EOF:
  2319.         if (db_last(sp, &lno))
  2320.             return;
  2321.         if (lno != 0) {
  2322.             msgq(sp, M_ERR,
  2323.                 "102|Illegal address: only %lu lines in the file",
  2324.                 lno);
  2325.             break;
  2326.         }
  2327.         /* FALLTHROUGH */
  2328.     case A_EMPTY:
  2329.         msgq(sp, M_ERR, "103|Illegal address: the file is empty");
  2330.         break;
  2331.     case A_NOTSET:
  2332.         abort();
  2333.         /* NOTREACHED */
  2334.     case A_ZERO:
  2335.         msgq(sp, M_ERR,
  2336.             "104|The %s command doesn't permit an address of 0",
  2337.             cp->name);
  2338.         break;
  2339.     }
  2340.     return;
  2341. }
  2342.  
  2343. #if defined(DEBUG) && defined(COMLOG)
  2344. /*
  2345.  * ex_comlog --
  2346.  *    Log ex commands.
  2347.  */
  2348. static void
  2349. ex_comlog(sp, ecp)
  2350.     SCR *sp;
  2351.     EXCMD *ecp;
  2352. {
  2353.     TRACE(sp, "ecmd: %s", ecp->cmd->name);
  2354.     if (ecp->addrcnt > 0) {
  2355.         TRACE(sp, " a1 %d", ecp->addr1.lno);
  2356.         if (ecp->addrcnt > 1)
  2357.             TRACE(sp, " a2: %d", ecp->addr2.lno);
  2358.     }
  2359.     if (ecp->lineno)
  2360.         TRACE(sp, " line %d", ecp->lineno);
  2361.     if (ecp->flags)
  2362.         TRACE(sp, " flags 0x%x", ecp->flags);
  2363.     if (F_ISSET(&exc, E_BUFFER))
  2364.         TRACE(sp, " buffer %c", ecp->buffer);
  2365.     if (ecp->argc)
  2366.         for (cnt = 0; cnt < ecp->argc; ++cnt)
  2367.             TRACE(sp, " arg %d: {%s}", cnt, ecp->argv[cnt]->bp);
  2368.     TRACE(sp, "\n");
  2369. }
  2370. #endif
  2371.