[LOG2LINES] Add a TODO, Use a snprintf()
[reactos.git] / sdk / tools / log2lines / log2lines.c
1 /*
2 * ReactOS log2lines
3 * Written by Jan Roeloffzen
4 *
5 * - Initialization, translation and main loop
6 */
7
8 #include <errno.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12
13 #include "util.h"
14 #include "version.h"
15 #include "compat.h"
16 #include "options.h"
17 #include "image.h"
18 #include "cache.h"
19 #include "log2lines.h"
20 #include "help.h"
21 #include "cmd.h"
22 #include "match.h"
23
24
25 static FILE *dbgIn = NULL;
26 static FILE *dbgOut = NULL;
27 static FILE *conIn = NULL;
28 static FILE *conOut = NULL;
29 static const char *kdbg_prompt = KDBG_PROMPT;
30 static const char *kdbg_cont = KDBG_CONT;
31
32 LIST sources;
33 LINEINFO lastLine;
34 FILE *logFile = NULL;
35 LIST cache;
36 SUMM summ;
37 REVINFO revinfo;
38
39
40 static void
41 clearLastLine(void)
42 {
43 memset(&lastLine, 0, sizeof(LINEINFO));
44 }
45
46 static void
47 log_file(FILE *outFile, char *fileName, int line)
48 {
49 int i = 0, min = 0, max = 0;
50 char s[LINESIZE];
51 FILE *src;
52
53 strcpy(s, opt_SourcesPath);
54 strcat(s, fileName);
55
56 max = line + opt_SrcPlus;
57 if ((src = fopen(s, "r")))
58 {
59 min = line - opt_Source;
60 min = (min < 0) ? 0 : min;
61 while (i < max && fgets(s, LINESIZE, src))
62 {
63 if (i >= min)
64 {
65 if (i == line)
66 log(outFile, "| ----\n");
67 log(outFile, "| %4.4d %s", i + 1, s);
68 }
69 i++;
70 }
71 fclose(src);
72 if ( i < min )
73 log(outFile, "| S--- source has only %d lines! (check source/revision)\n", i);
74 }
75 else
76 l2l_dbg(1, "Can't open: %s (check " SOURCES_ENV ")\n", s);
77 }
78
79 static void
80 logSource(FILE *outFile)
81 {
82 log_file(outFile, lastLine.file1, lastLine.nr1);
83 if (lastLine.nr2)
84 {
85 log(outFile, "| ---- [%u] ----\n", lastLine.nr2);
86 log_file(outFile, lastLine.file2, lastLine.nr2);
87 }
88 }
89
90 static void
91 reportSource(FILE *outFile)
92 {
93 if (!opt_Source)
94 return;
95 if (lastLine.valid)
96 logSource(outFile);
97 }
98
99 static void
100 report(FILE *outFile)
101 {
102 reportRevision(outFile);
103 reportSource(outFile);
104 clearLastLine();
105 }
106
107
108 static int
109 print_offset(void *data, size_t offset, char *toString)
110 {
111 PSYMBOLFILE_HEADER RosSymHeader = (PSYMBOLFILE_HEADER)data;
112 PROSSYM_ENTRY e = NULL;
113 PROSSYM_ENTRY e2 = NULL;
114 int bFileOffsetChanged = 0;
115 char fmt[LINESIZE];
116 char *Strings = (char *)data + RosSymHeader->StringsOffset;
117
118 fmt[0] = '\0';
119 e = find_offset(data, offset);
120 if (opt_twice)
121 {
122 e2 = find_offset(data, offset - 1);
123
124 if (e == e2)
125 e2 = NULL;
126 else
127 summ.diff++;
128
129 if (opt_Twice && e2)
130 {
131 e = e2;
132 e2 = NULL;
133 /* replaced (transparantly), but updated stats */
134 }
135 }
136 if (e || e2)
137 {
138 strcpy(lastLine.file1, &Strings[e->FileOffset]);
139 strcpy(lastLine.func1, &Strings[e->FunctionOffset]);
140 lastLine.nr1 = e->SourceLine;
141 sources_entry_create(&sources, lastLine.file1, SVN_PREFIX);
142 lastLine.valid = 1;
143 if (e2)
144 {
145 strcpy(lastLine.file2, &Strings[e2->FileOffset]);
146 strcpy(lastLine.func2, &Strings[e2->FunctionOffset]);
147 lastLine.nr2 = e2->SourceLine;
148 sources_entry_create(&sources, lastLine.file2, SVN_PREFIX);
149 bFileOffsetChanged = e->FileOffset != e2->FileOffset;
150 if (e->FileOffset != e2->FileOffset || e->FunctionOffset != e2->FunctionOffset)
151 summ.majordiff++;
152
153 /*
154 * - "%.0s" displays nothing, but processes argument
155 * - bFileOffsetChanged implies always display 2nd SourceLine even if the same
156 * - also for FunctionOffset
157 */
158 strcat(fmt, "%s");
159 if (bFileOffsetChanged)
160 strcat(fmt, "[%s]");
161 else
162 strcat(fmt, "%.0s");
163
164 strcat(fmt, ":%u");
165 if (e->SourceLine != e2->SourceLine || bFileOffsetChanged)
166 strcat(fmt, "[%u]");
167 else
168 strcat(fmt, "%.0u");
169
170 strcat(fmt, " (%s");
171 if (e->FunctionOffset != e2->FunctionOffset || bFileOffsetChanged)
172 strcat(fmt, "[%s])");
173 else
174 strcat(fmt, "%.0s)");
175
176 if (toString)
177 { // put in toString if provided
178 snprintf(toString, LINESIZE, fmt,
179 &Strings[e->FileOffset],
180 &Strings[e2->FileOffset],
181 (unsigned int)e->SourceLine,
182 (unsigned int)e2->SourceLine,
183 &Strings[e->FunctionOffset],
184 &Strings[e2->FunctionOffset]);
185 }
186 else
187 {
188 strcat(fmt, "\n");
189 printf(fmt,
190 &Strings[e->FileOffset],
191 &Strings[e2->FileOffset],
192 (unsigned int)e->SourceLine,
193 (unsigned int)e2->SourceLine,
194 &Strings[e->FunctionOffset],
195 &Strings[e2->FunctionOffset]);
196 }
197 }
198 else
199 {
200 if (toString)
201 { // put in toString if provided
202 snprintf(toString, LINESIZE, "%s:%u (%s)",
203 &Strings[e->FileOffset],
204 (unsigned int)e->SourceLine,
205 &Strings[e->FunctionOffset]);
206 }
207 else
208 {
209 printf("%s:%u (%s)\n",
210 &Strings[e->FileOffset],
211 (unsigned int)e->SourceLine,
212 &Strings[e->FunctionOffset]);
213 }
214 }
215 return 0;
216 }
217 return 1;
218 }
219
220 static int
221 process_data(const void *FileData, size_t offset, char *toString)
222 {
223 int res;
224
225 PIMAGE_SECTION_HEADER PERosSymSectionHeader = get_sectionheader((char *)FileData);
226 if (!PERosSymSectionHeader)
227 return 2;
228
229 res = print_offset((char *)FileData + PERosSymSectionHeader->PointerToRawData, offset, toString);
230 if (res)
231 {
232 if (toString)
233 sprintf(toString, "??:0");
234 else
235 printf("??:0");
236 l2l_dbg(1, "Offset not found: %x\n", (unsigned int)offset);
237 summ.offset_errors++;
238 }
239
240 return res;
241 }
242
243 static int
244 process_file(const char *file_name, size_t offset, char *toString)
245 {
246 void *FileData;
247 size_t FileSize;
248 int res = 1;
249
250 FileData = load_file(file_name, &FileSize);
251 if (!FileData)
252 {
253 l2l_dbg(0, "An error occured loading '%s'\n", file_name);
254 }
255 else
256 {
257 res = process_data(FileData, offset, toString);
258 free(FileData);
259 }
260 return res;
261 }
262
263 static int
264 translate_file(const char *cpath, size_t offset, char *toString)
265 {
266 size_t base = 0;
267 LIST_MEMBER *pentry = NULL;
268 int res = 0;
269 char *path, *dpath;
270
271 dpath = path = convert_path(cpath);
272 if (!path)
273 return 1;
274
275 // The path could be absolute:
276 if (get_ImageBase(path, &base))
277 {
278 pentry = entry_lookup(&cache, path);
279 if (pentry)
280 {
281 path = pentry->path;
282 base = pentry->ImageBase;
283 if (base == INVALID_BASE)
284 {
285 l2l_dbg(1, "No, or invalid base address: %s\n", path);
286 res = 2;
287 }
288 }
289 else
290 {
291 l2l_dbg(1, "Not found in cache: %s\n", path);
292 res = 3;
293 }
294 }
295
296 if (!res)
297 {
298 res = process_file(path, offset, toString);
299 }
300
301 free(dpath);
302 return res;
303 }
304
305 static void
306 translate_char(int c, FILE *outFile)
307 {
308 fputc(c, outFile);
309 if (logFile)
310 fputc(c, logFile);
311 }
312
313 static char *
314 remove_mark(char *Line)
315 {
316 if (Line[1] == ' ' && Line[2] == '<')
317 if (Line[0] == '*' || Line[0] == '?')
318 return Line + 2;
319 return Line;
320 }
321
322 static void
323 translate_line(FILE *outFile, char *Line, char *path, char *LineOut)
324 {
325 unsigned int offset;
326 int cnt, res;
327 char *sep, *tail, *mark, *s;
328 unsigned char ch;
329
330 if (!*Line)
331 return;
332
333 res = 1;
334 mark = "";
335 s = remove_mark(Line);
336 if (opt_undo)
337 {
338 /* Strip all lines added by this tool: */
339 char buf[NAMESIZE];
340 if (sscanf(s, "| %s", buf) == 1)
341 if (buf[0] == '0' || strcmp(buf, "----") == 0 || strcmp(buf, "L2L-") == 0 || strcmp(buf, "S---") == 0 || strcmp(buf, "R---") == 0 || atoi(buf))
342 res = 0;
343 }
344
345 sep = strchr(s, ':');
346 if (sep)
347 {
348 *sep = ' ';
349 cnt = sscanf(s, "<%s %x%c", path, &offset, &ch);
350 if (opt_undo)
351 {
352 if (cnt == 3 && ch == ' ')
353 {
354 tail = strchr(s, '>');
355 tail = tail ? tail - 1 : tail;
356 if (tail && tail[0] == ')' && tail[1] == '>')
357 {
358 res = 0;
359 tail += 2;
360 mark = opt_mark ? "* " : "";
361 if (opt_redo && !(res = translate_file(path, offset, LineOut)))
362 {
363 log(outFile, "%s<%s:%x (%s)>%s", mark, path, offset, LineOut, tail);
364 summ.redo++;
365 }
366 else
367 {
368 log(outFile, "%s<%s:%x>%s", mark, path, offset, tail);
369 summ.undo++;
370 }
371 }
372 else
373 {
374 mark = opt_Mark ? "? " : "";
375 summ.skipped++;
376 }
377 summ.total++;
378 }
379 }
380
381 if (!opt_undo || opt_redo)
382 {
383 if (cnt == 3 && ch == '>')
384 {
385 tail = strchr(s, '>') + 1;
386 if (!(res = translate_file(path, offset, LineOut)))
387 {
388 mark = opt_mark ? "* " : "";
389 log(outFile, "%s<%s:%x (%s)>%s", mark, path, offset, LineOut, tail);
390 summ.translated++;
391 }
392 else
393 {
394 mark = opt_Mark ? "? " : "";
395 summ.skipped++;
396 }
397 summ.total++;
398 }
399 }
400 }
401 if (res)
402 {
403 if (sep)
404 *sep = ':'; // restore because not translated
405 log(outFile, "%s%s", mark, s);
406 }
407 memset(Line, '\0', LINESIZE); // flushed
408 }
409
410 static int
411 translate_files(FILE *inFile, FILE *outFile)
412 {
413 char Line[LINESIZE + 1];
414 char path[LINESIZE + 1];
415 char LineOut[LINESIZE + 1];
416 int c;
417 unsigned char ch;
418 int i = 0;
419 const char *pc = kdbg_cont;
420 const char *p = kdbg_prompt;
421 const char *p_eos = p + sizeof(KDBG_PROMPT) - 1; //end of string pos
422
423 memset(Line, '\0', LINESIZE + 1);
424 if (opt_console)
425 {
426 while ((c = fgetc(inFile)) != EOF)
427 {
428 if (opt_quit)break;
429
430 ch = (unsigned char)c;
431 if (!opt_raw)
432 {
433 switch (ch)
434 {
435 case '\n':
436 if ( strncmp(Line, KDBG_DISCARD, sizeof(KDBG_DISCARD)-1) == 0 )
437 {
438 memset(Line, '\0', LINESIZE); // flushed
439 }
440 else
441 {
442 Line[1] = handle_escape_cmd(outFile, Line, path, LineOut);
443 if (Line[1] != KDBG_ESC_CHAR)
444 {
445 if (p == p_eos)
446 {
447 // kdbg prompt, so already echoed char by char
448 memset(Line, '\0', LINESIZE);
449 translate_char(c, outFile);
450 }
451 else
452 {
453 if (match_line(outFile, Line))
454 {
455 translate_line(outFile, Line, path, LineOut);
456 translate_char(c, outFile);
457 report(outFile);
458 }
459 }
460 }
461 }
462 i = 0;
463 p = kdbg_prompt;
464 pc = kdbg_cont;
465 break;
466 case '<':
467 i = 0;
468 Line[i++] = ch;
469 break;
470 case '>':
471 if (ch == *p)
472 {
473 p = p_eos;
474 translate_line(outFile, Line, path, LineOut);
475 }
476
477 if (p != p_eos)
478 {
479 if (i < LINESIZE)
480 {
481 Line[i++] = ch;
482 translate_line(outFile, Line, path, LineOut);
483 }
484 else
485 {
486 translate_line(outFile, Line, path, LineOut);
487 translate_char(c, outFile);
488 }
489 }
490 else
491 translate_char(c, outFile);
492 i = 0;
493 break;
494 default:
495 if (ch == *p)p++;
496 if (ch == *pc)pc++;
497 if (i < LINESIZE)
498 {
499 Line[i++] = ch;
500 if (p == p_eos)
501 {
502 translate_char(c, outFile);
503 }
504 else if (!*pc)
505 {
506 translate_line(outFile, Line, path, LineOut);
507 i = 0;
508 }
509 }
510 else
511 {
512 translate_line(outFile, Line, path, LineOut);
513 translate_char(c, outFile);
514 i = 0;
515 }
516 }
517 }
518 else
519 translate_char(c, outFile);
520 }
521 }
522 else
523 { // Line by line, slightly faster but less interactive
524 while (fgets(Line, LINESIZE, inFile) != NULL)
525 {
526 if (opt_quit)break;
527
528 if (!opt_raw)
529 {
530 translate_line(outFile, Line, path, LineOut);
531 report(outFile);
532 }
533 else
534 log(outFile, "%s", Line);
535 }
536 }
537
538 if (opt_Revision && (strstr(opt_Revision, "regscan") == opt_Revision))
539 {
540 char *s = strchr(opt_Revision, ',');
541 if (s)
542 {
543 *s++ = '\0';
544 revinfo.range = atoi(s);
545 }
546 regscan(outFile);
547 }
548
549 if (opt_stats)
550 {
551 stat_print(outFile, &summ);
552 if (logFile)
553 stat_print(logFile, &summ);
554 }
555 return 0;
556 }
557
558
559 int
560 main(int argc, const char **argv)
561 {
562 int res = 0;
563 int optInit = 0;
564 int optCount = 0;
565
566 dbgIn = stdin;
567 conOut = stdout;
568 (void)conIn;
569 (void)dbgOut;
570
571 memset(&cache, 0, sizeof(LIST));
572 memset(&sources, 0, sizeof(LIST));
573 stat_clear(&summ);
574 memset(&revinfo, 0, sizeof(REVINFO));
575 clearLastLine();
576
577 optInit = optionInit(argc, argv);
578 optCount = optionParse(argc, argv);
579
580 if (optCount < 0 || optInit < 0)
581 {
582 res = optCount;
583 goto cleanup;
584 }
585
586 argc -= optCount;
587
588 if (opt_Revision && (strcmp(opt_Revision, "update") == 0))
589 {
590 res = updateSvnlog();
591 goto cleanup;
592 }
593
594 if (check_directory(opt_force))
595 {
596 res = 3;
597 goto cleanup;
598 }
599
600 create_cache(opt_force, 0);
601 if (opt_exit)
602 {
603 res = 0;
604 goto cleanup;
605 }
606
607 read_cache();
608 l2l_dbg(4, "Cache read complete\n");
609
610 if (set_LogFile(&logFile))
611 {
612 res = 2;
613 goto cleanup;
614 }
615 l2l_dbg(4, "opt_logFile processed\n");
616
617 if (opt_Pipe)
618 {
619 l2l_dbg(3, "Command line: \"%s\"\n",opt_Pipe);
620
621 if (!(dbgIn = POPEN(opt_Pipe, "r")))
622 {
623 dbgIn = stdin; //restore
624 l2l_dbg(0, "Could not popen '%s' (%s)\n", opt_Pipe, strerror(errno));
625 free(opt_Pipe);
626 opt_Pipe = NULL;
627 }
628 }
629 l2l_dbg(4, "opt_Pipe processed\n");
630
631 if (argc > 1)
632 { // translate {<exefile> <offset>}
633 int i = 1;
634 const char *exefile = NULL;
635 const char *offset = NULL;
636 char Line[LINESIZE + 1];
637 char PathBuffer[LINESIZE + 1];
638 char LineOutBuffer[LINESIZE + 1];
639
640 // TODO: Re-use one translate_files(), instead of repeated translate_line().
641 while (i < argc)
642 {
643 offset = argv[optCount + i++];
644 if (isOffset(offset))
645 {
646 if (exefile)
647 {
648 l2l_dbg(2, "translating %s %s\n", exefile, offset);
649
650 snprintf(Line, LINESIZE, "<%s:%s>\n", exefile, offset);
651 translate_line(conOut, Line, PathBuffer, LineOutBuffer);
652 report(conOut);
653 }
654 else
655 {
656 l2l_dbg(0, "<exefile> expected\n");
657 res = 3;
658 break;
659 }
660 }
661 else
662 {
663 // Not an offset so must be an exefile:
664 exefile = offset;
665 }
666 }
667 }
668 else
669 { // translate logging from stdin
670 translate_files(dbgIn, conOut);
671 }
672
673 if (logFile)
674 fclose(logFile);
675
676 if (opt_Pipe)
677 PCLOSE(dbgIn);
678
679 cleanup:
680 // See optionParse().
681 if (opt_Revision)
682 {
683 free(opt_Revision);
684 opt_Revision = NULL;
685 }
686
687 // See optionInit().
688 if (opt_Pipe)
689 {
690 free(opt_Pipe);
691 opt_Pipe = NULL;
692 }
693
694 list_clear(&sources);
695 list_clear(&cache);
696
697 return res;
698 }
699
700 /* EOF */