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