home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1708 / ex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  13.1 KB  |  598 lines

  1. /* ex.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    16820 SW Tallac Way
  6.  *    Beaverton, OR 97006
  7.  *    kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda
  8.  */
  9.  
  10.  
  11. /* This file contains the code for reading ex commands. */
  12.  
  13. #include "config.h"
  14. #include <ctype.h>
  15. #include "vi.h"
  16.  
  17. #ifndef isascii
  18. # define isascii(c) !((c)&~0x7f)
  19. #endif
  20.  
  21. /* This data type is used to describe the possible argument combinations */
  22. typedef short ARGT;
  23. #define FROM    1        /* allow a linespec */
  24. #define    TO    2        /* allow a second linespec */
  25. #define BANG    4        /* allow a ! after the command name */
  26. #define EXTRA    8        /* allow extra args after command name */
  27. #define XFILE    16        /* expand wildcards in extra part */
  28. #define NOSPC    32        /* no spaces allowed in the extra part */
  29. #define    DFLALL    64        /* default file range is 1,$ */
  30. #define DFLNONE    128        /* no default file range */
  31. #define NODFL    256        /* do not default to the current file name */
  32. #define EXRCOK    512        /* can be in a .exrc file */
  33. #define FILES    (XFILE + EXTRA)    /* multiple extra files allowed */
  34. #define WORD1    (EXTRA + NOSPC)    /* one extra word allowed */
  35. #define FILE1    (FILES + NOSPC)    /* 1 file allowed, defaults to current file */
  36. #define NAMEDF    (FILE1 + NODFL)    /* 1 file allowed, defaults to "" */
  37. #define NAMEDFS    (FILES + NODFL)    /* multiple files allowed, default is "" */
  38. #define RANGE    (FROM + TO)    /* range of linespecs allowed */
  39. #define NONE    0        /* no args allowed at all */
  40.  
  41. /* This array maps ex command names to command codes. The order in which
  42.  * command names are listed below is significant -- ambiguous abbreviations
  43.  * are always resolved to be the first possible match.  (e.g. "r" is taken
  44.  * to mean "read", not "rewind", because "read" comes before "rewind")
  45.  */
  46. static struct
  47. {
  48.     char    *name;    /* name of the command */
  49.     CMD    code;    /* enum code of the command */
  50.     void    (*fn)();/* function which executes the command */
  51.     ARGT    argt;    /* command line arguments permitted/needed/used */
  52. }
  53.     cmdnames[] =
  54. {   /*    cmd name    cmd code    function    arguments */
  55.     {"append",    CMD_APPEND,    cmd_append,    FROM        },
  56. #ifdef DEBUG
  57.     {"bug",        CMD_DEBUG,    cmd_debug,    RANGE+BANG+EXTRA},
  58. #endif
  59.     {"change",    CMD_CHANGE,    cmd_append,    RANGE        },
  60.     {"delete",    CMD_DELETE,    cmd_delete,    RANGE+WORD1    },
  61.     {"edit",    CMD_EDIT,    cmd_edit,    BANG+FILE1    },
  62.     {"file",    CMD_FILE,    cmd_file,    NONE        },
  63.     {"global",    CMD_GLOBAL,    cmd_global,    RANGE+BANG+EXTRA+DFLALL},
  64.     {"insert",    CMD_INSERT,    cmd_append,    FROM        },
  65.     {"join",    CMD_INSERT,    cmd_join,    RANGE        },
  66.     {"k",        CMD_MARK,    cmd_mark,    FROM+WORD1    },
  67.     {"list",    CMD_LIST,    cmd_list,    RANGE        },
  68.     {"move",    CMD_MOVE,    cmd_move,    RANGE+EXTRA    },
  69.     {"next",    CMD_NEXT,    cmd_next,    BANG+NAMEDFS    },
  70.     {"Next",    CMD_PREVIOUS,    cmd_next,    BANG        },
  71.     {"print",    CMD_PRINT,    cmd_print,    RANGE        },
  72.     {"quit",    CMD_QUIT,    cmd_quit,    BANG        },
  73.     {"read",    CMD_READ,    cmd_read,    FROM+BANG+NAMEDF},
  74.     {"substitute",    CMD_SUBSTITUTE,    cmd_substitute,    RANGE+EXTRA    },
  75.     {"to",        CMD_COPY,    cmd_move,    RANGE+EXTRA    },
  76.     {"undo",    CMD_UNDO,    cmd_undo,    NONE        },
  77.     {"vglobal",    CMD_VGLOBAL,    cmd_global,    RANGE+EXTRA+DFLALL},
  78.     {"write",    CMD_WRITE,    cmd_write,    RANGE+BANG+FILE1+DFLALL},
  79.     {"xit",        CMD_XIT,    cmd_xit,    BANG        },
  80.     {"yank",    CMD_YANK,    cmd_delete,    RANGE+WORD1    },
  81.  
  82.     {"!",        CMD_BANG,    cmd_shell,    EXRCOK+RANGE+NAMEDFS+DFLNONE},
  83.     {"<",        CMD_SHIFTL,    cmd_shift,    RANGE        },
  84.     {">",        CMD_SHIFTR,    cmd_shift,    RANGE        },
  85.     {"=",        CMD_FILE,    cmd_file,    RANGE        },
  86.  
  87.     {"args",    CMD_ARGS,    cmd_args,    EXRCOK+NAMEDFS    },
  88.     {"cd",        CMD_CD,        cmd_cd,        EXRCOK+NAMEDF    },
  89.     {"copy",    CMD_COPY,    cmd_move,    RANGE+EXTRA    },
  90. #ifndef NO_DIGRAPH
  91.     {"digraph",    CMD_DIGRAPH,    cmd_digraph,    EXRCOK+BANG+EXTRA},
  92. #endif
  93.     {"ex",        CMD_EDIT,    cmd_edit,    BANG+FILE1    },
  94.     {"map",        CMD_MAP,    cmd_map,    EXRCOK+BANG+EXTRA},
  95. #ifndef NO_EXTENSIONS
  96.     {"mkexrc",    CMD_MKEXRC,    cmd_mkexrc,    NONE        },
  97. #endif
  98.     {"put",        CMD_PUT,    cmd_put,    FROM+WORD1    },
  99.     {"set",        CMD_SET,    cmd_set,    EXRCOK+EXTRA    },
  100.     {"shell",    CMD_SHELL,    cmd_shell,    NONE        },
  101.     {"source",    CMD_SOURCE,    cmd_source,    EXRCOK+NAMEDF    },
  102.     {"tag",        CMD_TAG,    cmd_tag,    BANG+WORD1    },
  103.     {"version",    CMD_VERSION,    cmd_version,    EXRCOK+NONE    },
  104.     {"visual",    CMD_VISUAL,    cmd_visual,    NONE        },
  105.     {"wq",        CMD_WQUIT,    cmd_xit,    NONE        },
  106.  
  107. #ifdef DEBUG
  108.     {"debug",    CMD_DEBUG,    cmd_debug,    RANGE+BANG+EXTRA},
  109.     {"validate",    CMD_VALIDATE,    cmd_validate,    BANG        },
  110. #endif
  111.     {"chdir",    CMD_CD,        cmd_cd,        EXRCOK+NAMEDF    },
  112.     {"mark",    CMD_MARK,    cmd_mark,    FROM+WORD1    },
  113.     {"previous",    CMD_PREVIOUS,    cmd_next,    BANG        },
  114.     {"rewind",    CMD_REWIND,    cmd_next,    BANG        },
  115.     {"unmap",    CMD_UNMAP,    cmd_map,    EXRCOK+BANG+EXTRA},
  116.  
  117.     {(char *)0}
  118. };
  119.  
  120.  
  121. /* This function parses a search pattern - given a pointer to a / or ?,
  122.  * it replaces the ending / or ? with a \0, and returns a pointer to the
  123.  * stuff that came after the pattern.
  124.  */
  125. char    *parseptrn(ptrn)
  126.     register char    *ptrn;
  127. {
  128.     register char     *scan;
  129.  
  130.  
  131.     for (scan = ptrn + 1;
  132.          *scan && *scan != *ptrn;
  133.          scan++)
  134.     {
  135.         /* allow backslashed versions of / and ? in the pattern */
  136.         if (*scan == '\\' && scan[1] != '\0')
  137.         {
  138.             scan++;
  139.         }
  140.     }
  141.     if (*scan)
  142.     {
  143.         *scan++ = '\0';
  144.     }
  145.  
  146.     return scan;
  147. }
  148.  
  149.  
  150. /* This function parses a line specifier for ex commands */
  151. char *linespec(s, markptr)
  152.     register char    *s;        /* start of the line specifier */
  153.     MARK        *markptr;    /* where to store the mark's value */
  154. {
  155.     long        num;
  156.     register char    *t;
  157.  
  158.     /* parse each ;-delimited clause of this linespec */
  159.     do
  160.     {
  161.         /* skip an initial ';', if any */
  162.         if (*s == ';')
  163.         {
  164.             s++;
  165.         }
  166.  
  167.         /* skip leading spaces */
  168.         while (isascii(*s) && isspace(*s))
  169.         {
  170.             s++;
  171.         }
  172.     
  173.         /* dot means current position */
  174.         if (*s == '.')
  175.         {
  176.             s++;
  177.             *markptr = cursor;
  178.         }
  179.         /* '$' means the last line */
  180.         else if (*s == '$')
  181.         {
  182.             s++;
  183.             *markptr = m_toline(cursor, nlines);
  184.         }
  185.         /* digit means an absolute line number */
  186.         else if (isascii(*s) && isdigit(*s))
  187.         {
  188.             for (num = 0; isascii(*s) && isdigit(*s); s++)
  189.             {
  190.                 num = num * 10 + *s - '0';
  191.             }
  192.             *markptr = m_toline(cursor, num);
  193.         }
  194.         /* appostrophe means go to a set mark */
  195.         else if (*s == '\'')
  196.         {
  197.             s++;
  198.             *markptr = m_tomark(cursor, 1L, (int)*s);
  199.             s++;
  200.         }
  201.         /* slash means do a search */
  202.         else if (*s == '/' || *s == '?')
  203.         {
  204.             /* put a '\0' at the end of the search pattern */
  205.             t = parseptrn(s);
  206.     
  207.             /* search for the pattern */
  208.             if (*s == '/')
  209.             {
  210.                 pfetch(markline(*markptr));
  211.                 *markptr = (*markptr & ~(BLKSIZE - 1)) + plen - 1;
  212.                 *markptr = m_fsrch(*markptr, s + 1);
  213.             }
  214.             else
  215.             {
  216.                 *markptr &= ~(BLKSIZE - 1);
  217.                 *markptr = m_bsrch(*markptr, s + 1);
  218.             }
  219.     
  220.             /* adjust command string pointer */
  221.             s = t;
  222.         }
  223.     
  224.         /* if linespec was faulty, quit now */
  225.         if (!*markptr)
  226.         {
  227.             return s;
  228.         }
  229.     
  230.         /* maybe add an offset */
  231.         if (*s == '-')
  232.         {
  233.             s++;
  234.             for (num = 0; *s >= '0' && *s <= '9'; s++)
  235.             {
  236.                 num = num * 10 + *s - '0';
  237.             }
  238.             if (num == 0)
  239.             {
  240.                 num = 1;
  241.             }
  242.             *markptr = m_up(*markptr, num);
  243.         }
  244.         else if (*s == '+')
  245.         {
  246.             s++;
  247.             for (num = 0; *s >= '0' && *s <= '9'; s++)
  248.             {
  249.                 num = num * 10 + *s - '0';
  250.             }
  251.             if (num == 0)
  252.             {
  253.                 num = 1;
  254.             }
  255.             *markptr = m_down(*markptr, num);
  256.         }
  257.     } while (*s == ';' || *s == '+' || *s == '-');
  258.  
  259.     return s;
  260. }
  261.  
  262.  
  263.  
  264. /* This function reads an ex command and executes it. */
  265. ex()
  266. {
  267.     char        cmdbuf[80];
  268.     register int    cmdlen;
  269.  
  270.     /* read a line */
  271.     cmdlen = vgets(':', cmdbuf, sizeof cmdbuf);
  272.     if (cmdlen < 0)
  273.     {
  274.         return;
  275.     }
  276.     addch('\n');
  277.     refresh();
  278.  
  279.     /* if empty line, assume ".+1" */
  280.     if (cmdlen == 0)
  281.     {
  282.         strcpy(cmdbuf, ".+1");
  283.     }
  284.  
  285.     /* parse & execute the command */
  286.     doexcmd(cmdbuf);
  287. }
  288.  
  289. doexcmd(cmdbuf)
  290.     char        *cmdbuf;    /* string containing an ex command */
  291. {
  292.     register char    *scan;        /* used to scan thru cmdbuf */
  293.     MARK        frommark;    /* first linespec */
  294.     MARK        tomark;        /* second linespec */
  295.     register int    cmdlen;        /* length of the command name given */
  296.     CMD        cmd;        /* what command is this? */
  297.     ARGT        argt;        /* argument types for this command */
  298.     short        forceit;    /* bang version of a command? */
  299.     register int    cmdidx;        /* index of command */
  300.     register char    *build;        /* used while copying filenames */
  301.     int        iswild;        /* boolean: filenames use wildcards? */
  302.     int        isdfl;        /* using default line ranges? */
  303.     int        didsub;        /* did we substitute file names for % or # */
  304.  
  305.  
  306.     /* ex commands can't be undone via the shift-U command */
  307.     U_line = 0L;
  308.  
  309.     /* ignore command lines that start with "#" */
  310.     if (*cmdbuf == '#' || *cmdbuf == '"')
  311.     {
  312.         return;
  313.     }
  314.  
  315.     /* permit extra colons at the start of the line */
  316.     while (*cmdbuf == ':')
  317.     {
  318.         cmdbuf++;
  319.     }
  320.  
  321.     /* parse the line specifier */
  322.     scan = cmdbuf;
  323.     if (nlines < 1)
  324.     {
  325.         /* no file, so don't allow addresses */
  326.     }
  327.     else if (*scan == '%')
  328.     {
  329.         /* '%' means all lines */
  330.         frommark = m_toline(cursor, 1L);
  331.         tomark = m_toline(cursor, nlines);
  332.         scan++;
  333.     }
  334.     else
  335.     {
  336.         frommark = cursor;
  337.         scan = linespec(scan, &frommark);
  338.         tomark = frommark;
  339.         if (frommark && *scan == ',')
  340.         {
  341.             scan++;
  342.             scan = linespec(scan, &tomark);
  343.         }
  344.         if (!tomark)
  345.         {
  346.             /* faulty line spec -- fault already described */
  347.             return;
  348.         }
  349.         if (frommark > tomark)
  350.         {
  351.             msg("first address exceeds the second");
  352.             return;
  353.         }
  354.     }
  355.     isdfl = (scan == cmdbuf);
  356.  
  357.     /* skip whitespace */
  358.     while (isascii(*scan) && isspace(*scan))
  359.     {
  360.         scan++;
  361.     }
  362.  
  363.     /* if no command, then just move the cursor to the mark & print */
  364.     if (!*scan)
  365.     {
  366.         cursor = tomark;
  367.         if (mode != MODE_EX)
  368.         {
  369.             return;
  370.         }
  371.         scan = "p";
  372.     }
  373.  
  374.     /* figure out how long the command name is */
  375.     if (isascii(*scan) && !isalpha(*scan))
  376.     {
  377.         cmdlen = 1;
  378.     }
  379.     else
  380.     {
  381.         for (cmdlen = 1;
  382.              !isascii(scan[cmdlen]) || isalpha(scan[cmdlen]);
  383.              cmdlen++)
  384.         {
  385.         }
  386.     }
  387.  
  388.     /* lookup the command code */
  389.     for (cmdidx = 0;
  390.          cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
  391.          cmdidx++)
  392.     {
  393.     }
  394.     argt = cmdnames[cmdidx].argt;
  395.     cmd = cmdnames[cmdidx].code;
  396.     if (cmd == CMD_NULL)
  397.     {
  398.         msg("Unknown command \"%.*s\"", cmdlen, scan);
  399.         return;
  400.     }
  401.  
  402.     /* if the command ended with a bang, set the forceit flag */
  403.     scan += cmdlen;
  404.     if ((argt & BANG) && *scan == '!')
  405.     {
  406.         scan++;
  407.         forceit = 1;
  408.     }
  409.     else
  410.     {
  411.         forceit = 0;
  412.     }
  413.  
  414.     /* skip any more whitespace, to leave scan pointing to arguments */
  415.     while (isascii(*scan) && isspace(*scan))
  416.     {
  417.         scan++;
  418.     }
  419.  
  420.     /* a couple of special cases for filenames */
  421.     if (argt & XFILE)
  422.     {
  423.         /* if names were given, process them */
  424.         if (*scan)
  425.         {
  426.             for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
  427.             {
  428.                 switch (*scan)
  429.                 {
  430.                   case '%':
  431.                     if (!*origname)
  432.                     {
  433.                         msg("No filename to substitute for %");
  434.                         return;
  435.                     }
  436.                     strcpy(build, origname);
  437.                     while (*build)
  438.                     {
  439.                         build++;
  440.                     }
  441.                     didsub = TRUE;
  442.                     break;
  443.     
  444.                   case '#':
  445.                     if (!*prevorig)
  446.                     {
  447.                         msg("No filename to substitute for #");
  448.                         return;
  449.                     }
  450.                     strcpy(build, prevorig);
  451.                     while (*build)
  452.                     {
  453.                         build++;
  454.                     }
  455.                     didsub = TRUE;
  456.                     break;
  457.     
  458.                   case '*':
  459.                   case '?':
  460. #if    ! (MSDOS || TOS)
  461.                   case '[':
  462.                   case '`':
  463.                   case '{': /* } */
  464.                   case '$':
  465.                   case '~':
  466. #endif
  467.                     *build++ = *scan;
  468.                     iswild = TRUE;
  469.                     break;
  470.  
  471.                   default:
  472.                     *build++ = *scan;
  473.                 }
  474.             }
  475.             *build = '\0';
  476.     
  477.             if (cmd == CMD_BANG
  478.              || cmd == CMD_READ && (forceit || tmpblk.c[0] != '!'))
  479.             {
  480.                 if (didsub)
  481.                 {
  482.                     addch('\n');
  483.                     addstr(tmpblk.c);
  484.                     addch('\n');
  485.                     exrefresh();
  486.                 }
  487.             }
  488.             else
  489.             {
  490.                 if (iswild && tmpblk.c[0] != '>')
  491.                 {
  492.                     scan = wildcard(tmpblk.c);
  493.                 }
  494.             }
  495.         }
  496.         else /* no names given, maybe assume origname */
  497.         {
  498.             if (!(argt & NODFL))
  499.             {
  500.                 strcpy(tmpblk.c, origname);
  501.             }
  502.             else
  503.             {
  504.                 *tmpblk.c = '\0';
  505.             }
  506.         }
  507.  
  508.         scan = tmpblk.c;
  509.     }
  510.  
  511.     /* bad arguments? */
  512.     if (!(argt & EXRCOK) && nlines < 1L)
  513.     {
  514.         msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
  515.         return;
  516.     }
  517.     if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
  518.     {
  519.         msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
  520.         return;
  521.     }
  522.     if (!(argt & TO) && tomark != frommark && nlines >= 1L)
  523.     {
  524.         msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
  525.         return;
  526.     }
  527.     if (!(argt & EXTRA) && *scan)
  528.     {
  529.         msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
  530.         return;
  531.     }
  532.     if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
  533.     {
  534.         for (build = scan; *build; build++)
  535.         {
  536.             if (isspace(*build))
  537.             {
  538.                 msg("Too many %s to \"%s\" command.",
  539.                     (argt & XFILE) ? "filenames" : "arguments",
  540.                     cmdnames[cmdidx].name);
  541.                 return;
  542.             }
  543.         }
  544.     }
  545.  
  546.     /* some commands have special default ranges */
  547.     if (isdfl && (argt & DFLALL))
  548.     {
  549.         frommark = MARK_FIRST;
  550.         tomark = MARK_LAST;
  551.     }
  552.     else if (isdfl && (argt & DFLNONE))
  553.     {
  554.         frommark = tomark = 0L;
  555.     }
  556.  
  557.     /* act on the command */
  558.     (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
  559. }
  560.  
  561.  
  562. /* This function executes EX commands from a file.  It returns 1 normally, or
  563.  * 0 if the file could not be opened for reading.
  564.  */
  565. int doexrc(filename)
  566.     char    *filename;    /* name of a ".exrc" file */
  567. {
  568.     int    fd;        /* file descriptor */
  569.     int    len;        /* length of the ".exrc" file */
  570.     char    *cmd;        /* start of a command */
  571.     char    *end;        /* used to search for the end of cmd */
  572.     char    buf[MAXRCLEN];    /* buffer, holds the entire .exrc file */
  573.  
  574.     /* open the file, read it, and close */
  575.     fd = open(filename, O_RDONLY);
  576.     if (fd < 0)
  577.     {
  578.         return 0;
  579.     }
  580.     len = tread(fd, buf, MAXRCLEN);
  581.     close(fd);
  582.  
  583.     /* find & do each command */
  584.     for (cmd = buf; cmd < &buf[len]; cmd = end + 1)
  585.     {
  586.         /* find the end of the command */
  587.         for (end = cmd; *end != '\n'; end++)
  588.         {
  589.         }
  590.         *end = '\0';
  591.  
  592.         /* do it */
  593.         doexcmd(cmd);
  594.     }
  595.  
  596.     return 1;
  597. }
  598.