home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / v / vim_src.zip / CMDLINE.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  44KB  |  2,233 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMitation
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony 
  8.  *                            G. R. (Fred) Walter        watmath!watcgl!grwalter 
  9.  */
  10.  
  11. /*
  12.  * cmdline.c: functions for reading in the command line and executing it
  13.  */
  14.  
  15. #include "vim.h"
  16. #include "globals.h"
  17. #include "proto.h"
  18. #include "param.h"
  19. #include "cmdtab.h"
  20. #include "ops.h"            /* included because we call functions in ops.c */
  21. #include "fcntl.h"            /* for chdir() */
  22.  
  23. #ifdef LATTICE
  24. # define mktemp(a)    tmpnam(a)
  25. #endif
  26.  
  27. /*
  28.  * the history list of alternate files
  29.  */
  30. #define NUMALTFILES 20
  31.  
  32. static char    *altfiles[NUMALTFILES];    /* alternate files */
  33. static linenr_t altlnum[NUMALTFILES];    /* line # in alternate file */
  34. static linenr_t doecmdlnum = 0;            /* line # in new file for doecmd() */
  35.  
  36. /*
  37.  * variables shared between getcmdline() and redrawcmdline()
  38.  */
  39. static int         cmdlen;        /* number of chars on command line */
  40. static int         cmdpos;        /* current cursor position */
  41. static int         cmdslen;        /* lenght of command line on screen */
  42. static int         cmdspos;        /* cursor position on screen */
  43. static int         cmdredraw;     /* max. number of lines of the command - 1 */
  44. static int         cmdfirstc;     /* ':', '/' or '?' */
  45. static u_char    *cmdbuff;        /* pointer to command line buffer */
  46.  
  47. /*
  48.  * The next two variables contain the bounds of any range given in a command.
  49.  * They are set by docmdline().
  50.  */
  51. static linenr_t     line1, line2;
  52. static int            forceit;
  53. static int            regname;
  54.  
  55. static void        cmdchecklen __ARGS((void));
  56. static void        cursorcmd __ARGS((void));
  57. static u_char    *DoOneCmd __ARGS((u_char *));
  58. static void        dobang __ARGS((int, u_char *));
  59. static int        autowrite __ARGS((void));
  60. static int        dowrite __ARGS((u_char *, int));
  61. static int        doecmd __ARGS((char *));
  62. static void        doshell __ARGS((char *));
  63. static void        dofilter __ARGS((u_char *, int, int));
  64. static int        check_readonly __ARGS((void));
  65. static int        check_changed __ARGS((int));
  66. static int        check_fname __ARGS((void));
  67. static int        check_more __ARGS((void));
  68. #ifdef WILD_CARDS
  69. static char        *ExpandOne __ARGS((u_char *, int, int));
  70. static void        showmatches __ARGS((char *, int));
  71. static char        *addstar __ARGS((char *, int));
  72. #endif
  73. static linenr_t get_address __ARGS((u_char **, linenr_t));
  74.  
  75. extern char        *mktemp __ARGS((char *));
  76.  
  77. extern int global_busy, global_wait;    /* shared with csearch.c */
  78.  
  79. /*
  80.  * variable shared with quickfix.c
  81.  */
  82. extern int qf_index;
  83.  
  84. /*
  85.  * getcmdline() - accept a command line starting with ':', '!', '/', or '?'
  86.  *
  87.  * For searches the optional matching '?' or '/' is removed.
  88.  */
  89.  
  90.     int
  91. getcmdline(firstc, buff)
  92.     int            firstc;     /* either ':', '/', or '?' */
  93.     u_char        *buff;         /* buffer for command string */
  94. {
  95.     register u_char     c;
  96.              int        nextc = 0;
  97.     register int        i;
  98.              int        retval;
  99.              int        hiscnt;                /* current history line in use */
  100.     static     char         **history = NULL;    /* history table */
  101.     static     int        hislen = 0;         /* actual lengt of history table */
  102.              int        newlen;                /* new length of history table */
  103.     static     int        hisidx = -1;        /* last entered entry */
  104.              char        **temp;
  105.              int        j;
  106.  
  107. #ifdef WILD_CARDS
  108.              int        gotesc = FALSE;        /* TRUE when last char typed was <ESC> */
  109.              char        *p1, *p2;
  110.              int        oldlen;
  111.              int        difflen;
  112.              int        findex;
  113. #endif
  114.  
  115.  
  116.  
  117. /*
  118.  * set some variables for redrawcmd()
  119.  */
  120.     cmdfirstc = firstc;
  121.     cmdbuff = buff;
  122.     cmdlen = cmdpos = 0;
  123.     cmdslen = cmdspos = 1;
  124.     cmdredraw = 0;
  125.     State = CMDLINE;
  126.     gotocmdline(TRUE, firstc);
  127.  
  128. /*
  129.  * if size of history table changed, reallocate it
  130.  */
  131.     newlen = p_hi;
  132.     if (newlen != hislen)                        /* history length changed */
  133.     {
  134.         temp = (char **)alloc((int)(newlen * sizeof(char *)));
  135.         if (temp != NULL)
  136.         {
  137.                 if (newlen > hislen)            /* array becomes bigger */
  138.                 {
  139.                         for (i = 0; i <= hisidx; ++i)
  140.                                 temp[i] = history[i];
  141.                         j = i;
  142.                         for ( ; i <= newlen - (hislen - hisidx); ++i)
  143.                                 temp[i] = NULL;
  144.                         for ( ; j < hislen; ++i, ++j)
  145.                                 temp[i] = history[j];
  146.                 }
  147.                 else                            /* array becomes smaller */
  148.                 {
  149.                         j = hisidx;
  150.                         hisidx = newlen - 1;
  151.                         for (i = hisidx; i >= 0; --i)
  152.                         {
  153.                                 temp[i] = history[j];
  154.                                 if (--j < 0)
  155.                                         j = hislen - 1;
  156.                         }
  157.                 }
  158.                 free(history);
  159.                 history = temp;
  160.                 hislen = newlen;
  161.         }
  162.     }
  163.     hiscnt = hislen;            /* set hiscnt to impossible history value */
  164.  
  165.     /* collect the command string, handling '\b', @ and much more */
  166.     for (;;)
  167.     {
  168.         cursorcmd();    /* set the cursor on the right spot */
  169.         if (nextc)        /* character remaining from CTRL-V */
  170.         {
  171.             c = nextc;
  172.             nextc = 0;
  173.         }
  174.         else
  175.             c = vgetc();
  176.  
  177. #ifdef WILD_CARDS
  178.         if (c != ESC && c != CTRL('N') && c != CTRL('P') && gotesc)
  179.         {
  180.             (void)ExpandOne(NULL, FALSE, -2);    /* may free expanded file names */
  181.             gotesc = FALSE;
  182.         }
  183. #endif
  184.  
  185.         if (c == '\n' || c == '\r')
  186.         {
  187.                 outchar('\r');
  188.                 flushbuf();
  189.                 break;
  190.         }
  191.  
  192.         switch (c)
  193.         {
  194.         case BS:
  195.         case DEL:
  196.                 /*
  197.                  * delete current character is the same as backspace on next
  198.                  * character, except at end of line
  199.                  */
  200.                 if (c == DEL && cmdpos != cmdlen)
  201.                     ++cmdpos;
  202.                 if (cmdpos > 0)
  203.                 {
  204.                     --cmdpos;
  205.                     --cmdlen;
  206.                     for (i = cmdpos; i < cmdlen; ++i)
  207.                         buff[i] = buff[i + 1];
  208.                     redrawcmd();
  209.                 }
  210.                 else if (cmdlen == 0)
  211.                 {
  212.                     retval = FALSE;
  213.                     msg("");
  214.                     goto returncmd;     /* back to cmd mode */
  215.                 }
  216.                 continue;
  217.  
  218. /*        case '@':    only in very old vi */
  219.         case CTRL('U'):
  220. clearline:
  221.                 cmdpos = 0;
  222.                 cmdlen = 0;
  223.                 cmdslen = 1;
  224.                 cmdspos = 1;
  225.                 gotocmdline(TRUE, firstc);
  226.                 continue;
  227.  
  228.         case ESC:
  229. #ifndef WILD_CARDS
  230.                 retval = FALSE;
  231.                 msg("");
  232.                 goto returncmd;     /* back to cmd mode */
  233. #else
  234.             /*
  235.              * hitting <ESC> twice means: abandon command line
  236.              */
  237.             if (gotesc)
  238.             {
  239.                 retval = FALSE;
  240.                 msg("");
  241.                 goto returncmd;     /* back to cmd mode */
  242.             }
  243.             gotesc = TRUE;
  244.             findex = 0;
  245.  
  246. doexpand:
  247.             outstr("...");        /* show that we are busy */
  248.             flushbuf();
  249.             i = cmdslen;
  250.             cmdslen = cmdpos + 4;
  251.             cmdchecklen();        /* check if we caused a scrollup */
  252.             cmdslen = i;
  253.  
  254.             for (i = cmdpos; i > 0 && buff[i - 1] != ' '; --i)
  255.                 ;
  256.             oldlen = cmdpos - i;
  257.  
  258.                 /* add a "*" to the file name and expand it */
  259.             if ((p1 = addstar((char *)&buff[i], oldlen)) != NULL)
  260.             {
  261.                 if ((p2 = ExpandOne((u_char *)p1, FALSE, findex)) != NULL)
  262.                 {
  263.                     if (cmdlen + (difflen = strlen(p2) - oldlen) > CMDBUFFSIZE - 4)
  264.                         emsg(e_toolong);
  265.                     else
  266.                     {
  267.                         strncpy((char *)&buff[cmdpos + difflen], (char *)&buff[cmdpos], (size_t)(cmdlen - cmdpos));
  268.                         strncpy((char *)&buff[i], p2, strlen(p2));
  269.                         cmdlen += difflen;
  270.                         cmdpos += difflen;
  271.                     }
  272.                     free(p2);
  273.                 }
  274.                 free(p1);
  275.             }
  276.             redrawcmd();
  277.             continue;
  278.  
  279.         case CTRL('D'):
  280.             {
  281.                 for (i = cmdpos; i > 0 && buff[i - 1] != ' '; --i)
  282.                         ;
  283.                 showmatches((char *)&buff[i], cmdpos - i);
  284.  
  285.                 redrawcmd();
  286.                 continue;
  287.             }
  288. #endif
  289.  
  290.         case K_RARROW:
  291.         case K_SRARROW:
  292.                 do
  293.                 {
  294.                         if (cmdpos >= cmdlen)
  295.                                 break;
  296.                         cmdspos += charsize(buff[cmdpos]);
  297.                         ++cmdpos;
  298.                 }
  299.                 while (c == K_SRARROW && buff[cmdpos] != ' ');
  300.                 continue;
  301.  
  302.         case K_LARROW:
  303.         case K_SLARROW:
  304.                 do
  305.                 {
  306.                         if (cmdpos <= 0)
  307.                                 break;
  308.                         --cmdpos;
  309.                         cmdspos -= charsize(buff[cmdpos]);
  310.                 }
  311.                 while (c == K_SLARROW && buff[cmdpos - 1] != ' ');
  312.                 continue;
  313.  
  314.         case CTRL('N'):        /* next match */
  315.         case CTRL('P'):        /* previous match */
  316.                 if (gotesc)
  317.                 {
  318.                     findex = (c == CTRL('P')) ? 2 : 1;
  319.                     goto doexpand;
  320.                 }
  321.  
  322.         case K_UARROW:
  323.         case K_DARROW:
  324.                 i = hiscnt;
  325.                 if (c == K_UARROW || c == CTRL('P'))
  326.                 {
  327.                         if (hiscnt == hislen)
  328.                                 hiscnt = hisidx;
  329.                         else if (hiscnt == 0 && hisidx != hislen - 1)
  330.                                 hiscnt = hislen - 1;
  331.                         else if (hiscnt != hisidx + 1)
  332.                                 --hiscnt;
  333.                 }
  334.                 else
  335.                 {
  336.                         if (hiscnt == hisidx)    /* on last entry, clear the line */
  337.                         {
  338.                                 hiscnt = hislen;
  339.                                 goto clearline;
  340.                         }
  341.                         if (hiscnt == hislen)    /* not on a history line, nothing to do */
  342.                                 continue;
  343.                         if (hiscnt == hislen - 1)
  344.                                 hiscnt = 0;
  345.                         else
  346.                                 ++hiscnt;
  347.                 }
  348.                 if (hiscnt < 0 || history[hiscnt] == NULL)
  349.                         hiscnt = i;
  350.                 else
  351.                 {
  352.                         strcpy((char *)buff, history[hiscnt]);
  353.                         cmdpos = cmdlen = strlen((char *)buff);
  354.                         redrawcmd();
  355.                 }
  356.                 continue;
  357.  
  358.         case CTRL('V'):
  359.                 outchar('^');
  360.                 outtrans((char *)(buff + cmdpos), cmdlen - cmdpos);
  361.                 ++cmdslen;
  362.                 cmdchecklen();
  363.                 --cmdslen;
  364.                 cursorcmd();
  365.                 c = get_literal(&nextc);    /* get next (two) character(s) */
  366.         }
  367.  
  368.         /* we come here if we have entered a normal character */
  369.         if (cmdlen < CMDBUFFSIZE - 2)
  370.         {
  371.                 for (i = cmdlen++; i > cmdpos; --i)
  372.                         buff[i] = buff[i - 1];
  373.                 buff[cmdpos] = c;
  374.                 outtrans((char *)(buff + cmdpos), cmdlen - cmdpos);
  375.                 ++cmdpos;
  376.                 i = charsize(c);
  377.                 cmdslen += i;
  378.                 cmdspos += i;
  379.         }
  380.         cmdchecklen();
  381.     }
  382.     buff[cmdlen] = NUL;
  383.  
  384.     if (++hisidx == hislen)
  385.         hisidx = 0;
  386.     free(history[hisidx]);
  387.     history[hisidx] = strsave((char *)buff);
  388.     retval = TRUE;
  389.  
  390. returncmd:
  391.     if (cmdredraw)
  392.         updateScreen(CLEAR);
  393.     State = NORMAL;
  394.     script_winsize_pp();
  395.     return retval;
  396. }
  397.  
  398. /*
  399.  * Check if the command line spans more than one screen line.
  400.  * The maximum number of lines is remembered.
  401.  */
  402.     static void
  403. cmdchecklen()
  404. {
  405.         if (cmdslen / Columns > cmdredraw)
  406.                 cmdredraw = cmdslen / Columns;
  407. }
  408.  
  409. /*
  410.  * this fuction is called when the screen size changes
  411.  */
  412.     void
  413. redrawcmdline()
  414. {
  415.         cmdredraw = 0;
  416.         redrawcmd();
  417.         cursorcmd();
  418. }
  419.  
  420. /*
  421.  * Redraw what is currently on the command line.
  422.  */
  423.     void
  424. redrawcmd()
  425. {
  426.     register int i;
  427.  
  428.     windgoto((int)Rows - 1 - cmdredraw, 0);
  429.     outchar(cmdfirstc);
  430.     cmdslen = 1;
  431.     cmdspos = 1;
  432.     outtrans((char *)cmdbuff, cmdlen);
  433.     for (i = 0; i < cmdlen; )
  434.     {
  435.         cmdslen += charsize(cmdbuff[i]);
  436.         if (++i == cmdpos)
  437.                 cmdspos = cmdslen;
  438.     }
  439.     for (i = (cmdredraw + 1) * Columns - cmdslen; --i > 0; )
  440.         outchar(' ');
  441.     cmdchecklen();
  442. }
  443.  
  444.     static void
  445. cursorcmd()
  446. {
  447.     windgoto((int)Rows - 1 - cmdredraw + (cmdspos / (int)Columns), cmdspos % (int)Columns);
  448. }
  449.  
  450. /*
  451.  * docmdline(): execute an Ex command line
  452.  *
  453.  * 1. If no line given, get one.
  454.  * 2. Split up in parts separated with '|'.
  455.  *
  456.  * This function may be called recursively!
  457.  */
  458.     void
  459. docmdline(cmdline)
  460.     u_char        *cmdline;
  461. {
  462.     u_char        buff[CMDBUFFSIZE];        /* command line */
  463.     u_char        *nextcomm;
  464.  
  465. /*
  466.  * 1. If no line given: get one.
  467.  */
  468.     if (cmdline == NULL)
  469.     {
  470.         if (!getcmdline(':', buff))
  471.                 return;
  472.     }
  473.     else
  474.     {
  475.         if (strlen((char *)cmdline) > CMDBUFFSIZE - 2)
  476.         {
  477.                 emsg(e_toolong);
  478.                 return;
  479.         }
  480.         /* Make a copy of the command so we can mess with it. */
  481.         strcpy((char *)buff, (char *)cmdline);
  482.     }
  483.  
  484. /*
  485.  * 2. Loop for each '|' separated command.
  486.  *    DoOneCmd will set nextcommand to NULL if there is no trailing '|'.
  487.  */
  488.     for (;;)
  489.     {
  490.         nextcomm = DoOneCmd(buff);
  491.         if (nextcomm == NULL)
  492.             break;
  493.         strcpy((char *)buff, (char *)nextcomm);
  494.     }
  495. }
  496.  
  497. /*
  498.  * Execute one Ex command.
  499.  *
  500.  * 2. skip comment lines and leading space
  501.  * 3. parse range
  502.  * 4. parse command
  503.  * 5. parse arguments
  504.  * 6. switch on command name
  505.  *
  506.  * This function may be called recursively!
  507.  */
  508.     static u_char *
  509. DoOneCmd(buff)
  510.     u_char *buff;
  511. {
  512.     u_char                cmdbuf[CMDBUFFSIZE];    /* for '%' and '#' expansion */
  513.     u_char                c;
  514.     register u_char        *p;
  515.     char                *q;
  516.     u_char                *cmd, *arg;
  517.     int                 i;
  518.     int                    cmdidx;
  519.     int                    argt;
  520.     register linenr_t    lnum;
  521.     long                n;
  522.     int                    addr_count;    /* number of address specifications */
  523.     FPOS                pos;
  524.     int                    append = FALSE;            /* write with append */
  525.     u_char                *nextcomm;
  526.  
  527.  
  528. /*
  529.  * 2. skip comment lines and leading space, colons or bars
  530.  */
  531.     for (cmd = buff; *cmd && strchr(" \t:|", *cmd) != NULL; cmd++)
  532.         ;
  533.  
  534.     nextcomm = NULL;        /* default: no next command */
  535.     if (strchr("#\"", *cmd) != NULL)    /* ignore comment and empty lines */
  536.         goto doend;
  537.  
  538. /*
  539.  * 3. parse a range specifier of the form: addr [,addr] [;addr] ..
  540.  *
  541.  * where 'addr' is:
  542.  *
  543.  * %          (entire file)
  544.  * $  [+-NUM]
  545.  * 'x [+-NUM] (where x denotes a currently defined mark)
  546.  * .  [+-NUM]
  547.  * [+-NUM]..
  548.  * NUM
  549.  *
  550.  * The cmd pointer is updated to point to the first character following the
  551.  * range spec. If an initial address is found, but no second, the upper bound
  552.  * is equal to the lower.
  553.  */
  554.  
  555.     addr_count = 0;
  556.     --cmd;
  557.     do {
  558.         ++cmd;                            /* skip ',' or ';' */
  559.         line1 = line2;
  560.         line2 = Curpos.lnum;            /* default is current line number */
  561.         skipspace((char **)&cmd);
  562.         lnum = get_address(&cmd, Curpos.lnum);
  563.         if (lnum == INVLNUM)
  564.         {
  565.             if (*cmd == '%')            /* '%' - all lines */
  566.             {
  567.                 ++cmd;
  568.                 line1 = 1;
  569.                 line2 = line_count;
  570.                 ++addr_count;
  571.             }
  572.         } else
  573.             line2 = lnum;
  574.         addr_count++;
  575.  
  576.         if (*cmd == ';')
  577.         {
  578.             if (line2 == 0)
  579.                 Curpos.lnum = 1;
  580.             else
  581.                 Curpos.lnum = line2;
  582.         }
  583.     } while (*cmd == ',' || *cmd == ';');
  584.  
  585.     /* One address given: set start and end lines */
  586.     if (addr_count == 1) {
  587.         line1 = line2;
  588.         /* ... but only implicit: really no address given */
  589.         if (lnum == INVLNUM) {
  590.             addr_count = 0;
  591.         }
  592.     }
  593.  
  594.     if (line1 > line2 || line2 > line_count)
  595.     {
  596.         emsg(e_invrange);
  597.         goto doend;
  598.     }
  599.  
  600. /*
  601.  * 4. parse command
  602.  */
  603.  
  604.     skipspace((char **)&cmd);
  605.  
  606.     /*
  607.      * If we got a line, but no command, then go to the line.
  608.      */
  609.     if (*cmd == NUL || *cmd == '"' || (*cmd == '|' && (nextcomm = cmd) != NULL))
  610.     {
  611.         if (addr_count != 0)
  612.         {
  613.             if (line2 == 0)
  614.                 Curpos.lnum = 1;
  615.             else
  616.                 Curpos.lnum = line2;
  617.             Curpos.col = 0;
  618.             cursupdate();
  619.         }
  620.         goto doend;
  621.     }
  622.  
  623.     /*
  624.      * isolate the command and search for it in the command table
  625.      */
  626.     p = cmd;
  627.     if (*cmd != 'k')
  628.         while (isalpha(*p))
  629.             ++p;
  630.     if (p == cmd && strchr("@!=><&k", *p) != NULL)    /* non-alpha or 'k' command */
  631.         ++p;
  632.     i = p - cmd;
  633.  
  634.     for (cmdidx = 0; cmdidx < CMD_SIZE; ++cmdidx)
  635.         if (strncmp(cmdnames[cmdidx].cmd_name, (char *)cmd, (size_t)i) == 0)
  636.             break;
  637.  
  638.     if (i == 0 || cmdidx == CMD_SIZE)
  639.     {
  640.         emsg(e_invcmd);
  641.         goto doend;
  642.     }
  643.  
  644.     if (*p == '!')                    /* forced commands */
  645.     {
  646.         ++p;
  647.         forceit = TRUE;
  648.     }
  649.     else
  650.         forceit = FALSE;
  651.  
  652. /*
  653.  * 5. parse arguments
  654.  */
  655.     argt = cmdnames[cmdidx].cmd_argt;
  656.  
  657.     if (!(argt & RANGE) && addr_count)
  658.     {
  659.         emsg(e_norange);
  660.         goto doend;
  661.     }
  662.  
  663.     if (!(argt & ZEROR))            /* zero in range not allowed */
  664.     {
  665.         if (line1 == 0)
  666.             line1 = 1;
  667.         if (line2 == 0)
  668.             line2 = 1;
  669.     }
  670.  
  671.     arg = p;                        /* remember start of argument */
  672.     skipspace((char **)&arg);
  673.  
  674.     if ((argt & NEEDARG) && *arg == NUL)
  675.     {
  676.         emsg(e_argreq);
  677.         goto doend;
  678.     }
  679.  
  680.     /*
  681.      * check for '|' to separate commands and '"' to start comments
  682.      */
  683.     if (argt & TRLBAR)
  684.     {
  685.         while (*p)
  686.         {
  687.             if (*p == CTRL('V'))
  688.                 strcpy((char *)p, (char *)p + 1);
  689.             else if ((*p == '"' && !(argt & NOTRLCOM)) || *p == '|')
  690.             {                        /* remove the backslash or ^V */
  691.                 if (*(p - 1) == '\\')
  692.                 {
  693.                     strcpy((char *)p - 1, (char *)p);
  694.                     --p;
  695.                 }
  696.                 else
  697.                 {
  698.                     if (*p == '|')
  699.                         nextcomm = p + 1;
  700.                     *p = NUL;
  701.                     break;
  702.                 }
  703.             }
  704.             ++p;
  705.         }
  706.     }
  707.  
  708.     if ((argt & DFLALL) && addr_count == 0)
  709.     {
  710.         line1 = 1;
  711.         line2 = line_count;
  712.     }
  713.  
  714.     regname = 0;
  715.         /* accept numbered register only when no count allowed (:put) */
  716.     if ((argt & REGSTR) && (isalpha(*arg) || *arg == '.' || (!(argt & COUNT) && isdigit(*arg))))
  717.     {
  718.         regname = *arg;
  719.         ++arg;
  720.         skipspace((char **)&arg);
  721.     }
  722.  
  723.     if ((argt & COUNT) && isdigit(*arg))
  724.     {
  725.         i = getdigits((char **)&arg);
  726.         skipspace((char **)&arg);
  727.         if (i <= 0)
  728.         {
  729.             emsg(e_zerocount);
  730.             goto doend;
  731.         }
  732.         line1 = line2;
  733.         line2 += i - 1;
  734.     }
  735.  
  736.     if (!(argt & EXTRA) && strchr("|\"#", *arg) == NULL)    /* no arguments allowed */
  737.     {
  738.         emsg(e_trailing);
  739.         goto doend;
  740.     }
  741.  
  742.     if (cmdidx == CMD_write && *arg == '>' && *(arg + 1) == '>')    /* append */
  743.     {
  744.         arg += 2;
  745.         skipspace((char **)&arg);
  746.         append = TRUE;
  747.     }
  748.  
  749.     /*
  750.      * change '%' to Filename, '#' to altfile
  751.      */
  752.     if (argt & XFILE)
  753.     {
  754.         for (p = arg; *p; ++p)
  755.         {
  756.             c = *p;
  757.             if (c != '%' && c != '#')    /* nothing to expand */
  758.                 continue;
  759.             if (*(p - 1) == '\\')        /* remove escaped char */
  760.             {
  761.                 strcpy((char *)p - 1, (char *)p);
  762.                 --p;
  763.                 continue;
  764.             }
  765.  
  766.             n = 1;                /* length of what we expand */
  767.             if (c == '%')
  768.             {
  769.                 if (check_fname())
  770.                         goto doend;
  771.                 q = Filename;
  772.             }
  773.             else
  774.             {
  775.                 q = (char *)p + 1;
  776.                 i = getdigits(&q);
  777.                 n = q - (char *)p;
  778.  
  779.                 if (i >= NUMALTFILES || altfiles[i] == NULL)
  780.                 {
  781.                         emsg(e_noalt);
  782.                         goto doend;
  783.                 }
  784.                 doecmdlnum = altlnum[i];
  785.                 q = altfiles[i];
  786.             }
  787.             i = strlen((char *)arg) + strlen(q) + 3;
  788.             if (nextcomm)
  789.                 i += strlen((char *)nextcomm);
  790.             if (i > CMDBUFFSIZE)
  791.             {
  792.                 emsg(e_toolong);
  793.                 goto doend;
  794.             }
  795.             /*
  796.              * we built the new argument in cmdbuf[], then copy it back to buff[]
  797.              */
  798.             *p = NUL;
  799.             strcpy((char *)cmdbuf, (char *)arg);
  800.             strcat((char *)cmdbuf, q);
  801.             i = strlen((char *)cmdbuf);
  802.             strcat((char *)cmdbuf, (char *)p+n);
  803.             p = buff + i - 1;
  804.             if (nextcomm)
  805.             {
  806.                 i = strlen((char *)cmdbuf) + 1;
  807.                 strcpy((char *)cmdbuf + i, (char *)nextcomm);
  808.                 nextcomm = buff + i;
  809.             }
  810.             strcpy((char *)buff, (char *)cmdbuf);
  811.             arg = buff;
  812.         }
  813. #ifdef WILD_CARDS
  814.         if (argt & NOSPC)        /* one file argument: expand wildcards */
  815.         {
  816.             for (p = arg; *p; ++p)
  817. #ifdef UNIX
  818.                 if (strchr("*?[{}`~$", *p) != NULL)
  819. #else
  820.                 if (strchr("*?[{}`", *p) != NULL)
  821. #endif
  822.                     break;
  823.             if (*p)                    /* has a wildcard */
  824.             {
  825.                 if ((p = (u_char *)ExpandOne(arg, TRUE, -1)) == NULL)
  826.                     goto doend;
  827.                 if (strlen((char *)p) + arg - buff < CMDBUFFSIZE - 2)
  828.                     strcpy((char *)arg, (char *)p);
  829.                 else
  830.                     emsg(e_toolong);
  831.                 free(p);
  832.             }
  833.         }
  834. #endif
  835.     }
  836.  
  837. /*
  838.  * 6. switch on command name
  839.  */
  840.     switch (cmdidx)
  841.     {
  842.         case CMD_quit:
  843.                 exiting = TRUE;
  844.                 settmode(0);        /* allows typeahead */
  845.                 if (check_changed(FALSE) || check_more())
  846.                 {
  847.                     exiting = FALSE;
  848.                     settmode(1);
  849.                     break;
  850.                 }
  851.                 getout(0);
  852.  
  853.         case CMD_stop:
  854.                 if (!forceit && Changed)
  855.                     autowrite();
  856.                 gotocmdline(TRUE, NUL);
  857.                 flushbuf();
  858.                 mch_suspend();        /* call machine specific function */
  859.                 updateScreen(CLEAR);
  860.                 break;
  861.  
  862.         case CMD_xit:
  863.         case CMD_wq:
  864.                 exiting = TRUE;
  865.                 settmode(0);        /* allows typeahead */
  866.                 if ((cmdidx == CMD_wq || Changed) && (check_readonly() || !dowrite(arg, FALSE)))
  867.                 {
  868.                     exiting = FALSE;
  869.                     settmode(1);
  870.                     break;
  871.                 }
  872.                 if (check_more())
  873.                 {
  874.                     exiting = FALSE;
  875.                     settmode(1);
  876.                     break;
  877.                 }
  878.                 getout(0);
  879.  
  880.         case CMD_args:
  881.                 gotocmdline(TRUE, NUL);
  882.                 for (i = 0; i < numfiles; ++i)
  883.                 {
  884.                     if (i == curfile)
  885.                         outchar('[');
  886.                     outstrn(files[i]);
  887.                     if (i == curfile)
  888.                         outchar(']');
  889.                     outchar(' ');
  890.                 }
  891.                 outchar('\n');
  892.                 wait_return(TRUE);
  893.                 break;
  894.  
  895.         case CMD_wnext:
  896.                 n = line2;
  897.                 line1 = 1;
  898.                 line2 = line_count;
  899.                 dowrite(arg, FALSE);
  900.                 line2 = n;
  901.                 arg = (u_char *)"";        /* no file list */
  902.                 /*FALLTHROUGH*/
  903.  
  904.         case CMD_next:
  905.                 if (check_changed(TRUE))
  906.                         break;
  907.                 if (*arg != NUL)        /* redefine file list */
  908.                 {
  909.                     if (doarglist((char *)arg))
  910.                         break;
  911.                     i = 0;
  912.                 }
  913.                 else
  914.                 {
  915.                     if (addr_count == 0)
  916.                         i = curfile + 1;
  917.                     else
  918.                         i = curfile + line2;
  919.                 }
  920.  
  921. donextfile:        if (i < 0 || i >= numfiles)
  922.                 {
  923.                     emsg(e_nomore);
  924.                     break;
  925.                 }
  926.                 if (check_changed(TRUE))
  927.                         break;
  928.                 curfile = i;
  929.                 doecmd(files[curfile]);
  930.                 break;
  931.  
  932.         case CMD_previous:
  933.         case CMD_Next:
  934.                 if (addr_count == 0)
  935.                     i = curfile - 1;
  936.                 else
  937.                     i = curfile - line2;
  938.                 goto donextfile;
  939.  
  940.         case CMD_rewind:
  941.                 i = 0;
  942.                 goto donextfile;
  943.  
  944.         case CMD_write:
  945.                 if (*arg == '!')        /* input lines to shell command */
  946.                     dofilter(arg + 1, TRUE, FALSE);
  947.                 else
  948.                     dowrite(arg, append);
  949.                 break;
  950.  
  951.         case CMD_edit:
  952.         case CMD_ex:
  953.         case CMD_visual:
  954.                 doecmd((char *)arg);
  955.                 break;
  956.  
  957.         case CMD_file:
  958.                 if (*arg == NUL)
  959.                         fileinfo();
  960.                 else
  961.                 {
  962.                         setfname((char *)arg);
  963.                         filemess(Filename, "");
  964.                         maketitle();
  965.                 }
  966.                 break;
  967.  
  968.         case CMD_files:
  969.                 settmode(0);
  970.                 for (i = 0; i < NUMALTFILES; ++i)
  971.                 {
  972.                     if (altfiles[i])
  973.                     {
  974.                         sprintf(IObuff, "%2d \"%s\" line %ld\n", i, altfiles[i], (long)altlnum[i]);
  975.                         outstrn(IObuff);
  976.                     }
  977.                     flushbuf();
  978.                 }
  979.                 settmode(1);
  980.                 wait_return(TRUE);
  981.                 break;
  982.  
  983.         case CMD_read:
  984.                 if (forceit || (*arg == '!' && ++arg))
  985.                 {
  986.                         dofilter(arg, FALSE, TRUE);            /* :r!cmd */
  987.                         break;
  988.                 }
  989.                 if (!u_save(line2, (linenr_t)(line2 + 1)))
  990.                         break;
  991.                 if (readfile((char *)arg, line2, FALSE))
  992.                 {
  993.                     emsg(e_notopen);
  994.                     break;
  995.                 }
  996.                 updateScreen(NOT_VALID);
  997.                 CHANGED;
  998.                 break;
  999.  
  1000.         case CMD_cd:
  1001.         case CMD_chdir:
  1002.                 if (*arg == NUL)
  1003.                 {
  1004.                     if (dirname(IObuff, IOSIZE))
  1005.                         msg(IObuff);
  1006.                     else
  1007.                         emsg(e_unknown);
  1008.                 }
  1009.                 else
  1010.                 {
  1011.                     if (chdir((char *)arg))
  1012.                         emsg(e_failed);
  1013.                 }
  1014.                 break;
  1015.  
  1016.         case CMD_equal:
  1017.                 smsg("line %ld", (long)line2);
  1018.                 break;
  1019.  
  1020.         case CMD_print:
  1021.                 settmode(0);            /* set cooked mode, so output can be halted */
  1022.                 do
  1023.                 {
  1024.                     if (p_nu)
  1025.                     {
  1026.                         sprintf(IObuff, "%7ld ", (long)line1);
  1027.                         outstrn(IObuff);
  1028.                     }
  1029.                     prt_line(nr2ptr(line1));
  1030.                     outchar('\n');
  1031.                     flushbuf();
  1032.                 } while (++line1 <= line2);
  1033.                 settmode(1);
  1034.  
  1035.                 if (global_busy)
  1036.                     global_wait = 1;
  1037.                 else
  1038.                     wait_return(TRUE);
  1039.                 break;
  1040.  
  1041.         case CMD_shell:
  1042.                 doshell(NULL);
  1043.                 break;
  1044.  
  1045.         case CMD_tag:
  1046.                 dotag((char *)arg, 0, addr_count ? (int)line2 : 1);
  1047.                 break;
  1048.  
  1049.         case CMD_pop:
  1050.                 dotag("", 1, addr_count ? (int)line2 : 1);
  1051.                 break;
  1052.  
  1053.         case CMD_tags:
  1054.                 dotags();
  1055.                 break;
  1056.  
  1057.         case CMD_marks:
  1058.                 domarks();
  1059.                 break;
  1060.  
  1061.         case CMD_jumps:
  1062.                 dojumps();
  1063.                 break;
  1064.  
  1065.         case CMD_digraph:
  1066. #ifdef DIGRAPHS
  1067.                 if (*arg)
  1068.                     putdigraph((char *)arg);
  1069.                 else
  1070.                     listdigraphs();
  1071. #else
  1072.                 emsg("No digraphs in this version");
  1073. #endif /* DIGRAPHS */
  1074.                 break;
  1075.  
  1076.         case CMD_set:
  1077.                 doset((char *)arg);
  1078.                 break;
  1079.  
  1080.         case CMD_map:
  1081.         case CMD_unmap:
  1082.                 switch (domap(*cmd == 'u', (char *)arg, forceit ? INSERT : NORMAL))
  1083.                 {
  1084.                     case 1: emsg(e_invarg);
  1085.                             break;
  1086.                     case 2: emsg(e_nomap);
  1087.                             break;
  1088.                     case 3: emsg(e_ambmap);
  1089.                             break;
  1090.                 }
  1091.                 break;
  1092.  
  1093.         case CMD_display:
  1094.                 dodis();        /* display buffer contents */
  1095.                 break;
  1096.  
  1097.         case CMD_help:
  1098.                 help();
  1099.                 break;
  1100.  
  1101.         case CMD_version:
  1102.                 msg(longVersion);
  1103.                 break;
  1104.  
  1105.         case CMD_winsize:
  1106.                 line1 = getdigits((char **)&arg);
  1107.                 skipspace((char **)&arg);
  1108.                 line2 = getdigits((char **)&arg);
  1109.                 set_winsize((int)line1, (int)line2, TRUE);
  1110.                 break;
  1111.  
  1112.         case CMD_delete:
  1113.         case CMD_yank:
  1114.         case CMD_rshift:
  1115.         case CMD_lshift:
  1116.                 yankbuffer = regname;
  1117.                 startop.lnum = line1;
  1118.                 endop.lnum = line2;
  1119.                 nlines = line2 - line1 + 1;
  1120.                 mtype = MLINE;
  1121.                 Curpos.lnum = line1;
  1122.                 switch (cmdidx)
  1123.                 {
  1124.                 case CMD_delete:
  1125.                     dodelete();
  1126.                     break;
  1127.                 case CMD_yank:
  1128.                     doyank(FALSE);
  1129.                     break;
  1130.                 case CMD_rshift:
  1131.                     doshift(RSHIFT);
  1132.                     break;
  1133.                 case CMD_lshift:
  1134.                     doshift(LSHIFT);
  1135.                     break;
  1136.                 }
  1137.                 break;
  1138.  
  1139.         case CMD_put:
  1140.                 yankbuffer = regname;
  1141.                 Curpos.lnum = line2;
  1142.                 doput(forceit ? BACKWARD : FORWARD, -1L);
  1143.                 break;
  1144.  
  1145.         case CMD_t:
  1146.         case CMD_copy:        /* copy: first yank, then put */
  1147.         case CMD_move:        /* move: first delete, then put */
  1148.                 n = get_address(&arg, line2);
  1149.                 if (n == INVLNUM)
  1150.                 {
  1151.                     emsg(e_invaddr);
  1152.                     break;
  1153.                 }
  1154.  
  1155.                 yankbuffer = 0;        /* use default yank buffer */
  1156.                 startop.lnum = line1;
  1157.                 endop.lnum = line2;
  1158.                 nlines = line2 - line1 + 1;
  1159.                 mtype = MLINE;
  1160.                 Curpos.lnum = line1;
  1161.                 if (cmdidx == CMD_move)
  1162.                 {
  1163.                     if (n > line2)        /* correct n for the deleted lines */
  1164.                         n -= nlines;
  1165.                     else if (n >= line1)
  1166.                         n = line1 - 1;
  1167.                     dodelete();
  1168.                 }
  1169.                 else
  1170.                     doyank(FALSE);
  1171.  
  1172.                 if (n == 0)                /* put in above first line */
  1173.                 {
  1174.                     Curpos.lnum = 1;
  1175.                     doput(BACKWARD, -1L);
  1176.                 }
  1177.                 else
  1178.                 {
  1179.                     Curpos.lnum = n;
  1180.                     doput(FORWARD, -1L);
  1181.                 }
  1182.                 break;
  1183.  
  1184.         case CMD_and:
  1185.         case CMD_substitute:
  1186.                 dosub(line1, line2, (char *)arg, &nextcomm);
  1187.                 break;
  1188.  
  1189.         case CMD_join:
  1190.                 Curpos.lnum = line1;
  1191.                 if (line1 == line2)
  1192.                 {
  1193.                     if (line2 == line_count)
  1194.                     {
  1195.                         beep();
  1196.                         break;
  1197.                     }
  1198.                     ++line2;
  1199.                 }
  1200.                 dodojoin(line2 - line1 + 1, !forceit, TRUE);
  1201.                 break;
  1202.  
  1203.         case CMD_global:
  1204.                 if (forceit)
  1205.                     *cmd = 'v';
  1206.         case CMD_vglobal:
  1207.                 doglob(*cmd, line1, line2, (char *)arg);
  1208.                 break;
  1209.  
  1210.         case CMD_at:                /* :[addr]@r */
  1211.                 Curpos.lnum = line2;
  1212.                 if (!doexecbuf(*arg))        /* put the register in mapbuf */
  1213.                     beep();
  1214.                 else
  1215.                     docmdline(NULL);        /* execute from the mapbuf */
  1216.                 break;
  1217.  
  1218.         case CMD_bang:
  1219.                 dobang(addr_count, arg);
  1220.                 break;
  1221.  
  1222.         case CMD_undo:
  1223.                 u_undo(1);
  1224.                 break;
  1225.  
  1226.         case CMD_source:
  1227.                 if (forceit)    /* :so! read vi commands */
  1228.                     openscript((char *)arg);
  1229.                 else if (dosource((char *)arg))        /* :so read ex commands */
  1230.                     emsg(e_notopen);
  1231.                 break;
  1232.  
  1233.         case CMD_mkexrc:
  1234.                 {
  1235.                     FILE    *fd;
  1236.  
  1237.                     if (*arg == NUL)
  1238.                         arg = (u_char *)".exrc";
  1239.                     if (!forceit && (fd = fopen((char *)arg, "r")) != NULL)
  1240.                     {
  1241.                         fclose(fd);
  1242.                         emsg(e_exists);
  1243.                         break;
  1244.                     }
  1245.  
  1246.                     if ((fd = fopen((char *)arg, "w")) == NULL)
  1247.                     {
  1248.                         emsg(e_notcreate);
  1249.                         break;
  1250.                     }
  1251.                     if (makemap(fd) || makeset(fd))
  1252.                         emsg(e_write);
  1253.                     fclose(fd);
  1254.                     break;
  1255.                 }
  1256.  
  1257.         case CMD_cc:
  1258.                     qf_jump(atoi((char *)arg));
  1259.                     break;
  1260.  
  1261.         case CMD_cf:
  1262.                     if (*arg == NUL)
  1263.                         arg = (u_char *)p_ef;
  1264.                     if (!qf_init((char *)arg))
  1265.                         qf_jump(0);
  1266.                     break;
  1267.  
  1268.         case CMD_cl:
  1269.                     qf_list();
  1270.                     break;
  1271.  
  1272.         case CMD_cn:
  1273.                     qf_jump(qf_index + 1);
  1274.                     break;
  1275.  
  1276.         case CMD_cp:
  1277.                     qf_jump(qf_index - 1);
  1278.                     break;
  1279.  
  1280.         case CMD_cq:
  1281.                     getout(1);        /* this does not always work. why? */
  1282.  
  1283.         case CMD_mark:
  1284.         case CMD_k:
  1285.                     pos = Curpos;            /* save Curpos */
  1286.                     Curpos.lnum = line2;
  1287.                     Curpos.col = 0;
  1288.                     setmark(*arg);            /* set mark */
  1289.                     Curpos = pos;            /* restore Curpos */
  1290.                     break;
  1291.  
  1292. #ifdef SETKEYMAP
  1293.         case CMD_setkeymap:
  1294.                     set_keymap(arg);
  1295.                     break;
  1296. #endif
  1297.  
  1298.         default:
  1299.                     emsg(e_invcmd);
  1300.     }
  1301.  
  1302.  
  1303. doend:
  1304.     return nextcomm;
  1305. }
  1306.  
  1307. /*
  1308.  * handle the :! command.
  1309.  * We replace the extra bangs by the previously entered command and remember
  1310.  * the command.
  1311.  */
  1312.     static void
  1313. dobang(addr_count, arg)
  1314.     int        addr_count;
  1315.     u_char    *arg;
  1316. {
  1317.     static    char    *prevcmd = NULL;        /* the previous command */
  1318.     char            *t;
  1319.     char            *trailarg;
  1320.     int             len;
  1321.  
  1322.     len = strlen((char *)arg) + 1;
  1323.  
  1324.     if (Changed)
  1325.         autowrite();
  1326.     /*
  1327.      * try to find an embedded bang, like in :!<cmd> ! [args]
  1328.      * (:!! is indicated by the 'forceit' variable)
  1329.      */
  1330.     for (trailarg = (char *)arg; *trailarg && !isspace(*trailarg); ++trailarg)
  1331.         ;
  1332.     skipspace(&trailarg);
  1333.     if (*trailarg == '!')
  1334.         *trailarg++ = NUL;
  1335.     else
  1336.         trailarg = NULL;
  1337.  
  1338.     if (forceit || trailarg != NULL)            /* use the previous command */
  1339.     {
  1340.         if (prevcmd == NULL)
  1341.         {
  1342.             emsg(e_noprev);
  1343.             return;
  1344.         }
  1345.         len += strlen(prevcmd) * (trailarg != NULL && forceit ? 2 : 1);
  1346.     }
  1347.  
  1348.     if (len > CMDBUFFSIZE)
  1349.     {
  1350.         emsg(e_toolong);
  1351.         return;
  1352.     }
  1353.     if ((t = alloc(len)) == NULL)
  1354.         return;
  1355.     *t = NUL;
  1356.     if (forceit)
  1357.         strcpy(t, prevcmd);
  1358.     strcat(t, (char *)arg);
  1359.     if (trailarg != NULL)
  1360.     {
  1361.         strcat(t, prevcmd);
  1362.         strcat(t, trailarg);
  1363.     }
  1364.     free(prevcmd);
  1365.     prevcmd = t;
  1366.  
  1367.     if (addr_count == 0)
  1368.     {
  1369.         smsg(":!%s", prevcmd);
  1370.         doshell(prevcmd);                 /* :! */
  1371.     }
  1372.     else
  1373.     {
  1374.         smsg(":%ld,%ld!%s", (long)line1, (long)line2, prevcmd);
  1375.         dofilter((u_char *)prevcmd, TRUE, TRUE);        /* :range! */
  1376.     }
  1377. }
  1378.  
  1379.     static int
  1380. autowrite()
  1381. {
  1382.     if (!p_aw || check_readonly() || check_fname())
  1383.         return FALSE;
  1384.     return (writeit(Filename, (linenr_t)1, line_count, 0, 0));
  1385. }
  1386.  
  1387.     static int
  1388. dowrite(arg, append)
  1389.     u_char    *arg;
  1390.     int        append;
  1391. {
  1392.     FILE *f;
  1393.  
  1394.     if ((*arg == NUL || (Filename && !strcmp((char *)arg, Filename))) && check_readonly())
  1395.         return FALSE;
  1396.     if (*arg == NUL)
  1397.     {
  1398.         if (check_fname())
  1399.             return FALSE;
  1400.         return (writeit(Filename, line1, line2, append, forceit));
  1401.     }
  1402.     if (!forceit && !append && !p_wa && (f = fopen((char *)arg, "r")) != NULL)
  1403.     {                                /* don't overwrite existing file */
  1404.             fclose(f);
  1405.             emsg(e_exists);
  1406.             return 0;
  1407.     }
  1408.     return (writeit((char *)arg, line1, line2, append, forceit));
  1409. }
  1410.  
  1411.     static int
  1412. doecmd(arg)
  1413.     char        *arg;
  1414. {
  1415.     int            setalt;
  1416.     char        *command = NULL;
  1417.     int            i;
  1418.     linenr_t    newlnum;
  1419.  
  1420.     newlnum = doecmdlnum;
  1421.     doecmdlnum = 0;                        /* reset it for next time */
  1422.  
  1423.     if (*arg == '+')
  1424.     {
  1425.         ++arg;
  1426. #if 0
  1427.         if (isdigit(*arg))                /* :e +num file */
  1428.             newlnum = getdigits(&arg);
  1429.         else if (*arg == NUL || isspace(*arg))            /* :e + file */
  1430.             newlnum = INVLNUM;
  1431.         else                            /* :e +command file */
  1432.         {
  1433.             command = arg;
  1434.             while (*arg && !isspace(*arg))
  1435.                 ++arg;
  1436.             if (*arg)
  1437.                 *arg++ = NUL;
  1438.         }
  1439. #endif
  1440.         if (isspace(*arg))
  1441.             command = "$";
  1442.         else
  1443.         {
  1444.             command = arg;
  1445.             while (*arg && !isspace(*arg))
  1446.                 ++arg;
  1447.         }
  1448.         if (*arg)
  1449.             *arg++ = NUL;
  1450.         
  1451.         skipspace(&arg);
  1452.     }
  1453.  
  1454. #ifdef AMIGA
  1455.     fname_case(arg);        /* set correct case for filename */
  1456. #endif
  1457.     FullName(arg, IObuff, IOSIZE);
  1458.     setalt = (*arg != NUL && (Filename == NULL || fnamecmp(IObuff, Filename)));
  1459.     if (setalt)
  1460.     {
  1461.         free(altfiles[NUMALTFILES - 1]);
  1462.         for (i = NUMALTFILES - 1; i > 0; --i)
  1463.         {
  1464.             altfiles[i] = altfiles[i - 1];
  1465.             altlnum[i] = altlnum[i - 1];
  1466.         }
  1467.         incrmarks();        /* increment file number for all jumpmarks */
  1468.         incrtags();            /* increment file number for all tags */
  1469.     }
  1470.     if (check_changed(FALSE))
  1471.     {
  1472.         if (setalt)
  1473.         {
  1474.             altfiles[0] = strsave(arg);
  1475.             altlnum[0] = 1;
  1476.         }
  1477.         decrmarks();        /* decrement file number for jumpmarks in current file */
  1478.         decrtags();            /* decrement file number for tags in current file */
  1479.         return FALSE;
  1480.     }
  1481.     if (setalt)
  1482.     {
  1483.         altfiles[0] = Filename;
  1484.         Filename = NULL;
  1485.         altlnum[0] = Curpos.lnum;
  1486.         setfname(arg);
  1487.     }
  1488.     else if (newlnum == 0)
  1489.         newlnum = Curpos.lnum;
  1490.     maketitle();
  1491.     if (check_fname())
  1492.         return FALSE;
  1493.  
  1494.     /* clear mem and read file */
  1495.     freeall();
  1496.     filealloc();
  1497.     UNCHANGED;
  1498.     startscript();        /* re-start auto script file */
  1499.  
  1500.     i = RedrawingDisabled;
  1501.     RedrawingDisabled = TRUE;        /* don't redraw until the cursor is in
  1502.                                      * the right line */
  1503.     readfile(Filename, (linenr_t)0, TRUE);
  1504.     if (newlnum && command == NULL)
  1505.     {
  1506.         if (newlnum != INVLNUM)
  1507.             Curpos.lnum = newlnum;
  1508.         else
  1509.             Curpos.lnum = line_count;
  1510.         Curpos.col = 0;
  1511.     }
  1512.     if (command)
  1513.         docmdline((u_char *)command);
  1514.     RedrawingDisabled = i;            /* cursupdate() will redraw the screen */
  1515.     if (p_im)
  1516.         stuffReadbuff("i");            /* start editing in insert mode */
  1517.     return TRUE;
  1518. }
  1519.  
  1520.     static void
  1521. doshell(cmd)
  1522.     char    *cmd;
  1523. {
  1524.     gotocmdline(FALSE, '\n');
  1525.  
  1526.     call_shell(cmd, 0);
  1527.  
  1528.     if (global_busy)
  1529.         global_wait = 1;
  1530.     else
  1531. #ifdef AMIGA
  1532.         wait_return(!term_console);
  1533. #else
  1534.         wait_return(TRUE);
  1535. #endif
  1536.  
  1537.     /* in an Amiga window redrawing is caused by asking the window size */
  1538. #ifdef AMIGA
  1539.     if (term_console)
  1540.         outstr("\033[0 q");     /* get window size */
  1541. #endif /* AMIGA */
  1542. }
  1543.  
  1544. /*
  1545.  * dofilter: filter lines through a command given by the user
  1546.  *
  1547.  * We use temp files and the call_shell() routine here. This would normally
  1548.  * be done using pipes on a UNIX machine, but this is more portable to
  1549.  * the machines we usually run on. The call_shell() routine needs to be able
  1550.  * to deal with redirection somehow, and should handle things like looking
  1551.  * at the PATH env. variable, and adding reasonable extensions to the
  1552.  * command name given by the user. All reasonable versions of call_shell()
  1553.  * do this.
  1554.  * We use input redirection if do_in is TRUE.
  1555.  * We use output redirection if do_out is TRUE.
  1556.  */
  1557.     static void
  1558. dofilter(buff, do_in, do_out)
  1559.     u_char        *buff;
  1560.     int            do_in, do_out;
  1561. {
  1562.         char        itmp[TMPNAMELEN];
  1563.         char        otmp[TMPNAMELEN];
  1564.         linenr_t     linecount;
  1565.  
  1566.         if (*buff == NUL)        /* no filter command */
  1567.                 return;
  1568.         linecount = line2 - line1 + 1;
  1569.         Curpos.lnum = line1;
  1570.         Curpos.col = 0;
  1571.         cursupdate();
  1572.         gotocmdline(FALSE, '\n');
  1573.  
  1574.         /*
  1575.          * 1. Form temp file names
  1576.          * 2. Write the lines to a temp file
  1577.          * 3. Run the filter command on the temp file
  1578.          * 4. Read the output of the command into the buffer
  1579.          * 5. Delete the original lines to be filtered
  1580.          * 6. Remove the temp files
  1581.          */
  1582.  
  1583.         strcpy(itmp, TMPNAME1);
  1584.         strcpy(otmp, TMPNAME2);
  1585.  
  1586.         if ((do_in && *mktemp(itmp) == NUL) || (do_out && *mktemp(otmp) == NUL))
  1587.         {
  1588.                 emsg(e_notmp);
  1589.                 return;
  1590.         }
  1591.  
  1592.         if (do_in && !writeit(itmp, line1, line2, FALSE, 0))
  1593.         {
  1594.                 emsg(e_notcreate);
  1595.                 return;
  1596.         }
  1597.         if (!do_out)
  1598.             outchar('\n');
  1599.  
  1600.         sprintf(IObuff, "%s %c %s %c %s", buff,
  1601.                         do_in ? '<' : ' ', do_in ? itmp : "",
  1602.                         do_out ? '>' : ' ', do_out ? otmp : "");
  1603.  
  1604.         if (call_shell(IObuff, 1))
  1605.         {
  1606.             linecount = 0;
  1607.             goto error;
  1608.         }
  1609.  
  1610.         if (do_out)
  1611.         {
  1612.                 if (!u_save((linenr_t)(line1 - 1), (linenr_t)(line2 + 1)))
  1613.                 {
  1614.                     linecount = 0;
  1615.                     goto error;
  1616.                 }
  1617.                 if (readfile(otmp, line2, FALSE))
  1618.                 {
  1619.                     emsg(e_notread);
  1620.                     linecount = 0;
  1621.                     goto error;
  1622.                 }
  1623.  
  1624.                 if (do_in)
  1625.                 {
  1626.                     Curpos.lnum = line1;
  1627.                     dellines(linecount, TRUE);
  1628.                 }
  1629.         }
  1630.         else
  1631.         {
  1632. error:
  1633.                 if (global_busy)
  1634.                     global_wait = 1;
  1635.                 else
  1636.                     wait_return(FALSE);
  1637.         }
  1638.         updateScreen(CLEAR);
  1639.  
  1640.         if (linecount > p_report)
  1641.         {
  1642.                 if (!do_in && do_out)
  1643.                         msgmore(linecount);
  1644.                 else
  1645.                         smsg("%ld lines filtered", (long)linecount);
  1646.         }
  1647.         remove(itmp);
  1648.         remove(otmp);
  1649.         return;
  1650.     }
  1651.  
  1652. /* 
  1653.  * Redefine the argument list to 'str'.
  1654.  * Return TRUE for failure.
  1655.  */
  1656.     int
  1657. doarglist(str)
  1658.     char *str;
  1659. {
  1660.     int        new_numfiles = 0;
  1661.     char    **new_files = NULL;
  1662. #ifdef WILD_CARDS
  1663.     int        exp_numfiles;
  1664.     char    **exp_files;
  1665. #endif
  1666.     char    **t;
  1667.     char    *p;
  1668.     int        inquote;
  1669.     int        i;
  1670.  
  1671.     while (*str)
  1672.     {
  1673.         /*
  1674.          * create a new entry in new_files[]
  1675.          */
  1676.         t = (char **)alloc((unsigned)(sizeof(char *) * (new_numfiles + 1)));
  1677.         if (t != NULL)
  1678.             for (i = new_numfiles; --i >= 0; )
  1679.                 t[i] = new_files[i];
  1680.         free(new_files);
  1681.         if (t == NULL)
  1682.             return TRUE;
  1683.         new_files = t;
  1684.         new_files[new_numfiles++] = str;
  1685.  
  1686.         /*
  1687.          * isolate one argument, taking quotes
  1688.          */
  1689.         inquote = FALSE;
  1690.         for (p = str; *str; ++str)
  1691.         {
  1692.             if (*str == '\\' && *(str + 1) != NUL)
  1693.                 *p++ = *++str;
  1694.             else
  1695.             {
  1696.                 if (!inquote && isspace(*str))
  1697.                     break;
  1698.                 if (*str == '"')
  1699.                     inquote ^= TRUE;
  1700.                 else
  1701.                     *p++ = *str;
  1702.             }
  1703.         }
  1704.         skipspace(&str);
  1705.         *p = NUL;
  1706.     }
  1707.     
  1708. #ifdef WILD_CARDS
  1709.     if (ExpandWildCards(new_numfiles, new_files, &exp_numfiles, &exp_files, FALSE, TRUE) != 0)
  1710.     {
  1711.         emsg((char *)exp_files);
  1712.         return TRUE;
  1713.     }
  1714.     else if (exp_numfiles == 0)
  1715.     {
  1716.         emsg(e_nomatch);
  1717.         return TRUE;
  1718.     }
  1719.     FreeWild(numfiles, files);
  1720.     files = exp_files;
  1721.     numfiles = exp_numfiles;
  1722.  
  1723. #else
  1724.     files = new_files;
  1725.     numfiles = new_numfiles;
  1726. #endif
  1727.  
  1728.     return FALSE;
  1729. }
  1730.  
  1731. extern int redraw_msg;        /* this is in screen.c */
  1732.  
  1733.     void
  1734. gotocmdline(clr, firstc)
  1735.     int                clr;
  1736.     int                firstc;
  1737. {
  1738.     windgoto((int)Rows - 1, 0);
  1739.     if (clr)
  1740.     {
  1741.         clear_line();            /* clear the bottom line */
  1742.         windgoto((int)Rows - 1, 0);
  1743.         redraw_msg = TRUE;
  1744.     }
  1745.     if (firstc)
  1746.         outchar(firstc);
  1747. }
  1748.  
  1749.     static int
  1750. check_readonly()
  1751. {
  1752.     if (!forceit && p_ro)
  1753.     {
  1754.         emsg(e_readonly);
  1755.         return TRUE;
  1756.     }
  1757.     return FALSE;
  1758. }
  1759.  
  1760.     static int
  1761. check_changed(checkaw)
  1762.     int        checkaw;
  1763. {
  1764.     if (!forceit && Changed && (!checkaw || !autowrite()))
  1765.     {
  1766.         if (exiting)
  1767.             settmode(1);        /* set raw again for typeahead */
  1768.         emsg(e_nowrtmsg);
  1769.         return TRUE;
  1770.     }
  1771.     return FALSE;
  1772. }
  1773.  
  1774.     static int
  1775. check_fname()
  1776. {
  1777.     if (Filename == NULL)
  1778.     {
  1779.         emsg(e_noname);
  1780.         return TRUE;
  1781.     }
  1782.     return FALSE;
  1783. }
  1784.  
  1785.     static int
  1786. check_more()
  1787. {
  1788.     if (!forceit && curfile + 1 < numfiles)
  1789.     {
  1790.         if (exiting)
  1791.             settmode(1);        /* set raw again for typeahead */
  1792.         emsg(e_more);
  1793.         return TRUE;
  1794.     }
  1795.     return FALSE;
  1796. }
  1797.  
  1798. /*
  1799.  * try to abandon current file and edit "fname"
  1800.  * return 1 for "normal" error, 2 for "not written" error, 0 for success
  1801.  * -1 for succesfully opening another file
  1802.  */
  1803.     int
  1804. getfile(fname, setpm)
  1805.     char    *fname;
  1806.     int        setpm;
  1807. {
  1808.     int other;
  1809.  
  1810.     FullName(fname, IObuff, IOSIZE);
  1811.     if (Filename == NULL)
  1812.         other = TRUE;
  1813.     else
  1814.         other = fnamecmp(IObuff, Filename);
  1815.     if (other && !forceit && Changed && !autowrite())
  1816.     {
  1817.         emsg(e_nowrtmsg);
  1818.         return 2;        /* file has been changed */
  1819.     }
  1820.     if (setpm)
  1821.         setpcmark();
  1822.     if (!other)
  1823.         return 0;        /* it's in the same file */
  1824.     if (doecmd(fname))
  1825.         return -1;        /* opened another file */
  1826.     return 1;            /* error encountered */
  1827. }
  1828.  
  1829. /*
  1830.  * return TRUE if alternate file n is the same as the current file
  1831.  */
  1832.     int
  1833. samealtfile(n)
  1834.     int            n;
  1835. {
  1836.     if (n < NUMALTFILES && altfiles[n] != NULL && Filename != NULL &&
  1837.                     fnamecmp(altfiles[n], Filename) == 0)
  1838.         return TRUE;
  1839.     return FALSE;
  1840. }
  1841.  
  1842. /*
  1843.  * get alternate file n
  1844.  * set linenr to lnum or altlnum if lnum == 0
  1845.  * if (setpm) setpcmark
  1846.  * return 1 for failure, 0 for success
  1847.  */
  1848.     int
  1849. getaltfile(n, lnum, setpm)
  1850.     int            n;
  1851.     linenr_t    lnum;
  1852.     int            setpm;
  1853. {
  1854.     if (n < 0 || n >= NUMALTFILES || altfiles[n] == NULL)
  1855.         return 1;
  1856.     if (lnum == 0)
  1857.         lnum = altlnum[n];        /* altlnum may be changed by getfile() */
  1858.     RedrawingDisabled = TRUE;
  1859.     if (getfile(altfiles[n], setpm) <= 0)
  1860.     {
  1861.         RedrawingDisabled = FALSE;
  1862.         if (lnum == 0 || lnum > line_count)        /* check for valid lnum */
  1863.             Curpos.lnum = 1;
  1864.         else
  1865.             Curpos.lnum = lnum;
  1866.  
  1867.         Curpos.col = 0;
  1868.         return 0;
  1869.     }
  1870.     RedrawingDisabled = FALSE;
  1871.     return 1;
  1872. }
  1873.  
  1874. /*
  1875.  * get name of alternate file
  1876.  */
  1877.      char *
  1878. getaltfname(n)
  1879.     int n;
  1880. {
  1881.     if (n >= NUMALTFILES)
  1882.         return NULL;
  1883.     return altfiles[n];
  1884. }
  1885.  
  1886. #ifdef WILD_CARDS
  1887. /*
  1888.  * Do wildcard expansion on the string 'str'.
  1889.  * Return a pointer to alloced memory containing the new string.
  1890.  * Return NULL for failure.
  1891.  *
  1892.  * mode = -2: only release file names
  1893.  * mode = -1: normal expansion, do not keep file names
  1894.  * mode =  0: normal expansion, keep file names
  1895.  * mode =  1: use next match in multiple match
  1896.  * mode =  2: use previous match in multiple match
  1897.  */
  1898.     static char *
  1899. ExpandOne(str, list_notfound, mode)
  1900.     u_char    *str;
  1901.     int        list_notfound;
  1902.     int        mode;
  1903. {
  1904.     char        *ss = NULL;
  1905.     static char **cmd_files = NULL;      /* list of input files */
  1906.     static int  cmd_numfiles = -1;      /* number of input files */
  1907.     static int    index;
  1908.     int            i, found = 0;
  1909.     char        *filesuf, *setsuf, *nextsetsuf;
  1910.     int            filesuflen, setsuflen;
  1911.  
  1912. /*
  1913.  * first handle the case of using an old match
  1914.  */
  1915.     if (mode >= 1)
  1916.     {
  1917.         if (cmd_numfiles > 0)
  1918.         {
  1919.             if (mode == 1)
  1920.                 ++index;
  1921.             else    /* mode == 2 */
  1922.                 --index;
  1923.             if (index < 0)
  1924.                 index = 0;
  1925.             if (index > cmd_numfiles - 1)
  1926.                 index = cmd_numfiles - 1;
  1927.             return strsave(cmd_files[index]);
  1928.         }
  1929.         else
  1930.             return NULL;
  1931.     }
  1932.  
  1933. /* free old names */
  1934.     if (cmd_numfiles != -1)
  1935.         FreeWild(cmd_numfiles, cmd_files);
  1936.     cmd_numfiles = -1;
  1937.     index = -1;
  1938.  
  1939.     if (mode == -2)        /* only release file name */
  1940.         return NULL;
  1941.  
  1942.     if (ExpandWildCards(1, (char **)&str, &cmd_numfiles, &cmd_files, FALSE, list_notfound) != 0)
  1943.         emsg((char *)cmd_files);
  1944.     else if (cmd_numfiles == 0)
  1945.         emsg(e_nomatch);
  1946.     else
  1947.     {
  1948.         if (cmd_numfiles > 1)        /* more than one match; check suffixes */
  1949.         {
  1950.             found = -2;
  1951.             for (i = 0; i < cmd_numfiles; ++i)
  1952.             {
  1953.                 if ((filesuf = strrchr(cmd_files[i], '.')) != NULL)
  1954.                 {
  1955.                     filesuflen = strlen(filesuf);
  1956.                     for (setsuf = p_su; *setsuf; setsuf = nextsetsuf)
  1957.                     {
  1958.                         if ((nextsetsuf = strchr(setsuf + 1, '.')) == NULL)
  1959.                             nextsetsuf = setsuf + strlen(setsuf);
  1960.                         setsuflen = nextsetsuf - setsuf;
  1961.                         if (filesuflen == setsuflen &&
  1962.                                     strncmp(setsuf, filesuf, (size_t)setsuflen) == 0)
  1963.                             break;
  1964.                     }
  1965.                     if (*setsuf)                /* suffix matched: ignore file */
  1966.                         continue;
  1967.                 }
  1968.                 if (found >= 0)
  1969.                 {
  1970.                     found = -2;
  1971.                     break;
  1972.                 }
  1973.                 found = i;
  1974.             }
  1975.         }
  1976.         if (found < 0)
  1977.             emsg(e_toomany);
  1978.         else
  1979.             ss = strsave(cmd_files[found]);
  1980.     }
  1981.         
  1982.     if (found != -2 || mode == -1)
  1983.     {
  1984.         FreeWild(cmd_numfiles, cmd_files);
  1985.         cmd_numfiles = -1;
  1986.     }
  1987.     return ss;
  1988. }
  1989.  
  1990. /*
  1991.  * show all filenames that match the string "file" with length "len"
  1992.  */
  1993.     static void
  1994. showmatches(file, len)
  1995.     char *file;
  1996.     int    len;
  1997. {
  1998.     char *file_str;
  1999.     int num_files;
  2000.     char **files_found;
  2001.     int i, j, k;
  2002.     int maxlen;
  2003.     int lines;
  2004.     int columns;
  2005.  
  2006.     file_str = addstar(file, len);        /* add star to file name */
  2007.     if (file_str != NULL)
  2008.     {
  2009.         outchar('\n');
  2010.         flushbuf();
  2011.  
  2012.         /* find all files that match the description */
  2013.         ExpandWildCards(1, &file_str, &num_files, &files_found, FALSE, FALSE);
  2014.  
  2015.         /* find the maximum length of the file names */
  2016.         maxlen = 0;
  2017.         for (i = 0; i < num_files; ++i)
  2018.         {
  2019.             j = strlen(files_found[i]);
  2020.             if (j > maxlen)
  2021.                 maxlen = j;
  2022.         }
  2023.  
  2024.         /* compute the number of columns and lines for the listing */
  2025.         maxlen += 2;    /* two spaces between file names */
  2026.         columns = (Columns + 2) / maxlen;
  2027.         if (columns < 1)
  2028.             columns = 1;
  2029.         lines = (num_files + columns - 1) / columns;
  2030.  
  2031.         /* list the files line by line */
  2032.         settmode(0);        /* allow output to be halted */
  2033.         for (i = 0; i < lines; ++i)
  2034.         {
  2035.             for (k = i; k < num_files; k += lines)
  2036.             {
  2037.                 if (k > i)
  2038.                     for (j = maxlen - strlen(files_found[k - lines]); --j >= 0; )
  2039.                         outchar(' ');
  2040.                 j = isdir(files_found[k]);    /* highlight directories */
  2041.                 if (j)
  2042.                 {
  2043. #ifdef AMIGA
  2044.                     if (term_console)
  2045.                         outstr("\033[33m");        /* use highlight color */
  2046.                     else
  2047. #endif /* AMIGA */
  2048.                         outstr(T_TI);
  2049.                 }
  2050.                 outstrn(files_found[k]);
  2051.                 if (j)
  2052.                 {
  2053. #ifdef AMIGA
  2054.                     if (term_console)
  2055.                         outstr("\033[0m");        /* use normal color */
  2056.                     else
  2057. #endif /* AMIGA */
  2058.                         outstr(T_TP);
  2059.                 }
  2060.             }
  2061.             outchar('\n');
  2062.             flushbuf();
  2063.         }
  2064.         free(file_str);
  2065.         FreeWild(num_files, files_found);
  2066.         settmode(1);
  2067.  
  2068.         wait_return(TRUE);
  2069.     }
  2070. }
  2071.  
  2072. /*
  2073.  * copy the file name into allocated memory and add a '*' at the end
  2074.  */
  2075.     static char *
  2076. addstar(fname, len)
  2077.     char    *fname;
  2078.     int        len;
  2079. {
  2080.     char    *retval;
  2081. #ifdef MSDOS
  2082.     int        i;
  2083. #endif
  2084.  
  2085.     retval = alloc(len + 4);
  2086.     if (retval != NULL)
  2087.     {
  2088.         strncpy(retval, fname, (size_t)len);
  2089. #ifdef MSDOS
  2090.     /*
  2091.      * if there is no dot in the file name, add "*.*" instead of "*".
  2092.      */
  2093.         for (i = len - 1; i >= 0; --i)
  2094.             if (retval[i] == '.' || retval[i] == '\\' || retval[i] == ':')
  2095.                 break;
  2096.         if (retval[i] != '.')
  2097.         {
  2098.             retval[len++] = '*';
  2099.             retval[len++] = '.';
  2100.         }
  2101. #endif
  2102.         retval[len] = '*';
  2103.         retval[len + 1] = 0;
  2104.     }
  2105.     return retval;
  2106. }
  2107. #endif /* WILD_CARDS */
  2108.  
  2109. /*
  2110.  * dosource: read the file "fname" and execute its lines as EX commands
  2111.  *
  2112.  * This function may be called recursively!
  2113.  */
  2114.     int
  2115. dosource(fname)
  2116.     register char *fname;
  2117. {
  2118.     register FILE *fp;
  2119.     register char *s;
  2120.  
  2121.     expand_env(fname, IObuff, IOSIZE);        /* use IObuff for expanded name */
  2122.     if ((fp = fopen(IObuff, "r")) == NULL)
  2123.         return 1;
  2124.  
  2125.     while (fgets(IObuff, IOSIZE, fp) != NULL && !got_int)
  2126.     {
  2127.         s = IObuff + strlen(IObuff) - 1;
  2128.         if (*s == '\n')    /* remove trailing newline */
  2129.             *s = NUL;
  2130.         docmdline((u_char *)IObuff);
  2131.         breakcheck();
  2132.     }
  2133.     fclose(fp);
  2134.     if (got_int)
  2135.         emsg(e_interr);
  2136.     return 0;
  2137. }
  2138.  
  2139. /*
  2140.  * get single EX address
  2141.  */
  2142.     static linenr_t
  2143. get_address(ptr, curpos_lnum)
  2144.     u_char        **ptr;
  2145.     linenr_t    curpos_lnum;
  2146. {
  2147.     int            c;
  2148.     int            i;
  2149.     long        n;
  2150.     u_char        *p;
  2151.     u_char      *cmd;
  2152.     FPOS        pos;
  2153.     FPOS        *fp;
  2154.     linenr_t    lnum;
  2155.  
  2156.     cmd = *ptr;
  2157.     skipspace((char **)&cmd);
  2158.     lnum = INVLNUM;
  2159.     do
  2160.     {
  2161.         switch (*cmd)
  2162.         {
  2163.             case '.':                         /* '.' - Cursor position */
  2164.                         ++cmd;
  2165.                         lnum = curpos_lnum;
  2166.                         break;
  2167.  
  2168.             case '$':                         /* '$' - last line */
  2169.                         ++cmd;
  2170.                         lnum = line_count;
  2171.                         break;
  2172.  
  2173.             case '\'':                         /* ''' - mark */
  2174.                         if (*++cmd == NUL || (fp = getmark(*cmd++, FALSE)) == NULL)
  2175.                         {
  2176.                             emsg(e_umark);
  2177.                             goto error;
  2178.                         }
  2179.                         lnum = fp->lnum;
  2180.                         break;
  2181.  
  2182.             case '/':
  2183.             case '?':                        /* '/' or '?' - search */
  2184.                         c = *cmd++;
  2185.  
  2186.                         for (p = cmd; *p && (*p != c || *(p - 1) == '\\'); ++p)
  2187.                             ;
  2188.                         i = *p;
  2189.                         *p = NUL;    /* put a '\0' at the end of the pattern */
  2190.                 
  2191.                         /* search for the pattern */
  2192.                         pos.lnum = curpos_lnum;
  2193.                         pos.col = -1;    /* searchit() will increment the col */
  2194.                         if (c == '/')
  2195.                             ++pos.lnum;
  2196.                         if (searchit(&pos, c == '/' ? FORWARD : BACKWARD, (char *)cmd, (long)1, 0))
  2197.                             lnum = pos.lnum;
  2198.                 
  2199.                         /* adjust command string pointer */
  2200.                         cmd = p;
  2201.                         if (i != NUL)
  2202.                             ++cmd;
  2203.  
  2204.                         break;
  2205.  
  2206.             default:
  2207.                         if (isdigit(*cmd))                /* absolute line number */
  2208.                             lnum = getdigits((char **)&cmd);
  2209.         }
  2210.         
  2211.         while (*cmd == '-' || *cmd == '+')
  2212.         {
  2213.             if (lnum == INVLNUM)
  2214.                 lnum = curpos_lnum;
  2215.             i = *cmd++;
  2216.             if (!isdigit(*cmd))    /* '+' is '+1', but '+0' is not '+1' */
  2217.                 n = 1;
  2218.             else 
  2219.                 n = getdigits((char **)&cmd);
  2220.             if (i == '-')
  2221.                 lnum -= n;
  2222.             else
  2223.                 lnum += n;
  2224.         }
  2225.  
  2226.         curpos_lnum = lnum;
  2227.     } while (*cmd == '/' || *cmd == '?');
  2228.  
  2229. error:
  2230.     *ptr = cmd;
  2231.     return lnum;
  2232. }
  2233.