1 /* o_messages.cpp
2 *
3 * Copyright (c) 1994-1996, Marko Macek
4 *
5 * You may distribute under the terms of either the GNU General Public
6 * License or the Artistic License, as specified in the README file.
7 *
8 */
9
10 #include "o_messages.h"
11
12 #ifdef CONFIG_OBJ_MESSAGES
13
14 #include "c_commands.h"
15 #include "c_config.h"
16 #include "i_view.h"
17 #include "o_buflist.h"
18 #include "s_files.h"
19 #include "s_string.h"
20 #include "s_util.h"
21
22 #include <stdio.h>
23
24 #define MAXREGEXP 32
25
26 EMessages *CompilerMsgs = 0;
27
28 struct Error {
29 EBuffer *Buf;
30 StlString file;
31 StlString msg;
32 StlString text;
33 int line;
34 int hilit;
35
ErrorError36 Error(const char *fn, int ln, const char *mesg, const char *txt, int hlt) :
37 Buf(0), file(fn), msg(mesg), text(txt), line(ln), hilit(hlt)
38 {}
39 };
40
41 static int NCRegexp = 0;
42 static struct {
43 int RefFile;
44 int RefLine;
45 int RefMsg;
46 RxNode *rx;
47 } CRegexp[MAXREGEXP];
48
AddCRegexp(int file,int line,int msg,const char * regexp)49 int AddCRegexp(int file, int line, int msg, const char *regexp) {
50 //fprintf(stderr, "ADD EXP %d: %s %d %d %d\n", NCRegexp, regexp, file, line, msg);
51 if (NCRegexp >= MAXREGEXP) return 0;
52 CRegexp[NCRegexp].RefFile = file;
53 CRegexp[NCRegexp].RefLine = line;
54 CRegexp[NCRegexp].RefMsg = msg;
55 if ((CRegexp[NCRegexp].rx = RxCompile(regexp)) == NULL) {
56 return 0;
57 }
58 NCRegexp++;
59 return 1;
60 }
61
FreeCRegexp()62 void FreeCRegexp()
63 {
64 while (NCRegexp--)
65 RxFree(CRegexp[NCRegexp].rx);
66 }
67
EMessages(int createFlags,EModel ** ARoot,const char * ADir,const char * ACommand)68 EMessages::EMessages(int createFlags, EModel **ARoot, const char *ADir, const char *ACommand) :
69 EList(createFlags, ARoot, "Messages"),
70 Running(1),
71 BufLen(0),
72 BufPos(0),
73 ReturnCode(-1),
74 MatchCount(0)
75 {
76 CompilerMsgs = this;
77 RunPipe(ADir, ACommand);
78 }
79
~EMessages()80 EMessages::~EMessages() {
81 gui->ClosePipe(PipeId);
82 FreeErrors();
83 CompilerMsgs = 0;
84 }
85
NotifyDelete(EModel * Deleting)86 void EMessages::NotifyDelete(EModel *Deleting) {
87 vector_iterate(Error*, ErrList, it) {
88 if ((*it)->Buf == Deleting) {
89 /* NOT NEEDED!
90 char bk[16];
91 sprintf(bk, "_MSG.%d", i);
92 ((EBuffer *)Deleting)->RemoveBookmark(bk);
93 */
94 (*it)->Buf = 0;
95 }
96 }
97 }
98
FindErrorFiles()99 void EMessages::FindErrorFiles() {
100 unsigned i = 0;
101 vector_iterate(Error*, ErrList, it) {
102 if ((*it)->Buf == 0 && (*it)->file.empty())
103 FindErrorFile(i);
104 ++i;
105 }
106 }
107
FindErrorFile(unsigned err)108 void EMessages::FindErrorFile(unsigned err) {
109 assert(err < ErrList.size());
110 if (ErrList[err]->file.empty())
111 return;
112
113 ErrList[err]->Buf = 0;
114
115 EBuffer *B = FindFile(ErrList[err]->file.c_str());
116 if (B == 0)
117 return;
118
119 if (B->Loaded == 0)
120 return;
121
122 AddFileError(B, err);
123 }
124
AddFileError(EBuffer * B,unsigned err)125 void EMessages::AddFileError(EBuffer *B, unsigned err) {
126
127 assert(err < ErrList.size());
128
129 char bk[16];
130 sprintf(bk, "_MSG.%d", err);
131 EPoint P(ErrList[err]->line - 1, 0);
132
133 if (P.Row >= B->RCount)
134 P.Row = B->RCount - 1;
135 if (P.Row < 0)
136 P.Row = 0;
137
138 if (B->PlaceBookmark(bk, P) == 1)
139 ErrList[err]->Buf = B;
140 }
141
FindFileErrors(EBuffer * B)142 void EMessages::FindFileErrors(EBuffer *B) {
143 unsigned i = 0;
144 vector_iterate(Error*, ErrList, it) {
145 if ((*it)->Buf == 0 && !(*it)->file.empty()) {
146 if (filecmp(B->FileName, (*it)->file.c_str()) == 0)
147 AddFileError(B, i);
148 }
149 ++i;
150 }
151 }
152
RunPipe(const char * ADir,const char * ACommand)153 int EMessages::RunPipe(const char *ADir, const char *ACommand) {
154 if (!KeepMessages)
155 FreeErrors();
156
157 Command = ACommand;
158 Directory = ADir;
159
160 MatchCount = 0;
161 ReturnCode = -1;
162 Running = 1;
163 BufLen = BufPos = 0;
164 Row = (int)ErrList.size() - 1;
165
166 char s[2 * MAXPATH * 4];
167
168 sprintf(s, "[running '%s' in '%s']", ACommand, ADir);
169 AddError(0, -1, 0, s);
170
171 sprintf(s, "Messages [%s]: %s", ADir, ACommand);
172 SetTitle(s);
173
174 ChangeDir(ADir);
175 PipeId = gui->OpenPipe(ACommand, this);
176 return 0;
177 }
178
GetEventMap()179 EEventMap *EMessages::GetEventMap() {
180 return FindEventMap("MESSAGES");
181 }
182
ExecCommand(ExCommands Command,ExState & State)183 int EMessages::ExecCommand(ExCommands Command, ExState &State) {
184 switch (Command) {
185 case ExChildClose:
186 if (Running == 0 || PipeId == -1)
187 break;
188 ReturnCode = gui->ClosePipe(PipeId);
189 PipeId = -1;
190 Running = 0;
191 char s[30];
192 sprintf(s, "[aborted, status=%d]", ReturnCode);
193 AddError(0, -1, 0, s);
194 return 1;
195 case ExActivateInOtherWindow:
196 ShowError(View->Next, Row);
197 return 1;
198 case ExFind:
199 fprintf(stderr, "FIND\n");
200 return 1;
201 default:
202 ;
203 }
204 return EList::ExecCommand(Command, State);
205 }
206
AddError(const char * file,int line,const char * msg,const char * text,int hilit)207 void EMessages::AddError(const char *file, int line, const char *msg, const char *text, int hilit) {
208 ErrList.push_back(new Error(file, line, msg, text, hilit));
209 //fprintf(stderr, "Error %s %d %s %s\n", file, line, msg, text);
210 FindErrorFile((unsigned)ErrList.size() - 1);
211
212 if ((int)ErrList.size() > Count)
213 if (Row >= Count - 1)
214 Row = (int)ErrList.size() - 1;
215
216 UpdateList();
217 }
218
FreeErrors()219 void EMessages::FreeErrors() {
220 unsigned i = 0;
221 vector_iterate(Error*, ErrList, it) {
222 if ((*it)->Buf != 0) {
223 char bk[16];
224 sprintf(bk, "_MSG.%d", i);
225 (*it)->Buf->RemoveBookmark(bk);
226 }
227 delete *it;
228 i++;
229 }
230 ErrList.clear();
231 BufLen = BufPos = 0;
232 }
233
GetLine(char * Line,size_t maxim)234 int EMessages::GetLine(char *Line, size_t maxim) {
235 ssize_t rc;
236 char *p;
237 int l;
238
239 //fprintf(stderr, "GetLine: %d\n", Running);
240
241 *Line = 0;
242 if (Running && PipeId != -1) {
243 rc = gui->ReadPipe(PipeId, MsgBuf + BufLen, sizeof(MsgBuf) - BufLen);
244 //fprintf(stderr, "GetLine: ReadPipe rc = %d\n", rc);
245 if (rc == -1) {
246 ReturnCode = gui->ClosePipe(PipeId);
247 PipeId = -1;
248 Running = 0;
249 }
250 if (rc > 0)
251 BufLen += rc;
252 }
253 l = maxim - 1;
254 if (BufLen - BufPos < l)
255 l = BufLen - BufPos;
256 //fprintf(stderr, "GetLine: Data %d\n", l);
257 p = (char *)memchr(MsgBuf + BufPos, '\n', l);
258 if (p) {
259 *p = 0;
260 UnEscStr(Line, maxim, MsgBuf + BufPos, p - (MsgBuf + BufPos));
261 //strcpy(Line, MsgBuf + BufPos);
262 l = strlen(Line);
263 if (l > 0 && Line[l - 1] == '\r')
264 Line[l - 1] = 0;
265 BufPos = p + 1 - MsgBuf;
266 //fprintf(stderr, "GetLine: Line %d\n", strlen(Line));
267 } else if (Running && sizeof(MsgBuf) != BufLen) {
268 memmove(MsgBuf, MsgBuf + BufPos, BufLen - BufPos);
269 BufLen -= BufPos;
270 BufPos = 0;
271 //fprintf(stderr, "GetLine: Line Incomplete\n");
272 return 0;
273 } else {
274 if (l == 0)
275 return 0;
276 UnEscStr(Line, maxim, MsgBuf + BufPos, l);
277 //memcpy(Line, MsgBuf + BufPos, l);
278 Line[l] = 0;
279 if (l > 0 && Line[l - 1] == '\r')
280 Line[l - 1] = 0;
281 BufPos += l;
282 //fprintf(stderr, "GetLine: Line Last %d\n", l);
283 }
284 memmove(MsgBuf, MsgBuf + BufPos, BufLen - BufPos);
285 BufLen -= BufPos;
286 BufPos = 0;
287 //fprintf(stderr, "GetLine: Got Line\n");
288 return 1;
289 }
290
291
getWord(char * dest,const char * pin)292 static void getWord(char* dest, const char* pin)
293 {
294 char *pout, *pend;
295 char ch, ec;
296
297 while (*pin == ' ' || *pin == '\t')
298 pin++;
299
300 pout = dest;
301 pend = dest + 256 - 1;
302 if (*pin == '\'' || *pin == '"' || *pin == '`') {
303 ec = *pin++;
304 if (ec == '`')
305 ec = '\'';
306 for (;;) {
307 ch = *pin++;
308 if (ch == '`')
309 ch = '\'';
310 if (ch == ec || ch == 0)
311 break;
312
313 if (pout < pend)
314 *pout++ = ch;
315 }
316 if (ch == 0)
317 pin--;
318 } else {
319 for(;;) {
320 ch = *pin++;
321 if (ch == ' ' || ch == '\t' || ch == 0)
322 break;
323 if (pout < pend) *pout++ = ch;
324 }
325 }
326 *pout = 0;
327 }
328
329
GetErrors()330 void EMessages::GetErrors() {
331 char line[4096];
332 RxMatchRes RM;
333 //int retc;
334 int i, n;
335 int didmatch = 0;
336 int WasRunning = Running;
337 char fn[256];
338
339 //fprintf(stderr, "Reading pipe\n");
340 while (GetLine(line, sizeof(line))) {
341 size_t len = strlen(line);
342 if (len > 0 && line[len - 1] == '\n')
343 line[--len] = 0;
344 didmatch = 0;
345 for (i = 0; i < NCRegexp; i++) {
346 if (RxExec(CRegexp[i].rx, line, len, line, &RM) == 1) {
347 char ln[256];
348 char msg[256];
349 char fn1[256];
350 char fn2[256];
351 char *file;
352
353 n = CRegexp[i].RefFile;
354 unsigned s = RM.Close[n] - RM.Open[n];
355 if (s < sizeof(fn))
356 memcpy(fn, line + RM.Open[n], s);
357 else
358 s = 0;
359 fn[s] = 0;
360
361 n = CRegexp[i].RefLine;
362 s = RM.Close[n] - RM.Open[n];
363 if (s < sizeof(ln))
364 memcpy(ln, line + RM.Open[n], s);
365 else
366 s = 0;
367 ln[s] = 0;
368
369 n = CRegexp[i].RefMsg;
370 s = RM.Close[n] - RM.Open[n];
371 if (s < sizeof(msg))
372 memcpy(msg, line + RM.Open[n], s);
373 else
374 s = 0;
375 msg[s] = 0;
376 //fprintf(stderr, "File:%s msg:%s rex:%d c:%d o:%d>%s<8\nTXT:%s\n", fn, ln, i, RM.Close[n], RM.Open[n], msg, line);
377 if (IsFullPath(fn))
378 file = fn;
379 else {
380 /*
381 * for now - try only with top most dir
382 * later we might try to find the file in all stacked dirs
383 * as with parallel makes it's hard to guess the right directory path
384 */
385 strlcpy(fn1, DirLevel.size() ? DirLevel.back().c_str() : Directory.c_str(),
386 sizeof(fn1));
387 Slash(fn1, 1);
388 strlcat(fn1, fn, sizeof(fn1));
389 if (ExpandPath(fn1, fn2, sizeof(fn2)) == 0)
390 file = fn2;
391 else
392 file = fn1;
393 }
394 AddError(file, atoi(ln), msg, line, 1);
395 didmatch = 1;
396 MatchCount++;
397 break;
398 }
399 }
400 if (!didmatch)
401 {
402 AddError(0, -1, 0, line);
403 //** Quicky: check for gnumake 'entering directory'
404 //** make[x]: entering directory `xxx'
405 //** make[x]: leaving...
406 static const char t1[] = "entering directory";
407 static const char t2[] = "leaving directory";
408 const char *pin;
409
410 if ( (pin = strstr(line, "]:")) != 0)
411 {
412 //** It *is* some make line.. Check for 'entering'..
413 pin += 2;
414 while (*pin == ' ')
415 pin++;
416 if (strnicmp(pin, t1, sizeof(t1)-1) == 0) { // Entering?
417 //** Get the directory name from the line,
418 pin += sizeof(t1)-1;
419 getWord(fn, pin);
420 //dbg("entering %s", fn);
421
422 if (*fn)
423 //** Indeed entering directory! Link in list,
424 DirLevel.push_back(fn);
425 } else if (strnicmp(pin, t2, sizeof(t2)-1) == 0) { // Leaving?
426 pin += sizeof(t2)-1;
427 getWord(fn, pin); // Get dirname,
428 //dbg("leaving %s", fn);
429 int found = 0;
430 for (unsigned i = DirLevel.size(); i-- > 0;) {
431 /*
432 * remove leaved director from our list of Dirs
433 * as many users runs make in parallel mode
434 * we might get pretty mangled order of dirs
435 * so remove the last added with the same name
436 */
437 if (stricmp(DirLevel[i].c_str(), fn) == 0) {
438 DirLevel.erase(DirLevel.begin() + i);
439 found++;
440 break;
441 }
442 }
443 if (!found) {
444 //** Mismatch filenames -> error, and revoke stack.
445 //dbg("mismatch on %s", fn);
446 AddError(0, -1, 0, "fte: mismatch in directory stack!?");
447 //** In this case we totally die the stack..
448 DirLevel.clear();
449 }
450 }
451 }
452 }
453 }
454 //fprintf(stderr, "Reading Stopped\n");
455 if (!Running && WasRunning) {
456 char s[30];
457
458 sprintf(s, "[done, status=%d]", ReturnCode);
459 AddError(0, -1, 0, s);
460 }
461 //UpdateList();
462 //NeedsUpdate = 1;
463 }
464
CompilePrevError(EView * V)465 int EMessages::CompilePrevError(EView *V) {
466 if (!ErrList.size()) {
467 V->Msg(S_INFO, "No errors.");
468 return 0;
469 }
470
471 while (Row > 0) {
472 Row--;
473 if (ErrList[Row]->line != -1 && !ErrList[Row]->file.empty()) {
474 ShowError(V, Row);
475 return 1;
476 }
477 }
478
479 V->Msg(S_INFO, "No previous error.");
480 return 0;
481 }
482
CompileNextError(EView * V)483 int EMessages::CompileNextError(EView *V) {
484 if (!ErrList.size()) {
485 V->Msg(S_INFO, (Running) ? "No errors (yet)." : "No errors.");
486 return 0;
487 }
488
489 while ((Row + 1) < (int)ErrList.size()) {
490 Row++;
491 if (ErrList[Row]->line != -1 && !ErrList[Row]->file.empty()) {
492 ShowError(V, Row);
493 return 1;
494 }
495 }
496
497 V->Msg(S_INFO, (Running) ? "No more errors (yet)." : "No more errors.");
498 return 0;
499 }
500
Compile(char *)501 int EMessages::Compile(char * /*Command*/) {
502 return 0;
503 }
504
ShowError(EView * V,unsigned err)505 void EMessages::ShowError(EView *V, unsigned err) {
506 if (err < ErrList.size()) {
507 if (!ErrList[err]->file.empty()) {
508 // should check if relative path
509 // possibly scan for (gnumake) directory info in output
510 if (ErrList[err]->Buf) {
511 char bk[16];
512
513 V->SwitchToModel(ErrList[err]->Buf);
514
515 sprintf(bk, "_MSG.%d", err);
516 ErrList[err]->Buf->GotoBookmark(bk);
517 } else {
518 if (FileLoad(0, ErrList[err]->file.c_str(), 0, V) == 1) {
519 V->SwitchToModel(ActiveModel);
520 ((EBuffer *)ActiveModel)->CenterNearPosR(0, ErrList[err]->line - 1);
521 }
522 }
523 if (!ErrList[err]->msg.empty())
524 V->Msg(S_INFO, "%s", ErrList[err]->msg.c_str());
525 else
526 V->Msg(S_INFO, "%s", ErrList[err]->text.c_str());
527 }
528 }
529 }
530
DrawLine(PCell B,int Line,int Col,ChColor color,int Width)531 void EMessages::DrawLine(PCell B, int Line, int Col, ChColor color, int Width) {
532 if (Line < (int)ErrList.size())
533 if (Col < int(ErrList[Line]->text.size())) {
534 char str[1024];
535 size_t len;
536
537 len = UnTabStr(str, sizeof(str),
538 ErrList[Line]->text.c_str(),
539 ErrList[Line]->text.size());
540
541 if ((int)len > Col)
542 MoveStr(B, 0, Width, str + Col, color, Width);
543 }
544 }
545
FormatLine(int Line)546 char* EMessages::FormatLine(int Line) {
547 if (Line < (int)ErrList.size())
548 return strdup(ErrList[Line]->text.c_str());
549
550 return 0;
551 }
552
IsHilited(int Line)553 int EMessages::IsHilited(int Line) {
554 return (Line >= 0 && Line < (int)ErrList.size()) ? ErrList[Line]->hilit : 0;
555 }
556
UpdateList()557 void EMessages::UpdateList() {
558 Count = (int)ErrList.size();
559 EList::UpdateList();
560 }
561
Activate(int)562 int EMessages::Activate(int /*No*/) {
563 //assert(No == Row);
564 //Row = No;
565 ShowError(View, Row);
566 return 1;
567 }
568
CanActivate(int Line)569 int EMessages::CanActivate(int Line) {
570 //return (Line < (int)ErrList.size());
571 return (Line < (int)ErrList.size()
572 && (!ErrList[Line]->file.empty()
573 || ErrList[Line]->line != -1)) ? 1 : 0;
574 }
575
NotifyPipe(int APipeId)576 void EMessages::NotifyPipe(int APipeId) {
577 //fprintf(stderr, "Got notified");
578 if (APipeId == PipeId)
579 GetErrors();
580 }
581
GetName(char * AName,size_t MaxLen)582 void EMessages::GetName(char *AName, size_t MaxLen) {
583 strlcpy(AName, "Messages", MaxLen);
584 }
585
GetInfo(char * AInfo,size_t)586 void EMessages::GetInfo(char *AInfo, size_t /*MaxLen*/) {
587 sprintf(AInfo, "%2d %04d/%03d Messages: %d (%s)",
588 ModelNo,Row, Count, MatchCount, Command.c_str());
589 }
590
GetPath(char * APath,size_t MaxLen)591 void EMessages::GetPath(char *APath, size_t MaxLen) {
592 strlcpy(APath, Directory.c_str(), MaxLen);
593 Slash(APath, 0);
594 }
595
GetTitle(char * ATitle,size_t MaxLen,char * ASTitle,size_t SMaxLen)596 void EMessages::GetTitle(char *ATitle, size_t MaxLen, char *ASTitle, size_t SMaxLen) {
597 snprintf(ATitle, MaxLen, "Messages: %s", Command.c_str());
598 strlcpy(ASTitle, "Messages", SMaxLen);
599 }
600
601 // get row length for specified row, used in MoveLineEnd to get actual row length
GetRowLength(int ARow)602 size_t EMessages::GetRowLength(int ARow)
603 {
604 if ((ARow >= 0) && (ARow < (int)ErrList.size()))
605 return ErrList[ARow]->text.size();
606
607 return 0;
608 }
609
610 #endif // CONFIG_OBJ_MESSAGES
611