606b57af14b18b3fd6739346a04727da78afcbb2
[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 = malloc(LINESIZE + 1);
414 char *path = malloc(LINESIZE + 1);
415 char *LineOut = malloc(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 if (Line && path && LineOut)
424 {
425 memset(Line, '\0', LINESIZE + 1);
426 if (opt_console)
427 {
428 while ((c = fgetc(inFile)) != EOF)
429 {
430 if (opt_quit)break;
431
432 ch = (unsigned char)c;
433 if (!opt_raw)
434 {
435 switch (ch)
436 {
437 case '\n':
438 if ( strncmp(Line, KDBG_DISCARD, sizeof(KDBG_DISCARD)-1) == 0 )
439 {
440 memset(Line, '\0', LINESIZE); // flushed
441 }
442 else
443 {
444 Line[1] = handle_escape_cmd(outFile, Line, path, LineOut);
445 if (Line[1] != KDBG_ESC_CHAR)
446 {
447 if (p == p_eos)
448 {
449 // kdbg prompt, so already echoed char by char
450 memset(Line, '\0', LINESIZE);
451 translate_char(c, outFile);
452 }
453 else
454 {
455 if (match_line(outFile, Line))
456 {
457 translate_line(outFile, Line, path, LineOut);
458 translate_char(c, outFile);
459 report(outFile);
460 }
461 }
462 }
463 }
464 i = 0;
465 p = kdbg_prompt;
466 pc = kdbg_cont;
467 break;
468 case '<':
469 i = 0;
470 Line[i++] = ch;
471 break;
472 case '>':
473 if (ch == *p)
474 {
475 p = p_eos;
476 translate_line(outFile, Line, path, LineOut);
477 }
478
479 if (p != p_eos)
480 {
481 if (i < LINESIZE)
482 {
483 Line[i++] = ch;
484 translate_line(outFile, Line, path, LineOut);
485 }
486 else
487 {
488 translate_line(outFile, Line, path, LineOut);
489 translate_char(c, outFile);
490 }
491 }
492 else
493 translate_char(c, outFile);
494 i = 0;
495 break;
496 default:
497 if (ch == *p)p++;
498 if (ch == *pc)pc++;
499 if (i < LINESIZE)
500 {
501 Line[i++] = ch;
502 if (p == p_eos)
503 {
504 translate_char(c, outFile);
505 }
506 else if (!*pc)
507 {
508 translate_line(outFile, Line, path, LineOut);
509 i = 0;
510 }
511 }
512 else
513 {
514 translate_line(outFile, Line, path, LineOut);
515 translate_char(c, outFile);
516 i = 0;
517 }
518 }
519 }
520 else
521 translate_char(c, outFile);
522 }
523 }
524 else
525 { // Line by line, slightly faster but less interactive
526 while (fgets(Line, LINESIZE, inFile) != NULL)
527 {
528 if (opt_quit)break;
529
530 if (!opt_raw)
531 {
532 translate_line(outFile, Line, path, LineOut);
533 report(outFile);
534 }
535 else
536 log(outFile, "%s", Line);
537 }
538 }
539 }
540
541 if (opt_Revision && (strstr(opt_Revision, "regscan") == opt_Revision))
542 {
543 char *s = strchr(opt_Revision, ',');
544 if (s)
545 {
546 *s++ = '\0';
547 revinfo.range = atoi(s);
548 }
549 regscan(outFile);
550 }
551
552 if (opt_stats)
553 {
554 stat_print(outFile, &summ);
555 if (logFile)
556 stat_print(logFile, &summ);
557 }
558 free(LineOut);
559 free(Line);
560 free(path);
561 return 0;
562 }
563
564
565 int
566 main(int argc, const char **argv)
567 {
568 int res = 0;
569 int optInit = 0;
570 int optCount = 0;
571
572 dbgIn = stdin;
573 conOut = stdout;
574 (void)conIn;
575 (void)dbgOut;
576
577 memset(&cache, 0, sizeof(LIST));
578 memset(&sources, 0, sizeof(LIST));
579 stat_clear(&summ);
580 memset(&revinfo, 0, sizeof(REVINFO));
581 clearLastLine();
582
583 optInit = optionInit(argc, argv);
584 optCount = optionParse(argc, argv);
585
586 if (optCount < 0 || optInit < 0)
587 {
588 return optCount;
589 }
590
591 argc -= optCount;
592
593 if (opt_Revision && (strcmp(opt_Revision, "update") == 0))
594 {
595 res = updateSvnlog();
596 return res;
597 }
598
599 if (check_directory(opt_force))
600 return 3;
601
602 create_cache(opt_force, 0);
603 if (opt_exit)
604 return 0;
605
606 read_cache();
607 l2l_dbg(4, "Cache read complete\n");
608
609 if (set_LogFile(&logFile))
610 return 2;
611 l2l_dbg(4, "opt_logFile processed\n");
612
613 if (opt_Pipe)
614 {
615 l2l_dbg(3, "Command line: \"%s\"\n",opt_Pipe);
616
617 if (!(dbgIn = POPEN(opt_Pipe, "r")))
618 {
619 dbgIn = stdin; //restore
620 l2l_dbg(0, "Could not popen '%s' (%s)\n", opt_Pipe, strerror(errno));
621 free(opt_Pipe); opt_Pipe = NULL;
622 }
623 }
624 l2l_dbg(4, "opt_Pipe processed\n");
625
626 if (argc > 1)
627 { // translate {<exefile> <offset>}
628 int i = 1;
629 const char *exefile = NULL;
630 const char *offset = NULL;
631 char Line[LINESIZE + 1];
632
633 while (i < argc)
634 {
635 Line[0] = '\0';
636 offset = argv[optCount + i++];
637 if (isOffset(offset))
638 {
639 if (exefile)
640 {
641 l2l_dbg(2, "translating %s %s\n", exefile, offset);
642 translate_file(exefile, my_atoi(offset), Line);
643 printf("%s\n", Line);
644 report(conOut);
645 }
646 else
647 {
648 l2l_dbg(0, "<exefile> expected\n");
649 res = 3;
650 break;
651 }
652 }
653 else
654 {
655 // Not an offset so must be an exefile:
656 exefile = offset;
657 }
658 }
659 }
660 else
661 { // translate logging from stdin
662 translate_files(dbgIn, conOut);
663 }
664
665 if (logFile)
666 fclose(logFile);
667
668 if (opt_Pipe)
669 PCLOSE(dbgIn);
670
671 return res;
672 }
673
674 /* EOF */