Commit | Line | Data |
---|---|---|
fb51ca13 C |
1 | /************************************************************************* |
2 | * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is * | |
3 | * provided to you without charge for use only on a licensed Unix * | |
4 | * system. You may copy JOVE provided that this notice is included with * | |
5 | * the copy. You may not sell copies of this program or versions * | |
6 | * modified for use on microcomputer systems, unless the copies are * | |
7 | * included with a Unix system distribution and the source is provided. * | |
8 | *************************************************************************/ | |
9 | ||
10 | #include "jove.h" | |
11 | #include "io.h" | |
12 | #include "termcap.h" | |
13 | ||
14 | #include <signal.h> | |
15 | ||
16 | private char | |
17 | *errfmt = "^\\{\",\\}\\([^:\"( \t]*\\)\\{\"\\, line ,:,(\\} *\\([0-9][0-9]*\\)[:)]"; | |
18 | ||
19 | struct error { | |
20 | Buffer *er_buf; /* Buffer error is in */ | |
21 | Line *er_mess, /* Actual error message */ | |
22 | *er_text; /* Actual error */ | |
23 | int er_char; /* char pos of error */ | |
24 | struct error *er_prev, /* List of errors */ | |
25 | *er_next; | |
26 | }; | |
27 | ||
28 | struct error *cur_error = 0, | |
29 | *errorlist = 0; | |
30 | Buffer *perr_buf = 0; /* Buffer with error messages */ | |
31 | ||
32 | int WtOnMk = 1; /* Write the modified files when we make */ | |
33 | ||
34 | /* Add an error to the end of the list of errors. This is used for | |
35 | parse-{C,LINT}-errors and for the spell-buffer command */ | |
36 | ||
37 | private struct error * | |
38 | AddError(laste, errline, buf, line, charpos) | |
39 | struct error *laste; | |
40 | Line *errline, | |
41 | *line; | |
42 | Buffer *buf; | |
43 | { | |
44 | struct error *new = (struct error *) emalloc(sizeof *new); | |
45 | ||
46 | new->er_prev = laste; | |
47 | if (laste) | |
48 | laste->er_next = new; | |
49 | else { | |
50 | if (errorlist) /* Free up old errors */ | |
51 | ErrFree(); | |
52 | cur_error = errorlist = new; | |
53 | } | |
54 | laste = new; | |
55 | new->er_next = 0; | |
56 | new->er_buf = buf; | |
57 | new->er_text = line; | |
58 | new->er_char = charpos; | |
59 | new->er_mess = errline; | |
60 | ||
61 | return new; | |
62 | } | |
63 | ||
64 | ParseAll() | |
65 | { | |
66 | ErrParse(errfmt); | |
67 | } | |
68 | ||
69 | XParse() | |
70 | { | |
71 | char *sstr; | |
72 | ||
73 | sstr = ask(errfmt, ProcFmt); | |
74 | ErrParse(sstr); | |
75 | } | |
76 | ||
77 | /* Parse for {C,LINT} errors (or anything that matches fmtstr) in the | |
78 | current buffer. Set up for the next-error command. This is neat | |
79 | because this will work for any kind of output that prints a file | |
80 | name and a line number on the same line. */ | |
81 | ||
82 | ErrParse(fmtstr) | |
83 | char *fmtstr; | |
84 | { | |
85 | Bufpos *bp; | |
86 | char fname[FILESIZE], | |
87 | lineno[10], | |
88 | REbuf[128], | |
89 | *REalts[10]; | |
90 | int lnum, | |
91 | last_lnum = -1; | |
92 | struct error *ep = 0; | |
93 | Buffer *buf, | |
94 | *lastb = 0; | |
95 | Line *err_line; | |
96 | ||
97 | ErrFree(); /* This is important! */ | |
98 | ToFirst(); | |
99 | perr_buf = curbuf; | |
100 | REcompile(fmtstr, 1, REbuf, REalts); | |
101 | /* Find a line with a number on it. */ | |
102 | while (bp = docompiled(FORWARD, REbuf, REalts)) { | |
103 | SetDot(bp); | |
104 | putmatch(1, fname, sizeof fname); | |
105 | putmatch(2, lineno, sizeof lineno); | |
106 | buf = do_find((Window *) 0, fname, 1); | |
107 | if (buf != lastb) { | |
108 | lastb = buf; | |
109 | last_lnum = -1; /* signals new file */ | |
110 | err_line = buf->b_first; | |
111 | } | |
112 | lnum = chr_to_int(lineno, 10, 0); | |
113 | if (lnum == last_lnum) /* one error per line is nicer */ | |
114 | continue; | |
115 | if (last_lnum == -1) | |
116 | last_lnum = 1; /* that's where we really are */ | |
117 | err_line = next_line(err_line, lnum - last_lnum); | |
118 | ep = AddError(ep, curline, buf, err_line, 0); | |
119 | last_lnum = lnum; | |
120 | } | |
121 | if (cur_error != 0) | |
122 | ShowErr(); | |
123 | exp = 1; | |
124 | } | |
125 | ||
126 | /* Free up all the errors */ | |
127 | ||
128 | ErrFree() | |
129 | { | |
130 | register struct error *ep; | |
131 | ||
132 | for (ep = errorlist; ep != 0; ep = ep->er_next) | |
133 | free((char *) ep); | |
134 | errorlist = cur_error = 0; | |
135 | } | |
136 | ||
137 | /* Internal next error sets cur_error to the next error, taking the | |
138 | argument count, supplied by the user, into consideration. */ | |
139 | ||
140 | private char errbounds[] = "You're at the %s error.", | |
141 | noerrs[] = "No errors!"; | |
142 | ||
143 | private | |
144 | toerror(forward) | |
145 | { | |
146 | register int i; | |
147 | register struct error *e = cur_error; | |
148 | ||
149 | if (e == 0) | |
150 | complain(noerrs); | |
151 | if ((forward && (e->er_next == 0)) || | |
152 | (!forward && (e->er_prev == 0))) | |
153 | complain(errbounds, forward ? "last" : "first"); | |
154 | ||
155 | for (i = 0; i < exp; i++) { | |
156 | if ((e = forward ? e->er_next : e->er_prev) == 0) | |
157 | break; | |
158 | cur_error = e; | |
159 | } | |
160 | } | |
161 | ||
162 | NextError() | |
163 | { | |
164 | ToError(1); | |
165 | } | |
166 | ||
167 | PrevError() | |
168 | { | |
169 | ToError(0); | |
170 | } | |
171 | ||
172 | private | |
173 | okay_error() | |
174 | { | |
175 | return ((inlist(perr_buf->b_first, cur_error->er_mess)) && | |
176 | (inlist(cur_error->er_buf->b_first, cur_error->er_text))); | |
177 | } | |
178 | ||
179 | /* Go the the next error, if there is one. Put the error buffer in | |
180 | one window and the buffer with the error in another window. | |
181 | It checks to make sure that the error actually exists. */ | |
182 | ||
183 | ToError(forward) | |
184 | { | |
185 | do { | |
186 | toerror(forward); | |
187 | exp = 1; | |
188 | } while (!okay_error()); | |
189 | ShowErr(); | |
190 | } | |
191 | ||
192 | int EWSize = 20; /* percentage of screen the error window | |
193 | should be */ | |
194 | ||
195 | /* Show the current error, i.e. put the line containing the error message | |
196 | in one window, and the buffer containing the actual error in another | |
197 | window. */ | |
198 | ||
199 | ShowErr() | |
200 | { | |
201 | Window *err_wind, | |
202 | *buf_wind; | |
203 | int w_size; /* size of window */ | |
204 | ||
205 | if (cur_error == 0) | |
206 | complain(noerrs); | |
207 | if (!okay_error()) { | |
208 | rbell(); | |
209 | return; | |
210 | } | |
211 | err_wind = windbp(perr_buf); | |
212 | buf_wind = windbp(cur_error->er_buf); | |
213 | ||
214 | if (err_wind && !buf_wind) { | |
215 | SetWind(err_wind); | |
216 | pop_wind(cur_error->er_buf->b_name, 0, -1); | |
217 | buf_wind = curwind; | |
218 | } else if (!err_wind && buf_wind) { | |
219 | SetWind(buf_wind); | |
220 | pop_wind(perr_buf->b_name, 0, -1); | |
221 | err_wind = curwind; | |
222 | } else if (!err_wind && !buf_wind) { | |
223 | pop_wind(perr_buf->b_name, 0, -1); | |
224 | err_wind = curwind; | |
225 | pop_wind(cur_error->er_buf->b_name, 0, -1); | |
226 | buf_wind = curwind; | |
227 | } | |
228 | ||
229 | /* Put the current error message at the top of its Window */ | |
230 | SetWind(err_wind); | |
231 | SetLine(cur_error->er_mess); | |
232 | SetTop(curwind, (curwind->w_line = cur_error->er_mess)); | |
233 | w_size = (ILI * EWSize) / 100; | |
234 | if (w_size >= 1) | |
235 | WindSize(curwind, w_size - (curwind->w_height - 1)); | |
236 | ||
237 | /* now go to the the line with the error in the other window */ | |
238 | SetWind(buf_wind); | |
239 | DotTo(cur_error->er_text, cur_error->er_char); | |
240 | } | |
241 | ||
242 | char ShcomBuf[128] = {0}; | |
243 | ||
244 | /* Make a buffer name given the command `command', i.e. "fgrep -n foo *.c" | |
245 | will return the buffer name "fgrep". */ | |
246 | ||
247 | char * | |
248 | MakeName(command) | |
249 | char *command; | |
250 | { | |
251 | static char bufname[50]; | |
252 | register char *cp = bufname, | |
253 | c; | |
254 | ||
255 | while ((c = *command++) && (c == ' ' || c == '\t')) | |
256 | ; | |
257 | do | |
258 | *cp++ = c; | |
259 | while ((c = *command++) && (c != ' ' && c != '\t')); | |
260 | *cp = 0; | |
261 | strcpy(bufname, basename(bufname)); | |
262 | ||
263 | return bufname; | |
264 | } | |
265 | ||
266 | /* Run make, first writing all the modified buffers (if the WtOnMk flag is | |
267 | non-zero), parse the errors, and go the first error. */ | |
268 | ||
269 | char make_cmd[128] = "make"; | |
270 | ||
271 | MakeErrors() | |
272 | { | |
273 | Window *old = curwind; | |
274 | int status, | |
275 | compilation; | |
276 | ||
277 | if (WtOnMk) | |
278 | put_bufs(0); | |
279 | /* When we're not doing make or cc (i.e., the last command | |
280 | was probably a grep or something) and the user just types | |
281 | C-X C-E, he probably (possibly, hopefully, usually (in my | |
282 | case)) doesn't want to do the grep again but rather wants | |
283 | to do a make again; so we ring the bell and insert the | |
284 | default command and let the person decide. */ | |
285 | ||
286 | compilation = (sindex("make", make_cmd) || sindex("cc", make_cmd)); | |
287 | if (exp_p || !compilation) { | |
288 | if (!compilation) { | |
289 | rbell(); | |
290 | Inputp = make_cmd; /* insert the default for the | |
291 | user */ | |
292 | } | |
293 | null_ncpy(make_cmd, ask(make_cmd, "Compilation command: "), | |
294 | sizeof (make_cmd) - 1); | |
295 | } | |
296 | status = UnixToBuf(MakeName(make_cmd), YES, EWSize, YES, Shell, basename(Shell), ShFlags, make_cmd, 0); | |
297 | com_finish(status, make_cmd); | |
298 | ||
299 | ErrParse(errfmt); | |
300 | ||
301 | if (!cur_error) | |
302 | SetWind(old); | |
303 | } | |
304 | ||
305 | #ifdef SPELL | |
306 | ||
307 | SpelBuffer() | |
308 | { | |
309 | char *Spell = "Spell", | |
310 | com[100]; | |
311 | Window *savewp = curwind; | |
312 | ||
313 | put_bufs(0); | |
314 | sprintf(com, "spell %s", curbuf->b_fname); | |
315 | (void) UnixToBuf(Spell, YES, EWSize, YES, Shell, basename(Shell), ShFlags, com, 0); | |
316 | message("[Delete the irrelevant words and then type C-X C-C]"); | |
317 | Recur(); | |
318 | SetWind(savewp); | |
319 | SpelParse(Spell); | |
320 | } | |
321 | ||
322 | SpelWords() | |
323 | { | |
324 | char *buftospel; | |
325 | Buffer *wordsb = curbuf; | |
326 | ||
327 | if ((buftospel = ask_buf((Buffer *) 0)) == 0) | |
328 | return; | |
329 | SetBuf(do_select(curwind, buftospel)); | |
330 | SpelParse(wordsb->b_name); | |
331 | } | |
332 | ||
333 | SpelParse(bname) | |
334 | char *bname; | |
335 | { | |
336 | Buffer *buftospel, | |
337 | *wordsb; | |
338 | char wordspel[100]; | |
339 | Bufpos *bp; | |
340 | struct error *ep = 0; | |
341 | ||
342 | ErrFree(); /* This is important! */ | |
343 | ||
344 | buftospel = curbuf; | |
345 | wordsb = buf_exists(bname); | |
346 | perr_buf = wordsb; /* This is important (buffer containing | |
347 | error messages) */ | |
348 | SetBuf(wordsb); | |
349 | ToFirst(); | |
350 | f_mess("Finding misspelled words ... "); | |
351 | while (!lastp(curline)) { | |
352 | sprintf(wordspel, "\\<%s\\>", linebuf); | |
353 | SetBuf(buftospel); | |
354 | ToFirst(); | |
355 | while (bp = dosearch(wordspel, 1, 1)) { | |
356 | SetDot(bp); | |
357 | ep = AddError(ep, wordsb->b_dot, buftospel, | |
358 | curline, curchar); | |
359 | } | |
360 | SetBuf(wordsb); | |
361 | line_move(FORWARD, NO); | |
362 | } | |
363 | add_mess("Done."); | |
364 | SetBuf(buftospel); | |
365 | ShowErr(); | |
366 | } | |
367 | ||
368 | #endif SPELL | |
369 | ||
370 | ShToBuf() | |
371 | { | |
372 | char bufname[100]; | |
373 | ||
374 | strcpy(bufname, ask((char *) 0, "Buffer: ")); | |
375 | DoShell(bufname, ask(ShcomBuf, "Command: ")); | |
376 | } | |
377 | ||
378 | ShellCom() | |
379 | { | |
380 | null_ncpy(ShcomBuf, ask(ShcomBuf, ProcFmt), (sizeof ShcomBuf) - 1); | |
381 | DoShell(MakeName(ShcomBuf), ShcomBuf); | |
382 | } | |
383 | ||
384 | /* Run the shell command into `bufname'. Empty the buffer except when we | |
385 | give a numeric argument, in which case it inserts the output at the | |
386 | current position in the buffer. */ | |
387 | ||
388 | private | |
389 | DoShell(bufname, command) | |
390 | char *bufname, | |
391 | *command; | |
392 | { | |
393 | Window *savewp = curwind; | |
394 | int status; | |
395 | ||
396 | exp = 1; | |
397 | status = UnixToBuf(bufname, YES, 0, !exp_p, Shell, basename(Shell), | |
398 | ShFlags, command, 0); | |
399 | com_finish(status, command); | |
400 | SetWind(savewp); | |
401 | } | |
402 | ||
403 | private | |
404 | com_finish(status, com) | |
405 | char *com; | |
406 | { | |
407 | s_mess("\"%s\" completed %ssuccessfully.", com, status ? "un" : NullStr); | |
408 | } | |
409 | ||
410 | dowait(pid, status) | |
411 | int pid, | |
412 | *status; | |
413 | { | |
414 | #ifndef IPROCS | |
415 | ||
416 | int rpid; | |
417 | ||
418 | while ((rpid = wait(status)) != pid) | |
419 | ; | |
420 | #else | |
421 | ||
422 | #ifdef BSD4_2 | |
423 | # include <sys/wait.h> | |
424 | #else | |
425 | # include <wait.h> | |
426 | #endif | |
427 | ||
428 | union wait w; | |
429 | int rpid; | |
430 | ||
431 | for (;;) { | |
432 | #ifndef VMUNIX | |
433 | rpid = wait2(&w.w_status, 0); | |
434 | #else | |
435 | rpid = wait3(&w, 0, (struct rusage *) 0); | |
436 | #endif | |
437 | if (rpid == pid) { | |
438 | if (status) | |
439 | *status = w.w_status; | |
440 | break; | |
441 | } else | |
442 | kill_off(rpid, w); | |
443 | } | |
444 | #endif IPROCS | |
445 | } | |
446 | ||
447 | /* Run the command to bufname, erase the buffer if clobber is non-zero, | |
448 | and redisplay if disp is non-zero. Leaves current buffer in `bufname' | |
449 | and leaves any windows it creates lying around. It's up to the caller | |
450 | to fix everything up after we're done. (Usually there's nothing to | |
451 | fix up.) */ | |
452 | ||
453 | /* VARARGS3 */ | |
454 | ||
455 | UnixToBuf(bufname, disp, wsize, clobber, cmd, args) | |
456 | char *bufname, | |
457 | *cmd; | |
458 | { | |
459 | int p[2], | |
460 | pid; | |
461 | extern int ninbuf; | |
462 | Buffer *bp; | |
463 | ||
464 | if (clobber && (bp = buf_exists(bufname)) != 0 && | |
465 | bp->b_type != B_PROCESS && bp->b_type != B_IPROCESS) | |
466 | complain("Command would over-write buffer %s.", bufname); | |
467 | if (disp) { | |
468 | message("Starting up..."); | |
469 | pop_wind(bufname, clobber, clobber ? B_PROCESS : B_FILE); | |
470 | wsize = (LI * wsize) / 100; | |
471 | if (wsize >= 1 && !one_windp()) | |
472 | WindSize(curwind, wsize - (curwind->w_height - 1)); | |
473 | redisplay(); | |
474 | } | |
475 | exp = 1; | |
476 | dopipe(p); | |
477 | pid = fork(); | |
478 | if (pid == -1) { | |
479 | pclose(p); | |
480 | complain("[Fork failed]"); | |
481 | } | |
482 | if (pid == 0) { | |
483 | (void) close(0); | |
484 | (void) open("/dev/null", 0); | |
485 | (void) close(1); | |
486 | (void) close(2); | |
487 | (void) dup(p[1]); | |
488 | (void) dup(p[1]); | |
489 | pclose(p); | |
490 | execv(cmd, (char **) &args); | |
491 | (void) write(1, "Execl failed.\n", 14); | |
492 | _exit(1); | |
493 | } else { | |
494 | int status; | |
495 | int (*oldint)() = signal(SIGINT, SIG_IGN); | |
496 | char *mess; | |
497 | File *fp; | |
498 | ||
499 | #ifdef IPROCS | |
500 | sighold(SIGCHLD); | |
501 | #endif | |
502 | ||
503 | (void) close(p[1]); | |
504 | fp = fd_open(cmd, F_READ, p[0], iobuff, LBSIZE); | |
505 | while (inIOread = 1, f_gets(fp, genbuf, LBSIZE) != EOF) { | |
506 | inIOread = 0; | |
507 | ins_str(genbuf, YES); | |
508 | LineInsert(); | |
509 | if (disp != 0 && fp->f_cnt <= 0) { | |
510 | #ifdef LOAD_AV | |
511 | { | |
512 | double theavg; | |
513 | ||
514 | get_la(&theavg); | |
515 | if (theavg < 2.0) | |
516 | mess = "Screaming along..."; | |
517 | else if (theavg < 5.0) | |
518 | mess = "Chugging along..."; | |
519 | else | |
520 | mess = "Crawling along..."; | |
521 | } | |
522 | #else | |
523 | mess = "Chugging along..."; | |
524 | #endif LOAD_AV | |
525 | message(mess); | |
526 | redisplay(); | |
527 | } | |
528 | } | |
529 | if (disp) | |
530 | DrawMesg(NO); | |
531 | close_file(fp); | |
532 | (void) signal(SIGINT, oldint); | |
533 | dowait(pid, &status); | |
534 | #ifdef IPROCS | |
535 | sigrelse(SIGCHLD); | |
536 | #endif | |
537 | return status; | |
538 | } | |
539 | return 0; | |
540 | } | |
541 | ||
542 | #ifdef BSD4_2 | |
543 | ||
544 | private int SigMask = 0; | |
545 | ||
546 | sighold(sig) | |
547 | { | |
548 | (void) sigblock(SigMask |= (1 << (sig - 1))); | |
549 | } | |
550 | ||
551 | sigrelse(sig) | |
552 | { | |
553 | (void) sigsetmask(SigMask &= ~(1 << (sig - 1))); | |
554 | } | |
555 | ||
556 | #endif | |
557 | ||
558 | FilterRegion() | |
559 | { | |
560 | char *cmd = ask((char *) 0, ": %f (through command) ", ProcFmt); | |
561 | ||
562 | RegToUnix(curbuf, cmd); | |
563 | } | |
564 | ||
565 | /* Send the current region to CMD and insert the output from the | |
566 | command into OUT_BUF. */ | |
567 | ||
568 | RegToUnix(outbuf, cmd) | |
569 | Buffer *outbuf; | |
570 | char *cmd; | |
571 | { | |
572 | Mark *m = CurMark(); | |
573 | char *tname = mktemp("/tmp/jfilterXXXXXX"), | |
574 | combuf[130]; | |
575 | Window *save_wind = curwind; | |
576 | int status; | |
577 | File *fp; | |
578 | ||
579 | CATCH | |
580 | fp = open_file(tname, iobuff, F_WRITE, COMPLAIN, QUIET); | |
581 | putreg(fp, m->m_line, m->m_char, curline, curchar, YES); | |
582 | DelReg(); | |
583 | sprintf(combuf, "%s < %s", cmd, tname); | |
584 | status = UnixToBuf(outbuf->b_name, NO, 0, outbuf->b_type == B_SCRATCH, | |
585 | Shell, basename(Shell), ShFlags, combuf, 0); | |
586 | ONERROR | |
587 | ; /* Do nothing ... but fall through and delete the tmp | |
588 | file. */ | |
589 | ENDCATCH | |
590 | f_close(fp); | |
591 | (void) unlink(tname); | |
592 | SetWind(save_wind); | |
593 | com_finish(status, combuf); | |
594 | } |