unstack, sort: cleanup and improvement
[minix.git] / commands / elvis / cmd1.c
blob493005267195edd7173791138fe6bf0182276ec9
1 /* cmd1.c */
3 /* Author:
4 * Steve Kirkendall
5 * 14407 SW Teal Blvd. #C
6 * Beaverton, OR 97005
7 * kirkenda@cs.pdx.edu
8 */
11 /* This file contains some of the EX commands - mostly ones that deal with
12 * files, options, etc. -- anything except text.
15 #include "config.h"
16 #include "ctype.h"
17 #include "vi.h"
18 #include "regexp.h"
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;
30 REG char *scan;
31 REG long l;
32 REG int i;
33 int len;
35 /* scan lnum[] to determine which block its in */
36 l = markline(frommark);
37 for (i = 1; l > lnum[i]; i++)
43 /* fetch text of the block containing that line */
44 scan = blkget(i)->c;
46 /* calculate its length */
47 if (scan[BLKSIZE - 1])
49 len = BLKSIZE;
51 else
53 len = strlen(scan);
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)
63 while (--len >= 0)
65 addch(*scan);
66 scan++;
69 exrefresh();
71 /* next block */
72 i++;
73 } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
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;
86 char *scan;
87 int i;
88 int nlcnt; /* used to count newlines */
89 int len; /* counts non-NUL characters */
91 /* check lnum[0] */
92 if (lnum[0] != 0L)
94 msg("lnum[0] = %ld", lnum[0]);
97 /* check each block */
98 for (i = 1; lnum[i] <= nlines; i++)
100 scan = blkget(i)->c;
101 if (scan[BLKSIZE - 1])
103 msg("block %d has no NUL at the end", i);
105 else
107 for (nlcnt = len = 0; *scan; scan++, len++)
109 if (*scan == '\n')
111 nlcnt++;
114 if (scan[-1] != '\n')
116 msg("block %d doesn't end with '\\n' (length %d)", i, len);
118 if (bang || nlcnt != lnum[i] - lnum[i - 1])
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]);
124 exrefresh();
127 /* check lnum again */
128 if (lnum[i] != INFINITY)
130 msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
131 i, hdr.n[i], i, lnum[i]);
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));
137 #endif /* DEBUG */
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;
148 /* validate the name of the mark */
149 if (*extra == '"')
151 extra++;
153 /* valid mark names are lowercase ascii characters */
154 if (!isascii(*extra) || !islower(*extra) || extra[1])
156 msg("Invalid mark name");
157 return;
160 mark[*extra - 'a'] = tomark;
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;
171 int fd;
172 int append; /* boolean: write in "append" mode? */
173 REG long l;
174 REG char *scan;
175 REG int i;
177 /* if writing to a filter, then let filter() handle it */
178 if (*extra == '!')
180 filter(frommark, tomark, extra + 1, FALSE);
181 return;
184 /* if all lines are to be written, use tmpsave() */
185 if (frommark == MARK_FIRST && tomark == MARK_LAST && cmd == CMD_WRITE)
187 tmpsave(extra, bang);
188 return;
191 /* see if we're going to do this in append mode or not */
192 append = FALSE;
193 if (extra[0] == '>' && extra[1] == '>')
195 extra += 2;
196 append = TRUE;
199 /* either the file must not exist, or we must have a ! or be appending */
200 if (access(extra, 0) == 0 && !bang && !append)
202 msg("File already exists - Use :w! to overwrite");
203 return;
206 /* else do it line-by-line, like cmd_print() */
207 if (append)
209 #ifdef O_APPEND
210 fd = open(extra, O_WRONLY|O_APPEND);
211 #else
212 fd = open(extra, O_WRONLY);
213 if (fd >= 0)
215 lseek(fd, 0L, 2);
217 #endif
219 else
221 fd = -1; /* so we know the file isn't open yet */
224 if (fd < 0)
226 fd = creat(extra, FILEPERMS);
227 if (fd < 0)
229 msg("Can't write to \"%s\"", extra);
230 return;
233 for (l = markline(frommark); l <= markline(tomark); l++)
235 /* get the next line */
236 scan = fetchline(l);
237 i = strlen(scan);
238 scan[i++] = '\n';
240 /* print the line */
241 if (twrite(fd, scan, i) < i)
243 msg("Write failed");
244 break;
247 rptlines = markline(tomark) - markline(frommark) + 1;
248 rptlabel = "written";
249 close(fd);
253 /*ARGSUSED*/
254 void cmd_shell(frommark, tomark, cmd, bang, extra)
255 MARK frommark, tomark;
256 CMD cmd;
257 int bang;
258 char *extra;
260 static char prevextra[80];
262 /* special case: ":sh" means ":!sh" */
263 if (cmd == CMD_SHELL)
265 extra = o_shell;
266 frommark = tomark = 0L;
269 /* if extra is "!", substitute previous command */
270 if (*extra == '!')
272 if (!*prevextra)
274 msg("No previous shell command to substitute for '!'");
275 return;
277 extra = prevextra;
279 else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
281 strcpy(prevextra, extra);
284 /* warn the user if the file hasn't been saved yet */
285 if (*o_warn && tstflag(file, MODIFIED))
287 if (mode == MODE_VI)
289 mode = MODE_COLON;
291 msg("Warning: \"%s\" has been modified but not yet saved", origname);
294 /* if no lines were specified, just run the command */
295 suspend_curses();
296 if (frommark == 0L)
298 system(extra);
300 else /* pipe lines from the file through the command */
302 filter(frommark, tomark, extra, TRUE);
305 /* resume curses quietly for MODE_EX, but noisily otherwise */
306 resume_curses(mode == MODE_EX);
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 */
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 */
325 /* can't nest global commands */
326 if (doingglobal)
328 msg("Can't nest global commands.");
329 rptlines = -1L;
330 return;
333 /* ":g! ..." is the same as ":v ..." */
334 if (bang)
336 cmd = CMD_VGLOBAL;
339 /* make sure we got a search pattern */
340 if (*extra != '/' && *extra != '?')
342 msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
343 return;
346 /* parse & compile the search pattern */
347 cmdptr = parseptrn(extra);
348 if (!extra[1])
350 msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
351 return;
353 re = regcomp(extra + 1);
354 if (!re)
356 /* regcomp found & described an error */
357 return;
360 /* for each line in the range */
361 doingglobal = TRUE;
362 ChangeText
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.
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--)
376 /* fetch the line */
377 line = fetchline(nlines - l);
379 /* if it contains the search pattern... */
380 if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
382 /* move the cursor to that line */
383 cursor = MARK_AT_LINE(nlines - l);
385 /* do the ex command (without mucking up
386 * the original copy of the command line)
388 strcpy(cmdln, cmdptr);
389 rptlines = 0L;
390 doexcmd(cmdln);
391 nchanged += rptlines;
395 doingglobal = FALSE;
397 /* free the regexp */
398 free(re);
400 /* Reporting...*/
401 rptlines = nchanged;
405 /*ARGSUSED*/
406 void cmd_file(frommark, tomark, cmd, bang, extra)
407 MARK frommark, tomark;
408 CMD cmd;
409 int bang;
410 char *extra;
412 #ifndef CRUNCH
413 /* if we're given a new filename, use it as this file's name */
414 if (extra && *extra)
416 strcpy(origname, extra);
417 storename(origname);
418 setflag(file, NOTEDITED);
420 #endif
421 if (cmd == CMD_FILE)
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);
438 #ifndef CRUNCH
439 else if (markline(frommark) != markline(tomark))
441 msg("range \"%ld,%ld\" contains %ld lines",
442 markline(frommark),
443 markline(tomark),
444 markline(tomark) - markline(frommark) + 1L);
446 #endif
447 else
449 msg("%ld", markline(frommark));
454 /*ARGSUSED*/
455 void cmd_edit(frommark, tomark, cmd, bang, extra)
456 MARK frommark, tomark;
457 CMD cmd;
458 int bang;
459 char *extra;
461 long line = 1L; /* might be set to prevline */
462 #ifndef CRUNCH
463 char *init = (char *)0;
464 #endif
467 /* if ":vi", then switch to visual mode, and if no file is named
468 * then don't switch files.
470 if (cmd == CMD_VISUAL)
472 mode = MODE_VI;
473 msg("");
474 if (!*extra)
476 return;
480 /* Editing previous file? Then start at previous line */
481 if (!strcmp(extra, prevorig))
483 line = prevline;
486 #ifndef CRUNCH
487 /* if we were given an explicit starting line, then start there */
488 if (*extra == '+')
490 for (init = ++extra; !isspace(*extra); extra++)
493 while (isspace(*extra))
495 *extra++ = '\0';
497 if (!*init)
499 init = "$";
501 if (!extra)
503 extra = origname;
506 #endif /* not CRUNCH */
508 /* switch files */
509 if (tmpabort(bang))
511 tmpstart(extra);
512 if (line <= nlines && line >= 1L)
514 cursor = MARK_AT_LINE(line);
516 #ifndef CRUNCH
517 if (init)
519 doexcmd(init);
521 #endif
523 else
525 msg("Use edit! to abort changes, or w to save changes");
527 /* so we can say ":e!#" next time... */
528 strcpy(prevorig, extra);
529 prevline = 1L;
533 /* This code is also used for rewind -- GB */
535 /*ARGSUSED*/
536 void cmd_next(frommark, tomark, cmd, bang, extra)
537 MARK frommark, tomark;
538 CMD cmd;
539 int bang;
540 char *extra;
542 int i, j;
543 char *scan;
545 /* if extra stuff given, use ":args" to define a new args list */
546 if (cmd == CMD_NEXT && extra && *extra)
548 cmd_args(frommark, tomark, cmd, bang, extra);
551 /* move to the next arg */
552 if (cmd == CMD_NEXT)
554 i = argno + 1;
556 else if (cmd == CMD_PREVIOUS)
558 i = argno - 1;
560 else /* cmd == CMD_REWIND */
562 i = 0;
564 if (i < 0 || i >= nargs)
566 msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
567 return;
570 /* find & isolate the name of the file to edit */
571 for (j = i, scan = args; j > 0; j--)
573 while(*scan++)
578 /* switch to the next file */
579 if (tmpabort(bang))
581 tmpstart(scan);
582 argno = i;
584 else
586 msg("Use :%s! to abort changes, or w to save changes",
587 cmd == CMD_NEXT ? "next" :
588 cmd == CMD_PREVIOUS ? "previous" :
589 "rewind");
593 /* also called from :wq -- always writes back in this case */
595 /*ARGSUSED*/
596 void cmd_xit(frommark, tomark, cmd, bang, extra)
597 MARK frommark, tomark;
598 CMD cmd;
599 int bang;
600 char *extra;
602 static long whenwarned; /* when the user was last warned of extra files */
603 int oldflag;
605 /* if there are more files to edit, then warn user */
606 if (argno >= 0 && argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
608 msg("More files to edit -- Use \":n\" to go to next file");
609 whenwarned = changes;
610 return;
613 if (cmd == CMD_QUIT)
615 oldflag = *o_autowrite;
616 *o_autowrite = FALSE;
617 if (tmpabort(bang))
619 mode = MODE_QUIT;
621 else
623 msg("Use q! to abort changes, or wq to save changes");
625 *o_autowrite = oldflag;
627 else
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))
635 mode = MODE_QUIT;
637 else
639 msg("Could not save file -- use quit! to abort changes, or w filename");
641 if (!oldflag)
642 clrflag(file, MODIFIED);
647 /*ARGSUSED*/
648 void cmd_args(frommark, tomark, cmd, bang, extra)
649 MARK frommark, tomark;
650 CMD cmd;
651 int bang;
652 char *extra;
654 char *scan;
655 int col;
656 int arg;
657 int scrolled = FALSE;
658 int width;
660 /* if no extra names given, or just current name, then report the args
661 * we have now.
663 if (!extra || !*extra)
665 /* empty args list? */
666 if (nargs == 1 && !*args)
668 return;
671 /* list the arguments */
672 for (scan = args, col = arg = 0;
673 arg < nargs;
674 scan += width + 1, col += width, arg++)
676 width = strlen(scan);
677 if (col + width >= COLS - 4)
679 addch('\n');
680 col = 0;
681 scrolled = TRUE;
683 else if (col > 0)
685 addch(' ');
686 col++;
688 if (arg == argno)
690 addch('[');
691 addstr(scan);
692 addch(']');
693 col += 2;
695 else
697 addstr(scan);
701 /* write a trailing newline */
702 if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
704 addch('\n');
706 exrefresh();
708 else /* new args list given */
710 for (scan = args, nargs = 1; *extra; )
712 if (isspace(*extra))
714 *scan++ = '\0';
715 while (isspace(*extra))
717 extra++;
719 if (*extra)
721 nargs++;
724 else
726 *scan++ = *extra++;
729 *scan = '\0';
731 /* reset argno to before the first, so :next will go to first */
732 argno = -1;
734 if (nargs != 1)
736 msg("%d files to edit", nargs);
742 /*ARGSUSED*/
743 void cmd_cd(frommark, tomark, cmd, bang, extra)
744 MARK frommark, tomark;
745 CMD cmd;
746 int bang;
747 char *extra;
749 char *getenv();
751 #ifndef CRUNCH
752 /* if current file is modified, and no '!' was given, then error */
753 if (tstflag(file, MODIFIED) && !bang)
755 msg("File modified; use \"cd! %s\" to switch anyway", extra);
757 #endif
759 /* default directory name is $HOME */
760 if (!*extra)
762 extra = getenv("HOME");
763 if (!extra)
765 msg("environment variable $HOME not set");
766 return;
770 /* go to the directory */
771 if (chdir(extra) < 0)
773 perror(extra);
778 /*ARGSUSED*/
779 void cmd_map(frommark, tomark, cmd, bang, extra)
780 MARK frommark, tomark;
781 CMD cmd;
782 int bang;
783 char *extra;
785 char *mapto;
786 char *build, *scan;
787 #ifndef NO_FKEY
788 static char *fnames[NFKEYS] =
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
805 int key;
806 #endif
808 /* "map" with no extra will dump the map table contents */
809 if (!*extra)
811 #ifndef NO_ABBR
812 if (cmd == CMD_ABBR)
814 dumpkey(bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, TRUE);
816 else
817 #endif
819 dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, FALSE);
822 else
824 /* "extra" is key to map, followed by what it maps to */
826 /* handle quoting inside the "raw" string */
827 for (build = mapto = extra;
828 *mapto && (*mapto != ' ' && *mapto != '\t');
829 *build++ = *mapto++)
831 if (*mapto == ctrl('V') && mapto[1])
833 mapto++;
837 /* skip whitespace, and mark the end of the "raw" string */
838 while ((*mapto == ' ' || *mapto == '\t'))
840 *mapto++ = '\0';
842 *build = '\0';
844 /* strip ^Vs from the "cooked" string */
845 for (scan = build = mapto; *scan; *build++ = *scan++)
847 if (*scan == ctrl('V') && scan[1])
849 scan++;
852 *build = '\0';
854 #ifndef NO_FKEY
855 /* if the mapped string is '#' and a number, then assume
856 * the user wanted that function key
858 if (extra[0] == '#' && isdigit(extra[1]))
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]);
879 else
880 #endif
881 #ifndef NO_ABBR
882 if (cmd == CMD_ABBR || cmd == CMD_UNABBR)
884 mapkey(extra, mapto, bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, "abbr");
886 else
887 #endif
889 mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
895 /*ARGSUSED*/
896 void cmd_set(frommark, tomark, cmd, bang, extra)
897 MARK frommark, tomark;
898 CMD cmd;
899 int bang;
900 char *extra;
902 if (!*extra)
904 dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
906 else if (!strcmp(extra, "all"))
908 dumpopts(TRUE); /* "TRUE" means "dump all" - even unset vars */
910 else
912 setopts(extra);
914 /* That option may have affected the appearence of text */
915 changes++;
919 /*ARGSUSED*/
920 void cmd_tag(frommark, tomark, cmd, bang, extra)
921 MARK frommark, tomark;
922 CMD cmd;
923 int bang;
924 char *extra;
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];
939 /* if no tag is given, use the previous tag */
940 if (!extra || !*extra)
942 if (!*prevtag)
944 msg("No previous tag");
945 return;
947 extra = prevtag;
949 else
951 strncpy(prevtag, extra, sizeof prevtag);
952 prevtag[sizeof prevtag - 1] = '\0';
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)
961 msg("Can't run \"%s\"", tmpblk.c);
962 return;
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)
971 *scan = '\0';
973 /* close the pipe. abort if error */
974 if (rpclose(fd) != 0 || scan < tmpblk.c + 3)
976 msg("tag \"%s\" not found", extra);
977 return;
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)
985 msg("No tags file");
986 return;
989 /* Hmmm... this would have been a lot easier with <stdio.h> */
991 /* find the line with our tag in it */
992 for(scan = end = tmpblk.c, cmp = extra; ; scan++)
994 /* read a block, if necessary */
995 if (scan >= end)
997 end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
998 scan = tmpblk.c;
999 if (scan >= end)
1001 msg("tag \"%s\" not found", extra);
1002 close(fd);
1003 return;
1007 /* if we're comparing, compare... */
1008 if (cmp)
1010 /* matched??? wow! */
1011 if (!*cmp && *scan == '\t')
1013 break;
1015 if (*cmp++ != *scan)
1017 /* failed! skip to newline */
1018 cmp = (char *)0;
1022 /* if we're skipping to newline, do it fast! */
1023 if (!cmp)
1025 while (scan < end && *scan != '\n')
1027 scan++;
1029 if (scan < end)
1031 cmp = extra;
1036 /* found it! get the rest of the line into memory */
1037 for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
1039 *cmp++ = *scan++;
1041 if (scan == end)
1043 tread(fd, cmp, BLKSIZE - (int)(cmp - tmpblk.c));
1045 else
1046 *cmp = *scan;
1048 /* we can close the tags file now */
1049 close(fd);
1050 #endif /* INTERNAL_TAGS */
1052 /* extract the filename from the line, and edit the file */
1053 for (scan = tmpblk.c; *scan != '\t'; scan++)
1056 *scan++ = '\0';
1057 if (strcmp(origname, tmpblk.c) != 0)
1059 if (!tmpabort(bang))
1061 msg("Use :tag! to abort changes, or :w to save changes");
1062 return;
1064 tmpstart(tmpblk.c);
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)
1076 cursor = MARK_FIRST;
1077 msg("Tag's address is out of date");
1079 #ifndef NO_MAGIC
1080 *o_magic = wasmagic;
1081 #endif
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;
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
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;
1123 int fd;
1125 /* the default name for the .exrc file EXRC */
1126 if (!*extra)
1128 extra = EXRC;
1131 /* create the .exrc file */
1132 fd = creat(extra, FILEPERMS);
1133 if (fd < 0)
1135 msg("Couldn't create a new \"%s\" file", extra);
1136 return;
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
1152 /* close the file */
1153 close(fd);
1154 msg("Configuration saved");
1156 #endif
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;
1167 do_digraph(bang, extra);
1169 #endif
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 */
1177 /* This static function tries to parse an error message.
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.
1184 * For Coherent, error messages look like "line#: filename: message".
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.
1190 static char *parse_errmsg(text)
1191 REG char *text;
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;
1201 /* Skip to the start of the filename */
1202 while (*text && *text++ != ':')
1205 if (!*text++)
1206 return (char *)0;
1208 /* copy the filename to errfile */
1209 for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
1212 if (!*text++)
1213 return (char *)0;
1214 cpy[-1] = '\0';
1216 return text;
1217 # else /* not a Mark Williams compiler */
1218 char *errmsg;
1220 /* the error message is the whole line, by default */
1221 errmsg = text;
1223 /* skip leading garbage */
1224 while (*text && !isalnum(*text))
1226 text++;
1229 /* copy over the filename */
1230 cpy = errfile;
1231 while(isalnum(*text) || *text == '.')
1233 *cpy++ = *text++;
1235 *cpy = '\0';
1237 /* ignore the name "Error" and filenames that contain a '/' */
1238 if (*text == '/' || !*errfile || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
1240 return (char *)0;
1243 /* skip garbage between filename and line number */
1244 while (*text && !isdigit(*text))
1246 text++;
1249 /* if the number is part of a larger word, then ignore this line */
1250 if (*text && isalpha(text[-1]))
1252 return (char *)0;
1255 /* get the error line */
1256 errline = 0L;
1257 while (isdigit(*text))
1259 errline *= 10;
1260 errline += (*text - '0');
1261 text++;
1264 /* any line which lacks a filename or line number should be ignored */
1265 if (!errfile[0] || !errline)
1267 return (char *)0;
1270 /* locate the beginning of the error description */
1271 while (*text && !isspace(*text))
1273 text++;
1275 while (*text)
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))
1285 text++;
1286 } while (*text && *text != ':');
1287 continue;
1289 # endif
1291 /* anything other than whitespace or a colon is important */
1292 if (!isspace(*text) && *text != ':')
1294 errmsg = text;
1295 break;
1298 /* else keep looking... */
1299 text++;
1302 return errmsg;
1303 # endif /* not COHERENT */
1306 /*ARGSUSED*/
1307 void cmd_errlist(frommark, tomark, cmd, bang, extra)
1308 MARK frommark, tomark;
1309 CMD cmd;
1310 int bang;
1311 char *extra;
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;
1318 /* if a new errlist file is named, open it */
1319 if (extra && extra[0])
1321 /* close the old one */
1322 if (errfd >= 0)
1324 close(errfd);
1327 /* open the new one */
1328 errfd = open(extra, O_RDONLY);
1329 offset = 0L;
1330 endline = nlines;
1332 else if (errfd < 0)
1334 /* open the default file */
1335 errfd = open(ERRLIST, O_RDONLY);
1336 offset = 0L;
1337 endline = nlines;
1340 /* do we have an errlist file now? */
1341 if (errfd < 0)
1343 msg("There is no errlist file");
1344 beep();
1345 return;
1348 /* find the next error message in the file */
1351 /* read the next line from the errlist */
1352 lseek(errfd, offset, 0);
1353 if (tread(errfd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
1355 msg("No more errors");
1356 beep();
1357 close(errfd);
1358 errfd = -2;
1359 return;
1361 for (i = 0; tmpblk.c[i] != '\n'; i++)
1364 tmpblk.c[i++] = 0;
1366 /* look for an error message in the line */
1367 errmsg = parse_errmsg(tmpblk.c);
1368 if (!errmsg)
1370 offset += i;
1373 } while (!errmsg);
1375 /* switch to the file containing the error, if this isn't it */
1376 if (strcmp(origname, errfile))
1378 if (!tmpabort(bang))
1380 msg("Use :er! to abort changes, or :w to save changes");
1381 beep();
1382 return;
1384 tmpstart(errfile);
1385 endline = nlines;
1387 else if (endline == 0L)
1389 endline = nlines;
1392 /* go to the line where the error was detected */
1393 cursor = MARK_AT_LINE(errline + (nlines - endline));
1394 if (cursor > MARK_LAST)
1396 cursor = MARK_LAST;
1398 if (mode == MODE_VI)
1400 redraw(cursor, FALSE);
1403 /* display the error message */
1404 #ifdef CRUNCH
1405 msg("%.70s", errmsg);
1406 #else
1407 if (nlines > endline)
1409 msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
1411 else if (nlines < endline)
1413 msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
1415 else
1417 msg("line %ld: %.65s", errline, errmsg);
1419 #endif
1421 /* remember where the NEXT error line will start */
1422 offset += i;
1426 /*ARGSUSED*/
1427 void cmd_make(frommark, tomark, cmd, bang, extra)
1428 MARK frommark, tomark;
1429 CMD cmd;
1430 int bang;
1431 char *extra;
1433 BLK buf;
1435 /* if the file hasn't been saved, then complain unless ! */
1436 if (tstflag(file, MODIFIED) && !bang)
1438 msg("\"%s\" not saved yet", origname);
1439 return;
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');
1447 /* close the old errlist file, if any */
1448 if (errfd >= 0)
1450 close(errfd);
1451 errfd = -3;
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;
1461 /* run the "errlist" command */
1462 cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
1464 #endif
1468 #ifndef NO_COLOR
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
1485 /* the attribute bytes used in each of "when"s */
1486 static char bytes[NCOLORS];
1488 static struct
1490 char *word; /* a legal word */
1491 int type; /* what type of word this is */
1492 int val; /* some other value */
1494 words[] =
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
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},
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}
1539 /*ARGSUSED*/
1540 void cmd_color(frommark, tomark, cmd, bang, extra)
1541 MARK frommark, tomark;
1542 CMD cmd;
1543 int bang;
1544 char *extra;
1546 int attrbyte;
1547 int cmode;
1548 int nowbg; /* BOOLEAN: is the next color background? */
1550 REG char *scan;
1551 REG i;
1554 #ifndef CRUNCH
1555 /* if no args are given, then report the current colors */
1556 if (!*extra)
1558 /* if no colors are set, then say so */
1559 if (!bytes[0])
1561 msg("no colors have been set");
1562 return;
1565 /* report all five color combinations */
1566 for (i = 0; i < NCOLORS; i++)
1568 qaddstr("color ");
1569 qaddstr(words[i].word);
1570 qaddch(' ');
1571 if (bytes[i] & 0x80)
1572 qaddstr("blinking ");
1573 switch (bytes[i] & 0xf)
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);
1583 qaddstr(" on ");
1584 qaddstr(words[((bytes[i] >> 4) & 0x07) + NCOLORS].word);
1585 addch('\n');
1586 exrefresh();
1588 return;
1590 #endif
1592 /* The default background color is the same as "normal" chars.
1593 * There is no default foreground color.
1595 cmode = A_NORMAL;
1596 attrbyte = bytes[0] & 0x70;
1597 nowbg = FALSE;
1599 /* parse each word in the "extra" text */
1600 for (scan = extra; *extra; extra = scan)
1602 /* locate the end of the word */
1603 while (*scan && *scan != ' ')
1605 scan++;
1608 /* skip whitespace at the end of the word */
1609 while(*scan == ' ')
1611 *scan++ = '\0';
1614 /* lookup the word */
1615 for (i = 0; words[i].word && strcmp(words[i].word, extra); i++)
1619 /* if not a word, then complain */
1620 if (!words[i].word)
1622 msg("Invalid color name: %s", extra);
1623 return;
1626 /* process the word */
1627 switch (words[i].type)
1629 case 1:
1630 cmode = words[i].val;
1631 break;
1633 case 2:
1634 attrbyte |= words[i].val;
1635 break;
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;
1647 /* if nowbg isn't set now, then we were never given a foreground color */
1648 if (!nowbg)
1650 msg("usage: color [when] [\"bright\"] [\"blinking\"] foreground [background]");
1651 return;
1654 /* the first ":color" command MUST define the "normal" colors */
1655 if (!bytes[0])
1656 cmode = A_NORMAL;
1658 /* we should now have a cmode and an attribute byte... */
1660 /* set the color */
1661 setcolor(cmode, attrbyte);
1663 /* remember what we just did */
1664 bytes[cmode] = attrbyte;
1666 /* if the other colors haven't been set yet, then set them to defaults */
1667 if (!bytes[1])
1669 /* standout is the opposite of normal */
1670 bytes[1] = ((attrbyte << 4) & 0x70 | (attrbyte >> 4) & 0x07);
1671 setcolor(A_STANDOUT, bytes[1]);
1673 /* if "normal" isn't bright, then bold defaults to normal+bright
1674 * else bold defaults to bright white.
1676 bytes[2] = attrbyte | ((attrbyte & 0x08) ? 0x0f : 0x08);
1677 setcolor(A_BOLD, bytes[2]);
1679 /* all others default to the "standout" colors, without blinking */
1680 for (i = 3; i < NCOLORS; i++)
1682 bytes[i] = (bytes[1] & 0x7f);
1683 setcolor(words[i].val, bytes[i]);
1687 /* force a redraw, so we see the new colors */
1688 redraw(MARK_UNSET, FALSE);
1693 void savecolor(fd)
1694 int fd; /* file descriptor to write colors to */
1696 int i;
1697 char buf[80];
1699 /* if no colors are set, then return */
1700 if (!bytes[0])
1702 return;
1705 /* save all five color combinations */
1706 for (i = 0; i < NCOLORS; i++)
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)
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);
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));
1729 #endif
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;
1741 void (*func)(); /* stores the previous setting of SIGTSTP */
1743 #if ANY_UNIX
1744 /* the Bourne shell can't handle ^Z */
1745 if (!strcmp(o_shell, "/bin/sh"))
1747 msg("The /bin/sh shell doesn't support ^Z");
1748 return;
1750 #endif
1752 move(LINES - 1, 0);
1753 if (tstflag(file, MODIFIED))
1755 addstr("Warning: \"");
1756 addstr(origname);
1757 addstr("\" modified but not yet saved");
1758 clrtoeol();
1760 refresh();
1761 suspend_curses();
1762 func = signal(SIGTSTP, SIG_DFL);
1763 kill (0, SIGTSTP);
1765 /* the process stops and resumes here */
1767 signal(SIGTSTP, func);
1768 resume_curses(TRUE);
1769 if (mode == MODE_VI || mode == MODE_COLON)
1770 redraw(MARK_UNSET, FALSE);
1771 else
1772 refresh ();
1774 #endif