Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 551 lines (484 sloc) 14.814 kb
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
1 /* exmake.c */
2 /* Copyright 1995 by Steve Kirkendall */
3
97d8998 @mbert Import Elvis 2.1_4 (written by Steve Kirkendall)
authored
4 char id_make[] = "$Id: exmake.c,v 2.30 1999/09/30 18:21:36 steve Exp $";
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
5
6 #include "elvis.h"
7
8 #if USE_PROTOTYPES
9 static BOOLEAN parse_errmsg(void);
10 static RESULT gotoerr(EXINFO *xinf);
11 static void errprep(void);
12 #endif
13
3a9bb55 @mbert Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
14 BOOLEAN makeflag;
15
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
16 static char *maybedir; /* directory name extracted from errlist */
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
17 static CHAR *errfile; /* name of file where error was detected */
18 static long errline; /* the line number for an error */
19 static CHAR *errdesc; /* description of the error */
20 static MARK errnext; /* used for stepping through the error list */
21
22
23 /* This function tries to parse an error message. Each error message is
24 * assumed to fit on a single line. Within the line, this parser attempts to
25 * locate three fields: the source file name, the line number, and the
26 * description of the error.
27 *
28 * The line is scanned for words. "Words", for this purpose, are considered
29 * to be contiguous strings of non-whitespace characters other than ':'
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
30 * (unless followed by a backslash) or any of ( ) , " '
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
31 *
32 * If a word happens to be the name of an existing file which is writable
33 * by the user, then it is taken to be the filename. Otherwise, if it contains
34 * only digits then it is taken as the line number.
35 *
36 * After both the filename and line number have been found, the remainder of
37 * the line is taken to be the description... except that any garbage between
38 * the filename/line# and the interesting part of the description will be
39 * discarded. Usually, this discarded text consists simly of a colon and some
40 * whitespace.
41 *
42 * Returns True normally, or False at the end of the list. If it returns True,
43 * then the errfile, errline, and errdesc variables will have been set to
44 * reflect anything that was found in the line; if all three are non-NULL
45 * then the line contained an error message.
46 */
47 static BOOLEAN parse_errmsg()
48 {
49 BUFFER errlist; /* buffer containing err list */
50 CHAR *word; /* dynamically allocated string */
51 CHAR *cp; /* used for scanning line */
52 DIRPERM perms; /* permissions of a file */
53 long top; /* start of line */
54 WINDOW errwin; /* window which displays errors */
55
56 /* if we aren't already reading the errlist, then start at beginning */
57 if (!errnext)
58 {
59 errlist = buffind(toCHAR(ERRLIST_BUF));
60 if (!errlist)
61 goto NoMoreErrors;
62 errnext = markalloc(errlist, 0);
63 }
64
65 /* if we reached the end of the errlist, then say so */
66 if (markoffset(errnext) >= o_bufchars(markbuffer(errnext)))
67 goto NoMoreErrors;
68
69 /* prepare to start scanning */
70 top = markoffset(errnext);
71 if (errfile)
72 safefree(errfile);
73 if (errdesc)
74 safefree(errdesc);
75 errfile = NULL;
76 errline = 0;
77 errdesc = NULL;
78 word = NULL;
79
80 /* scan the line */
81 for (scanalloc(&cp, errnext); cp && *cp != '\n'; cp && scannext(&cp))
82 {
83 /* is the character legal in a word? */
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
84 if (!isspace(*cp) && CHARchr("():\"'`,", *cp) == NULL)
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
85 {
86 /* character in word */
87 buildCHAR(&word, *cp);
88 continue;
89 }
90 else if (*cp == ':' && word)
91 {
92 /* followed by a backslash? */
93 if (scannext(&cp) && *cp == '\\')
94 {
95 /* both characters are in the word */
96 buildCHAR(&word, (_CHAR_)':');
97 buildCHAR(&word, *cp);
98 continue;
99 }
100
101 /* The ':' isn't in the word, and we shouldn't have
102 * used up the following character yet. Go back if
103 * possible.
104 */
105 if (cp)
106 scanprev(&cp);
107 }
108 else if (errfile && errline && (*cp == '"' && *cp == '\'' && *cp != '`'))
109 {
110 /* quotes are allowed at the start of a description */
111 buildCHAR(&word, *cp);
112 continue;
113 }
114
115 /* if we get here, then we aren't in a word. If no word was
116 * in progress before this character, then we can ignore this
117 * character.
118 */
119 if (!word)
120 continue;
121
122 /* So we must have a word that we need to process. Is it the
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
123 * name of an existing, writable text file?
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
124 */
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
125 if (!errfile
97d8998 @mbert Import Elvis 2.1_4 (written by Steve Kirkendall)
authored
126 && isalnum(*word)
127 && ((perms = dirperm(tochar8(word))) == DIR_READWRITE
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
128 || (o_anyerror && perms == DIR_READONLY))
129 && *ioeol(tochar8(word)) != 'b')
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
130 {
131 /* this is the name of the source file */
132 errfile = word;
133 }
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
134 else if (dirperm(tochar8(word)) == DIR_NOTFILE)
135 {
136 /* this may be the name of a directory where
137 * source files reside */
138 if (maybedir)
139 safefree(maybedir);
140 maybedir = tochar8(word);
141 }
142 else if (!errfile
143 && maybedir
144 && ((perms = dirperm(dirpath(maybedir, tochar8(word))) ) == DIR_READWRITE
145 || (o_anyerror && perms == DIR_READONLY))
146 && *ioeol(dirpath(maybedir, tochar8(word))) != 'b')
147 {
148 errfile = CHARdup(toCHAR(dirpath(maybedir, tochar8(word))));
149 safefree(word);
150 }
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
151 else if (!errline && calcnumber(word))
152 {
153 /* this is the line number */
154 errline = atol(tochar8(word));
155 safefree(word);
156 }
157 else if (errfile && errline &&
158 (!CHARcmp(word + 1, toCHAR("arning"))
159 || !CHARcmp(word + 1, toCHAR("rror"))))
160 {
161 /* skip over error number and other garbage */
162 safefree(word);
163 word = NULL;
164 while (cp && *cp != '\n' &&
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
165 (isspace(*cp) || isdigit(*cp) || *cp == ':' || *cp == '-'))
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
166 {
167 scannext(&cp);
168 }
169
170 /* if we hit the end of the line, then we have no
171 * description.
172 */
173 if (!cp || *cp == '\n')
174 {
175 errdesc = CHARdup(toCHAR("unknown error"));
176 }
177
178 /* back up one character, if possible */
179 if (cp)
180 {
181 scanprev(&cp);
182 }
183 }
184 else if (errfile && errline)
185 {
186 /* This word marks the start of the description.
187 * Collect the rest of it.
188 */
189 while (cp && *cp != '\n')
190 {
191 buildCHAR(&word, *cp);
192 scannext(&cp);
193 }
194 break;
195 }
196 else
197 {
198 /* just some word mixed in with filename & line# */
199 safefree(word);
200 }
201
202 /* prepare for next word */
203 word = NULL;
204 }
205
206 /* if we're in a word here, it must be the description */
207 if (word)
208 {
209 assert(!errdesc);
210 errdesc = word;
211 }
212
213 /* if scanning ended at '\n', then move past the '\n' */
214 if (cp && *cp == '\n')
215 {
216 scannext(&cp);
217 }
218
219 /* remember where next line starts */
220 if (cp)
221 {
222 marksetoffset(errnext, markoffset(scanmark(&cp)));
223 }
224 else
225 {
226 marksetoffset(errnext, o_bufchars(markbuffer(errnext)));
227 }
228
229 /* If a window is showing the error list, then highlight the current
230 * line, and leave the cursor at its end.
231 */
232 errwin = winofbuf(NULL, markbuffer(errnext));
233 if (errwin && !errwin->state->acton)
234 {
235 /* move the cursor to the end of the line */
236 marksetoffset(errwin->cursor, markoffset(errnext) - 2);
237 marksetoffset(errwin->state->top, markoffset(errwin->cursor));
238 marksetoffset(errwin->state->bottom, markoffset(errwin->cursor));
239
240 /* select the line, so it appears highlighted */
241 if (!errwin->seltop) errwin->seltop = markdup(errwin->cursor);
242 if (!errwin->selbottom) errwin->selbottom = markdup(errwin->cursor);
243 marksetoffset(errwin->seltop, top);
244 marksetoffset(errwin->selbottom, markoffset(errnext) - 1);
245 errwin->selleft = 0;
246 errwin->selright = INFINITY;
247 errwin->seltype = 'l';
248 }
249
250 /* Set the changepos to end of the message. This is so that if, after
251 * finding one or more messages, the user creates a new window for
252 * viewing the error list, the new window's cursor will start at the
253 * end of the current message instead of at the top of the buffer.
254 */
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
255 markaddoffset(errnext, -1L);
256 bufwilldo(errnext, False);
257 markaddoffset(errnext, 1L);
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
258
259 /* return True since there was a line for us to parse */
260 scanfree(&cp);
261 return True;
262
263 NoMoreErrors:
264 /* if a window is showing the error list, then unhighlight the old
265 * line (if any was highlighted)
266 */
267 errlist = buffind(toCHAR(ERRLIST_BUF));
268 errwin = errlist ? winofbuf(NULL, errlist) : NULL;
269 if (errwin && !errwin->state->acton)
270 {
271 /* leave the cursor after the last error message */
272 if (o_bufchars(errlist) > 2)
273 marksetoffset(errwin->cursor, o_bufchars(errlist) - 2);
274 else
275 marksetoffset(errwin->cursor, 0L);
276 marksetoffset(errwin->state->top, markoffset(errwin->cursor));
277 marksetoffset(errwin->state->bottom, markoffset(errwin->cursor));
278
279 /* unselect the line, so nothing appears highlighted */
280 if (errwin->seltop)
281 {
282 assert(errwin->selbottom);
283 markfree(errwin->seltop);
284 markfree(errwin->selbottom);
285 errwin->seltop = errwin->selbottom = NULL;
286 }
287
288 /* make sure the window will be redrawn */
289 errwin->di->logic = DRAW_CHANGED;
290 }
291
292 return False;
293 }
294
295
296 /* This function moves the cursor to the next error that was detected, and
297 * outputs an informational line describing the error. This function is
298 * called by both ex_errlist() and ex_make().
299 */
300 static RESULT gotoerr(xinf)
301 EXINFO *xinf;
302 {
303 BUFFER blamebuf;
304 long blameline;
305 WINDOW errwin;
306
307 /* if there is a window showing the error buffer, then search forward
308 * from its cursor. (Else search forward from end of previous error.)
309 */
310 errwin = (errnext ? winofbuf(NULL, markbuffer(errnext)) : NULL);
311 if (errwin)
312 marksetoffset(errnext, markoffset(errwin->cursor));
313
314 /* parse lines from the errlist buffer until we find an error message */
315 do
316 {
317 if (!parse_errmsg())
318 {
319 msg(MSG_ERROR, "no more errors");
320 return RESULT_ERROR;
321 }
322 } while (!errfile || !errline || !errdesc);
323
324 /* load (if necessary) the file where error was detected. */
325 blamebuf = bufload(NULL, tochar8(errfile), False);
326 assert(blamebuf != NULL);
327
328 /* figure out which line the cursor should be left on, taking into
329 * account the fact that lines may have been inserted/deleted since
330 * the errlist was created.
331 */
332 blameline = errline + o_buflines(blamebuf) - o_errlines(blamebuf);
333 if (blameline < 1)
334 blameline = 1;
335 else if (blameline > o_buflines(blamebuf))
336 blameline = o_buflines(blamebuf);
337
338 /* move the cursor to the erroneous line of the new buffer */
339 xinf->newcurs = markalloc(blamebuf,
340 lowline(bufbufinfo(blamebuf), blameline));
341
342 /* describe the error */
343 if (o_buflines(blamebuf) == o_errlines(blamebuf))
344 {
345 msg(MSG_INFO, "[dS]line $1: $2", errline, errdesc);
346 }
347 else if (o_buflines(blamebuf) < o_errlines(blamebuf))
348 {
349 msg(MSG_INFO, "[ddS]line $1-$2: $3", errline,
350 o_errlines(blamebuf) - o_buflines(blamebuf), errdesc);
351 }
352 else
353 {
354 msg(MSG_INFO, "[ddS]line $1+$2: $3", errline,
355 o_buflines(blamebuf) - o_errlines(blamebuf), errdesc);
356 }
357
358 return RESULT_COMPLETE;
359 }
360
361
362 /* This function does some preparatory steps towards creating a new errlist */
363 static void errprep()
364 {
365 BUFFER buf;
366 MARKBUF top;
367 MARKBUF end;
368
369 /* find the "Elvis error list" buffer */
370 buf = buffind(toCHAR(ERRLIST_BUF));
371 assert(buf != NULL);
372
373 /* if it has old text in it, discard that text */
374 if (o_bufchars(buf) > 0)
375 {
376 bufreplace(marktmp(top, buf, 0),
377 marktmp(end, buf, o_bufchars(buf)),
378 NULL, 0);
379 }
380
381 /* reset the "errnext" mark so we start at the top of the buffer */
382 if (errnext)
383 {
384 markfree(errnext);
385 errnext = NULL;
386 }
387
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
388 /* clobber the maybedir */
389 if (maybedir)
390 {
391 safefree(maybedir);
392 maybedir = NULL;
393 }
394
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
395 /* remember how many lines each buffer has now */
396 for (buf = NULL; (buf = buflist(buf)) != NULL; )
397 {
398 o_errlines(buf) = o_buflines(buf);
399 }
400 }
401
402
403 RESULT ex_errlist(xinf)
404 EXINFO *xinf;
405 {
406 BUFFER errbuf;
407 WINDOW errwin;
408 MARKBUF from;
409 BOOLEAN retval;
410
411 assert(xinf->command == EX_ERRLIST);
412
413 /* was a filename given? */
414 if (xinf->nfiles > 0 || xinf->rhs)
415 {
416 /* prepare to create a new error list */
417 errprep();
418
419 /* load the buffer from the named file */
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
420 errbuf = bufalloc(toCHAR(ERRLIST_BUF), 0, True);
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
421 retval = bufread(marktmp(from, errbuf, 0), xinf->rhs ? tochar8(xinf->rhs) : xinf->file[0]);
422
423 /* turn off the "modified" flag on the (Elvis error list) buf */
424 o_modified(errbuf) = False;
425
426 /* if there are any windows showing this buffer, move its cursor
427 * to offset 0
428 */
429 for (errwin = NULL; (errwin = winofbuf(errwin, errbuf)) != NULL; )
430 {
431 marksetoffset(errwin->cursor, 0L);
432 }
433
434 /* if failed to read errors, then return RESULT_ERROR */
435 if (!retval)
436 return RESULT_ERROR;
437 }
438
439 /* move the cursor to the next error */
440 return gotoerr(xinf);
441 }
442
443
444 RESULT ex_make(xinf)
445 EXINFO *xinf;
446 {
447 CHAR *args[3];/* arguments to calculate() expression */
448 CHAR *str; /* shell command string */
449 CHAR *io; /* buffer for reading chars from program */
450 int nio; /* number of characters read */
451 MARKBUF start, end;/* ends of errlist buffer, used for appending */
452 BOOLEAN origrefresh;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
453 long origpollfreq;
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
454 BUFFER buf;
455 WINDOW errwin; /* window which is displaying errors */
456
457 assert(xinf->command == EX_MAKE || xinf->command == EX_CC);
458
459 /* if any user buffer was modified & not saved, then complain unless
460 * '!' given.
461 */
462 if (!xinf->bang)
463 {
464 for (buf = NULL; (buf = buflist(buf)) != NULL; )
465 {
466 if (!o_internal(buf) && o_modified(buf))
467 {
468 msg(MSG_ERROR, "[S]$1 modified, not saved", o_bufname(buf));
469 return RESULT_ERROR;
470 }
471 }
472 }
473
474 /* create the shell command line that we'll be running */
475 buf = markbuffer(&xinf->defaddr);
476 args[0] = (xinf->rhs ? xinf->rhs : toCHAR(""));
477 args[1] = (o_filename(buf) ? o_filename(buf) : toCHAR(""));
478 args[2] = NULL;
479 str = calculate(xinf->command==EX_CC ? o_cc(buf) : o_make(buf), args, True);
480 if (!str)
481 {
482 /* error message already given */
483 return RESULT_ERROR;
484 }
485
486 /* output the command name, so the user knows what's happening */
487 origrefresh = o_exrefresh;
488 o_exrefresh = True;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
489 origpollfreq = o_pollfrequency;
490 o_pollfrequency = 1;
491 (void)guipoll(True);
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
492 drawextext(xinf->window, str, (int)CHARlen(str));
493 drawextext(xinf->window, toCHAR("\n"), 1);
494
495 /* prepare to create the new errlist */
496 errprep();
497
498 /* run the program, and read its stdout/stderr. Write this to the
499 * window as ex output text, and also store it in the errlist buffer.
500 */
501 if (!prgopen(tochar8(str), False, True) || !prggo())
502 {
503 /* failed -- error message already given */
504 o_exrefresh = origrefresh;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
505 o_pollfrequency = origpollfreq;
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
506 return RESULT_ERROR;
507 }
508 buf = buffind(toCHAR(ERRLIST_BUF));
509 assert(buf != NULL);
510 if (o_bufchars(buf) > 0)
511 {
512 bufreplace(marktmp(start, buf, 0), marktmp(end, buf, o_bufchars(buf)), NULL, 0);
513 }
514 io = (CHAR *)safealloc(1024, sizeof(CHAR));
515 (void)marktmp(end, buf, 0);
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
516 while (!guipoll(False) && (nio = prgread(io, 1024)) > 0)
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
517 {
518 /* show it on the screen */
519 drawextext(xinf->window, io, nio);
520
521 /* append it to the buffer */
522 bufreplace(&end, &end, io, nio);
523 markaddoffset(&end, nio);
524 }
525 (void)prgclose();
526 safefree(io);
527 o_exrefresh = origrefresh;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
528 o_pollfrequency = origpollfreq;
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
529
530 /* turn off the "modified" flag on the (Elvis error list) buf */
531 o_modified(buf) = False;
532
533 /* if there is a window showing this buffer, move its cursor
534 * to offset 0
535 */
536 for (errwin = NULL; (errwin = winofbuf(errwin, buf)) != NULL; )
537 {
538 marksetoffset(errwin->cursor, 0L);
539 }
540
3a9bb55 @mbert Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
541 /* delay the first error message until after <Enter> */
542 if (eventcounter > 5)
543 {
544 makeflag = True;
545 morehit = False;
546 }
547
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored
548 /* move the cursor to the first error */
549 return gotoerr(xinf);
550 }
Something went wrong with that request. Please try again.