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