BSD 4_3 development
[unix-history] / usr / contrib / jove / proc.c
CommitLineData
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
16private char
17 *errfmt = "^\\{\",\\}\\([^:\"( \t]*\\)\\{\"\\, line ,:,(\\} *\\([0-9][0-9]*\\)[:)]";
18
19struct 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
28struct error *cur_error = 0,
29 *errorlist = 0;
30Buffer *perr_buf = 0; /* Buffer with error messages */
31
32int 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
37private struct error *
38AddError(laste, errline, buf, line, charpos)
39struct error *laste;
40Line *errline,
41 *line;
42Buffer *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
64ParseAll()
65{
66 ErrParse(errfmt);
67}
68
69XParse()
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
82ErrParse(fmtstr)
83char *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
128ErrFree()
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
140private char errbounds[] = "You're at the %s error.",
141 noerrs[] = "No errors!";
142
143private
144toerror(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
162NextError()
163{
164 ToError(1);
165}
166
167PrevError()
168{
169 ToError(0);
170}
171
172private
173okay_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
183ToError(forward)
184{
185 do {
186 toerror(forward);
187 exp = 1;
188 } while (!okay_error());
189 ShowErr();
190}
191
192int 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
199ShowErr()
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
242char 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
247char *
248MakeName(command)
249char *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
269char make_cmd[128] = "make";
270
271MakeErrors()
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
307SpelBuffer()
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
322SpelWords()
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
333SpelParse(bname)
334char *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
370ShToBuf()
371{
372 char bufname[100];
373
374 strcpy(bufname, ask((char *) 0, "Buffer: "));
375 DoShell(bufname, ask(ShcomBuf, "Command: "));
376}
377
378ShellCom()
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
388private
389DoShell(bufname, command)
390char *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
403private
404com_finish(status, com)
405char *com;
406{
407 s_mess("\"%s\" completed %ssuccessfully.", com, status ? "un" : NullStr);
408}
409
410dowait(pid, status)
411int 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
455UnixToBuf(bufname, disp, wsize, clobber, cmd, args)
456char *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
544private int SigMask = 0;
545
546sighold(sig)
547{
548 (void) sigblock(SigMask |= (1 << (sig - 1)));
549}
550
551sigrelse(sig)
552{
553 (void) sigsetmask(SigMask &= ~(1 << (sig - 1)));
554}
555
556#endif
557
558FilterRegion()
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
568RegToUnix(outbuf, cmd)
569Buffer *outbuf;
570char *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}