Commit | Line | Data |
---|---|---|
f5bcab4b WJ |
1 | /* ex.c */ |
2 | ||
3 | /* Author: | |
4 | * Steve Kirkendall | |
5 | * 14407 SW Teal Blvd. #C | |
6 | * Beaverton, OR 97005 | |
7 | * kirkenda@cs.pdx.edu | |
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 | /* This data type is used to describe the possible argument combinations */ | |
18 | typedef short ARGT; | |
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 */ | |
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+ZERO+BANG }, | |
56 | #ifdef DEBUG | |
57 | {"bug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, | |
58 | #endif | |
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 }, | |
81 | ||
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 }, | |
88 | #ifndef NO_AT | |
89 | {"@", CMD_AT, cmd_at, EXTRA }, | |
90 | #endif | |
91 | ||
92 | #ifndef NO_ABBR | |
93 | {"abbreviate", CMD_ABBR, cmd_map, EXRCOK+BANG+EXTRA}, | |
94 | #endif | |
95 | {"args", CMD_ARGS, cmd_args, EXRCOK+NAMEDFS }, | |
96 | #ifndef NO_ERRLIST | |
97 | {"cc", CMD_CC, cmd_make, BANG+FILES }, | |
98 | #endif | |
99 | {"cd", CMD_CD, cmd_cd, EXRCOK+BANG+NAMEDF}, | |
100 | {"copy", CMD_COPY, cmd_move, RANGE+EXTRA }, | |
101 | #ifndef NO_DIGRAPH | |
102 | {"digraph", CMD_DIGRAPH, cmd_digraph, EXRCOK+BANG+EXTRA}, | |
103 | #endif | |
104 | #ifndef NO_ERRLIST | |
105 | {"errlist", CMD_ERRLIST, cmd_errlist, BANG+NAMEDF }, | |
106 | #endif | |
107 | {"ex", CMD_EDIT, cmd_edit, BANG+FILE1 }, | |
108 | {"mark", CMD_MARK, cmd_mark, FROM+WORD1 }, | |
109 | #ifndef NO_MKEXRC | |
110 | {"mkexrc", CMD_MKEXRC, cmd_mkexrc, NAMEDF }, | |
111 | #endif | |
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 }, | |
117 | #ifdef SIGTSTP | |
118 | {"stop", CMD_STOP, cmd_suspend, NONE }, | |
119 | #endif | |
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 }, | |
124 | ||
125 | #ifdef DEBUG | |
126 | {"debug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, | |
127 | {"validate", CMD_VALIDATE, cmd_validate, BANG+NL }, | |
128 | #endif | |
129 | {"chdir", CMD_CD, cmd_cd, EXRCOK+BANG+NAMEDF}, | |
130 | #ifndef NO_COLOR | |
131 | {"color", CMD_COLOR, cmd_color, EXRCOK+EXTRA }, | |
132 | #endif | |
133 | #ifndef NO_ERRLIST | |
134 | {"make", CMD_MAKE, cmd_make, BANG+NAMEDFS }, | |
135 | #endif | |
136 | {"map", CMD_MAP, cmd_map, EXRCOK+BANG+EXTRA}, | |
137 | {"previous", CMD_PREVIOUS, cmd_next, BANG }, | |
138 | {"rewind", CMD_REWIND, cmd_next, BANG }, | |
139 | #ifdef SIGTSTP | |
140 | {"suspend", CMD_SUSPEND, cmd_suspend, NONE }, | |
141 | #endif | |
142 | {"unmap", CMD_UNMAP, cmd_map, EXRCOK+BANG+EXTRA}, | |
143 | #ifndef NO_ABBR | |
144 | {"unabbreviate",CMD_UNABBR, cmd_map, EXRCOK+WORD1 }, | |
145 | #endif | |
146 | ||
147 | {(char *)0} | |
148 | }; | |
149 | ||
150 | ||
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. | |
154 | */ | |
155 | char *parseptrn(ptrn) | |
156 | REG char *ptrn; | |
157 | { | |
158 | REG char *scan; | |
159 | ||
160 | for (scan = ptrn + 1; | |
161 | *scan && *scan != *ptrn; | |
162 | scan++) | |
163 | { | |
164 | /* allow backslashed versions of / and ? in the pattern */ | |
165 | if (*scan == '\\' && scan[1] != '\0') | |
166 | { | |
167 | scan++; | |
168 | } | |
169 | } | |
170 | if (*scan) | |
171 | { | |
172 | *scan++ = '\0'; | |
173 | } | |
174 | ||
175 | return scan; | |
176 | } | |
177 | ||
178 | ||
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 */ | |
183 | { | |
184 | long num; | |
185 | REG char *t; | |
186 | ||
187 | /* parse each ;-delimited clause of this linespec */ | |
188 | do | |
189 | { | |
190 | /* skip an initial ';', if any */ | |
191 | if (*s == ';') | |
192 | { | |
193 | s++; | |
194 | } | |
195 | ||
196 | /* skip leading spaces */ | |
197 | while (isspace(*s)) | |
198 | { | |
199 | s++; | |
200 | } | |
201 | ||
202 | /* dot means current position */ | |
203 | if (*s == '.') | |
204 | { | |
205 | s++; | |
206 | *markptr = cursor; | |
207 | } | |
208 | /* '$' means the last line */ | |
209 | else if (*s == '$') | |
210 | { | |
211 | s++; | |
212 | *markptr = MARK_LAST; | |
213 | } | |
214 | /* digit means an absolute line number */ | |
215 | else if (isdigit(*s)) | |
216 | { | |
217 | for (num = 0; isdigit(*s); s++) | |
218 | { | |
219 | num = num * 10 + *s - '0'; | |
220 | } | |
221 | *markptr = MARK_AT_LINE(num); | |
222 | } | |
223 | /* appostrophe means go to a set mark */ | |
224 | else if (*s == '\'') | |
225 | { | |
226 | s++; | |
227 | *markptr = m_tomark(cursor, 1L, (int)*s); | |
228 | s++; | |
229 | } | |
230 | /* slash means do a search */ | |
231 | else if (*s == '/' || *s == '?') | |
232 | { | |
233 | /* put a '\0' at the end of the search pattern */ | |
234 | t = parseptrn(s); | |
235 | ||
236 | /* search for the pattern */ | |
237 | *markptr &= ~(BLKSIZE - 1); | |
238 | if (*s == '/') | |
239 | { | |
240 | pfetch(markline(*markptr)); | |
241 | if (plen > 0) | |
242 | *markptr += plen - 1; | |
243 | *markptr = m_fsrch(*markptr, s); | |
244 | } | |
245 | else | |
246 | { | |
247 | *markptr = m_bsrch(*markptr, s); | |
248 | } | |
249 | ||
250 | /* adjust command string pointer */ | |
251 | s = t; | |
252 | } | |
253 | ||
254 | /* if linespec was faulty, quit now */ | |
255 | if (!*markptr) | |
256 | { | |
257 | return s; | |
258 | } | |
259 | ||
260 | /* maybe add an offset */ | |
261 | t = s; | |
262 | if (*t == '-' || *t == '+') | |
263 | { | |
264 | s++; | |
265 | for (num = 0; isdigit(*s); s++) | |
266 | { | |
267 | num = num * 10 + *s - '0'; | |
268 | } | |
269 | if (num == 0) | |
270 | { | |
271 | num = 1; | |
272 | } | |
273 | *markptr = m_updnto(*markptr, num, *t); | |
274 | } | |
275 | } while (*s == ';' || *s == '+' || *s == '-'); | |
276 | ||
277 | /* protect against invalid line numbers */ | |
278 | num = markline(*markptr); | |
279 | if (num < 1L || num > nlines) | |
280 | { | |
281 | msg("Invalid line number -- must be from 1 to %ld", nlines); | |
282 | *markptr = MARK_UNSET; | |
283 | } | |
284 | ||
285 | return s; | |
286 | } | |
287 | ||
288 | ||
289 | ||
290 | /* This function reads an ex command and executes it. */ | |
291 | void ex() | |
292 | { | |
293 | char cmdbuf[150]; | |
294 | REG int cmdlen; | |
295 | static long oldline; | |
296 | ||
297 | significant = FALSE; | |
298 | oldline = markline(cursor); | |
299 | ||
300 | while (mode == MODE_EX) | |
301 | { | |
302 | /* read a line */ | |
303 | #ifdef CRUNCH | |
304 | cmdlen = vgets(':', cmdbuf, sizeof(cmdbuf)); | |
305 | #else | |
306 | cmdlen = vgets(*o_prompt ? ':' : '\0', cmdbuf, sizeof(cmdbuf)); | |
307 | #endif | |
308 | if (cmdlen < 0) | |
309 | { | |
310 | return; | |
311 | } | |
312 | ||
313 | /* if empty line, assume ".+1" */ | |
314 | if (cmdlen == 0) | |
315 | { | |
316 | strcpy(cmdbuf, ".+1"); | |
317 | qaddch('\r'); | |
318 | clrtoeol(); | |
319 | } | |
320 | else | |
321 | { | |
322 | addch('\n'); | |
323 | } | |
324 | refresh(); | |
325 | ||
326 | /* parse & execute the command */ | |
327 | doexcmd(cmdbuf); | |
328 | ||
329 | /* handle autoprint */ | |
330 | if (significant || markline(cursor) != oldline) | |
331 | { | |
332 | significant = FALSE; | |
333 | oldline = markline(cursor); | |
334 | if (*o_autoprint && mode == MODE_EX) | |
335 | { | |
336 | cmd_print(cursor, cursor, CMD_PRINT, FALSE, ""); | |
337 | } | |
338 | } | |
339 | } | |
340 | } | |
341 | ||
342 | void doexcmd(cmdbuf) | |
343 | char *cmdbuf; /* string containing an ex command */ | |
344 | { | |
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 # */ | |
357 | ||
358 | /* ex commands can't be undone via the shift-U command */ | |
359 | U_line = 0L; | |
360 | ||
361 | /* permit extra colons at the start of the line */ | |
362 | for (; *cmdbuf == ':'; cmdbuf++) | |
363 | { | |
364 | } | |
365 | ||
366 | /* ignore command lines that start with a double-quote */ | |
367 | if (*cmdbuf == '"') | |
368 | { | |
369 | return; | |
370 | } | |
371 | scan = cmdbuf; | |
372 | ||
373 | /* parse the line specifier */ | |
374 | if (nlines < 1) | |
375 | { | |
376 | /* no file, so don't allow addresses */ | |
377 | } | |
378 | else if (*scan == '%') | |
379 | { | |
380 | /* '%' means all lines */ | |
381 | frommark = MARK_FIRST; | |
382 | tomark = MARK_LAST; | |
383 | scan++; | |
384 | } | |
385 | else if (*scan == '0') | |
386 | { | |
387 | frommark = tomark = MARK_UNSET; | |
388 | scan++; | |
389 | } | |
390 | else | |
391 | { | |
392 | frommark = cursor; | |
393 | scan = linespec(scan, &frommark); | |
394 | tomark = frommark; | |
395 | if (frommark && *scan == ',') | |
396 | { | |
397 | scan++; | |
398 | scan = linespec(scan, &tomark); | |
399 | } | |
400 | if (!tomark) | |
401 | { | |
402 | /* faulty line spec -- fault already described */ | |
403 | return; | |
404 | } | |
405 | if (frommark > tomark) | |
406 | { | |
407 | msg("first address exceeds the second"); | |
408 | return; | |
409 | } | |
410 | } | |
411 | isdfl = (scan == cmdbuf); | |
412 | ||
413 | /* skip whitespace */ | |
414 | while (isspace(*scan)) | |
415 | { | |
416 | scan++; | |
417 | } | |
418 | ||
419 | /* if no command, then just move the cursor to the mark */ | |
420 | if (!*scan) | |
421 | { | |
422 | if (tomark != MARK_UNSET) | |
423 | cursor = tomark; | |
424 | return; | |
425 | } | |
426 | ||
427 | /* figure out how long the command name is */ | |
428 | if (!isalpha(*scan)) | |
429 | { | |
430 | cmdlen = 1; | |
431 | } | |
432 | else | |
433 | { | |
434 | for (cmdlen = 1; | |
435 | isalpha(scan[cmdlen]); | |
436 | cmdlen++) | |
437 | { | |
438 | } | |
439 | } | |
440 | ||
441 | /* lookup the command code */ | |
442 | for (cmdidx = 0; | |
443 | cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen); | |
444 | cmdidx++) | |
445 | { | |
446 | } | |
447 | argt = cmdnames[cmdidx].argt; | |
448 | cmd = cmdnames[cmdidx].code; | |
449 | if (cmd == CMD_NULL) | |
450 | { | |
451 | msg("Unknown command \"%.*s\"", cmdlen, scan); | |
452 | return; | |
453 | } | |
454 | ||
455 | /* !!! if the command doesn't have NOBAR set, then replace | with \0 */ | |
456 | ||
457 | /* if the command ended with a bang, set the forceit flag */ | |
458 | scan += cmdlen; | |
459 | if ((argt & BANG) && *scan == '!') | |
460 | { | |
461 | scan++; | |
462 | forceit = 1; | |
463 | } | |
464 | else | |
465 | { | |
466 | forceit = 0; | |
467 | } | |
468 | ||
469 | /* skip any more whitespace, to leave scan pointing to arguments */ | |
470 | while (isspace(*scan)) | |
471 | { | |
472 | scan++; | |
473 | } | |
474 | ||
475 | /* a couple of special cases for filenames */ | |
476 | if (argt & XFILE) | |
477 | { | |
478 | /* if names were given, process them */ | |
479 | if (*scan) | |
480 | { | |
481 | for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++) | |
482 | { | |
483 | switch (*scan) | |
484 | { | |
485 | case '\\': | |
486 | if (scan[1] == '\\' || scan[1] == '%' || scan[1] == '#') | |
487 | { | |
488 | *build++ = *++scan; | |
489 | } | |
490 | else | |
491 | { | |
492 | *build++ = '\\'; | |
493 | } | |
494 | break; | |
495 | ||
496 | case '%': | |
497 | if (!*origname) | |
498 | { | |
499 | msg("No filename to substitute for %%"); | |
500 | return; | |
501 | } | |
502 | strcpy(build, origname); | |
503 | while (*build) | |
504 | { | |
505 | build++; | |
506 | } | |
507 | didsub = TRUE; | |
508 | break; | |
509 | ||
510 | case '#': | |
511 | if (!*prevorig) | |
512 | { | |
513 | msg("No filename to substitute for #"); | |
514 | return; | |
515 | } | |
516 | strcpy(build, prevorig); | |
517 | while (*build) | |
518 | { | |
519 | build++; | |
520 | } | |
521 | didsub = TRUE; | |
522 | break; | |
523 | ||
524 | case '*': | |
525 | case '?': | |
526 | #if !(MSDOS || TOS) | |
527 | case '[': | |
528 | case '`': | |
529 | case '{': /* } */ | |
530 | case '$': | |
531 | case '~': | |
532 | #endif | |
533 | *build++ = *scan; | |
534 | iswild = TRUE; | |
535 | break; | |
536 | ||
537 | default: | |
538 | *build++ = *scan; | |
539 | } | |
540 | } | |
541 | *build = '\0'; | |
542 | ||
543 | if (cmd == CMD_BANG | |
544 | || cmd == CMD_READ && tmpblk.c[0] == '!' | |
545 | || cmd == CMD_WRITE && tmpblk.c[0] == '!') | |
546 | { | |
547 | if (didsub) | |
548 | { | |
549 | if (mode != MODE_EX) | |
550 | { | |
551 | addch('\n'); | |
552 | } | |
553 | addstr(tmpblk.c); | |
554 | addch('\n'); | |
555 | exrefresh(); | |
556 | } | |
557 | } | |
558 | else | |
559 | { | |
560 | if (iswild && tmpblk.c[0] != '>') | |
561 | { | |
562 | scan = wildcard(tmpblk.c); | |
563 | } | |
564 | } | |
565 | } | |
566 | else /* no names given, maybe assume origname */ | |
567 | { | |
568 | if (!(argt & NODFL)) | |
569 | { | |
570 | strcpy(tmpblk.c, origname); | |
571 | } | |
572 | else | |
573 | { | |
574 | *tmpblk.c = '\0'; | |
575 | } | |
576 | } | |
577 | ||
578 | scan = tmpblk.c; | |
579 | } | |
580 | ||
581 | /* bad arguments? */ | |
582 | if (!(argt & EXRCOK) && nlines < 1L) | |
583 | { | |
584 | msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC); | |
585 | return; | |
586 | } | |
587 | if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET) | |
588 | { | |
589 | msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name); | |
590 | return; | |
591 | } | |
592 | if (!(argt & FROM) && frommark != cursor && nlines >= 1L) | |
593 | { | |
594 | msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name); | |
595 | return; | |
596 | } | |
597 | if (!(argt & TO) && tomark != frommark && nlines >= 1L) | |
598 | { | |
599 | msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name); | |
600 | return; | |
601 | } | |
602 | if (!(argt & EXTRA) && *scan) | |
603 | { | |
604 | msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name); | |
605 | return; | |
606 | } | |
607 | if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!'))) | |
608 | { | |
609 | build = scan; | |
610 | #ifndef CRUNCH | |
611 | if ((argt & PLUS) && *build == '+') | |
612 | { | |
613 | while (*build && !isspace(*build)) | |
614 | { | |
615 | build++; | |
616 | } | |
617 | while (*build && isspace(*build)) | |
618 | { | |
619 | build++; | |
620 | } | |
621 | } | |
622 | #endif /* not CRUNCH */ | |
623 | for (; *build; build++) | |
624 | { | |
625 | if (isspace(*build)) | |
626 | { | |
627 | msg("Too many %s to \"%s\" command.", | |
628 | (argt & XFILE) ? "filenames" : "arguments", | |
629 | cmdnames[cmdidx].name); | |
630 | return; | |
631 | } | |
632 | } | |
633 | } | |
634 | ||
635 | /* some commands have special default ranges */ | |
636 | if (isdfl && (argt & DFLALL)) | |
637 | { | |
638 | frommark = MARK_FIRST; | |
639 | tomark = MARK_LAST; | |
640 | } | |
641 | else if (isdfl && (argt & DFLNONE)) | |
642 | { | |
643 | frommark = tomark = 0L; | |
644 | } | |
645 | ||
646 | /* write a newline if called from visual mode */ | |
647 | if ((argt & NL) && mode != MODE_EX && !exwrote) | |
648 | { | |
649 | addch('\n'); | |
650 | exrefresh(); | |
651 | } | |
652 | ||
653 | /* act on the command */ | |
654 | (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan); | |
655 | } | |
656 | ||
657 | ||
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. | |
660 | */ | |
661 | int doexrc(filename) | |
662 | char *filename; /* name of a ".exrc" file */ | |
663 | { | |
664 | int fd; /* file descriptor */ | |
665 | int len; /* length of the ".exrc" file */ | |
666 | ||
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. | |
669 | */ | |
670 | U_line = 0L; | |
671 | ||
672 | /* open the file, read it, and close */ | |
673 | fd = open(filename, O_RDONLY); | |
674 | if (fd < 0) | |
675 | { | |
676 | return 0; | |
677 | } | |
678 | len = tread(fd, U_text, BLKSIZE); | |
679 | close(fd); | |
680 | ||
681 | /* execute the string */ | |
682 | exstring(U_text, len, ctrl('V')); | |
683 | ||
684 | return 1; | |
685 | } | |
686 | ||
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 | |
690 | * may be longer. | |
691 | */ | |
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 */ | |
696 | { | |
697 | char single[133]; /* a single command */ | |
698 | char *src, *dest; | |
699 | int i; | |
700 | ||
701 | /* find & do each command */ | |
702 | for (src = buf; src < &buf[len]; src++) | |
703 | { | |
704 | /* Copy a single command into single[]. Convert any quoted | | |
705 | * into a normal |, and stop at a newline or unquoted |. | |
706 | */ | |
707 | for (dest = single, i = 0; | |
708 | i < 132 && src < &buf[len] && *src != '\n' && *src != '|'; | |
709 | src++, i++) | |
710 | { | |
711 | if (src[0] == qchar && src[1] == '|') | |
712 | { | |
713 | src++; | |
714 | } | |
715 | *dest++ = *src; | |
716 | } | |
717 | *dest = '\0'; | |
718 | ||
719 | /* do it */ | |
720 | doexcmd(single); | |
721 | } | |
722 | } |