5 * 14407 SW Teal Blvd. #C
11 /* This file contains the code for reading ex commands. */
17 /* This data type is used to describe the possible argument combinations */
19 #define FROM 1 /* allow a linespec */
20 #define TO 2 /* allow a second linespec */
21 #define BANG 4 /* allow a ! after the command name */
22 #define EXTRA 8 /* allow extra args after command name */
23 #define XFILE 16 /* expand wildcards in extra part */
24 #define NOSPC 32 /* no spaces allowed in the extra part */
25 #define DFLALL 64 /* default file range is 1,$ */
26 #define DFLNONE 128 /* no default file range */
27 #define NODFL 256 /* do not default to the current file name */
28 #define EXRCOK 512 /* can be in a .exrc file */
29 #define NL 1024 /* if mode!=MODE_EX, then write a newline first */
30 #define PLUS 2048 /* allow a line number, as in ":e +32 foo" */
31 #define ZERO 4096 /* allow 0 to be given as a line number */
32 #define NOBAR 8192 /* treat following '|' chars as normal */
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 */
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")
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 */
54 { /* cmd name cmd code function arguments */
55 {"append", CMD_APPEND
, cmd_append
, FROM
+ZERO
+BANG
},
57 {"bug", CMD_DEBUG
, cmd_debug
, RANGE
+BANG
+EXTRA
+NL
},
59 {"change", CMD_CHANGE
, cmd_append
, RANGE
+BANG
},
60 {"delete", CMD_DELETE
, cmd_delete
, RANGE
+WORD1
},
61 {"edit", CMD_EDIT
, cmd_edit
, BANG
+FILE1
+PLUS
},
62 {"file", CMD_FILE
, cmd_file
, NAMEDF
},
63 {"global", CMD_GLOBAL
, cmd_global
, RANGE
+BANG
+EXTRA
+DFLALL
+NOBAR
},
64 {"insert", CMD_INSERT
, cmd_append
, FROM
+BANG
},
65 {"join", CMD_INSERT
, cmd_join
, RANGE
+BANG
},
66 {"k", CMD_MARK
, cmd_mark
, FROM
+WORD1
},
67 {"list", CMD_LIST
, cmd_print
, RANGE
+NL
},
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
+NL
},
72 {"quit", CMD_QUIT
, cmd_xit
, BANG
},
73 {"read", CMD_READ
, cmd_read
, FROM
+ZERO
+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
+NOBAR
},
78 {"write", CMD_WRITE
, cmd_write
, RANGE
+BANG
+FILE1
+DFLALL
},
79 {"xit", CMD_XIT
, cmd_xit
, BANG
+NL
},
80 {"yank", CMD_YANK
, cmd_delete
, RANGE
+WORD1
},
82 {"!", CMD_BANG
, cmd_shell
, EXRCOK
+RANGE
+NAMEDFS
+DFLNONE
+NL
+NOBAR
},
83 {"#", CMD_NUMBER
, cmd_print
, RANGE
+NL
},
84 {"<", CMD_SHIFTL
, cmd_shift
, RANGE
},
85 {">", CMD_SHIFTR
, cmd_shift
, RANGE
},
86 {"=", CMD_EQUAL
, cmd_file
, RANGE
},
87 {"&", CMD_SUBAGAIN
, cmd_substitute
, RANGE
},
89 {"@", CMD_AT
, cmd_at
, EXTRA
},
93 {"abbreviate", CMD_ABBR
, cmd_map
, EXRCOK
+BANG
+EXTRA
},
95 {"args", CMD_ARGS
, cmd_args
, EXRCOK
+NAMEDFS
},
97 {"cc", CMD_CC
, cmd_make
, BANG
+FILES
},
99 {"cd", CMD_CD
, cmd_cd
, EXRCOK
+BANG
+NAMEDF
},
100 {"copy", CMD_COPY
, cmd_move
, RANGE
+EXTRA
},
102 {"digraph", CMD_DIGRAPH
, cmd_digraph
, EXRCOK
+BANG
+EXTRA
},
105 {"errlist", CMD_ERRLIST
, cmd_errlist
, BANG
+NAMEDF
},
107 {"ex", CMD_EDIT
, cmd_edit
, BANG
+FILE1
},
108 {"mark", CMD_MARK
, cmd_mark
, FROM
+WORD1
},
110 {"mkexrc", CMD_MKEXRC
, cmd_mkexrc
, NAMEDF
},
112 {"number", CMD_NUMBER
, cmd_print
, RANGE
+NL
},
113 {"put", CMD_PUT
, cmd_put
, FROM
+ZERO
+WORD1
},
114 {"set", CMD_SET
, cmd_set
, EXRCOK
+EXTRA
},
115 {"shell", CMD_SHELL
, cmd_shell
, NL
},
116 {"source", CMD_SOURCE
, cmd_source
, EXRCOK
+NAMEDF
},
118 {"stop", CMD_STOP
, cmd_suspend
, NONE
},
120 {"tag", CMD_TAG
, cmd_tag
, BANG
+WORD1
},
121 {"version", CMD_VERSION
, cmd_version
, EXRCOK
+NONE
},
122 {"visual", CMD_VISUAL
, cmd_edit
, BANG
+NAMEDF
},
123 {"wq", CMD_WQUIT
, cmd_xit
, NL
},
126 {"debug", CMD_DEBUG
, cmd_debug
, RANGE
+BANG
+EXTRA
+NL
},
127 {"validate", CMD_VALIDATE
, cmd_validate
, BANG
+NL
},
129 {"chdir", CMD_CD
, cmd_cd
, EXRCOK
+BANG
+NAMEDF
},
131 {"color", CMD_COLOR
, cmd_color
, EXRCOK
+EXTRA
},
134 {"make", CMD_MAKE
, cmd_make
, BANG
+NAMEDFS
},
136 {"map", CMD_MAP
, cmd_map
, EXRCOK
+BANG
+EXTRA
},
137 {"previous", CMD_PREVIOUS
, cmd_next
, BANG
},
138 {"rewind", CMD_REWIND
, cmd_next
, BANG
},
140 {"suspend", CMD_SUSPEND
, cmd_suspend
, NONE
},
142 {"unmap", CMD_UNMAP
, cmd_map
, EXRCOK
+BANG
+EXTRA
},
144 {"unabbreviate",CMD_UNABBR
, cmd_map
, EXRCOK
+WORD1
},
151 /* This function parses a search pattern - given a pointer to a / or ?,
152 * it replaces the ending / or ? with a \0, and returns a pointer to the
153 * stuff that came after the pattern.
155 char *parseptrn(ptrn
)
160 for (scan
= ptrn
+ 1;
161 *scan
&& *scan
!= *ptrn
;
164 /* allow backslashed versions of / and ? in the pattern */
165 if (*scan
== '\\' && scan
[1] != '\0')
179 /* This function parses a line specifier for ex commands */
180 char *linespec(s
, markptr
)
181 REG
char *s
; /* start of the line specifier */
182 MARK
*markptr
; /* where to store the mark's value */
187 /* parse each ;-delimited clause of this linespec */
190 /* skip an initial ';', if any */
196 /* skip leading spaces */
202 /* dot means current position */
208 /* '$' means the last line */
212 *markptr
= MARK_LAST
;
214 /* digit means an absolute line number */
215 else if (isdigit(*s
))
217 for (num
= 0; isdigit(*s
); s
++)
219 num
= num
* 10 + *s
- '0';
221 *markptr
= MARK_AT_LINE(num
);
223 /* appostrophe means go to a set mark */
227 *markptr
= m_tomark(cursor
, 1L, (int)*s
);
230 /* slash means do a search */
231 else if (*s
== '/' || *s
== '?')
233 /* put a '\0' at the end of the search pattern */
236 /* search for the pattern */
237 *markptr
&= ~(BLKSIZE
- 1);
240 pfetch(markline(*markptr
));
242 *markptr
+= plen
- 1;
243 *markptr
= m_fsrch(*markptr
, s
);
247 *markptr
= m_bsrch(*markptr
, s
);
250 /* adjust command string pointer */
254 /* if linespec was faulty, quit now */
260 /* maybe add an offset */
262 if (*t
== '-' || *t
== '+')
265 for (num
= 0; isdigit(*s
); s
++)
267 num
= num
* 10 + *s
- '0';
273 *markptr
= m_updnto(*markptr
, num
, *t
);
275 } while (*s
== ';' || *s
== '+' || *s
== '-');
277 /* protect against invalid line numbers */
278 num
= markline(*markptr
);
279 if (num
< 1L || num
> nlines
)
281 msg("Invalid line number -- must be from 1 to %ld", nlines
);
282 *markptr
= MARK_UNSET
;
290 /* This function reads an ex command and executes it. */
298 oldline
= markline(cursor
);
300 while (mode
== MODE_EX
)
304 cmdlen
= vgets(':', cmdbuf
, sizeof(cmdbuf
));
306 cmdlen
= vgets(*o_prompt
? ':' : '\0', cmdbuf
, sizeof(cmdbuf
));
313 /* if empty line, assume ".+1" */
316 strcpy(cmdbuf
, ".+1");
326 /* parse & execute the command */
329 /* handle autoprint */
330 if (significant
|| markline(cursor
) != oldline
)
333 oldline
= markline(cursor
);
334 if (*o_autoprint
&& mode
== MODE_EX
)
336 cmd_print(cursor
, cursor
, CMD_PRINT
, FALSE
, "");
343 char *cmdbuf
; /* string containing an ex command */
345 REG
char *scan
; /* used to scan thru cmdbuf */
346 MARK frommark
; /* first linespec */
347 MARK tomark
; /* second linespec */
348 REG
int cmdlen
; /* length of the command name given */
349 CMD cmd
; /* what command is this? */
350 ARGT argt
; /* argument types for this command */
351 short forceit
; /* bang version of a command? */
352 REG
int cmdidx
; /* index of command */
353 REG
char *build
; /* used while copying filenames */
354 int iswild
; /* boolean: filenames use wildcards? */
355 int isdfl
; /* using default line ranges? */
356 int didsub
; /* did we substitute file names for % or # */
358 /* ex commands can't be undone via the shift-U command */
361 /* permit extra colons at the start of the line */
362 for (; *cmdbuf
== ':'; cmdbuf
++)
366 /* ignore command lines that start with a double-quote */
373 /* parse the line specifier */
376 /* no file, so don't allow addresses */
378 else if (*scan
== '%')
380 /* '%' means all lines */
381 frommark
= MARK_FIRST
;
385 else if (*scan
== '0')
387 frommark
= tomark
= MARK_UNSET
;
393 scan
= linespec(scan
, &frommark
);
395 if (frommark
&& *scan
== ',')
398 scan
= linespec(scan
, &tomark
);
402 /* faulty line spec -- fault already described */
405 if (frommark
> tomark
)
407 msg("first address exceeds the second");
411 isdfl
= (scan
== cmdbuf
);
413 /* skip whitespace */
414 while (isspace(*scan
))
419 /* if no command, then just move the cursor to the mark */
422 if (tomark
!= MARK_UNSET
)
427 /* figure out how long the command name is */
435 isalpha(scan
[cmdlen
]);
441 /* lookup the command code */
443 cmdnames
[cmdidx
].name
&& strncmp(scan
, cmdnames
[cmdidx
].name
, cmdlen
);
447 argt
= cmdnames
[cmdidx
].argt
;
448 cmd
= cmdnames
[cmdidx
].code
;
451 msg("Unknown command \"%.*s\"", cmdlen
, scan
);
455 /* !!! if the command doesn't have NOBAR set, then replace | with \0 */
457 /* if the command ended with a bang, set the forceit flag */
459 if ((argt
& BANG
) && *scan
== '!')
469 /* skip any more whitespace, to leave scan pointing to arguments */
470 while (isspace(*scan
))
475 /* a couple of special cases for filenames */
478 /* if names were given, process them */
481 for (build
= tmpblk
.c
, iswild
= didsub
= FALSE
; *scan
; scan
++)
486 if (scan
[1] == '\\' || scan
[1] == '%' || scan
[1] == '#')
499 msg("No filename to substitute for %%");
502 strcpy(build
, origname
);
513 msg("No filename to substitute for #");
516 strcpy(build
, prevorig
);
544 || cmd
== CMD_READ
&& tmpblk
.c
[0] == '!'
545 || cmd
== CMD_WRITE
&& tmpblk
.c
[0] == '!')
560 if (iswild
&& tmpblk
.c
[0] != '>')
562 scan
= wildcard(tmpblk
.c
);
566 else /* no names given, maybe assume origname */
570 strcpy(tmpblk
.c
, origname
);
582 if (!(argt
& EXRCOK
) && nlines
< 1L)
584 msg("Can't use the \"%s\" command in a %s file", cmdnames
[cmdidx
].name
, EXRC
);
587 if (!(argt
& (ZERO
| EXRCOK
)) && frommark
== MARK_UNSET
)
589 msg("Can't use address 0 with \"%s\" command.", cmdnames
[cmdidx
].name
);
592 if (!(argt
& FROM
) && frommark
!= cursor
&& nlines
>= 1L)
594 msg("Can't use address with \"%s\" command.", cmdnames
[cmdidx
].name
);
597 if (!(argt
& TO
) && tomark
!= frommark
&& nlines
>= 1L)
599 msg("Can't use a range with \"%s\" command.", cmdnames
[cmdidx
].name
);
602 if (!(argt
& EXTRA
) && *scan
)
604 msg("Extra characters after \"%s\" command.", cmdnames
[cmdidx
].name
);
607 if ((argt
& NOSPC
) && !(cmd
== CMD_READ
&& (forceit
|| *scan
== '!')))
611 if ((argt
& PLUS
) && *build
== '+')
613 while (*build
&& !isspace(*build
))
617 while (*build
&& isspace(*build
))
622 #endif /* not CRUNCH */
623 for (; *build
; build
++)
627 msg("Too many %s to \"%s\" command.",
628 (argt
& XFILE
) ? "filenames" : "arguments",
629 cmdnames
[cmdidx
].name
);
635 /* some commands have special default ranges */
636 if (isdfl
&& (argt
& DFLALL
))
638 frommark
= MARK_FIRST
;
641 else if (isdfl
&& (argt
& DFLNONE
))
643 frommark
= tomark
= 0L;
646 /* write a newline if called from visual mode */
647 if ((argt
& NL
) && mode
!= MODE_EX
&& !exwrote
)
653 /* act on the command */
654 (*cmdnames
[cmdidx
].fn
)(frommark
, tomark
, cmd
, forceit
, scan
);
658 /* This function executes EX commands from a file. It returns 1 normally, or
659 * 0 if the file could not be opened for reading.
662 char *filename
; /* name of a ".exrc" file */
664 int fd
; /* file descriptor */
665 int len
; /* length of the ".exrc" file */
667 /* !!! kludge: we use U_text as the buffer. This has the side-effect
668 * of interfering with the shift-U visual command. Disable shift-U.
672 /* open the file, read it, and close */
673 fd
= open(filename
, O_RDONLY
);
678 len
= tread(fd
, U_text
, BLKSIZE
);
681 /* execute the string */
682 exstring(U_text
, len
, ctrl('V'));
687 /* This function executes EX commands from a string. The commands may be
688 * separated by newlines or by | characters. It also handles quoting.
689 * Each individual command is limited to 132 bytes, but the total string
692 void exstring(buf
, len
, qchar
)
693 char *buf
; /* the commands to execute */
694 int len
; /* the length of the string */
695 int qchar
; /* the quote character -- ^V for file, or \ for kbd */
697 char single
[133]; /* a single command */
701 /* find & do each command */
702 for (src
= buf
; src
< &buf
[len
]; src
++)
704 /* Copy a single command into single[]. Convert any quoted |
705 * into a normal |, and stop at a newline or unquoted |.
707 for (dest
= single
, i
= 0;
708 i
< 132 && src
< &buf
[len
] && *src
!= '\n' && *src
!= '|';
711 if (src
[0] == qchar
&& src
[1] == '|')