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