source: trunk/minix/commands/elvis/cmd1.c@ 11

Last change on this file since 11 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 33.5 KB
Line 
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*/
23void 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*/
79void 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*/
141void 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*/
164void 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*/
254void 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*/
311void 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*/
406void 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*/
455void 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*/
536void 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*/
596void 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*/
648void 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*/
743void 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*/
779void 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*/
896void 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*/
920void 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*/
1090void 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*/
1116void 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*/
1160void 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
1173static char errfile[256]; /* the name of a file containing an error */
1174static long errline; /* the line number for an error */
1175static 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 */
1190static 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*/
1307void 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*/
1427void 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 */
1486static char bytes[NCOLORS];
1487
1488static 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*/
1540void 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
1693void 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*/
1734void 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 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 move(LINES - 1, 0);
1753 if (tstflag(file, MODIFIED))
1754 {
1755 addstr("Warning: \"");
1756 addstr(origname);
1757 addstr("\" modified but not yet saved");
1758 clrtoeol();
1759 }
1760 refresh();
1761 suspend_curses();
1762 func = signal(SIGTSTP, SIG_DFL);
1763 kill (0, SIGTSTP);
1764
1765 /* the process stops and resumes here */
1766
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 ();
1773}
1774#endif
Note: See TracBrowser for help on using the repository browser.