|
|
1.1 root 1: /* cmd1.c */
2:
3: /* Author:
4: * Steve Kirkendall
5: * 16820 SW Tallac Way
6: * Beaverton, OR 97006
7: * [email protected], or ...uunet!tektronix!psueea!jove!kirkenda
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: #if MSDOS
21: #define DATE __DATE__
22: #endif
23:
24: #ifdef DEBUG
25: /* print the selected lines with info on the blocks */
26: /*ARGSUSED*/
27: void cmd_debug(frommark, tomark, cmd, bang, extra)
28: MARK frommark;
29: MARK tomark;
30: CMD cmd;
31: int bang;
32: char *extra;
33: {
34: REG char *scan;
35: REG long l;
36: REG int i;
37: int len;
38:
39: /* scan lnum[] to determine which block its in */
40: l = markline(frommark);
41: for (i = 1; l > lnum[i]; i++)
42: {
43: }
44:
45: do
46: {
47: /* fetch text of the block containing that line */
48: scan = blkget(i)->c;
49:
50: /* calculate its length */
51: if (scan[BLKSIZE - 1])
52: {
53: len = BLKSIZE;
54: }
55: else
56: {
57: len = strlen(scan);
58: }
59:
60: /* print block stats */
61: msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
62: i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
63: msg("##### len=%d, buf=0x%lx, %sdirty",
64: len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
65: if (bang)
66: {
67: while (--len >= 0)
68: {
69: addch(*scan);
70: scan++;
71: }
72: }
73: exrefresh();
74:
75: /* next block */
76: i++;
77: } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
78: }
79:
80:
81: /* This function checks a lot of conditions to make sure they aren't screwy */
82: /*ARGSUSED*/
83: void cmd_validate(frommark, tomark, cmd, bang, extra)
84: MARK frommark;
85: MARK tomark;
86: CMD cmd;
87: int bang;
88: char *extra;
89: {
90: char *scan;
91: int i;
92: int nlcnt; /* used to count newlines */
93: int len; /* counts non-NUL characters */
94:
95: /* check lnum[0] */
96: if (lnum[0] != 0L)
97: {
98: msg("lnum[0] = %ld", lnum[0]);
99: }
100:
101: /* check each block */
102: for (i = 1; lnum[i] <= nlines; i++)
103: {
104: scan = blkget(i)->c;
105: if (scan[BLKSIZE - 1])
106: {
107: msg("block %d has no NUL at the end", i);
108: }
109: else
110: {
111: for (nlcnt = len = 0; *scan; scan++, len++)
112: {
113: if (*scan == '\n')
114: {
115: nlcnt++;
116: }
117: }
118: if (scan[-1] != '\n')
119: {
120: msg("block %d doesn't end with '\\n' (length %d)", i, len);
121: }
122: if (bang || nlcnt != lnum[i] - lnum[i - 1])
123: {
124: msg("block %d (line %ld?) has %d lines, but should have %ld",
125: i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
126: }
127: }
128: exrefresh();
129: }
130:
131: /* check lnum again */
132: if (lnum[i] != INFINITY)
133: {
134: msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
135: i, hdr.n[i], i, lnum[i]);
136: }
137:
138: msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
139: }
140: #endif /* DEBUG */
141:
142:
143: /*ARGSUSED*/
144: void cmd_mark(frommark, tomark, cmd, bang, extra)
145: MARK frommark;
146: MARK tomark;
147: CMD cmd;
148: int bang;
149: char *extra;
150: {
151: /* validate the name of the mark */
152: if (!extra || *extra < 'a' || *extra > 'z' || extra[1])
153: {
154: msg("Invalid mark name");
155: return;
156: }
157:
158: mark[*extra - 'a'] = tomark;
159: }
160:
161: /*ARGSUSED*/
162: void cmd_write(frommark, tomark, cmd, bang, extra)
163: MARK frommark;
164: MARK tomark;
165: CMD cmd;
166: int bang;
167: char *extra;
168: {
169: int fd;
170: int append; /* boolean: write in "append" mode? */
171: REG long l;
172: REG char *scan;
173: REG int i;
174:
175: /* if all lines are to be written, use tmpsave() */
176: if (frommark == MARK_FIRST && tomark == MARK_LAST)
177: {
178: tmpsave(extra, bang);
179: return;
180: }
181:
182: /* see if we're going to do this in append mode or not */
183: append = FALSE;
184: if (extra[0] == '>' && extra[1] == '>')
185: {
186: extra += 2;
187: append = TRUE;
188: }
189:
190: /* either the file must not exist, or we must have a ! or be appending */
191: if (access(extra, 0) == 0 && !bang && !append)
192: {
193: msg("File already exists - Use :w! to overwrite");
194: return;
195: }
196:
197: /* else do it line-by-line, like cmd_print() */
198: if (append)
199: {
200: #ifdef O_APPEND
201: fd = open(extra, O_WRONLY|O_APPEND);
202: #else
203: fd = open(extra, O_WRONLY);
204: if (fd >= 0)
205: {
206: lseek(fd, 0L, 2);
207: }
208: #endif
209: }
210: else
211: {
212: fd = -1; /* so we know the file isn't open yet */
213: }
214:
215: if (fd < 0)
216: {
217: fd = creat(extra, FILEPERMS);
218: if (fd < 0)
219: {
220: msg("Can't write to \"%s\"", extra);
221: return;
222: }
223: }
224: for (l = markline(frommark); l <= markline(tomark); l++)
225: {
226: /* get the next line */
227: scan = fetchline(l);
228: i = strlen(scan);
229: scan[i++] = '\n';
230:
231: /* print the line */
232: twrite(fd, scan, i);
233: }
234: close(fd);
235: }
236:
237:
238: /*ARGSUSED*/
239: void cmd_shell(frommark, tomark, cmd, bang, extra)
240: MARK frommark, tomark;
241: CMD cmd;
242: int bang;
243: char *extra;
244: {
245: static char prevextra[80];
246:
247: /* special case: ":sh" means ":!sh" */
248: if (cmd == CMD_SHELL)
249: {
250: extra = o_shell;
251: frommark = tomark = 0L;
252: }
253:
254: /* if extra is "!", substute previous command */
255: if (*extra == '!')
256: {
257: if (!*prevextra)
258: {
259: msg("No previous shell command to substitute for '!'");
260: return;
261: }
262: extra = prevextra;
263: }
264: else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
265: {
266: strcpy(prevextra, extra);
267: }
268:
269: /* if no lines were specified, just run the command */
270: suspend_curses();
271: if (frommark == 0L)
272: {
273: system(extra);
274: }
275: else /* pipe lines from the file through the command */
276: {
277: filter(frommark, tomark, extra);
278: }
279:
280: /* resume curses quietly for MODE_EX, but noisily otherwise */
281: resume_curses(mode == MODE_EX);
282: }
283:
284:
285: /*ARGSUSED*/
286: void cmd_global(frommark, tomark, cmd, bang, extra)
287: MARK frommark, tomark;
288: CMD cmd;
289: int bang;
290: char *extra; /* rest of the command line */
291: {
292: char *cmdptr; /* the command from the command line */
293: char cmdln[100]; /* copy of the command from the command line */
294: char *line; /* a line from the file */
295: long l; /* used as a counter to move through lines */
296: long lqty; /* quantity of lines to be scanned */
297: long nchanged; /* number of lines changed */
298: regexp *re; /* the compiled search expression */
299:
300: /* can't nest global commands */
301: if (doingglobal)
302: {
303: msg("Can't nest global commands.");
304: rptlines = -1L;
305: return;
306: }
307:
308: /* ":g! ..." is the same as ":v ..." */
309: if (bang)
310: {
311: cmd = CMD_VGLOBAL;
312: }
313:
314: /* make sure we got a search pattern */
315: if (*extra != '/' && *extra != '?')
316: {
317: msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
318: return;
319: }
320:
321: /* parse & compile the search pattern */
322: cmdptr = parseptrn(extra);
323: if (!extra[1])
324: {
325: msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
326: return;
327: }
328: re = regcomp(extra + 1);
329: if (!re)
330: {
331: /* regcomp found & described an error */
332: return;
333: }
334:
335: /* for each line in the range */
336: doingglobal = TRUE;
337: ChangeText
338: {
339: /* NOTE: we have to go through the lines in a forward order,
340: * otherwise "g/re/p" would look funny. *BUT* for "g/re/d"
341: * to work, simply adding 1 to the line# on each loop won't
342: * work. The solution: count lines relative to the end of
343: * the file. Think about it.
344: */
345: for (l = nlines - markline(frommark),
346: lqty = markline(tomark) - markline(frommark) + 1L,
347: nchanged = 0L;
348: lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
349: l--, lqty--)
350: {
351: /* fetch the line */
352: line = fetchline(nlines - l);
353:
354: /* if it contains the search pattern... */
355: if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
356: {
357: /* move the cursor to that line */
358: cursor = MARK_AT_LINE(nlines - l);
359:
360: /* do the ex command (without mucking up
361: * the original copy of the command line)
362: */
363: strcpy(cmdln, cmdptr);
364: rptlines = 0L;
365: doexcmd(cmdln);
366: nchanged += rptlines;
367: }
368: }
369: }
370: doingglobal = FALSE;
371:
372: /* free the regexp */
373: free(re);
374:
375: /* Reporting...*/
376: rptlines = nchanged;
377: }
378:
379:
380: /*ARGSUSED*/
381: void cmd_file(frommark, tomark, cmd, bang, extra)
382: MARK frommark, tomark;
383: CMD cmd;
384: int bang;
385: char *extra;
386: {
387: #ifndef CRUNCH
388: /* if we're given a new filename, use it as this file's name */
389: if (extra && *extra)
390: {
391: strcpy(origname, extra);
392: }
393: #endif
394: if (cmd == CMD_FILE)
395: {
396: msg("\"%s\" %s%s %ld lines, line %ld [%ld%%]",
397: *origname ? origname : "[NO FILE]",
398: tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
399: tstflag(file, READONLY) ? "[READONLY]" : "",
400: nlines,
401: markline(frommark),
402: markline(frommark) * 100 / nlines);
403: }
404: else if (markline(frommark) == markline(tomark))
405: {
406: msg("%ld", markline(frommark));
407: }
408: else
409: {
410: msg("range \"%ld,%ld\" contains %ld lines",
411: markline(frommark),
412: markline(tomark),
413: markline(tomark) - markline(frommark) + 1L);
414: }
415: }
416:
417:
418: /*ARGSUSED*/
419: void cmd_edit(frommark, tomark, cmd, bang, extra)
420: MARK frommark, tomark;
421: CMD cmd;
422: int bang;
423: char *extra;
424: {
425: long line = 1L; /* might be set to prevline */
426:
427: /* Editing previous file? Then start at previous line */
428: if (!strcmp(extra, prevorig))
429: {
430: line = prevline;
431: }
432:
433: #ifndef CRUNCH
434: /* if we were given an explicit starting line, then start there */
435: if (*extra == '+')
436: {
437: for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++)
438: {
439: line *= 10L;
440: line += (*extra - '0');
441: }
442: while (isascii(*extra) && isspace(*extra))
443: {
444: extra++;
445: }
446: }
447: #endif /* not CRUNCH */
448:
449: /* switch files */
450: if (tmpabort(bang))
451: {
452: tmpstart(extra);
453: if (line <= nlines && line >= 1L)
454: {
455: cursor = MARK_AT_LINE(line);
456: }
457: }
458: else
459: {
460: msg("Use edit! to abort changes, or w to save changes");
461:
462: /* so we can say ":e!#" next time... */
463: strcpy(prevorig, extra);
464: prevline = 1L;
465: }
466: }
467:
468: /* This code is also used for rewind -- GB */
469:
470: /*ARGSUSED*/
471: void cmd_next(frommark, tomark, cmd, bang, extra)
472: MARK frommark, tomark;
473: CMD cmd;
474: int bang;
475: char *extra;
476: {
477: int i, j;
478: char *scan;
479: char *build;
480:
481: /* if extra stuff given, use ":args" to define a new args list */
482: if (cmd == CMD_NEXT && extra && *extra)
483: {
484: cmd_args(frommark, tomark, cmd, bang, extra);
485: }
486:
487: /* move to the next arg */
488: if (cmd == CMD_NEXT)
489: {
490: i = argno + 1;
491: }
492: else if (cmd == CMD_PREVIOUS)
493: {
494: i = argno - 1;
495: }
496: else /* cmd == CMD_REWIND */
497: {
498: i = 0;
499: }
500: if (i < 0 || i >= nargs)
501: {
502: msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
503: return;
504: }
505:
506: /* find & isolate the name of the file to edit */
507: for (j = i, scan = args; j > 0; j--)
508: {
509: while(!isascii(*scan) || !isspace(*scan))
510: {
511: scan++;
512: }
513: while (isascii(*scan) && isspace(*scan))
514: {
515: scan++;
516: }
517: }
518: for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); )
519: {
520: *build++ = *scan++;
521: }
522: *build = '\0';
523:
524: /* switch to the next file */
525: if (tmpabort(bang))
526: {
527: tmpstart(tmpblk.c);
528: argno = i;
529: }
530: else
531: {
532: msg("Use :%s! to abort changes, or w to save changes",
533: cmd == CMD_NEXT ? "next" :
534: cmd == CMD_PREVIOUS ? "previous" :
535: "rewind");
536: }
537: }
538:
539: /* also called from :wq -- always writes back in this case */
540:
541: /*ARGSUSED*/
542: void cmd_xit(frommark, tomark, cmd, bang, extra)
543: MARK frommark, tomark;
544: CMD cmd;
545: int bang;
546: char *extra;
547: {
548: static long whenwarned; /* when the user was last warned of extra files */
549: int oldflag;
550:
551: /* if there are more files to edit, then warn user */
552: if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
553: {
554: msg("More files to edit -- Use \":n\" to go to next file");
555: whenwarned = changes;
556: return;
557: }
558:
559: if (cmd == CMD_QUIT)
560: {
561: if (tmpabort(bang))
562: {
563: mode = MODE_QUIT;
564: }
565: else
566: {
567: msg("Use q! to abort changes, or wq to save changes");
568: }
569: }
570: else
571: {
572: /* else try to save this file */
573: oldflag = tstflag(file, MODIFIED);
574: if (cmd == CMD_WQUIT)
575: setflag(file, MODIFIED);
576: if (tmpend(bang))
577: {
578: mode = MODE_QUIT;
579: }
580: else
581: {
582: msg("Could not save file -- use quit! to abort changes, or w filename");
583: }
584: if (!oldflag)
585: clrflag(file, MODIFIED);
586: }
587: }
588:
589:
590: /*ARGSUSED*/
591: void cmd_args(frommark, tomark, cmd, bang, extra)
592: MARK frommark, tomark;
593: CMD cmd;
594: int bang;
595: char *extra;
596: {
597: char *scan;
598: char *eow;
599: int col;
600: int arg;
601: int addcols;
602: int scrolled = 0;
603:
604: /* if no extra names given, or just current name, then report the args
605: * we have now.
606: */
607: if (!extra || !*extra)
608: {
609: for (scan = args, col=arg=0; *scan; )
610: {
611: while (*scan && isascii(*scan) && isspace(*scan))
612: scan++;
613: eow = scan;
614: while (*eow && (!isascii(*++eow) || !isspace(*eow)))
615: ;
616: if (arg == argno)
617: addcols = 2;
618: else
619: addcols = 0;
620: if (col+addcols+(int)(eow-scan)+1>=COLS)
621: {
622: addch('\n');
623: scrolled=1;
624: col=0;
625: }
626: else if (arg)
627: { qaddch(' ');
628: col++;
629: }
630: if (arg == argno)
631: qaddch('[');
632: while (scan < eow)
633: { qaddch(*scan++);
634: col++;
635: }
636: if (arg == argno)
637: qaddch(']');
638: arg++;
639: col+=addcols;
640: }
641: /* write a trailing newline */
642: if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
643: addch('\n');
644: exrefresh();
645: }
646: else /* new args list given */
647: {
648: strcpy(args, extra);
649: argno = -1; /* before the first, so :next will go to first */
650:
651: /* count the names */
652: for (nargs = 0, scan = args; *scan; nargs++)
653: {
654: while (*scan && (!isascii(*scan) || !isspace(*scan)))
655: {
656: scan++;
657: }
658: while (isascii(*scan) && isspace(*scan))
659: {
660: scan++;
661: }
662: }
663: msg("%d files to edit", nargs);
664: }
665: }
666:
667:
668: /*ARGSUSED*/
669: void cmd_cd(frommark, tomark, cmd, bang, extra)
670: MARK frommark, tomark;
671: CMD cmd;
672: int bang;
673: char *extra;
674: {
675: char *getenv();
676:
677: /* default directory name is $HOME */
678: if (!*extra)
679: {
680: extra = getenv("HOME");
681: if (!extra)
682: {
683: msg("environment variable $HOME not set");
684: return;
685: }
686: }
687:
688: /* go to the directory */
689: if (chdir(extra) < 0)
690: {
691: perror(extra);
692: }
693: }
694:
695:
696: /*ARGSUSED*/
697: void cmd_map(frommark, tomark, cmd, bang, extra)
698: MARK frommark, tomark;
699: CMD cmd;
700: int bang;
701: char *extra;
702: {
703: char *mapto;
704:
705: /* "map" with no extra will dump the map table contents */
706: if (!*extra)
707: {
708: dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD);
709: }
710: else
711: {
712: /* "extra" is key to map, followed my what it maps to */
713: for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++)
714: {
715: }
716: while (*mapto == ' ' || *mapto == '\t')
717: {
718: *mapto++ = '\0';
719: }
720:
721: mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
722: }
723: }
724:
725:
726: /*ARGSUSED*/
727: void cmd_set(frommark, tomark, cmd, bang, extra)
728: MARK frommark, tomark;
729: CMD cmd;
730: int bang;
731: char *extra;
732: {
733: if (!*extra)
734: {
735: dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
736: }
737: else if (!strcmp(extra, "all"))
738: {
739: dumpopts(TRUE); /* "TRUE" means "dump all" - even unset vars */
740: }
741: else
742: {
743: setopts(extra);
744:
745: /* That option may have affected the appearence of text */
746: changes++;
747: }
748: }
749:
750: /*ARGSUSED*/
751: void cmd_tag(frommark, tomark, cmd, bang, extra)
752: MARK frommark, tomark;
753: CMD cmd;
754: int bang;
755: char *extra;
756: {
757: char *scan; /* used to scan through the tmpblk.c */
758: char *cmp; /* char of tag name we're comparing, or NULL */
759: char *end; /* marks the end of chars in tmpblk.c */
760: int fd; /* file descriptor used to read the file */
761: #ifndef NO_MAGIC
762: char wasmagic; /* preserves the original state of o_magic */
763: #endif
764: static char prevtag[30];
765:
766: /* if no tag is given, use the previous tag */
767: if (!extra || !*extra)
768: {
769: if (!*prevtag)
770: {
771: msg("No previous tag");
772: return;
773: }
774: extra = prevtag;
775: }
776: else
777: {
778: strncpy(prevtag, extra, sizeof prevtag);
779: }
780:
781: /* open the tags file */
782: fd = open(TAGS, O_RDONLY);
783: if (fd < 0)
784: {
785: msg("No tags file");
786: return;
787: }
788:
789: /* Hmmm... this would have been a lot easier with <stdio.h> */
790:
791: /* find the line with our tag in it */
792: for(scan = end = tmpblk.c, cmp = extra; ; scan++)
793: {
794: /* read a block, if necessary */
795: if (scan >= end)
796: {
797: end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
798: scan = tmpblk.c;
799: if (scan >= end)
800: {
801: msg("tag \"%s\" not found", extra);
802: close(fd);
803: return;
804: }
805: }
806:
807: /* if we're comparing, compare... */
808: if (cmp)
809: {
810: /* matched??? wow! */
811: if (!*cmp && *scan == '\t')
812: {
813: break;
814: }
815: if (*cmp++ != *scan)
816: {
817: /* failed! skip to newline */
818: cmp = (char *)0;
819: }
820: }
821:
822: /* if we're skipping to newline, do it fast! */
823: if (!cmp)
824: {
825: while (scan < end && *scan != '\n')
826: {
827: scan++;
828: }
829: if (scan < end)
830: {
831: cmp = extra;
832: }
833: }
834: }
835:
836: /* found it! get the rest of the line into memory */
837: for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
838: {
839: *cmp++ = *scan++;
840: }
841: if (scan == end)
842: {
843: tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c));
844: }
845:
846: /* we can close the tags file now */
847: close(fd);
848:
849: /* extract the filename from the line, and edit the file */
850: for (cmp = tmpblk.c; *cmp != '\t'; cmp++)
851: {
852: }
853: *cmp++ = '\0';
854: if (strcmp(origname, tmpblk.c) != 0)
855: {
856: if (!tmpabort(bang))
857: {
858: msg("Use :tag! to abort changes, or :w to save changes");
859: return;
860: }
861: tmpstart(tmpblk.c);
862: }
863:
864: /* move to the desired line (or to line 1 if that fails) */
865: #ifndef NO_MAGIC
866: wasmagic = *o_magic;
867: *o_magic = FALSE;
868: #endif
869: cursor = MARK_FIRST;
870: linespec(cmp, &cursor);
871: if (cursor == MARK_UNSET)
872: {
873: cursor = MARK_FIRST;
874: }
875: #ifndef NO_MAGIC
876: *o_magic = wasmagic;
877: #endif
878: }
879:
880:
881:
882: /*ARGSUSED*/
883: void cmd_visual(frommark, tomark, cmd, bang, extra)
884: MARK frommark, tomark;
885: CMD cmd;
886: int bang;
887: char *extra;
888: {
889: mode = MODE_VI;
890: msg("");
891: }
892:
893:
894:
895:
896:
897: /* describe this version of the program */
898: /*ARGSUSED*/
899: void cmd_version(frommark, tomark, cmd, bang, extra)
900: MARK frommark;
901: MARK tomark;
902: CMD cmd;
903: int bang;
904: char *extra;
905: {
906: #ifndef DATE
907: msg("%s", VERSION);
908: #else
909: msg("%s (%s)", VERSION, DATE);
910: #endif
911: #ifdef COMPILED_BY
912: msg("Compiled by %s", COMPILED_BY);
913: #endif
914: #ifdef CREDIT
915: msg("%s", CREDIT);
916: #endif
917: #ifdef COPYING
918: msg("%s", COPYING);
919: #endif
920: }
921:
922:
923: #ifndef NO_MKEXRC
924: /* make a .exrc file which describes the current configuration */
925: /*ARGSUSED*/
926: void cmd_mkexrc(frommark, tomark, cmd, bang, extra)
927: MARK frommark;
928: MARK tomark;
929: CMD cmd;
930: int bang;
931: char *extra;
932: {
933: int fd;
934:
935: /* the default name for the .exrc file EXRC */
936: if (!*extra)
937: {
938: extra = EXRC;
939: }
940:
941: /* create the .exrc file */
942: fd = creat(extra, FILEPERMS);
943: if (fd < 0)
944: {
945: msg("Couldn't create a new \"%s\" file", extra);
946: return;
947: }
948:
949: /* save stuff */
950: savekeys(fd);
951: saveopts(fd);
952: #ifndef NO_DIGRAPH
953: savedigs(fd);
954: #endif
955: #ifndef NO_ABBR
956: saveabbr(fd);
957: #endif
958:
959: /* close the file */
960: close(fd);
961: msg("Created a new \"%s\" file", extra);
962: }
963: #endif
964:
965: #ifndef NO_DIGRAPH
966: /*ARGSUSED*/
967: void cmd_digraph(frommark, tomark, cmd, bang, extra)
968: MARK frommark;
969: MARK tomark;
970: CMD cmd;
971: int bang;
972: char *extra;
973: {
974: do_digraph(bang, extra);
975: }
976: #endif
977:
978:
979: #ifndef NO_ERRLIST
980: static char errfile[256]; /* the name of a file containing an error */
981: static long errline; /* the line number for an error */
982:
983: /* This static function tries to parse an error message.
984: *
985: * For most compilers, the first word is taken to be the name of the erroneous
986: * file, and the first number after that is taken to be the line number where
987: * the error was detected. The description of the error follows, possibly
988: * preceded by an "error ... :" or "warning ... :" label which is skipped.
989: *
990: * For Coherent, error messages look like "line#: filename: message".
991: *
992: * For non-error lines, or unparsable error lines, this function returns NULL.
993: * Normally, though, it alters errfile and errline, and returns a pointer to
994: * the description.
995: */
996: static char *parse_errmsg(text)
997: REG char *text;
998: {
999: REG char *cpy;
1000: long atol();
1001: # if COHERENT
1002: /* Get the line number. If no line number, then ignore this line. */
1003: errline = atol(text);
1004: if (errline == 0L)
1005: return (char *)0;
1006:
1007: /* Skip to the start of the filename */
1008: while (*text && *text++ != ':')
1009: {
1010: }
1011: if (!*text++)
1012: return (char *)0;
1013:
1014: /* copy the filename to errfile */
1015: for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
1016: {
1017: }
1018: if (!*text++)
1019: return (char *)0;
1020: cpy[-1] = '\0';
1021:
1022: return text;
1023: # else /* not COHERENT */
1024: char *errmsg;
1025:
1026: /* the error message is the whole line, by default */
1027: errmsg = text;
1028:
1029: /* skip leading garbage */
1030: while (*text && !(isascii(*text) && isalnum(*text)))
1031: {
1032: text++;
1033: }
1034:
1035: /* copy over the filename */
1036: cpy = errfile;
1037: while(isascii(*text) && isalnum(*text) || *text == '.')
1038: {
1039: *cpy++ = *text++;
1040: }
1041: *cpy = '\0';
1042:
1043: /* ignore the name "Error" and filenames that contain a '/' */
1044: if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
1045: {
1046: return (char *)0;
1047: }
1048:
1049: /* skip garbage between filename and line number */
1050: while (*text && !(isascii(*text) && isdigit(*text)))
1051: {
1052: text++;
1053: }
1054:
1055: /* if the number is part of a larger word, then ignore this line */
1056: if (*text && isascii(text[-1]) && isalpha(text[-1]))
1057: {
1058: return (char *)0;
1059: }
1060:
1061: /* get the error line */
1062: errline = 0L;
1063: while (isascii(*text) && isdigit(*text))
1064: {
1065: errline *= 10;
1066: errline += (*text - '0');
1067: text++;
1068: }
1069:
1070: /* any line which lacks a filename or line number should be ignored */
1071: if (!errfile[0] || !errline)
1072: {
1073: return (char *)0;
1074: }
1075:
1076: /* locate the beginning of the error description */
1077: while (*text && isascii(*text) && !isspace(*text))
1078: {
1079: text++;
1080: }
1081: while (*text)
1082: {
1083: # ifndef CRUNCH
1084: /* skip "error #:" and "warning #:" clauses */
1085: if (!strncmp(text + 1, "rror ", 5)
1086: || !strncmp(text + 1, "arning ", 7)
1087: || !strncmp(text + 1, "atal error", 10))
1088: {
1089: do
1090: {
1091: text++;
1092: } while (*text && *text != ':');
1093: continue;
1094: }
1095: # endif
1096:
1097: /* anything other than whitespace or a colon is important */
1098: if (!isascii(*text) || (!isspace(*text) && *text != ':'))
1099: {
1100: errmsg = text;
1101: break;
1102: }
1103:
1104: /* else keep looking... */
1105: text++;
1106: }
1107:
1108: return errmsg;
1109: # endif /* not COHERENT */
1110: }
1111:
1112: /*ARGSUSED*/
1113: void cmd_errlist(frommark, tomark, cmd, bang, extra)
1114: MARK frommark, tomark;
1115: CMD cmd;
1116: int bang;
1117: char *extra;
1118: {
1119: static long endline;/* original number of lines in this file */
1120: static long offset; /* offset of the next line in the errlist file */
1121: static int fd = -2;/* fd of the errlist file */
1122: int i;
1123: char *errmsg;
1124:
1125: /* if a new errlist file is named, open it */
1126: if (extra && extra[0])
1127: {
1128: /* close the old one */
1129: if (fd >= 0)
1130: {
1131: close(fd);
1132: }
1133:
1134: fd = open(extra, O_RDONLY);
1135: offset = 0L;
1136: }
1137: else if (fd < 0)
1138: {
1139: fd = open(ERRLIST, O_RDONLY);
1140: offset = 0L;
1141: }
1142:
1143: /* do we have an errlist file now? */
1144: if (fd < 0)
1145: {
1146: msg("There is no errlist file");
1147: beep();
1148: return;
1149: }
1150:
1151: /* find the next error message in the file */
1152: do
1153: {
1154: /* read the next line from the errlist */
1155: lseek(fd, offset, 0);
1156: if (read(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
1157: {
1158: msg("No more errors");
1159: beep();
1160: close(fd);
1161: return;
1162: }
1163: for (i = 0; tmpblk.c[i] != '\n'; i++)
1164: {
1165: }
1166: tmpblk.c[i++] = 0;
1167:
1168: /* look for an error message in the line */
1169: errmsg = parse_errmsg(tmpblk.c);
1170: if (!errmsg)
1171: {
1172: offset += i;
1173: }
1174:
1175: } while (!errmsg);
1176:
1177: /* switch to the file containing the error, if this isn't it */
1178: if (strcmp(origname, errfile))
1179: {
1180: if (!tmpabort(bang))
1181: {
1182: msg("Use :er! to abort changes, or :w to save changes");
1183: beep();
1184: return;
1185: }
1186: tmpstart(errfile);
1187: endline = nlines;
1188: }
1189: else if (endline == 0L)
1190: {
1191: endline = nlines;
1192: }
1193:
1194: /* go to the line where the error was detected */
1195: cursor = MARK_AT_LINE(errline + (nlines - endline));
1196: if (cursor > MARK_LAST)
1197: {
1198: cursor = MARK_LAST;
1199: }
1200: if (mode == MODE_VI)
1201: {
1202: redraw(cursor, FALSE);
1203: }
1204:
1205: /* display the error message */
1206: if (nlines > endline)
1207: {
1208: msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
1209: }
1210: else if (nlines < endline)
1211: {
1212: msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
1213: }
1214: else
1215: {
1216: msg("line %ld: %.65s", errline, errmsg);
1217: }
1218:
1219: /* remember where the NEXT error line will start */
1220: offset += i;
1221: }
1222:
1223:
1224: /*ARGSUSED*/
1225: void cmd_make(frommark, tomark, cmd, bang, extra)
1226: MARK frommark, tomark;
1227: CMD cmd;
1228: int bang;
1229: char *extra;
1230: {
1231: BLK buf;
1232:
1233: /* if the file hasn't been saved, then complain unless ! */
1234: if (tstflag(file, MODIFIED) && !bang)
1235: {
1236: msg("\"%s\" not saved yet", origname);
1237: return;
1238: }
1239:
1240: /* build the command */
1241: sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
1242: qaddstr(buf.c);
1243: addch('\n');
1244:
1245: /* run the command, with curses temporarily disabled */
1246: suspend_curses();
1247: system(buf.c);
1248: resume_curses(mode == MODE_EX);
1249: if (mode == MODE_COLON)
1250: mode = MODE_VI;
1251:
1252: /* run the "errlist" command */
1253: cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
1254: }
1255: #endif
1256:
1257:
1258: #ifndef NO_ABBR
1259: /*ARGSUSED*/
1260: void cmd_abbr(frommark, tomark, cmd, bang, extra)
1261: MARK frommark, tomark;
1262: CMD cmd;
1263: int bang;
1264: char *extra;
1265: {
1266: do_abbr(extra);
1267: }
1268: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.