xref: /openbsd/usr.bin/vi/ex/ex_cmd.c (revision 63341175)
1 /*	$OpenBSD: ex_cmd.c,v 1.12 2018/07/13 20:06:10 bentley Exp $	*/
2 
3 /*-
4  * Copyright (c) 1992, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1992, 1993, 1994, 1995, 1996
7  *	Keith Bostic.  All rights reserved.
8  *
9  * See the LICENSE file for redistribution information.
10  */
11 
12 #include "config.h"
13 
14 #include <sys/types.h>
15 #include <sys/queue.h>
16 
17 #include <bitstring.h>
18 #include <limits.h>
19 #include <stdio.h>
20 
21 #include "../common/common.h"
22 
23 /*
24  * This array maps ex command names to command functions.
25  *
26  * The order in which command names are listed below is important --
27  * ambiguous abbreviations are resolved to be the first possible match,
28  * e.g. "r" means "read", not "rewind", because "read" is listed before
29  * "rewind".
30  *
31  * The syntax of the ex commands is unbelievably irregular, and a special
32  * case from beginning to end.  Each command has an associated "syntax
33  * script" which describes the "arguments" that are possible.  The script
34  * syntax is as follows:
35  *
36  *	!		-- ! flag
37  *	1		-- flags: [+-]*[pl#][+-]*
38  *	2		-- flags: [-.+^]
39  *	3		-- flags: [-.+^=]
40  *	b		-- buffer
41  *	c[01+a]		-- count (0-N, 1-N, signed 1-N, address offset)
42  *	f[N#][or]	-- file (a number or N, optional or required)
43  *	l		-- line
44  *	S		-- string with file name expansion
45  *	s		-- string
46  *	W		-- word string
47  *	w[N#][or]	-- word (a number or N, optional or required)
48  */
49 EXCMDLIST const cmds[] = {
50 /* C_SCROLL */
51 	{"\004",	ex_pr,		E_ADDR2,
52 	    "",
53 	    "^D",
54 	    "scroll lines"},
55 /* C_BANG */
56 	{"!",		ex_bang,	E_ADDR2_NONE | E_SECURE,
57 	    "S",
58 	    "[line [,line]] ! command",
59 	    "filter lines through commands or run commands"},
60 /* C_HASH */
61 	{"#",		ex_number,	E_ADDR2|E_CLRFLAG,
62 	    "ca1",
63 	    "[line [,line]] # [count] [l]",
64 	    "display numbered lines"},
65 /* C_SUBAGAIN */
66 	{"&",		ex_subagain,	E_ADDR2,
67 	    "s",
68 	    "[line [,line]] & [cgr] [count] [#lp]",
69 	    "repeat the last substitution"},
70 /* C_STAR */
71 	{"*",		ex_at,		0,
72 	    "b",
73 	    "* [buffer]",
74 	    "execute a buffer"},
75 /* C_SHIFTL */
76 	{"<",		ex_shiftl,	E_ADDR2|E_AUTOPRINT,
77 	    "ca1",
78 	    "[line [,line]] <[<...] [count] [flags]",
79 	    "shift lines left"},
80 /* C_EQUAL */
81 	{"=",		ex_equal,	E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
82 	    "1",
83 	    "[line] = [flags]",
84 	    "display line number"},
85 /* C_SHIFTR */
86 	{">",		ex_shiftr,	E_ADDR2|E_AUTOPRINT,
87 	    "ca1",
88 	    "[line [,line]] >[>...] [count] [flags]",
89 	    "shift lines right"},
90 /* C_AT */
91 	{"@",		ex_at,		E_ADDR2,
92 	    "b",
93 	    "@ [buffer]",
94 	    "execute a buffer"},
95 /* C_APPEND */
96 	{"append",	ex_append,	E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
97 	    "!",
98 	    "[line] a[ppend][!]",
99 	    "append input to a line"},
100 /* C_ABBR */
101 	{"abbreviate", 	ex_abbr,	0,
102 	    "W",
103 	    "ab[brev] [word replace]",
104 	    "specify an input abbreviation"},
105 /* C_ARGS */
106 	{"args",	ex_args,	0,
107 	    "",
108 	    "ar[gs]",
109 	    "display file argument list"},
110 /* C_BG */
111 	{"bg",		ex_bg,		E_VIONLY,
112 	    "",
113 	    "bg",
114 	    "put the current screen into the background"},
115 /* C_CHANGE */
116 	{"change",	ex_change,	E_ADDR2|E_ADDR_ZERODEF,
117 	    "!ca",
118 	    "[line [,line]] c[hange][!] [count]",
119 	    "change lines to input"},
120 /* C_CD */
121 	{"cd",		ex_cd,		0,
122 	    "!f1o",
123 	    "cd[!] [directory]",
124 	    "change the current directory"},
125 /* C_CHDIR */
126 	{"chdir",	ex_cd,		0,
127 	    "!f1o",
128 	    "chd[ir][!] [directory]",
129 	    "change the current directory"},
130 /* C_COPY */
131 	{"copy",	ex_copy,	E_ADDR2|E_AUTOPRINT,
132 	    "l1",
133 	    "[line [,line]] co[py] line [flags]",
134 	    "copy lines elsewhere in the file"},
135 /*
136  * !!!
137  * Adding new commands starting with 'd' may break the delete command code
138  * in ex_cmd() (the ex parser).  Read through the comments there, first.
139  */
140 /* C_DELETE */
141 	{"delete",	ex_delete,	E_ADDR2|E_AUTOPRINT,
142 	    "bca1",
143 	    "[line [,line]] d[elete][flags] [buffer] [count] [flags]",
144 	    "delete lines from the file"},
145 /* C_DISPLAY */
146 	{"display",	ex_display,	0,
147 	    "w1r",
148 	    "display b[uffers] | s[creens] | t[ags]",
149 	    "display buffers, screens or tags"},
150 /* C_EDIT */
151 	{"edit",	ex_edit,	E_NEWSCREEN,
152 	    "f1o",
153 	    "e[dit][!] [+cmd] [file]",
154 	    "begin editing another file"},
155 /* C_EX */
156 	{"ex",		ex_edit,	E_NEWSCREEN,
157 	    "f1o",
158 	    "ex[!] [+cmd] [file]",
159 	    "begin editing another file"},
160 /* C_EXUSAGE */
161 	{"exusage",	ex_usage,	0,
162 	    "w1o",
163 	    "[exu]sage [command]",
164 	    "display ex command usage statement"},
165 /* C_FILE */
166 	{"file",	ex_file,	0,
167 	    "f1o",
168 	    "f[ile] [name]",
169 	    "display (and optionally set) file name"},
170 /* C_FG */
171 	{"fg",		ex_fg,		E_NEWSCREEN|E_VIONLY,
172 	    "f1o",
173 	    "fg [file]",
174 	    "bring a backgrounded screen into the foreground"},
175 /* C_GLOBAL */
176 	{"global",	ex_global,	E_ADDR2_ALL,
177 	    "!s",
178 	    "[line [,line]] g[lobal][!] [;/]RE[;/] [commands]",
179 	    "execute a global command on lines matching an RE"},
180 /* C_HELP */
181 	{"help",	ex_help,	0,
182 	    "",
183 	    "he[lp]",
184 	    "display help statement"},
185 /* C_INSERT */
186 	{"insert",	ex_insert,	E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
187 	    "!",
188 	    "[line] i[nsert][!]",
189 	    "insert input before a line"},
190 /* C_JOIN */
191 	{"join",	ex_join,	E_ADDR2|E_AUTOPRINT,
192 	    "!ca1",
193 	    "[line [,line]] j[oin][!] [count] [flags]",
194 	    "join lines into a single line"},
195 /* C_K */
196 	{"k",		ex_mark,	E_ADDR1,
197 	    "w1r",
198 	    "[line] k key",
199 	    "mark a line position"},
200 /* C_LIST */
201 	{"list",	ex_list,	E_ADDR2|E_CLRFLAG,
202 	    "ca1",
203 	    "[line [,line]] l[ist] [count] [#]",
204 	    "display lines in an unambiguous form"},
205 /* C_MOVE */
206 	{"move",	ex_move,	E_ADDR2|E_AUTOPRINT,
207 	    "l",
208 	    "[line [,line]] m[ove] line",
209 	    "move lines elsewhere in the file"},
210 /* C_MARK */
211 	{"mark",	ex_mark,	E_ADDR1,
212 	    "w1r",
213 	    "[line] ma[rk] key",
214 	    "mark a line position"},
215 /* C_MAP */
216 	{"map",		ex_map,		0,
217 	    "!W",
218 	    "map[!] [keys replace]",
219 	    "map input or commands to one or more keys"},
220 /* C_MKEXRC */
221 	{"mkexrc",	ex_mkexrc,	0,
222 	    "!f1r",
223 	    "mkexrc[!] file",
224 	    "write a .exrc file"},
225 /* C_NEXT */
226 	{"next",	ex_next,	E_NEWSCREEN,
227 	    "!fN",
228 	    "n[ext][!] [+cmd] [file ...]",
229 	    "edit (and optionally specify) the next file"},
230 /* C_NUMBER */
231 	{"number",	ex_number,	E_ADDR2|E_CLRFLAG,
232 	    "ca1",
233 	    "[line [,line]] nu[mber] [count] [l]",
234 	    "change display to number lines"},
235 /* C_OPEN */
236 	{"open",	ex_open,	E_ADDR1,
237 	    "s",
238 	    "[line] o[pen] [/RE/] [flags]",
239 	    "enter \"open\" mode (not implemented)"},
240 /* C_PRINT */
241 	{"print",	ex_pr,		E_ADDR2|E_CLRFLAG,
242 	    "ca1",
243 	    "[line [,line]] p[rint] [count] [#l]",
244 	    "display lines"},
245 /* C_PRESERVE */
246 	{"preserve",	ex_preserve,	0,
247 	    "",
248 	    "pre[serve]",
249 	    "preserve an edit session for recovery"},
250 /* C_PREVIOUS */
251 	{"previous",	ex_prev,	E_NEWSCREEN,
252 	    "!",
253 	    "prev[ious][!]",
254 	    "edit the previous file in the file argument list"},
255 /* C_PUT */
256 	{"put",		ex_put,
257 	    E_ADDR1|E_AUTOPRINT|E_ADDR_ZERO|E_ADDR_ZERODEF,
258 	    "b",
259 	    "[line] pu[t] [buffer]",
260 	    "append a cut buffer to the line"},
261 /* C_QUIT */
262 	{"quit",	ex_quit,	0,
263 	    "!",
264 	    "q[uit][!]",
265 	    "exit ex/vi or close the current screen"},
266 /* C_READ */
267 	{"read",	ex_read,	E_ADDR1|E_ADDR_ZERO|E_ADDR_ZERODEF,
268 	    "s",
269 	    "[line] r[ead] [!cmd | [file]]",
270 	    "append input from a command or file to the line"},
271 /* C_RECOVER */
272 	{"recover",	ex_recover,	0,
273 	    "!f1r",
274 	    "recover[!] file",
275 	    "recover a saved file"},
276 /* C_RESIZE */
277 	{"resize",	ex_resize,	E_VIONLY,
278 	    "c+",
279 	    "resize [+-]rows",
280 	    "grow or shrink the current screen"},
281 /* C_REWIND */
282 	{"rewind",	ex_rew,		0,
283 	    "!",
284 	    "rew[ind][!]",
285 	    "re-edit all the files in the file argument list"},
286 /*
287  * !!!
288  * Adding new commands starting with 's' may break the substitute command code
289  * in ex_cmd() (the ex parser).  Read through the comments there, first.
290  */
291 /* C_SUBSTITUTE */
292 	{"s",		ex_s,		E_ADDR2,
293 	    "s",
294 	    "[line [,line]] s [[/;]RE[/;]repl[/;] [cgr] [count] [#lp]]",
295 	    "substitute on lines matching an RE"},
296 /* C_SCRIPT */
297 	{"script",	ex_script,	E_SECURE,
298 	    "!f1o",
299 	    "sc[ript][!] [file]",
300 	    "run a shell in a screen"},
301 /* C_SET */
302 	{"set",		ex_set,		0,
303 	    "wN",
304 	    "se[t] [option[=[value]]...] [nooption ...] [option? ...] [all]",
305 	    "set options (use \":set all\" to see all options)"},
306 /* C_SHELL */
307 	{"shell",	ex_shell,	E_SECURE,
308 	    "",
309 	    "sh[ell]",
310 	    "suspend editing and run a shell"},
311 /* C_SOURCE */
312 	{"source",	ex_source,	0,
313 	    "f1r",
314 	    "so[urce] file",
315 	    "read a file of ex commands"},
316 /* C_STOP */
317 	{"stop",	ex_stop,	E_SECURE,
318 	    "!",
319 	    "st[op][!]",
320 	    "suspend the edit session"},
321 /* C_SUSPEND */
322 	{"suspend",	ex_stop,	E_SECURE,
323 	    "!",
324 	    "su[spend][!]",
325 	    "suspend the edit session"},
326 /* C_T */
327 	{"t",		ex_copy,	E_ADDR2|E_AUTOPRINT,
328 	    "l1",
329 	    "[line [,line]] t line [flags]",
330 	    "copy lines elsewhere in the file"},
331 /* C_TAG */
332 	{"tag",		ex_tag_push,	E_NEWSCREEN,
333 	    "!w1o",
334 	    "ta[g][!] [string]",
335 	    "edit the file containing the tag"},
336 /* C_TAGNEXT */
337 	{"tagnext",	ex_tag_next,	0,
338 	    "!",
339 	    "tagn[ext][!]",
340 	    "move to the next tag"},
341 /* C_TAGPOP */
342 	{"tagpop",	ex_tag_pop,	0,
343 	    "!w1o",
344 	    "tagp[op][!] [number | file]",
345 	    "return to the previous group of tags"},
346 /* C_TAGPREV */
347 	{"tagprev",	ex_tag_prev,	0,
348 	    "!",
349 	    "tagpr[ev][!]",
350 	    "move to the previous tag"},
351 /* C_TAGTOP */
352 	{"tagtop",	ex_tag_top,	0,
353 	    "!",
354 	    "tagt[op][!]",
355 	    "discard all tags"},
356 /* C_UNDO */
357 	{"undo",	ex_undo,	E_AUTOPRINT,
358 	    "",
359 	    "u[ndo]",
360 	    "undo the most recent change"},
361 /* C_UNABBREVIATE */
362 	{"unabbreviate",ex_unabbr,	0,
363 	    "w1r",
364 	    "una[bbrev] word",
365 	    "delete an abbreviation"},
366 /* C_UNMAP */
367 	{"unmap",	ex_unmap,	0,
368 	    "!w1r",
369 	    "unm[ap][!] word",
370 	    "delete an input or command map"},
371 /* C_V */
372 	{"v",		ex_v,		E_ADDR2_ALL,
373 	    "s",
374 	    "[line [,line]] v [;/]RE[;/] [commands]",
375 	    "execute a global command on lines NOT matching an RE"},
376 /* C_VERSION */
377 	{"version",	ex_version,	0,
378 	    "",
379 	    "version",
380 	    "display the program version information"},
381 /* C_VISUAL_EX */
382 	{"visual",	ex_visual,	E_ADDR1|E_ADDR_ZERODEF,
383 	    "2c11",
384 	    "[line] vi[sual] [-|.|+|^] [window_size] [flags]",
385 	    "enter visual (vi) mode from ex mode"},
386 /* C_VISUAL_VI */
387 	{"visual",	ex_edit,	E_NEWSCREEN,
388 	    "f1o",
389 	    "vi[sual][!] [+cmd] [file]",
390 	    "edit another file (from vi mode only)"},
391 /* C_VIUSAGE */
392 	{"viusage",	ex_viusage,	0,
393 	    "w1o",
394 	    "[viu]sage [key]",
395 	    "display vi key usage statement"},
396 /* C_WRITE */
397 	{"write",	ex_write,	E_ADDR2_ALL|E_ADDR_ZERODEF,
398 	    "!s",
399 	    "[line [,line]] w[rite][!] [ !cmd | [>>] [file]]",
400 	    "write the file"},
401 /* C_WN */
402 	{"wn",		ex_wn,		E_ADDR2_ALL|E_ADDR_ZERODEF,
403 	    "!s",
404 	    "[line [,line]] wn[!] [>>] [file]",
405 	    "write the file and switch to the next file"},
406 /* C_WQ */
407 	{"wq",		ex_wq,		E_ADDR2_ALL|E_ADDR_ZERODEF,
408 	    "!s",
409 	    "[line [,line]] wq[!] [>>] [file]",
410 	    "write the file and exit"},
411 /* C_XIT */
412 	{"xit",		ex_xit,		E_ADDR2_ALL|E_ADDR_ZERODEF,
413 	    "!f1o",
414 	    "[line [,line]] x[it][!] [file]",
415 	    "write if modified and exit"},
416 /* C_YANK */
417 	{"yank",	ex_yank,	E_ADDR2,
418 	    "bca",
419 	    "[line [,line]] ya[nk] [buffer] [count]",
420 	    "copy lines to a cut buffer"},
421 /* C_Z */
422 	{"z",		ex_z,		E_ADDR1,
423 	    "3c01",
424 	    "[line] z [-|.|+|^|=] [count] [flags]",
425 	    "display different screens of the file"},
426 /* C_SUBTILDE */
427 	{"~",		ex_subtilde,	E_ADDR2,
428 	    "s",
429 	    "[line [,line]] ~ [cgr] [count] [#lp]",
430 	    "replace previous RE with previous replacement string"},
431 	{NULL},
432 };
433