Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* ex.c */ |
2 | ||
3 | /* Author: | |
4 | * Steve Kirkendall | |
5 | * 14407 SW Teal Blvd. #C | |
6 | * Beaverton, OR 97005 | |
7 | * kirkenda@cs.pdx.edu | |
15637ed4 RG |
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 */ | |
08746e8b AM |
55 | {"print", CMD_PRINT, cmd_print, RANGE+NL }, |
56 | ||
15637ed4 RG |
57 | {"append", CMD_APPEND, cmd_append, FROM+ZERO+BANG }, |
58 | #ifdef DEBUG | |
59 | {"bug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, | |
60 | #endif | |
61 | {"change", CMD_CHANGE, cmd_append, RANGE+BANG }, | |
62 | {"delete", CMD_DELETE, cmd_delete, RANGE+WORD1 }, | |
63 | {"edit", CMD_EDIT, cmd_edit, BANG+FILE1+PLUS }, | |
64 | {"file", CMD_FILE, cmd_file, NAMEDF }, | |
65 | {"global", CMD_GLOBAL, cmd_global, RANGE+BANG+EXTRA+DFLALL+NOBAR}, | |
66 | {"insert", CMD_INSERT, cmd_append, FROM+BANG }, | |
67 | {"join", CMD_INSERT, cmd_join, RANGE+BANG }, | |
68 | {"k", CMD_MARK, cmd_mark, FROM+WORD1 }, | |
69 | {"list", CMD_LIST, cmd_print, RANGE+NL }, | |
70 | {"move", CMD_MOVE, cmd_move, RANGE+EXTRA }, | |
71 | {"next", CMD_NEXT, cmd_next, BANG+NAMEDFS }, | |
72 | {"Next", CMD_PREVIOUS, cmd_next, BANG }, | |
15637ed4 RG |
73 | {"quit", CMD_QUIT, cmd_xit, BANG }, |
74 | {"read", CMD_READ, cmd_read, FROM+ZERO+NAMEDF}, | |
75 | {"substitute", CMD_SUBSTITUTE, cmd_substitute, RANGE+EXTRA }, | |
76 | {"to", CMD_COPY, cmd_move, RANGE+EXTRA }, | |
77 | {"undo", CMD_UNDO, cmd_undo, NONE }, | |
78 | {"vglobal", CMD_VGLOBAL, cmd_global, RANGE+EXTRA+DFLALL+NOBAR}, | |
79 | {"write", CMD_WRITE, cmd_write, RANGE+BANG+FILE1+DFLALL}, | |
80 | {"xit", CMD_XIT, cmd_xit, BANG+NL }, | |
81 | {"yank", CMD_YANK, cmd_delete, RANGE+WORD1 }, | |
82 | ||
83 | {"!", CMD_BANG, cmd_shell, EXRCOK+RANGE+NAMEDFS+DFLNONE+NL+NOBAR}, | |
84 | {"#", CMD_NUMBER, cmd_print, RANGE+NL }, | |
85 | {"<", CMD_SHIFTL, cmd_shift, RANGE }, | |
86 | {">", CMD_SHIFTR, cmd_shift, RANGE }, | |
87 | {"=", CMD_EQUAL, cmd_file, RANGE }, | |
88 | {"&", CMD_SUBAGAIN, cmd_substitute, RANGE }, | |
89 | #ifndef NO_AT | |
90 | {"@", CMD_AT, cmd_at, EXTRA }, | |
91 | #endif | |
92 | ||
93 | #ifndef NO_ABBR | |
94 | {"abbreviate", CMD_ABBR, cmd_map, EXRCOK+BANG+EXTRA}, | |
95 | #endif | |
96 | {"args", CMD_ARGS, cmd_args, EXRCOK+NAMEDFS }, | |
97 | #ifndef NO_ERRLIST | |
98 | {"cc", CMD_CC, cmd_make, BANG+FILES }, | |
99 | #endif | |
100 | {"cd", CMD_CD, cmd_cd, EXRCOK+BANG+NAMEDF}, | |
101 | {"copy", CMD_COPY, cmd_move, RANGE+EXTRA }, | |
102 | #ifndef NO_DIGRAPH | |
103 | {"digraph", CMD_DIGRAPH, cmd_digraph, EXRCOK+BANG+EXTRA}, | |
104 | #endif | |
105 | #ifndef NO_ERRLIST | |
106 | {"errlist", CMD_ERRLIST, cmd_errlist, BANG+NAMEDF }, | |
107 | #endif | |
108 | {"ex", CMD_EDIT, cmd_edit, BANG+FILE1 }, | |
109 | {"mark", CMD_MARK, cmd_mark, FROM+WORD1 }, | |
110 | #ifndef NO_MKEXRC | |
111 | {"mkexrc", CMD_MKEXRC, cmd_mkexrc, NAMEDF }, | |
112 | #endif | |
113 | {"number", CMD_NUMBER, cmd_print, RANGE+NL }, | |
08746e8b AM |
114 | #ifndef NO_TAGSTACK |
115 | {"pop", CMD_POP, cmd_pop, BANG+WORD1 }, | |
116 | #endif | |
15637ed4 RG |
117 | {"put", CMD_PUT, cmd_put, FROM+ZERO+WORD1 }, |
118 | {"set", CMD_SET, cmd_set, EXRCOK+EXTRA }, | |
119 | {"shell", CMD_SHELL, cmd_shell, NL }, | |
120 | {"source", CMD_SOURCE, cmd_source, EXRCOK+NAMEDF }, | |
121 | #ifdef SIGTSTP | |
122 | {"stop", CMD_STOP, cmd_suspend, NONE }, | |
123 | #endif | |
124 | {"tag", CMD_TAG, cmd_tag, BANG+WORD1 }, | |
125 | {"version", CMD_VERSION, cmd_version, EXRCOK+NONE }, | |
126 | {"visual", CMD_VISUAL, cmd_edit, BANG+NAMEDF }, | |
08746e8b | 127 | {"wq", CMD_WQUIT, cmd_xit, NL }, |
15637ed4 RG |
128 | |
129 | #ifdef DEBUG | |
130 | {"debug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, | |
131 | {"validate", CMD_VALIDATE, cmd_validate, BANG+NL }, | |
132 | #endif | |
133 | {"chdir", CMD_CD, cmd_cd, EXRCOK+BANG+NAMEDF}, | |
134 | #ifndef NO_COLOR | |
135 | {"color", CMD_COLOR, cmd_color, EXRCOK+EXTRA }, | |
136 | #endif | |
137 | #ifndef NO_ERRLIST | |
138 | {"make", CMD_MAKE, cmd_make, BANG+NAMEDFS }, | |
139 | #endif | |
140 | {"map", CMD_MAP, cmd_map, EXRCOK+BANG+EXTRA}, | |
141 | {"previous", CMD_PREVIOUS, cmd_next, BANG }, | |
142 | {"rewind", CMD_REWIND, cmd_next, BANG }, | |
143 | #ifdef SIGTSTP | |
144 | {"suspend", CMD_SUSPEND, cmd_suspend, NONE }, | |
145 | #endif | |
146 | {"unmap", CMD_UNMAP, cmd_map, EXRCOK+BANG+EXTRA}, | |
147 | #ifndef NO_ABBR | |
148 | {"unabbreviate",CMD_UNABBR, cmd_map, EXRCOK+WORD1 }, | |
149 | #endif | |
150 | ||
151 | {(char *)0} | |
152 | }; | |
153 | ||
154 | ||
155 | /* This function parses a search pattern - given a pointer to a / or ?, | |
156 | * it replaces the ending / or ? with a \0, and returns a pointer to the | |
157 | * stuff that came after the pattern. | |
158 | */ | |
159 | char *parseptrn(ptrn) | |
160 | REG char *ptrn; | |
161 | { | |
162 | REG char *scan; | |
163 | ||
164 | for (scan = ptrn + 1; | |
165 | *scan && *scan != *ptrn; | |
166 | scan++) | |
167 | { | |
168 | /* allow backslashed versions of / and ? in the pattern */ | |
169 | if (*scan == '\\' && scan[1] != '\0') | |
170 | { | |
171 | scan++; | |
172 | } | |
173 | } | |
174 | if (*scan) | |
175 | { | |
176 | *scan++ = '\0'; | |
177 | } | |
178 | ||
179 | return scan; | |
180 | } | |
181 | ||
182 | ||
183 | /* This function parses a line specifier for ex commands */ | |
184 | char *linespec(s, markptr) | |
185 | REG char *s; /* start of the line specifier */ | |
186 | MARK *markptr; /* where to store the mark's value */ | |
187 | { | |
188 | long num; | |
189 | REG char *t; | |
190 | ||
191 | /* parse each ;-delimited clause of this linespec */ | |
192 | do | |
193 | { | |
194 | /* skip an initial ';', if any */ | |
195 | if (*s == ';') | |
196 | { | |
197 | s++; | |
198 | } | |
199 | ||
200 | /* skip leading spaces */ | |
201 | while (isspace(*s)) | |
202 | { | |
203 | s++; | |
204 | } | |
205 | ||
206 | /* dot means current position */ | |
207 | if (*s == '.') | |
208 | { | |
209 | s++; | |
210 | *markptr = cursor; | |
211 | } | |
212 | /* '$' means the last line */ | |
213 | else if (*s == '$') | |
214 | { | |
215 | s++; | |
216 | *markptr = MARK_LAST; | |
217 | } | |
218 | /* digit means an absolute line number */ | |
219 | else if (isdigit(*s)) | |
220 | { | |
221 | for (num = 0; isdigit(*s); s++) | |
222 | { | |
223 | num = num * 10 + *s - '0'; | |
224 | } | |
225 | *markptr = MARK_AT_LINE(num); | |
226 | } | |
227 | /* appostrophe means go to a set mark */ | |
228 | else if (*s == '\'') | |
229 | { | |
230 | s++; | |
231 | *markptr = m_tomark(cursor, 1L, (int)*s); | |
232 | s++; | |
233 | } | |
234 | /* slash means do a search */ | |
235 | else if (*s == '/' || *s == '?') | |
236 | { | |
237 | /* put a '\0' at the end of the search pattern */ | |
238 | t = parseptrn(s); | |
239 | ||
240 | /* search for the pattern */ | |
241 | *markptr &= ~(BLKSIZE - 1); | |
242 | if (*s == '/') | |
243 | { | |
244 | pfetch(markline(*markptr)); | |
245 | if (plen > 0) | |
246 | *markptr += plen - 1; | |
247 | *markptr = m_fsrch(*markptr, s); | |
248 | } | |
249 | else | |
250 | { | |
251 | *markptr = m_bsrch(*markptr, s); | |
252 | } | |
253 | ||
254 | /* adjust command string pointer */ | |
255 | s = t; | |
256 | } | |
257 | ||
258 | /* if linespec was faulty, quit now */ | |
259 | if (!*markptr) | |
260 | { | |
261 | return s; | |
262 | } | |
263 | ||
264 | /* maybe add an offset */ | |
265 | t = s; | |
266 | if (*t == '-' || *t == '+') | |
267 | { | |
268 | s++; | |
269 | for (num = 0; isdigit(*s); s++) | |
270 | { | |
271 | num = num * 10 + *s - '0'; | |
272 | } | |
273 | if (num == 0) | |
274 | { | |
275 | num = 1; | |
276 | } | |
277 | *markptr = m_updnto(*markptr, num, *t); | |
278 | } | |
279 | } while (*s == ';' || *s == '+' || *s == '-'); | |
280 | ||
281 | /* protect against invalid line numbers */ | |
282 | num = markline(*markptr); | |
283 | if (num < 1L || num > nlines) | |
284 | { | |
285 | msg("Invalid line number -- must be from 1 to %ld", nlines); | |
286 | *markptr = MARK_UNSET; | |
287 | } | |
288 | ||
289 | return s; | |
290 | } | |
291 | ||
292 | ||
293 | ||
294 | /* This function reads an ex command and executes it. */ | |
295 | void ex() | |
296 | { | |
297 | char cmdbuf[150]; | |
298 | REG int cmdlen; | |
299 | static long oldline; | |
300 | ||
301 | significant = FALSE; | |
302 | oldline = markline(cursor); | |
303 | ||
304 | while (mode == MODE_EX) | |
305 | { | |
306 | /* read a line */ | |
307 | #ifdef CRUNCH | |
308 | cmdlen = vgets(':', cmdbuf, sizeof(cmdbuf)); | |
309 | #else | |
310 | cmdlen = vgets(*o_prompt ? ':' : '\0', cmdbuf, sizeof(cmdbuf)); | |
311 | #endif | |
312 | if (cmdlen < 0) | |
313 | { | |
314 | return; | |
315 | } | |
316 | ||
317 | /* if empty line, assume ".+1" */ | |
318 | if (cmdlen == 0) | |
319 | { | |
320 | strcpy(cmdbuf, ".+1"); | |
321 | qaddch('\r'); | |
322 | clrtoeol(); | |
323 | } | |
324 | else | |
325 | { | |
326 | addch('\n'); | |
327 | } | |
328 | refresh(); | |
329 | ||
330 | /* parse & execute the command */ | |
331 | doexcmd(cmdbuf); | |
332 | ||
333 | /* handle autoprint */ | |
334 | if (significant || markline(cursor) != oldline) | |
335 | { | |
336 | significant = FALSE; | |
337 | oldline = markline(cursor); | |
338 | if (*o_autoprint && mode == MODE_EX) | |
339 | { | |
340 | cmd_print(cursor, cursor, CMD_PRINT, FALSE, ""); | |
341 | } | |
342 | } | |
343 | } | |
344 | } | |
345 | ||
346 | void doexcmd(cmdbuf) | |
347 | char *cmdbuf; /* string containing an ex command */ | |
348 | { | |
349 | REG char *scan; /* used to scan thru cmdbuf */ | |
350 | MARK frommark; /* first linespec */ | |
351 | MARK tomark; /* second linespec */ | |
352 | REG int cmdlen; /* length of the command name given */ | |
353 | CMD cmd; /* what command is this? */ | |
354 | ARGT argt; /* argument types for this command */ | |
355 | short forceit; /* bang version of a command? */ | |
356 | REG int cmdidx; /* index of command */ | |
357 | REG char *build; /* used while copying filenames */ | |
358 | int iswild; /* boolean: filenames use wildcards? */ | |
359 | int isdfl; /* using default line ranges? */ | |
360 | int didsub; /* did we substitute file names for % or # */ | |
361 | ||
362 | /* ex commands can't be undone via the shift-U command */ | |
363 | U_line = 0L; | |
364 | ||
365 | /* permit extra colons at the start of the line */ | |
366 | for (; *cmdbuf == ':'; cmdbuf++) | |
367 | { | |
368 | } | |
369 | ||
370 | /* ignore command lines that start with a double-quote */ | |
371 | if (*cmdbuf == '"') | |
372 | { | |
373 | return; | |
374 | } | |
375 | scan = cmdbuf; | |
376 | ||
377 | /* parse the line specifier */ | |
378 | if (nlines < 1) | |
379 | { | |
380 | /* no file, so don't allow addresses */ | |
381 | } | |
382 | else if (*scan == '%') | |
383 | { | |
384 | /* '%' means all lines */ | |
385 | frommark = MARK_FIRST; | |
386 | tomark = MARK_LAST; | |
387 | scan++; | |
388 | } | |
389 | else if (*scan == '0') | |
390 | { | |
15637ed4 | 391 | scan++; |
08746e8b | 392 | frommark = tomark = (*scan ? MARK_UNSET : MARK_FIRST); |
15637ed4 RG |
393 | } |
394 | else | |
395 | { | |
396 | frommark = cursor; | |
397 | scan = linespec(scan, &frommark); | |
398 | tomark = frommark; | |
399 | if (frommark && *scan == ',') | |
400 | { | |
401 | scan++; | |
402 | scan = linespec(scan, &tomark); | |
403 | } | |
404 | if (!tomark) | |
405 | { | |
406 | /* faulty line spec -- fault already described */ | |
407 | return; | |
408 | } | |
409 | if (frommark > tomark) | |
410 | { | |
411 | msg("first address exceeds the second"); | |
412 | return; | |
413 | } | |
414 | } | |
415 | isdfl = (scan == cmdbuf); | |
416 | ||
417 | /* skip whitespace */ | |
418 | while (isspace(*scan)) | |
419 | { | |
420 | scan++; | |
421 | } | |
422 | ||
08746e8b AM |
423 | /* Figure out how long the command name is. If no command, then the |
424 | * length is 0, which will match the "print" command. | |
425 | */ | |
15637ed4 RG |
426 | if (!*scan) |
427 | { | |
08746e8b AM |
428 | /* if not in ex mode, and both endpoints are at the line, |
429 | * then just move to the start of that line without printing | |
430 | */ | |
431 | if (mode != MODE_EX && frommark == tomark) | |
432 | { | |
433 | if (tomark != MARK_UNSET) | |
434 | cursor = tomark; | |
435 | return; | |
436 | } | |
437 | cmdlen = 0; | |
15637ed4 | 438 | } |
08746e8b | 439 | else if (!isalpha(*scan)) |
15637ed4 RG |
440 | { |
441 | cmdlen = 1; | |
442 | } | |
443 | else | |
444 | { | |
445 | for (cmdlen = 1; | |
446 | isalpha(scan[cmdlen]); | |
447 | cmdlen++) | |
448 | { | |
449 | } | |
450 | } | |
451 | ||
452 | /* lookup the command code */ | |
453 | for (cmdidx = 0; | |
454 | cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen); | |
455 | cmdidx++) | |
456 | { | |
457 | } | |
458 | argt = cmdnames[cmdidx].argt; | |
459 | cmd = cmdnames[cmdidx].code; | |
460 | if (cmd == CMD_NULL) | |
461 | { | |
462 | msg("Unknown command \"%.*s\"", cmdlen, scan); | |
463 | return; | |
464 | } | |
465 | ||
466 | /* !!! if the command doesn't have NOBAR set, then replace | with \0 */ | |
467 | ||
468 | /* if the command ended with a bang, set the forceit flag */ | |
469 | scan += cmdlen; | |
470 | if ((argt & BANG) && *scan == '!') | |
471 | { | |
472 | scan++; | |
473 | forceit = 1; | |
474 | } | |
475 | else | |
476 | { | |
477 | forceit = 0; | |
478 | } | |
479 | ||
480 | /* skip any more whitespace, to leave scan pointing to arguments */ | |
481 | while (isspace(*scan)) | |
482 | { | |
483 | scan++; | |
484 | } | |
485 | ||
486 | /* a couple of special cases for filenames */ | |
487 | if (argt & XFILE) | |
488 | { | |
489 | /* if names were given, process them */ | |
490 | if (*scan) | |
491 | { | |
492 | for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++) | |
493 | { | |
494 | switch (*scan) | |
495 | { | |
496 | case '\\': | |
497 | if (scan[1] == '\\' || scan[1] == '%' || scan[1] == '#') | |
498 | { | |
499 | *build++ = *++scan; | |
500 | } | |
501 | else | |
502 | { | |
503 | *build++ = '\\'; | |
504 | } | |
505 | break; | |
506 | ||
507 | case '%': | |
508 | if (!*origname) | |
509 | { | |
510 | msg("No filename to substitute for %%"); | |
511 | return; | |
512 | } | |
513 | strcpy(build, origname); | |
514 | while (*build) | |
515 | { | |
516 | build++; | |
517 | } | |
518 | didsub = TRUE; | |
519 | break; | |
520 | ||
521 | case '#': | |
522 | if (!*prevorig) | |
523 | { | |
524 | msg("No filename to substitute for #"); | |
525 | return; | |
526 | } | |
527 | strcpy(build, prevorig); | |
528 | while (*build) | |
529 | { | |
530 | build++; | |
531 | } | |
532 | didsub = TRUE; | |
533 | break; | |
534 | ||
535 | case '*': | |
536 | case '?': | |
537 | #if !(MSDOS || TOS) | |
538 | case '[': | |
539 | case '`': | |
540 | case '{': /* } */ | |
541 | case '$': | |
542 | case '~': | |
543 | #endif | |
544 | *build++ = *scan; | |
545 | iswild = TRUE; | |
546 | break; | |
547 | ||
548 | default: | |
549 | *build++ = *scan; | |
550 | } | |
551 | } | |
552 | *build = '\0'; | |
553 | ||
554 | if (cmd == CMD_BANG | |
555 | || cmd == CMD_READ && tmpblk.c[0] == '!' | |
556 | || cmd == CMD_WRITE && tmpblk.c[0] == '!') | |
557 | { | |
558 | if (didsub) | |
559 | { | |
560 | if (mode != MODE_EX) | |
561 | { | |
562 | addch('\n'); | |
563 | } | |
564 | addstr(tmpblk.c); | |
565 | addch('\n'); | |
566 | exrefresh(); | |
567 | } | |
568 | } | |
569 | else | |
570 | { | |
571 | if (iswild && tmpblk.c[0] != '>') | |
572 | { | |
573 | scan = wildcard(tmpblk.c); | |
574 | } | |
575 | } | |
576 | } | |
577 | else /* no names given, maybe assume origname */ | |
578 | { | |
579 | if (!(argt & NODFL)) | |
580 | { | |
581 | strcpy(tmpblk.c, origname); | |
582 | } | |
583 | else | |
584 | { | |
585 | *tmpblk.c = '\0'; | |
586 | } | |
587 | } | |
588 | ||
589 | scan = tmpblk.c; | |
590 | } | |
591 | ||
592 | /* bad arguments? */ | |
593 | if (!(argt & EXRCOK) && nlines < 1L) | |
594 | { | |
595 | msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC); | |
596 | return; | |
597 | } | |
598 | if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET) | |
599 | { | |
600 | msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name); | |
601 | return; | |
602 | } | |
603 | if (!(argt & FROM) && frommark != cursor && nlines >= 1L) | |
604 | { | |
605 | msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name); | |
606 | return; | |
607 | } | |
608 | if (!(argt & TO) && tomark != frommark && nlines >= 1L) | |
609 | { | |
610 | msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name); | |
611 | return; | |
612 | } | |
613 | if (!(argt & EXTRA) && *scan) | |
614 | { | |
615 | msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name); | |
616 | return; | |
617 | } | |
618 | if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!'))) | |
619 | { | |
620 | build = scan; | |
621 | #ifndef CRUNCH | |
622 | if ((argt & PLUS) && *build == '+') | |
623 | { | |
624 | while (*build && !isspace(*build)) | |
625 | { | |
626 | build++; | |
627 | } | |
628 | while (*build && isspace(*build)) | |
629 | { | |
630 | build++; | |
631 | } | |
632 | } | |
633 | #endif /* not CRUNCH */ | |
634 | for (; *build; build++) | |
635 | { | |
636 | if (isspace(*build)) | |
637 | { | |
638 | msg("Too many %s to \"%s\" command.", | |
639 | (argt & XFILE) ? "filenames" : "arguments", | |
640 | cmdnames[cmdidx].name); | |
641 | return; | |
642 | } | |
643 | } | |
644 | } | |
645 | ||
646 | /* some commands have special default ranges */ | |
647 | if (isdfl && (argt & DFLALL)) | |
648 | { | |
649 | frommark = MARK_FIRST; | |
650 | tomark = MARK_LAST; | |
651 | } | |
652 | else if (isdfl && (argt & DFLNONE)) | |
653 | { | |
654 | frommark = tomark = 0L; | |
655 | } | |
656 | ||
657 | /* write a newline if called from visual mode */ | |
658 | if ((argt & NL) && mode != MODE_EX && !exwrote) | |
659 | { | |
660 | addch('\n'); | |
661 | exrefresh(); | |
662 | } | |
663 | ||
664 | /* act on the command */ | |
665 | (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan); | |
666 | } | |
667 | ||
668 | ||
669 | /* This function executes EX commands from a file. It returns 1 normally, or | |
670 | * 0 if the file could not be opened for reading. | |
671 | */ | |
672 | int doexrc(filename) | |
673 | char *filename; /* name of a ".exrc" file */ | |
674 | { | |
675 | int fd; /* file descriptor */ | |
676 | int len; /* length of the ".exrc" file */ | |
677 | ||
08746e8b AM |
678 | #ifdef CRUNCH |
679 | /* small address space - we need to conserve space */ | |
680 | ||
15637ed4 RG |
681 | /* !!! kludge: we use U_text as the buffer. This has the side-effect |
682 | * of interfering with the shift-U visual command. Disable shift-U. | |
683 | */ | |
684 | U_line = 0L; | |
08746e8b AM |
685 | #else |
686 | # if TINYSTACK | |
687 | # if TOS || MINT | |
688 | /* small stack, but big heap. Allocate buffer from heap */ | |
689 | char *U_text = (char *)malloc(4096); | |
690 | if (!U_text) | |
691 | { | |
692 | return 0; | |
693 | } | |
694 | # else | |
695 | /* small stack - we need to conserve space */ | |
696 | ||
697 | /* !!! kludge: we use U_text as the buffer. This has the side-effect | |
698 | * of interfering with the shift-U visual command. Disable shift-U. | |
699 | */ | |
700 | U_line = 0L; | |
701 | # endif | |
702 | # else | |
703 | /* This is how we would *like* to do it -- with a large buffer on the | |
704 | * stack, so we can handle large .exrc files and also recursion. | |
705 | */ | |
706 | char U_text[4096]; | |
707 | # endif | |
708 | #endif | |
15637ed4 RG |
709 | |
710 | /* open the file, read it, and close */ | |
711 | fd = open(filename, O_RDONLY); | |
712 | if (fd < 0) | |
713 | { | |
08746e8b AM |
714 | #if TINYSTACK && (TOS || MINT) |
715 | free(U_text); | |
716 | #endif | |
15637ed4 RG |
717 | return 0; |
718 | } | |
08746e8b AM |
719 | #if TINYSTACK && (TOS || MINT) |
720 | len = tread(fd, U_text, 4096); | |
721 | #else | |
722 | len = tread(fd, U_text, sizeof U_text); | |
723 | #endif | |
15637ed4 RG |
724 | close(fd); |
725 | ||
726 | /* execute the string */ | |
727 | exstring(U_text, len, ctrl('V')); | |
728 | ||
08746e8b AM |
729 | #if TINYSTACK && (TOS || MINT) |
730 | free(U_text); | |
731 | #endif | |
15637ed4 RG |
732 | return 1; |
733 | } | |
734 | ||
735 | /* This function executes EX commands from a string. The commands may be | |
736 | * separated by newlines or by | characters. It also handles quoting. | |
737 | * Each individual command is limited to 132 bytes, but the total string | |
738 | * may be longer. | |
739 | */ | |
740 | void exstring(buf, len, qchar) | |
741 | char *buf; /* the commands to execute */ | |
742 | int len; /* the length of the string */ | |
743 | int qchar; /* the quote character -- ^V for file, or \ for kbd */ | |
744 | { | |
745 | char single[133]; /* a single command */ | |
746 | char *src, *dest; | |
747 | int i; | |
748 | ||
749 | /* find & do each command */ | |
750 | for (src = buf; src < &buf[len]; src++) | |
751 | { | |
752 | /* Copy a single command into single[]. Convert any quoted | | |
753 | * into a normal |, and stop at a newline or unquoted |. | |
754 | */ | |
755 | for (dest = single, i = 0; | |
756 | i < 132 && src < &buf[len] && *src != '\n' && *src != '|'; | |
757 | src++, i++) | |
758 | { | |
759 | if (src[0] == qchar && src[1] == '|') | |
760 | { | |
761 | src++; | |
762 | } | |
763 | *dest++ = *src; | |
764 | } | |
765 | *dest = '\0'; | |
766 | ||
767 | /* do it */ | |
768 | doexcmd(single); | |
769 | } | |
770 | } |