Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* cmd1.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 some of the EX commands - mostly ones that deal with | |
12 | * files, options, etc. -- anything except text. | |
13 | */ | |
14 | ||
15 | #include "config.h" | |
16 | #include "ctype.h" | |
17 | #include "vi.h" | |
18 | #include "regexp.h" | |
19 | ||
20 | #ifdef DEBUG | |
21 | /* print the selected lines with info on the blocks */ | |
22 | /*ARGSUSED*/ | |
23 | void cmd_debug(frommark, tomark, cmd, bang, extra) | |
24 | MARK frommark; | |
25 | MARK tomark; | |
26 | CMD cmd; | |
27 | int bang; | |
28 | char *extra; | |
29 | { | |
30 | REG char *scan; | |
31 | REG long l; | |
32 | REG int i; | |
33 | int len; | |
34 | ||
35 | /* scan lnum[] to determine which block its in */ | |
36 | l = markline(frommark); | |
37 | for (i = 1; l > lnum[i]; i++) | |
38 | { | |
39 | } | |
40 | ||
41 | do | |
42 | { | |
43 | /* fetch text of the block containing that line */ | |
44 | scan = blkget(i)->c; | |
45 | ||
46 | /* calculate its length */ | |
47 | if (scan[BLKSIZE - 1]) | |
48 | { | |
49 | len = BLKSIZE; | |
50 | } | |
51 | else | |
52 | { | |
53 | len = strlen(scan); | |
54 | } | |
55 | ||
56 | /* print block stats */ | |
57 | msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)", | |
58 | i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]); | |
59 | msg("##### len=%d, buf=0x%lx, %sdirty", | |
60 | len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not "); | |
61 | if (bang) | |
62 | { | |
63 | while (--len >= 0) | |
64 | { | |
65 | addch(*scan); | |
66 | scan++; | |
67 | } | |
68 | } | |
69 | exrefresh(); | |
70 | ||
71 | /* next block */ | |
72 | i++; | |
73 | } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark)); | |
74 | } | |
75 | ||
76 | ||
77 | /* This function checks a lot of conditions to make sure they aren't screwy */ | |
78 | /*ARGSUSED*/ | |
79 | void cmd_validate(frommark, tomark, cmd, bang, extra) | |
80 | MARK frommark; | |
81 | MARK tomark; | |
82 | CMD cmd; | |
83 | int bang; | |
84 | char *extra; | |
85 | { | |
86 | char *scan; | |
87 | int i; | |
88 | int nlcnt; /* used to count newlines */ | |
89 | int len; /* counts non-NUL characters */ | |
90 | ||
91 | /* check lnum[0] */ | |
92 | if (lnum[0] != 0L) | |
93 | { | |
94 | msg("lnum[0] = %ld", lnum[0]); | |
95 | } | |
96 | ||
97 | /* check each block */ | |
98 | for (i = 1; lnum[i] <= nlines; i++) | |
99 | { | |
100 | scan = blkget(i)->c; | |
101 | if (scan[BLKSIZE - 1]) | |
102 | { | |
103 | msg("block %d has no NUL at the end", i); | |
104 | } | |
105 | else | |
106 | { | |
107 | for (nlcnt = len = 0; *scan; scan++, len++) | |
108 | { | |
109 | if (*scan == '\n') | |
110 | { | |
111 | nlcnt++; | |
112 | } | |
113 | } | |
114 | if (scan[-1] != '\n') | |
115 | { | |
116 | msg("block %d doesn't end with '\\n' (length %d)", i, len); | |
117 | } | |
118 | if (bang || nlcnt != lnum[i] - lnum[i - 1]) | |
119 | { | |
120 | msg("block %d (line %ld?) has %d lines, but should have %ld", | |
121 | i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]); | |
122 | } | |
123 | } | |
124 | exrefresh(); | |
125 | } | |
126 | ||
127 | /* check lnum again */ | |
128 | if (lnum[i] != INFINITY) | |
129 | { | |
130 | msg("hdr.n[%d] = %d, but lnum[%d] = %ld", | |
131 | i, hdr.n[i], i, lnum[i]); | |
132 | } | |
133 | ||
134 | msg("# = \"%s\", %% = \"%s\"", prevorig, origname); | |
135 | msg("V_from=%ld.%d, cursor=%ld.%d", markline(V_from), markidx(V_from), markline(cursor), markidx(cursor)); | |
136 | } | |
137 | #endif /* DEBUG */ | |
138 | ||
139 | ||
140 | /*ARGSUSED*/ | |
141 | void cmd_mark(frommark, tomark, cmd, bang, extra) | |
142 | MARK frommark; | |
143 | MARK tomark; | |
144 | CMD cmd; | |
145 | int bang; | |
146 | char *extra; | |
147 | { | |
148 | /* validate the name of the mark */ | |
149 | if (*extra == '"') | |
150 | { | |
151 | extra++; | |
152 | } | |
153 | /* valid mark names are lowercase ascii characters */ | |
154 | if (!isascii(*extra) || !islower(*extra) || extra[1]) | |
155 | { | |
156 | msg("Invalid mark name"); | |
157 | return; | |
158 | } | |
159 | ||
160 | mark[*extra - 'a'] = tomark; | |
161 | } | |
162 | ||
163 | /*ARGSUSED*/ | |
164 | void cmd_write(frommark, tomark, cmd, bang, extra) | |
165 | MARK frommark; | |
166 | MARK tomark; | |
167 | CMD cmd; | |
168 | int bang; | |
169 | char *extra; | |
170 | { | |
171 | int fd; | |
172 | int append; /* boolean: write in "append" mode? */ | |
173 | REG long l; | |
174 | REG char *scan; | |
175 | REG int i; | |
176 | ||
177 | /* if writing to a filter, then let filter() handle it */ | |
178 | if (*extra == '!') | |
179 | { | |
180 | filter(frommark, tomark, extra + 1, FALSE); | |
181 | return; | |
182 | } | |
183 | ||
184 | /* if all lines are to be written, use tmpsave() */ | |
185 | if (frommark == MARK_FIRST && tomark == MARK_LAST && cmd == CMD_WRITE) | |
186 | { | |
187 | tmpsave(extra, bang); | |
188 | return; | |
189 | } | |
190 | ||
191 | /* see if we're going to do this in append mode or not */ | |
192 | append = FALSE; | |
193 | if (extra[0] == '>' && extra[1] == '>') | |
194 | { | |
195 | extra += 2; | |
196 | append = TRUE; | |
197 | } | |
198 | ||
199 | /* either the file must not exist, or we must have a ! or be appending */ | |
200 | if (access(extra, 0) == 0 && !bang && !append) | |
201 | { | |
202 | msg("File already exists - Use :w! to overwrite"); | |
203 | return; | |
204 | } | |
205 | ||
206 | /* else do it line-by-line, like cmd_print() */ | |
207 | if (append) | |
208 | { | |
209 | #ifdef O_APPEND | |
210 | fd = open(extra, O_WRONLY|O_APPEND); | |
211 | #else | |
212 | fd = open(extra, O_WRONLY); | |
213 | if (fd >= 0) | |
214 | { | |
215 | lseek(fd, 0L, 2); | |
216 | } | |
217 | #endif | |
218 | } | |
219 | else | |
220 | { | |
221 | fd = -1; /* so we know the file isn't open yet */ | |
222 | } | |
223 | ||
224 | if (fd < 0) | |
225 | { | |
226 | fd = creat(extra, FILEPERMS); | |
227 | if (fd < 0) | |
228 | { | |
229 | msg("Can't write to \"%s\"", extra); | |
230 | return; | |
231 | } | |
232 | } | |
233 | for (l = markline(frommark); l <= markline(tomark); l++) | |
234 | { | |
235 | /* get the next line */ | |
236 | scan = fetchline(l); | |
237 | i = strlen(scan); | |
238 | scan[i++] = '\n'; | |
239 | ||
240 | /* print the line */ | |
241 | if (twrite(fd, scan, i) < i) | |
242 | { | |
243 | msg("Write failed"); | |
244 | break; | |
245 | } | |
246 | } | |
247 | rptlines = markline(tomark) - markline(frommark) + 1; | |
248 | rptlabel = "written"; | |
249 | close(fd); | |
250 | } | |
251 | ||
252 | ||
253 | /*ARGSUSED*/ | |
254 | void cmd_shell(frommark, tomark, cmd, bang, extra) | |
255 | MARK frommark, tomark; | |
256 | CMD cmd; | |
257 | int bang; | |
258 | char *extra; | |
259 | { | |
260 | static char prevextra[80]; | |
261 | ||
262 | /* special case: ":sh" means ":!sh" */ | |
263 | if (cmd == CMD_SHELL) | |
264 | { | |
265 | extra = o_shell; | |
266 | frommark = tomark = 0L; | |
267 | } | |
268 | ||
269 | /* if extra is "!", substitute previous command */ | |
270 | if (*extra == '!') | |
271 | { | |
272 | if (!*prevextra) | |
273 | { | |
274 | msg("No previous shell command to substitute for '!'"); | |
275 | return; | |
276 | } | |
277 | extra = prevextra; | |
278 | } | |
279 | else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1) | |
280 | { | |
281 | strcpy(prevextra, extra); | |
282 | } | |
283 | ||
284 | /* warn the user if the file hasn't been saved yet */ | |
285 | if (*o_warn && tstflag(file, MODIFIED)) | |
286 | { | |
287 | if (mode == MODE_VI) | |
288 | { | |
289 | mode = MODE_COLON; | |
290 | } | |
291 | msg("Warning: \"%s\" has been modified but not yet saved", origname); | |
292 | } | |
293 | ||
294 | /* if no lines were specified, just run the command */ | |
295 | suspend_curses(); | |
296 | if (frommark == 0L) | |
297 | { | |
298 | system(extra); | |
299 | } | |
300 | else /* pipe lines from the file through the command */ | |
301 | { | |
302 | filter(frommark, tomark, extra, TRUE); | |
303 | } | |
304 | ||
305 | /* resume curses quietly for MODE_EX, but noisily otherwise */ | |
306 | resume_curses(mode == MODE_EX); | |
307 | } | |
308 | ||
309 | ||
310 | /*ARGSUSED*/ | |
311 | void cmd_global(frommark, tomark, cmd, bang, extra) | |
312 | MARK frommark, tomark; | |
313 | CMD cmd; | |
314 | int bang; | |
315 | char *extra; /* rest of the command line */ | |
316 | { | |
317 | char *cmdptr; /* the command from the command line */ | |
318 | char cmdln[100]; /* copy of the command from the command line */ | |
319 | char *line; /* a line from the file */ | |
320 | long l; /* used as a counter to move through lines */ | |
321 | long lqty; /* quantity of lines to be scanned */ | |
322 | long nchanged; /* number of lines changed */ | |
323 | regexp *re; /* the compiled search expression */ | |
324 | ||
325 | /* can't nest global commands */ | |
326 | if (doingglobal) | |
327 | { | |
328 | msg("Can't nest global commands."); | |
329 | rptlines = -1L; | |
330 | return; | |
331 | } | |
332 | ||
333 | /* ":g! ..." is the same as ":v ..." */ | |
334 | if (bang) | |
335 | { | |
336 | cmd = CMD_VGLOBAL; | |
337 | } | |
338 | ||
339 | /* make sure we got a search pattern */ | |
340 | if (*extra != '/' && *extra != '?') | |
341 | { | |
342 | msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v'); | |
343 | return; | |
344 | } | |
345 | ||
346 | /* parse & compile the search pattern */ | |
347 | cmdptr = parseptrn(extra); | |
348 | if (!extra[1]) | |
349 | { | |
350 | msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v'); | |
351 | return; | |
352 | } | |
353 | re = regcomp(extra + 1); | |
354 | if (!re) | |
355 | { | |
356 | /* regcomp found & described an error */ | |
357 | return; | |
358 | } | |
359 | ||
360 | /* for each line in the range */ | |
361 | doingglobal = TRUE; | |
362 | ChangeText | |
363 | { | |
364 | /* NOTE: we have to go through the lines in a forward order, | |
365 | * otherwise "g/re/p" would look funny. *BUT* for "g/re/d" | |
366 | * to work, simply adding 1 to the line# on each loop won't | |
367 | * work. The solution: count lines relative to the end of | |
368 | * the file. Think about it. | |
369 | */ | |
370 | for (l = nlines - markline(frommark), | |
371 | lqty = markline(tomark) - markline(frommark) + 1L, | |
372 | nchanged = 0L; | |
373 | lqty > 0 && nlines - l >= 0 && nchanged >= 0L; | |
374 | l--, lqty--) | |
375 | { | |
376 | /* fetch the line */ | |
377 | line = fetchline(nlines - l); | |
378 | ||
379 | /* if it contains the search pattern... */ | |
380 | if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL)) | |
381 | { | |
382 | /* move the cursor to that line */ | |
383 | cursor = MARK_AT_LINE(nlines - l); | |
384 | ||
385 | /* do the ex command (without mucking up | |
386 | * the original copy of the command line) | |
387 | */ | |
388 | strcpy(cmdln, cmdptr); | |
389 | rptlines = 0L; | |
390 | doexcmd(cmdln); | |
391 | nchanged += rptlines; | |
392 | } | |
393 | } | |
394 | } | |
395 | doingglobal = FALSE; | |
396 | ||
397 | /* free the regexp */ | |
398 | free(re); | |
399 | ||
400 | /* Reporting...*/ | |
401 | rptlines = nchanged; | |
402 | } | |
403 | ||
404 | ||
405 | /*ARGSUSED*/ | |
406 | void cmd_file(frommark, tomark, cmd, bang, extra) | |
407 | MARK frommark, tomark; | |
408 | CMD cmd; | |
409 | int bang; | |
410 | char *extra; | |
411 | { | |
412 | #ifndef CRUNCH | |
413 | /* if we're given a new filename, use it as this file's name */ | |
414 | if (extra && *extra) | |
415 | { | |
416 | strcpy(origname, extra); | |
417 | storename(origname); | |
418 | setflag(file, NOTEDITED); | |
419 | } | |
420 | #endif | |
421 | if (cmd == CMD_FILE) | |
422 | { | |
423 | #ifndef CRUNCH | |
424 | msg("\"%s\" %s%s%s %ld lines, line %ld [%ld%%]", | |
425 | #else | |
426 | msg("\"%s\" %s%s %ld lines, line %ld [%ld%%]", | |
427 | #endif | |
428 | *origname ? origname : "[NO FILE]", | |
429 | tstflag(file, MODIFIED) ? "[MODIFIED]" : "", | |
430 | #ifndef CRUNCH | |
431 | tstflag(file, NOTEDITED) ?"[NOT EDITED]":"", | |
432 | #endif | |
433 | tstflag(file, READONLY) ? "[READONLY]" : "", | |
434 | nlines, | |
435 | markline(frommark), | |
436 | markline(frommark) * 100 / nlines); | |
437 | } | |
438 | #ifndef CRUNCH | |
439 | else if (markline(frommark) != markline(tomark)) | |
440 | { | |
441 | msg("range \"%ld,%ld\" contains %ld lines", | |
442 | markline(frommark), | |
443 | markline(tomark), | |
444 | markline(tomark) - markline(frommark) + 1L); | |
445 | } | |
446 | #endif | |
447 | else | |
448 | { | |
449 | msg("%ld", markline(frommark)); | |
450 | } | |
451 | } | |
452 | ||
453 | ||
454 | /*ARGSUSED*/ | |
455 | void cmd_edit(frommark, tomark, cmd, bang, extra) | |
456 | MARK frommark, tomark; | |
457 | CMD cmd; | |
458 | int bang; | |
459 | char *extra; | |
460 | { | |
461 | long line = 1L; /* might be set to prevline */ | |
462 | #ifndef CRUNCH | |
463 | char *init = (char *)0; | |
464 | #endif | |
465 | ||
466 | ||
467 | /* if ":vi", then switch to visual mode, and if no file is named | |
468 | * then don't switch files. | |
469 | */ | |
470 | if (cmd == CMD_VISUAL) | |
471 | { | |
472 | mode = MODE_VI; | |
473 | msg(""); | |
474 | if (!*extra) | |
475 | { | |
476 | return; | |
477 | } | |
478 | } | |
479 | ||
480 | /* Editing previous file? Then start at previous line */ | |
481 | if (!strcmp(extra, prevorig)) | |
482 | { | |
483 | line = prevline; | |
484 | } | |
485 | ||
486 | #ifndef CRUNCH | |
487 | /* if we were given an explicit starting line, then start there */ | |
488 | if (*extra == '+') | |
489 | { | |
490 | for (init = ++extra; !isspace(*extra); extra++) | |
491 | { | |
492 | } | |
493 | while (isspace(*extra)) | |
494 | { | |
495 | *extra++ = '\0'; | |
496 | } | |
497 | if (!*init) | |
498 | { | |
499 | init = "$"; | |
500 | } | |
501 | if (!extra) | |
502 | { | |
503 | extra = origname; | |
504 | } | |
505 | } | |
506 | #endif /* not CRUNCH */ | |
507 | ||
508 | /* switch files */ | |
509 | if (tmpabort(bang)) | |
510 | { | |
511 | tmpstart(extra); | |
512 | if (line <= nlines && line >= 1L) | |
513 | { | |
514 | cursor = MARK_AT_LINE(line); | |
515 | } | |
516 | #ifndef CRUNCH | |
517 | if (init) | |
518 | { | |
519 | doexcmd(init); | |
520 | } | |
521 | #endif | |
522 | } | |
523 | else | |
524 | { | |
525 | msg("Use edit! to abort changes, or w to save changes"); | |
526 | ||
527 | /* so we can say ":e!#" next time... */ | |
528 | strcpy(prevorig, extra); | |
529 | prevline = 1L; | |
530 | } | |
531 | } | |
532 | ||
533 | /* This code is also used for rewind -- GB */ | |
534 | ||
535 | /*ARGSUSED*/ | |
536 | void cmd_next(frommark, tomark, cmd, bang, extra) | |
537 | MARK frommark, tomark; | |
538 | CMD cmd; | |
539 | int bang; | |
540 | char *extra; | |
541 | { | |
542 | int i, j; | |
543 | char *scan; | |
544 | ||
545 | /* if extra stuff given, use ":args" to define a new args list */ | |
546 | if (cmd == CMD_NEXT && extra && *extra) | |
547 | { | |
548 | cmd_args(frommark, tomark, cmd, bang, extra); | |
549 | } | |
550 | ||
551 | /* move to the next arg */ | |
552 | if (cmd == CMD_NEXT) | |
553 | { | |
554 | i = argno + 1; | |
555 | } | |
556 | else if (cmd == CMD_PREVIOUS) | |
557 | { | |
558 | i = argno - 1; | |
559 | } | |
560 | else /* cmd == CMD_REWIND */ | |
561 | { | |
562 | i = 0; | |
563 | } | |
564 | if (i < 0 || i >= nargs) | |
565 | { | |
566 | msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more "); | |
567 | return; | |
568 | } | |
569 | ||
570 | /* find & isolate the name of the file to edit */ | |
571 | for (j = i, scan = args; j > 0; j--) | |
572 | { | |
573 | while(*scan++) | |
574 | { | |
575 | } | |
576 | } | |
577 | ||
578 | /* switch to the next file */ | |
579 | if (tmpabort(bang)) | |
580 | { | |
581 | tmpstart(scan); | |
582 | argno = i; | |
583 | } | |
584 | else | |
585 | { | |
586 | msg("Use :%s! to abort changes, or w to save changes", | |
587 | cmd == CMD_NEXT ? "next" : | |
588 | cmd == CMD_PREVIOUS ? "previous" : | |
589 | "rewind"); | |
590 | } | |
591 | } | |
592 | ||
593 | /* also called from :wq -- always writes back in this case */ | |
594 | ||
595 | /*ARGSUSED*/ | |
596 | void cmd_xit(frommark, tomark, cmd, bang, extra) | |
597 | MARK frommark, tomark; | |
598 | CMD cmd; | |
599 | int bang; | |
600 | char *extra; | |
601 | { | |
602 | static long whenwarned; /* when the user was last warned of extra files */ | |
603 | int oldflag; | |
604 | ||
605 | /* if there are more files to edit, then warn user */ | |
606 | if (argno >= 0 && argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT)) | |
607 | { | |
608 | msg("More files to edit -- Use \":n\" to go to next file"); | |
609 | whenwarned = changes; | |
610 | return; | |
611 | } | |
612 | ||
613 | if (cmd == CMD_QUIT) | |
614 | { | |
615 | oldflag = *o_autowrite; | |
616 | *o_autowrite = FALSE; | |
617 | if (tmpabort(bang)) | |
618 | { | |
619 | mode = MODE_QUIT; | |
620 | } | |
621 | else | |
622 | { | |
623 | msg("Use q! to abort changes, or wq to save changes"); | |
624 | } | |
625 | *o_autowrite = oldflag; | |
626 | } | |
627 | else | |
628 | { | |
629 | /* else try to save this file */ | |
630 | oldflag = tstflag(file, MODIFIED); | |
631 | if (cmd == CMD_WQUIT) | |
632 | setflag(file, MODIFIED); | |
633 | if (tmpend(bang)) | |
634 | { | |
635 | mode = MODE_QUIT; | |
636 | } | |
637 | else | |
638 | { | |
639 | msg("Could not save file -- use quit! to abort changes, or w filename"); | |
640 | } | |
641 | if (!oldflag) | |
642 | clrflag(file, MODIFIED); | |
643 | } | |
644 | } | |
645 | ||
646 | ||
647 | /*ARGSUSED*/ | |
648 | void cmd_args(frommark, tomark, cmd, bang, extra) | |
649 | MARK frommark, tomark; | |
650 | CMD cmd; | |
651 | int bang; | |
652 | char *extra; | |
653 | { | |
654 | char *scan; | |
655 | int col; | |
656 | int arg; | |
657 | int scrolled = FALSE; | |
658 | int width; | |
659 | ||
660 | /* if no extra names given, or just current name, then report the args | |
661 | * we have now. | |
662 | */ | |
663 | if (!extra || !*extra) | |
664 | { | |
665 | /* empty args list? */ | |
666 | if (nargs == 1 && !*args) | |
667 | { | |
668 | return; | |
669 | } | |
670 | ||
671 | /* list the arguments */ | |
672 | for (scan = args, col = arg = 0; | |
673 | arg < nargs; | |
674 | scan += width + 1, col += width, arg++) | |
675 | { | |
676 | width = strlen(scan); | |
677 | if (col + width >= COLS - 4) | |
678 | { | |
679 | addch('\n'); | |
680 | col = 0; | |
681 | scrolled = TRUE; | |
682 | } | |
683 | else if (col > 0) | |
684 | { | |
685 | addch(' '); | |
686 | col++; | |
687 | } | |
688 | if (arg == argno) | |
689 | { | |
690 | addch('['); | |
691 | addstr(scan); | |
692 | addch(']'); | |
693 | col += 2; | |
694 | } | |
695 | else | |
696 | { | |
697 | addstr(scan); | |
698 | } | |
699 | } | |
700 | ||
701 | /* write a trailing newline */ | |
702 | if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col) | |
703 | { | |
704 | addch('\n'); | |
705 | } | |
706 | exrefresh(); | |
707 | } | |
708 | else /* new args list given */ | |
709 | { | |
710 | for (scan = args, nargs = 1; *extra; ) | |
711 | { | |
712 | if (isspace(*extra)) | |
713 | { | |
714 | *scan++ = '\0'; | |
715 | while (isspace(*extra)) | |
716 | { | |
717 | extra++; | |
718 | } | |
719 | if (*extra) | |
720 | { | |
721 | nargs++; | |
722 | } | |
723 | } | |
724 | else | |
725 | { | |
726 | *scan++ = *extra++; | |
727 | } | |
728 | } | |
729 | *scan = '\0'; | |
730 | ||
731 | /* reset argno to before the first, so :next will go to first */ | |
732 | argno = -1; | |
733 | ||
734 | if (nargs != 1) | |
735 | { | |
736 | msg("%d files to edit", nargs); | |
737 | } | |
738 | } | |
739 | } | |
740 | ||
741 | ||
742 | /*ARGSUSED*/ | |
743 | void cmd_cd(frommark, tomark, cmd, bang, extra) | |
744 | MARK frommark, tomark; | |
745 | CMD cmd; | |
746 | int bang; | |
747 | char *extra; | |
748 | { | |
749 | char *getenv(); | |
750 | ||
751 | #ifndef CRUNCH | |
752 | /* if current file is modified, and no '!' was given, then error */ | |
753 | if (tstflag(file, MODIFIED) && !bang) | |
754 | { | |
755 | msg("File modified; use \"cd! %s\" to switch anyway", extra); | |
756 | } | |
757 | #endif | |
758 | ||
759 | /* default directory name is $HOME */ | |
760 | if (!*extra) | |
761 | { | |
762 | extra = getenv("HOME"); | |
763 | if (!extra) | |
764 | { | |
765 | msg("environment variable $HOME not set"); | |
766 | return; | |
767 | } | |
768 | } | |
769 | ||
770 | /* go to the directory */ | |
771 | if (chdir(extra) < 0) | |
772 | { | |
773 | perror(extra); | |
774 | } | |
775 | } | |
776 | ||
777 | ||
778 | /*ARGSUSED*/ | |
779 | void cmd_map(frommark, tomark, cmd, bang, extra) | |
780 | MARK frommark, tomark; | |
781 | CMD cmd; | |
782 | int bang; | |
783 | char *extra; | |
784 | { | |
785 | char *mapto; | |
786 | char *build, *scan; | |
787 | #ifndef NO_FKEY | |
788 | static char *fnames[NFKEYS] = | |
789 | { | |
790 | "#10", "#1", "#2", "#3", "#4", | |
791 | "#5", "#6", "#7", "#8", "#9", | |
792 | # ifndef NO_SHIFT_FKEY | |
793 | "#10s", "#1s", "#2s", "#3s", "#4s", | |
794 | "#5s", "#6s", "#7s", "#8s", "#9s", | |
795 | # ifndef NO_CTRL_FKEY | |
796 | "#10c", "#1c", "#2c", "#3c", "#4c", | |
797 | "#5c", "#6c", "#7c", "#8c", "#9c", | |
798 | # ifndef NO_ALT_FKEY | |
799 | "#10a", "#1a", "#2a", "#3a", "#4a", | |
800 | "#5a", "#6a", "#7a", "#8a", "#9a", | |
801 | # endif | |
802 | # endif | |
803 | # endif | |
804 | }; | |
805 | int key; | |
806 | #endif | |
807 | ||
808 | /* "map" with no extra will dump the map table contents */ | |
809 | if (!*extra) | |
810 | { | |
811 | #ifndef NO_ABBR | |
812 | if (cmd == CMD_ABBR) | |
813 | { | |
814 | dumpkey(bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, TRUE); | |
815 | } | |
816 | else | |
817 | #endif | |
818 | { | |
819 | dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, FALSE); | |
820 | } | |
821 | } | |
822 | else | |
823 | { | |
824 | /* "extra" is key to map, followed by what it maps to */ | |
825 | ||
826 | /* handle quoting inside the "raw" string */ | |
827 | for (build = mapto = extra; | |
828 | *mapto && (*mapto != ' ' && *mapto != '\t'); | |
829 | *build++ = *mapto++) | |
830 | { | |
831 | if (*mapto == ctrl('V') && mapto[1]) | |
832 | { | |
833 | mapto++; | |
834 | } | |
835 | } | |
836 | ||
837 | /* skip whitespace, and mark the end of the "raw" string */ | |
838 | while ((*mapto == ' ' || *mapto == '\t')) | |
839 | { | |
840 | *mapto++ = '\0'; | |
841 | } | |
842 | *build = '\0'; | |
843 | ||
844 | /* strip ^Vs from the "cooked" string */ | |
845 | for (scan = build = mapto; *scan; *build++ = *scan++) | |
846 | { | |
847 | if (*scan == ctrl('V') && scan[1]) | |
848 | { | |
849 | scan++; | |
850 | } | |
851 | } | |
852 | *build = '\0'; | |
853 | ||
854 | #ifndef NO_FKEY | |
855 | /* if the mapped string is '#' and a number, then assume | |
856 | * the user wanted that function key | |
857 | */ | |
858 | if (extra[0] == '#' && isdigit(extra[1])) | |
859 | { | |
860 | key = atoi(extra + 1) % 10; | |
861 | # ifndef NO_SHIFT_FKEY | |
862 | build = extra + strlen(extra) - 1; | |
863 | if (*build == 's') | |
864 | key += 10; | |
865 | # ifndef NO_CTRL_FKEY | |
866 | else if (*build == 'c') | |
867 | key += 20; | |
868 | # ifndef NO_ALT_FKEY | |
869 | else if (*build == 'a') | |
870 | key += 30; | |
871 | # endif | |
872 | # endif | |
873 | # endif | |
874 | if (FKEY[key]) | |
875 | mapkey(FKEY[key], mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, fnames[key]); | |
876 | else | |
877 | msg("This terminal has no %s key", fnames[key]); | |
878 | } | |
879 | else | |
880 | #endif | |
881 | #ifndef NO_ABBR | |
882 | if (cmd == CMD_ABBR || cmd == CMD_UNABBR) | |
883 | { | |
884 | mapkey(extra, mapto, bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, "abbr"); | |
885 | } | |
886 | else | |
887 | #endif | |
888 | { | |
889 | mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0); | |
890 | } | |
891 | } | |
892 | } | |
893 | ||
894 | ||
895 | /*ARGSUSED*/ | |
896 | void cmd_set(frommark, tomark, cmd, bang, extra) | |
897 | MARK frommark, tomark; | |
898 | CMD cmd; | |
899 | int bang; | |
900 | char *extra; | |
901 | { | |
902 | if (!*extra) | |
903 | { | |
904 | dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */ | |
905 | } | |
906 | else if (!strcmp(extra, "all")) | |
907 | { | |
908 | dumpopts(TRUE); /* "TRUE" means "dump all" - even unset vars */ | |
909 | } | |
910 | else | |
911 | { | |
912 | setopts(extra); | |
913 | ||
914 | /* That option may have affected the appearence of text */ | |
915 | changes++; | |
916 | } | |
917 | } | |
918 | ||
919 | /*ARGSUSED*/ | |
920 | void cmd_tag(frommark, tomark, cmd, bang, extra) | |
921 | MARK frommark, tomark; | |
922 | CMD cmd; | |
923 | int bang; | |
924 | char *extra; | |
925 | { | |
926 | int fd; /* file descriptor used to read the file */ | |
927 | char *scan; /* used to scan through the tmpblk.c */ | |
928 | #ifdef INTERNAL_TAGS | |
929 | char *cmp; /* char of tag name we're comparing, or NULL */ | |
930 | char *end; /* marks the end of chars in tmpblk.c */ | |
931 | #else | |
932 | int i; | |
933 | #endif | |
934 | #ifndef NO_MAGIC | |
935 | char wasmagic; /* preserves the original state of o_magic */ | |
936 | #endif | |
937 | static char prevtag[30]; | |
938 | ||
939 | /* if no tag is given, use the previous tag */ | |
940 | if (!extra || !*extra) | |
941 | { | |
942 | if (!*prevtag) | |
943 | { | |
944 | msg("No previous tag"); | |
945 | return; | |
946 | } | |
947 | extra = prevtag; | |
948 | } | |
949 | else | |
950 | { | |
951 | strncpy(prevtag, extra, sizeof prevtag); | |
952 | prevtag[sizeof prevtag - 1] = '\0'; | |
953 | } | |
954 | ||
955 | #ifndef INTERNAL_TAGS | |
956 | /* use "ref" to look up the tag info for this tag */ | |
957 | sprintf(tmpblk.c, "ref -t %s%s %s", (*origname ? "-f" : ""),origname, prevtag); | |
958 | fd = rpipe(tmpblk.c, 0); | |
959 | if (fd < 0) | |
960 | { | |
961 | msg("Can't run \"%s\"", tmpblk.c); | |
962 | return; | |
963 | } | |
964 | ||
965 | /* try to read the tag info */ | |
966 | for (scan = tmpblk.c; | |
967 | (i = tread(fd, scan, scan - tmpblk.c + BLKSIZE)) > 0; | |
968 | scan += i) | |
969 | { | |
970 | } | |
971 | *scan = '\0'; | |
972 | ||
973 | /* close the pipe. abort if error */ | |
974 | if (rpclose(fd) != 0 || scan < tmpblk.c + 3) | |
975 | { | |
976 | msg("tag \"%s\" not found", extra); | |
977 | return; | |
978 | } | |
979 | ||
980 | #else /* use internal code to look up the tag */ | |
981 | /* open the tags file */ | |
982 | fd = open(TAGS, O_RDONLY); | |
983 | if (fd < 0) | |
984 | { | |
985 | msg("No tags file"); | |
986 | return; | |
987 | } | |
988 | ||
989 | /* Hmmm... this would have been a lot easier with <stdio.h> */ | |
990 | ||
991 | /* find the line with our tag in it */ | |
992 | for(scan = end = tmpblk.c, cmp = extra; ; scan++) | |
993 | { | |
994 | /* read a block, if necessary */ | |
995 | if (scan >= end) | |
996 | { | |
997 | end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE); | |
998 | scan = tmpblk.c; | |
999 | if (scan >= end) | |
1000 | { | |
1001 | msg("tag \"%s\" not found", extra); | |
1002 | close(fd); | |
1003 | return; | |
1004 | } | |
1005 | } | |
1006 | ||
1007 | /* if we're comparing, compare... */ | |
1008 | if (cmp) | |
1009 | { | |
1010 | /* matched??? wow! */ | |
1011 | if (!*cmp && *scan == '\t') | |
1012 | { | |
1013 | break; | |
1014 | } | |
1015 | if (*cmp++ != *scan) | |
1016 | { | |
1017 | /* failed! skip to newline */ | |
1018 | cmp = (char *)0; | |
1019 | } | |
1020 | } | |
1021 | ||
1022 | /* if we're skipping to newline, do it fast! */ | |
1023 | if (!cmp) | |
1024 | { | |
1025 | while (scan < end && *scan != '\n') | |
1026 | { | |
1027 | scan++; | |
1028 | } | |
1029 | if (scan < end) | |
1030 | { | |
1031 | cmp = extra; | |
1032 | } | |
1033 | } | |
1034 | } | |
1035 | ||
1036 | /* found it! get the rest of the line into memory */ | |
1037 | for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; ) | |
1038 | { | |
1039 | *cmp++ = *scan++; | |
1040 | } | |
1041 | if (scan == end) | |
1042 | { | |
1043 | tread(fd, cmp, BLKSIZE - (int)(cmp - tmpblk.c)); | |
1044 | } | |
1045 | else | |
1046 | *cmp = *scan; | |
1047 | ||
1048 | /* we can close the tags file now */ | |
1049 | close(fd); | |
1050 | #endif /* INTERNAL_TAGS */ | |
1051 | ||
1052 | /* extract the filename from the line, and edit the file */ | |
1053 | for (scan = tmpblk.c; *scan != '\t'; scan++) | |
1054 | { | |
1055 | } | |
1056 | *scan++ = '\0'; | |
1057 | if (strcmp(origname, tmpblk.c) != 0) | |
1058 | { | |
1059 | if (!tmpabort(bang)) | |
1060 | { | |
1061 | msg("Use :tag! to abort changes, or :w to save changes"); | |
1062 | return; | |
1063 | } | |
1064 | tmpstart(tmpblk.c); | |
1065 | } | |
1066 | ||
1067 | /* move to the desired line (or to line 1 if that fails) */ | |
1068 | #ifndef NO_MAGIC | |
1069 | wasmagic = *o_magic; | |
1070 | *o_magic = FALSE; | |
1071 | #endif | |
1072 | cursor = MARK_FIRST; | |
1073 | linespec(scan, &cursor); | |
1074 | if (cursor == MARK_UNSET) | |
1075 | { | |
1076 | cursor = MARK_FIRST; | |
1077 | msg("Tag's address is out of date"); | |
1078 | } | |
1079 | #ifndef NO_MAGIC | |
1080 | *o_magic = wasmagic; | |
1081 | #endif | |
1082 | } | |
1083 | ||
1084 | ||
1085 | ||
1086 | ||
1087 | ||
1088 | /* describe this version of the program */ | |
1089 | /*ARGSUSED*/ | |
1090 | void cmd_version(frommark, tomark, cmd, bang, extra) | |
1091 | MARK frommark; | |
1092 | MARK tomark; | |
1093 | CMD cmd; | |
1094 | int bang; | |
1095 | char *extra; | |
1096 | { | |
1097 | msg("%s", VERSION); | |
1098 | #ifdef CREDIT | |
1099 | msg("%s", CREDIT); | |
1100 | #endif | |
1101 | #ifdef CREDIT2 | |
1102 | msg("%s", CREDIT2); | |
1103 | #endif | |
1104 | #ifdef COMPILED_BY | |
1105 | msg("Compiled by %s", COMPILED_BY); | |
1106 | #endif | |
1107 | #ifdef COPYING | |
1108 | msg("%s", COPYING); | |
1109 | #endif | |
1110 | } | |
1111 | ||
1112 | ||
1113 | #ifndef NO_MKEXRC | |
1114 | /* make a .exrc file which describes the current configuration */ | |
1115 | /*ARGSUSED*/ | |
1116 | void cmd_mkexrc(frommark, tomark, cmd, bang, extra) | |
1117 | MARK frommark; | |
1118 | MARK tomark; | |
1119 | CMD cmd; | |
1120 | int bang; | |
1121 | char *extra; | |
1122 | { | |
1123 | int fd; | |
1124 | ||
1125 | /* the default name for the .exrc file EXRC */ | |
1126 | if (!*extra) | |
1127 | { | |
1128 | extra = EXRC; | |
1129 | } | |
1130 | ||
1131 | /* create the .exrc file */ | |
1132 | fd = creat(extra, FILEPERMS); | |
1133 | if (fd < 0) | |
1134 | { | |
1135 | msg("Couldn't create a new \"%s\" file", extra); | |
1136 | return; | |
1137 | } | |
1138 | ||
1139 | /* save stuff */ | |
1140 | saveopts(fd); | |
1141 | savemaps(fd, FALSE); | |
1142 | #ifndef NO_ABBR | |
1143 | savemaps(fd, TRUE); | |
1144 | #endif | |
1145 | #ifndef NO_DIGRAPH | |
1146 | savedigs(fd); | |
1147 | #endif | |
1148 | #ifndef NO_COLOR | |
1149 | savecolor(fd); | |
1150 | #endif | |
1151 | ||
1152 | /* close the file */ | |
1153 | close(fd); | |
1154 | msg("Configuration saved"); | |
1155 | } | |
1156 | #endif | |
1157 | ||
1158 | #ifndef NO_DIGRAPH | |
1159 | /*ARGSUSED*/ | |
1160 | void cmd_digraph(frommark, tomark, cmd, bang, extra) | |
1161 | MARK frommark; | |
1162 | MARK tomark; | |
1163 | CMD cmd; | |
1164 | int bang; | |
1165 | char *extra; | |
1166 | { | |
1167 | do_digraph(bang, extra); | |
1168 | } | |
1169 | #endif | |
1170 | ||
1171 | ||
1172 | #ifndef NO_ERRLIST | |
1173 | static char errfile[256]; /* the name of a file containing an error */ | |
1174 | static long errline; /* the line number for an error */ | |
1175 | static int errfd = -2; /* fd of the errlist file */ | |
1176 | ||
1177 | /* This static function tries to parse an error message. | |
1178 | * | |
1179 | * For most compilers, the first word is taken to be the name of the erroneous | |
1180 | * file, and the first number after that is taken to be the line number where | |
1181 | * the error was detected. The description of the error follows, possibly | |
1182 | * preceded by an "error ... :" or "warning ... :" label which is skipped. | |
1183 | * | |
1184 | * For Coherent, error messages look like "line#: filename: message". | |
1185 | * | |
1186 | * For non-error lines, or unparsable error lines, this function returns NULL. | |
1187 | * Normally, though, it alters errfile and errline, and returns a pointer to | |
1188 | * the description. | |
1189 | */ | |
1190 | static char *parse_errmsg(text) | |
1191 | REG char *text; | |
1192 | { | |
1193 | REG char *cpy; | |
1194 | long atol(); | |
1195 | # if COHERENT || TOS /* any Mark Williams compiler */ | |
1196 | /* Get the line number. If no line number, then ignore this line. */ | |
1197 | errline = atol(text); | |
1198 | if (errline == 0L) | |
1199 | return (char *)0; | |
1200 | ||
1201 | /* Skip to the start of the filename */ | |
1202 | while (*text && *text++ != ':') | |
1203 | { | |
1204 | } | |
1205 | if (!*text++) | |
1206 | return (char *)0; | |
1207 | ||
1208 | /* copy the filename to errfile */ | |
1209 | for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; ) | |
1210 | { | |
1211 | } | |
1212 | if (!*text++) | |
1213 | return (char *)0; | |
1214 | cpy[-1] = '\0'; | |
1215 | ||
1216 | return text; | |
1217 | # else /* not a Mark Williams compiler */ | |
1218 | char *errmsg; | |
1219 | ||
1220 | /* the error message is the whole line, by default */ | |
1221 | errmsg = text; | |
1222 | ||
1223 | /* skip leading garbage */ | |
1224 | while (*text && !isalnum(*text)) | |
1225 | { | |
1226 | text++; | |
1227 | } | |
1228 | ||
1229 | /* copy over the filename */ | |
1230 | cpy = errfile; | |
1231 | while(isalnum(*text) || *text == '.') | |
1232 | { | |
1233 | *cpy++ = *text++; | |
1234 | } | |
1235 | *cpy = '\0'; | |
1236 | ||
1237 | /* ignore the name "Error" and filenames that contain a '/' */ | |
1238 | if (*text == '/' || !*errfile || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0) | |
1239 | { | |
1240 | return (char *)0; | |
1241 | } | |
1242 | ||
1243 | /* skip garbage between filename and line number */ | |
1244 | while (*text && !isdigit(*text)) | |
1245 | { | |
1246 | text++; | |
1247 | } | |
1248 | ||
1249 | /* if the number is part of a larger word, then ignore this line */ | |
1250 | if (*text && isalpha(text[-1])) | |
1251 | { | |
1252 | return (char *)0; | |
1253 | } | |
1254 | ||
1255 | /* get the error line */ | |
1256 | errline = 0L; | |
1257 | while (isdigit(*text)) | |
1258 | { | |
1259 | errline *= 10; | |
1260 | errline += (*text - '0'); | |
1261 | text++; | |
1262 | } | |
1263 | ||
1264 | /* any line which lacks a filename or line number should be ignored */ | |
1265 | if (!errfile[0] || !errline) | |
1266 | { | |
1267 | return (char *)0; | |
1268 | } | |
1269 | ||
1270 | /* locate the beginning of the error description */ | |
1271 | while (*text && !isspace(*text)) | |
1272 | { | |
1273 | text++; | |
1274 | } | |
1275 | while (*text) | |
1276 | { | |
1277 | # ifndef CRUNCH | |
1278 | /* skip "error #:" and "warning #:" clauses */ | |
1279 | if (!strncmp(text + 1, "rror ", 5) | |
1280 | || !strncmp(text + 1, "arning ", 7) | |
1281 | || !strncmp(text + 1, "atal error", 10)) | |
1282 | { | |
1283 | do | |
1284 | { | |
1285 | text++; | |
1286 | } while (*text && *text != ':'); | |
1287 | continue; | |
1288 | } | |
1289 | # endif | |
1290 | ||
1291 | /* anything other than whitespace or a colon is important */ | |
1292 | if (!isspace(*text) && *text != ':') | |
1293 | { | |
1294 | errmsg = text; | |
1295 | break; | |
1296 | } | |
1297 | ||
1298 | /* else keep looking... */ | |
1299 | text++; | |
1300 | } | |
1301 | ||
1302 | return errmsg; | |
1303 | # endif /* not COHERENT */ | |
1304 | } | |
1305 | ||
1306 | /*ARGSUSED*/ | |
1307 | void cmd_errlist(frommark, tomark, cmd, bang, extra) | |
1308 | MARK frommark, tomark; | |
1309 | CMD cmd; | |
1310 | int bang; | |
1311 | char *extra; | |
1312 | { | |
1313 | static long endline;/* original number of lines in this file */ | |
1314 | static long offset; /* offset of the next line in the errlist file */ | |
1315 | int i; | |
1316 | char *errmsg; | |
1317 | ||
1318 | /* if a new errlist file is named, open it */ | |
1319 | if (extra && extra[0]) | |
1320 | { | |
1321 | /* close the old one */ | |
1322 | if (errfd >= 0) | |
1323 | { | |
1324 | close(errfd); | |
1325 | } | |
1326 | ||
1327 | /* open the new one */ | |
1328 | errfd = open(extra, O_RDONLY); | |
1329 | offset = 0L; | |
1330 | endline = nlines; | |
1331 | } | |
1332 | else if (errfd < 0) | |
1333 | { | |
1334 | /* open the default file */ | |
1335 | errfd = open(ERRLIST, O_RDONLY); | |
1336 | offset = 0L; | |
1337 | endline = nlines; | |
1338 | } | |
1339 | ||
1340 | /* do we have an errlist file now? */ | |
1341 | if (errfd < 0) | |
1342 | { | |
1343 | msg("There is no errlist file"); | |
1344 | beep(); | |
1345 | return; | |
1346 | } | |
1347 | ||
1348 | /* find the next error message in the file */ | |
1349 | do | |
1350 | { | |
1351 | /* read the next line from the errlist */ | |
1352 | lseek(errfd, offset, 0); | |
1353 | if (tread(errfd, tmpblk.c, (unsigned)BLKSIZE) <= 0) | |
1354 | { | |
1355 | msg("No more errors"); | |
1356 | beep(); | |
1357 | close(errfd); | |
1358 | errfd = -2; | |
1359 | return; | |
1360 | } | |
1361 | for (i = 0; tmpblk.c[i] != '\n'; i++) | |
1362 | { | |
1363 | } | |
1364 | tmpblk.c[i++] = 0; | |
1365 | ||
1366 | /* look for an error message in the line */ | |
1367 | errmsg = parse_errmsg(tmpblk.c); | |
1368 | if (!errmsg) | |
1369 | { | |
1370 | offset += i; | |
1371 | } | |
1372 | ||
1373 | } while (!errmsg); | |
1374 | ||
1375 | /* switch to the file containing the error, if this isn't it */ | |
1376 | if (strcmp(origname, errfile)) | |
1377 | { | |
1378 | if (!tmpabort(bang)) | |
1379 | { | |
1380 | msg("Use :er! to abort changes, or :w to save changes"); | |
1381 | beep(); | |
1382 | return; | |
1383 | } | |
1384 | tmpstart(errfile); | |
1385 | endline = nlines; | |
1386 | } | |
1387 | else if (endline == 0L) | |
1388 | { | |
1389 | endline = nlines; | |
1390 | } | |
1391 | ||
1392 | /* go to the line where the error was detected */ | |
1393 | cursor = MARK_AT_LINE(errline + (nlines - endline)); | |
1394 | if (cursor > MARK_LAST) | |
1395 | { | |
1396 | cursor = MARK_LAST; | |
1397 | } | |
1398 | if (mode == MODE_VI) | |
1399 | { | |
1400 | redraw(cursor, FALSE); | |
1401 | } | |
1402 | ||
1403 | /* display the error message */ | |
1404 | #ifdef CRUNCH | |
1405 | msg("%.70s", errmsg); | |
1406 | #else | |
1407 | if (nlines > endline) | |
1408 | { | |
1409 | msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg); | |
1410 | } | |
1411 | else if (nlines < endline) | |
1412 | { | |
1413 | msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg); | |
1414 | } | |
1415 | else | |
1416 | { | |
1417 | msg("line %ld: %.65s", errline, errmsg); | |
1418 | } | |
1419 | #endif | |
1420 | ||
1421 | /* remember where the NEXT error line will start */ | |
1422 | offset += i; | |
1423 | } | |
1424 | ||
1425 | ||
1426 | /*ARGSUSED*/ | |
1427 | void cmd_make(frommark, tomark, cmd, bang, extra) | |
1428 | MARK frommark, tomark; | |
1429 | CMD cmd; | |
1430 | int bang; | |
1431 | char *extra; | |
1432 | { | |
1433 | BLK buf; | |
1434 | ||
1435 | /* if the file hasn't been saved, then complain unless ! */ | |
1436 | if (tstflag(file, MODIFIED) && !bang) | |
1437 | { | |
1438 | msg("\"%s\" not saved yet", origname); | |
1439 | return; | |
1440 | } | |
1441 | ||
1442 | /* build the command */ | |
1443 | sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST); | |
1444 | qaddstr(buf.c); | |
1445 | addch('\n'); | |
1446 | ||
1447 | /* close the old errlist file, if any */ | |
1448 | if (errfd >= 0) | |
1449 | { | |
1450 | close(errfd); | |
1451 | errfd = -3; | |
1452 | } | |
1453 | ||
1454 | /* run the command, with curses temporarily disabled */ | |
1455 | suspend_curses(); | |
1456 | system(buf.c); | |
1457 | resume_curses(mode == MODE_EX); | |
1458 | if (mode == MODE_COLON) | |
1459 | mode = MODE_VI; | |
1460 | ||
1461 | /* run the "errlist" command */ | |
1462 | cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST); | |
1463 | } | |
1464 | #endif | |
1465 | ||
1466 | ||
1467 | ||
1468 | #ifndef NO_COLOR | |
1469 | ||
1470 | /* figure out the number of text colors we use with this configuration */ | |
1471 | # ifndef NO_POPUP | |
1472 | # ifndef NO_VISIBLE | |
1473 | # define NCOLORS 7 | |
1474 | # else | |
1475 | # define NCOLORS 6 | |
1476 | # endif | |
1477 | # else | |
1478 | # ifndef NO_VISIBLE | |
1479 | # define NCOLORS 6 | |
1480 | # else | |
1481 | # define NCOLORS 5 | |
1482 | # endif | |
1483 | # endif | |
1484 | ||
1485 | /* the attribute bytes used in each of "when"s */ | |
1486 | static char bytes[NCOLORS]; | |
1487 | ||
1488 | static struct | |
1489 | { | |
1490 | char *word; /* a legal word */ | |
1491 | int type; /* what type of word this is */ | |
1492 | int val; /* some other value */ | |
1493 | } | |
1494 | words[] = | |
1495 | { | |
1496 | {"normal", 1, A_NORMAL}, /* all "when" names must come */ | |
1497 | {"standout", 1, A_STANDOUT}, /* at the top of the list. */ | |
1498 | {"bold", 1, A_BOLD}, /* The first 3 must be normal,*/ | |
1499 | {"underlined", 1, A_UNDERLINE}, /* standout, and bold; the */ | |
1500 | {"italics", 1, A_ALTCHARSET}, /* remaining names follow. */ | |
1501 | #ifndef NO_POPUP | |
1502 | {"popup", 1, A_POPUP}, | |
1503 | #endif | |
1504 | #ifndef NO_VISIBLE | |
1505 | {"visible", 1, A_VISIBLE}, | |
1506 | #endif | |
1507 | ||
1508 | {"black", 3, 0x00}, /* The color names start right*/ | |
1509 | {"blue", 3, 0x01}, /* after the "when" names. */ | |
1510 | {"green", 3, 0x02}, | |
1511 | {"cyan", 3, 0x03}, | |
1512 | {"red", 3, 0x04}, | |
1513 | {"magenta", 3, 0x05}, | |
1514 | {"brown", 3, 0x06}, | |
1515 | {"white", 3, 0x07}, | |
1516 | {"yellow", 3, 0x0E}, /* bright brown */ | |
1517 | {"gray", 3, 0x08}, /* bright black? of course! */ | |
1518 | {"grey", 3, 0x08}, | |
1519 | ||
1520 | {"bright", 2, 0x08}, | |
1521 | {"light", 2, 0x08}, | |
1522 | {"blinking", 2, 0x80}, | |
1523 | {"on", 0, 0}, | |
1524 | {"n", 1, A_NORMAL}, | |
1525 | {"s", 1, A_STANDOUT}, | |
1526 | {"b", 1, A_BOLD}, | |
1527 | {"u", 1, A_UNDERLINE}, | |
1528 | {"i", 1, A_ALTCHARSET}, | |
1529 | #ifndef NO_POPUP | |
1530 | {"p", 1, A_POPUP}, | |
1531 | {"menu", 1, A_POPUP}, | |
1532 | #endif | |
1533 | #ifndef NO_VISIBLE | |
1534 | {"v", 1, A_VISIBLE}, | |
1535 | #endif | |
1536 | {(char *)0, 0, 0} | |
1537 | }; | |
1538 | ||
1539 | /*ARGSUSED*/ | |
1540 | void cmd_color(frommark, tomark, cmd, bang, extra) | |
1541 | MARK frommark, tomark; | |
1542 | CMD cmd; | |
1543 | int bang; | |
1544 | char *extra; | |
1545 | { | |
1546 | int attrbyte; | |
1547 | int cmode; | |
1548 | int nowbg; /* BOOLEAN: is the next color background? */ | |
1549 | ||
1550 | REG char *scan; | |
1551 | REG i; | |
1552 | ||
1553 | ||
1554 | #ifndef CRUNCH | |
1555 | /* if no args are given, then report the current colors */ | |
1556 | if (!*extra) | |
1557 | { | |
1558 | /* if no colors are set, then say so */ | |
1559 | if (!bytes[0]) | |
1560 | { | |
1561 | msg("no colors have been set"); | |
1562 | return; | |
1563 | } | |
1564 | ||
1565 | /* report all five color combinations */ | |
1566 | for (i = 0; i < NCOLORS; i++) | |
1567 | { | |
1568 | qaddstr("color "); | |
1569 | qaddstr(words[i].word); | |
1570 | qaddch(' '); | |
1571 | if (bytes[i] & 0x80) | |
1572 | qaddstr("blinking "); | |
1573 | switch (bytes[i] & 0xf) | |
1574 | { | |
1575 | case 0x08: qaddstr("gray"); break; | |
1576 | case 0x0e: qaddstr("yellow"); break; | |
1577 | case 0x0f: qaddstr("bright white");break; | |
1578 | default: | |
1579 | if (bytes[i] & 0x08) | |
1580 | qaddstr("light "); | |
1581 | qaddstr(words[(bytes[i] & 0x07) + NCOLORS].word); | |
1582 | } | |
1583 | qaddstr(" on "); | |
1584 | qaddstr(words[((bytes[i] >> 4) & 0x07) + NCOLORS].word); | |
1585 | addch('\n'); | |
1586 | exrefresh(); | |
1587 | } | |
1588 | return; | |
1589 | } | |
1590 | #endif | |
1591 | ||
1592 | /* The default background color is the same as "normal" chars. | |
1593 | * There is no default foreground color. | |
1594 | */ | |
1595 | cmode = A_NORMAL; | |
1596 | attrbyte = bytes[0] & 0x70; | |
1597 | nowbg = FALSE; | |
1598 | ||
1599 | /* parse each word in the "extra" text */ | |
1600 | for (scan = extra; *extra; extra = scan) | |
1601 | { | |
1602 | /* locate the end of the word */ | |
1603 | while (*scan && *scan != ' ') | |
1604 | { | |
1605 | scan++; | |
1606 | } | |
1607 | ||
1608 | /* skip whitespace at the end of the word */ | |
1609 | while(*scan == ' ') | |
1610 | { | |
1611 | *scan++ = '\0'; | |
1612 | } | |
1613 | ||
1614 | /* lookup the word */ | |
1615 | for (i = 0; words[i].word && strcmp(words[i].word, extra); i++) | |
1616 | { | |
1617 | } | |
1618 | ||
1619 | /* if not a word, then complain */ | |
1620 | if (!words[i].word) | |
1621 | { | |
1622 | msg("Invalid color name: %s", extra); | |
1623 | return; | |
1624 | } | |
1625 | ||
1626 | /* process the word */ | |
1627 | switch (words[i].type) | |
1628 | { | |
1629 | case 1: | |
1630 | cmode = words[i].val; | |
1631 | break; | |
1632 | ||
1633 | case 2: | |
1634 | attrbyte |= words[i].val; | |
1635 | break; | |
1636 | ||
1637 | case 3: | |
1638 | if (nowbg) | |
1639 | attrbyte = ((attrbyte & ~0x70) | ((words[i].val & 0x07) << 4)); | |
1640 | else | |
1641 | attrbyte |= words[i].val; | |
1642 | nowbg = TRUE; | |
1643 | break; | |
1644 | } | |
1645 | } | |
1646 | ||
1647 | /* if nowbg isn't set now, then we were never given a foreground color */ | |
1648 | if (!nowbg) | |
1649 | { | |
1650 | msg("usage: color [when] [\"bright\"] [\"blinking\"] foreground [background]"); | |
1651 | return; | |
1652 | } | |
1653 | ||
1654 | /* the first ":color" command MUST define the "normal" colors */ | |
1655 | if (!bytes[0]) | |
1656 | cmode = A_NORMAL; | |
1657 | ||
1658 | /* we should now have a cmode and an attribute byte... */ | |
1659 | ||
1660 | /* set the color */ | |
1661 | setcolor(cmode, attrbyte); | |
1662 | ||
1663 | /* remember what we just did */ | |
1664 | bytes[cmode] = attrbyte; | |
1665 | ||
1666 | /* if the other colors haven't been set yet, then set them to defaults */ | |
1667 | if (!bytes[1]) | |
1668 | { | |
1669 | /* standout is the opposite of normal */ | |
1670 | bytes[1] = ((attrbyte << 4) & 0x70 | (attrbyte >> 4) & 0x07); | |
1671 | setcolor(A_STANDOUT, bytes[1]); | |
1672 | ||
1673 | /* if "normal" isn't bright, then bold defaults to normal+bright | |
1674 | * else bold defaults to bright white. | |
1675 | */ | |
1676 | bytes[2] = attrbyte | ((attrbyte & 0x08) ? 0x0f : 0x08); | |
1677 | setcolor(A_BOLD, bytes[2]); | |
1678 | ||
1679 | /* all others default to the "standout" colors, without blinking */ | |
1680 | for (i = 3; i < NCOLORS; i++) | |
1681 | { | |
1682 | bytes[i] = (bytes[1] & 0x7f); | |
1683 | setcolor(words[i].val, bytes[i]); | |
1684 | } | |
1685 | } | |
1686 | ||
1687 | /* force a redraw, so we see the new colors */ | |
1688 | redraw(MARK_UNSET, FALSE); | |
1689 | } | |
1690 | ||
1691 | ||
1692 | ||
1693 | void savecolor(fd) | |
1694 | int fd; /* file descriptor to write colors to */ | |
1695 | { | |
1696 | int i; | |
1697 | char buf[80]; | |
1698 | ||
1699 | /* if no colors are set, then return */ | |
1700 | if (!bytes[0]) | |
1701 | { | |
1702 | return; | |
1703 | } | |
1704 | ||
1705 | /* save all five color combinations */ | |
1706 | for (i = 0; i < NCOLORS; i++) | |
1707 | { | |
1708 | strcpy(buf, "color "); | |
1709 | strcat(buf, words[i].word); | |
1710 | strcat(buf, " "); | |
1711 | if (bytes[i] & 0x80) | |
1712 | strcat(buf, "blinking "); | |
1713 | switch (bytes[i] & 0xf) | |
1714 | { | |
1715 | case 0x08: strcat(buf, "gray"); break; | |
1716 | case 0x0e: strcat(buf, "yellow"); break; | |
1717 | case 0x0f: strcat(buf, "bright white");break; | |
1718 | default: | |
1719 | if (bytes[i] & 0x08) | |
1720 | strcat(buf, "light "); | |
1721 | strcat(buf, words[(bytes[i] & 0x07) + NCOLORS].word); | |
1722 | } | |
1723 | strcat(buf, " on "); | |
1724 | strcat(buf, words[((bytes[i] >> 4) & 0x07) + NCOLORS].word); | |
1725 | strcat(buf, "\n"); | |
1726 | twrite(fd, buf, (unsigned)strlen(buf)); | |
1727 | } | |
1728 | } | |
1729 | #endif | |
1730 | ||
1731 | #ifdef SIGTSTP | |
1732 | /* temporarily suspend elvis */ | |
1733 | /*ARGSUSED*/ | |
1734 | void cmd_suspend(frommark, tomark, cmd, bang, extra) | |
1735 | MARK frommark; | |
1736 | MARK tomark; | |
1737 | CMD cmd; | |
1738 | int bang; | |
1739 | char *extra; | |
1740 | { | |
1741 | void (*func)(); /* stores the previous setting of SIGTSTP */ | |
1742 | ||
1743 | #if !defined(__386BSD__) && defined(ANY_UNIX) | |
1744 | /* the Bourne shell can't handle ^Z */ | |
1745 | if (!strcmp(o_shell, "/bin/sh")) | |
1746 | { | |
1747 | msg("The /bin/sh shell doesn't support ^Z"); | |
1748 | return; | |
1749 | } | |
1750 | #endif | |
1751 | ||
1752 | func = signal(SIGTSTP, SIG_DFL); | |
1753 | if ( func == SIG_IGN ) { | |
1754 | msg("SIGTSTP is being ignored, you may not suspend the editor", func); | |
1755 | return; | |
1756 | } | |
1757 | move(LINES - 1, 0); | |
1758 | if (tstflag(file, MODIFIED)) | |
1759 | { | |
1760 | addstr("Warning: \""); | |
1761 | addstr(origname); | |
1762 | addstr("\" modified but not yet saved"); | |
1763 | clrtoeol(); | |
1764 | } | |
1765 | refresh(); | |
1766 | suspend_curses(); | |
1767 | /* was here func = signal(SIGTSTP, SIG_DFL); /* races ??? */ | |
1768 | kill (0, SIGTSTP); | |
1769 | ||
1770 | /* the process stops and resumes here */ | |
1771 | ||
1772 | signal(SIGTSTP, func); | |
1773 | resume_curses(TRUE); | |
1774 | if (mode == MODE_VI || mode == MODE_COLON) | |
1775 | redraw(MARK_UNSET, FALSE); | |
1776 | else | |
1777 | refresh (); | |
1778 | } | |
1779 | #endif |