Commit | Line | Data |
---|---|---|
970ed795 | 1 | /****************************************************************************** |
3abe9331 | 2 | * Copyright (c) 2000-2015 Ericsson Telecom AB |
970ed795 EL |
3 | * All rights reserved. This program and the accompanying materials |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | ******************************************************************************/ | |
8 | %option noyywrap | |
9 | %option nounput | |
10 | %{ | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <string.h> | |
14 | #include <stdlib.h> | |
15 | #include <unistd.h> | |
16 | #include <errno.h> | |
17 | ||
18 | #include "../common/memory.h" | |
19 | #include "../common/version_internal.h" | |
20 | ||
21 | #ifdef LICENSE | |
22 | #include "../common/license.h" | |
23 | #endif | |
24 | ||
25 | static const char *program_name = NULL; | |
26 | static size_t indent_depth = 4; | |
27 | static FILE *output_file = NULL; | |
28 | static int separate_files = 0; | |
3abe9331 | 29 | static int format_tab_newline = 1; |
30 | static int replaced_tab_newline = 0; | |
970ed795 EL |
31 | |
32 | static size_t indent_level = 0; | |
33 | static enum { OPEN_BRACE, CLOSE_BRACE, COMMA, OTHER, OTHER_WS } | |
34 | last_token = OTHER; | |
35 | ||
36 | static char *line_buf = NULL; | |
37 | static size_t buf_size = 0, buf_len = 0; | |
38 | #define MIN_BUFSIZE 1024 | |
39 | ||
40 | static void init_line_buf(void) | |
41 | { | |
42 | line_buf = (char *)Malloc(MIN_BUFSIZE); | |
43 | buf_size = MIN_BUFSIZE; | |
44 | buf_len = 0; | |
45 | } | |
46 | ||
47 | static void enlarge_line_buf(size_t size_incr) | |
48 | { | |
49 | size_t new_buf_size = buf_size; | |
50 | while (buf_len + size_incr > new_buf_size) new_buf_size *= 2; | |
51 | if (new_buf_size > buf_size) { | |
52 | line_buf = (char *)Realloc(line_buf, new_buf_size); | |
53 | buf_size = new_buf_size; | |
54 | } | |
55 | } | |
56 | ||
57 | static void free_line_buf(void) | |
58 | { | |
59 | Free(line_buf); | |
60 | line_buf = NULL; | |
61 | buf_size = 0; | |
62 | buf_len = 0; | |
63 | } | |
64 | ||
65 | static void append_str(size_t chunk_size, const char *chunk_ptr) | |
66 | { | |
67 | enlarge_line_buf(chunk_size); | |
68 | memcpy(line_buf + buf_len, chunk_ptr, chunk_size); | |
69 | buf_len += chunk_size; | |
70 | } | |
71 | ||
72 | static void append_char(char c) | |
73 | { | |
74 | if (buf_size <= buf_len) { | |
75 | buf_size *= 2; | |
76 | line_buf = (char *)Realloc(line_buf, buf_size); | |
77 | } | |
78 | line_buf[buf_len++] = c; | |
79 | } | |
80 | ||
81 | static void append_indentation(void) | |
82 | { | |
83 | if (indent_depth > 0) { | |
84 | append_char('\n'); | |
85 | if (indent_level > 0) { | |
86 | size_t nof_spaces = indent_level * indent_depth; | |
87 | enlarge_line_buf(nof_spaces); | |
88 | memset(line_buf + buf_len, ' ', nof_spaces); | |
89 | buf_len += nof_spaces; | |
90 | } | |
91 | } else { | |
92 | /* no indentation is made */ | |
93 | append_char(' '); | |
94 | } | |
95 | } | |
96 | ||
97 | static void indent_before_other_token(void) | |
98 | { | |
99 | switch (last_token) { | |
100 | case OPEN_BRACE: | |
101 | case COMMA: | |
102 | /* start a new line after an opening brace or comma */ | |
103 | append_indentation(); | |
104 | break; | |
105 | case CLOSE_BRACE: | |
106 | case OTHER_WS: | |
107 | /* add a single space as separator between the previous token or block */ | |
108 | append_char(' '); | |
109 | default: | |
110 | break; | |
111 | } | |
112 | last_token = OTHER; | |
113 | } | |
114 | ||
115 | static void write_failure(void) | |
116 | { | |
117 | fprintf(stderr, "%s: writing to output file failed: %s\n", | |
118 | program_name, strerror(errno)); | |
119 | exit(EXIT_FAILURE); | |
120 | } | |
121 | ||
3abe9331 | 122 | |
123 | char * | |
124 | str_replace ( const char *string, const char *substr, const char *replacement ){ | |
125 | char *tok = NULL; | |
126 | char *newstr = NULL; | |
127 | char *oldstr = NULL; | |
128 | char *head = NULL; | |
129 | int length_diff = strlen(substr) - strlen(replacement); | |
130 | ||
131 | /* if either substr or replacement is NULL, duplicate string a let caller handle it */ | |
132 | if ( substr == NULL || replacement == NULL ) return strdup (string); | |
133 | newstr = strdup (string); | |
134 | head = newstr; | |
135 | while ( (tok = strstr ( head, substr ))){ | |
136 | oldstr = newstr; | |
137 | newstr = malloc ( strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) + 1 ); | |
138 | /*failed to alloc mem, free old string and return NULL */ | |
139 | if ( newstr == NULL ){ | |
140 | free (oldstr); | |
141 | fprintf(stderr, "Failed to allocate memory.\n"); | |
142 | exit(EXIT_FAILURE); | |
143 | } | |
144 | //We have to count how many characters we replaced | |
145 | replaced_tab_newline += length_diff; | |
146 | memcpy ( newstr, oldstr, tok - oldstr ); | |
147 | memcpy ( newstr + (tok - oldstr), replacement, strlen ( replacement ) ); | |
148 | memcpy ( newstr + (tok - oldstr) + strlen( replacement ), tok + strlen ( substr ), strlen ( oldstr ) - strlen ( substr ) - ( tok - oldstr ) ); | |
149 | memset ( newstr + strlen ( oldstr ) - strlen ( substr ) + strlen ( replacement ) , 0, 1 ); | |
150 | /* move back head right after the last replacement */ | |
151 | head = newstr + (tok - oldstr) + strlen( replacement ); | |
152 | free (oldstr); | |
153 | } | |
154 | return newstr; | |
155 | } | |
156 | ||
157 | ||
970ed795 EL |
158 | static void write_line_buf(void) |
159 | { | |
160 | if (buf_len > 0) { | |
3abe9331 | 161 | if(format_tab_newline){ |
162 | char * temp = str_replace(line_buf, "\\n", "\n"); | |
163 | temp = str_replace(temp, "\\t", "\t"); | |
164 | strcpy(line_buf, temp); | |
165 | free(temp); | |
166 | } | |
167 | if (fwrite(line_buf, buf_len-replaced_tab_newline, 1, yyout) != 1) write_failure(); | |
970ed795 EL |
168 | /* append a newline character if it is missing from the end |
169 | * (e.g. because of EOF) */ | |
3abe9331 | 170 | if (line_buf[buf_len - replaced_tab_newline - 1] != '\n'){ |
970ed795 | 171 | if (putc('\n', yyout) == EOF) write_failure(); |
3abe9331 | 172 | } |
173 | replaced_tab_newline = 0; | |
970ed795 EL |
174 | } |
175 | if (buf_size > MIN_BUFSIZE && buf_size > 2 * buf_len) { | |
176 | /* reset the buffer size if a shorter one is enough */ | |
177 | Free(line_buf); | |
178 | line_buf = (char *)Malloc(MIN_BUFSIZE); | |
179 | buf_size = MIN_BUFSIZE; | |
180 | } | |
181 | buf_len = 0; | |
182 | indent_level = 0; | |
183 | last_token = OTHER; | |
184 | } | |
185 | ||
186 | static FILE *open_file(const char *path, const char *mode) | |
187 | { | |
188 | FILE *fp = fopen(path, mode); | |
189 | if (fp == NULL) { | |
190 | fprintf(stderr, "%s: cannot open file %s for %s: %s\n", | |
191 | program_name, path, mode[0] == 'r' ? "reading" : "writing", | |
192 | strerror(errno)); | |
193 | exit(EXIT_FAILURE); | |
194 | } | |
195 | return fp; | |
196 | } | |
197 | ||
198 | static int in_testcase = 0; | |
199 | static size_t n_testcases = 0; | |
200 | static char **testcases = NULL; | |
201 | ||
202 | static void begin_testcase(const char *testcase_name) | |
203 | { | |
204 | if (separate_files) { | |
205 | size_t i; | |
206 | if (in_testcase) fclose(yyout); | |
207 | else in_testcase = 1; | |
208 | for (i = 0; i < n_testcases; i++) { | |
209 | if (!strcmp(testcase_name, testcases[i])) { | |
210 | yyout = open_file(testcase_name, "a"); | |
211 | return; | |
212 | } | |
213 | } | |
214 | n_testcases++; | |
215 | testcases = (char **)Realloc(testcases, (n_testcases + 1) * | |
216 | sizeof(*testcases)); | |
217 | testcases[n_testcases - 1] = mcopystr(testcase_name); | |
218 | yyout = open_file(testcase_name, "w"); | |
219 | } | |
220 | } | |
221 | ||
222 | static void free_testcases(void) | |
223 | { | |
224 | size_t i; | |
225 | for (i = 0; i < n_testcases; i++) Free(testcases[i]); | |
226 | Free(testcases); | |
227 | n_testcases = 0; | |
228 | testcases = NULL; | |
229 | } | |
230 | ||
231 | static void end_testcase(void) | |
232 | { | |
233 | if (in_testcase) { | |
234 | fclose(yyout); | |
235 | yyout = output_file; | |
236 | in_testcase = 0; | |
237 | } | |
238 | } | |
239 | ||
240 | %} | |
241 | ||
242 | WHITESPACE [ \t] | |
243 | NEWLINE \r|\n|\r\n | |
244 | ||
245 | NUMBER 0|([1-9][0-9]*) | |
246 | ||
247 | IDENTIFIER [a-zA-Z][a-zA-Z0-9_]* | |
248 | ||
249 | VERDICT none|pass|inconc|fail|error | |
250 | ||
251 | YEAR [0-9]{4} | |
252 | MONTH Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec | |
253 | DATE [0-2][0-9]|3[01] | |
254 | HOUR [01][0-9]|2[0-3] | |
255 | MIN [0-5][0-9] | |
256 | SEC [0-5][0-9] | |
257 | MICROSEC [0-9]{6} | |
258 | ||
259 | TIMESTAMP1 {NUMBER}\.{MICROSEC} | |
260 | TIMESTAMP2 {HOUR}\:{MIN}\:{SEC}\.{MICROSEC} | |
261 | TIMESTAMP3 {YEAR}\/{MONTH}\/{DATE}" "{TIMESTAMP2} | |
262 | ||
263 | TIMESTAMP {TIMESTAMP1}|{TIMESTAMP2}|{TIMESTAMP3} | |
264 | ||
265 | %x SC_cstring SC_binstring SC_quadruple | |
266 | ||
267 | %% | |
268 | /* start parsing of a new file from the initial state */ | |
269 | BEGIN(INITIAL); | |
270 | ||
271 | <*>^{TIMESTAMP} { | |
272 | /* recognize the start of a new entry in all states in order to recover from | |
273 | * unterminated strings */ | |
274 | write_line_buf(); | |
275 | append_str(yyleng, yytext); | |
276 | BEGIN(INITIAL); | |
277 | } | |
278 | ||
279 | "Test case "{IDENTIFIER}" started."{NEWLINE} { | |
280 | size_t i; | |
281 | /* cut the newline character(s) from the end */ | |
282 | while (yytext[yyleng - 1] == '\r' || yytext[yyleng - 1] == '\n') yyleng--; | |
283 | append_str(yyleng, yytext); | |
284 | /* append a UNIX-style newline character */ | |
285 | append_char('\n'); | |
286 | /* find the end of testcase identifier */ | |
287 | for (i = 10; yytext[i] != ' '; i++); | |
288 | yytext[i] = '\0'; | |
289 | begin_testcase(yytext + 10); | |
290 | write_line_buf(); | |
291 | } | |
292 | ||
293 | "Test case "{IDENTIFIER}" finished. Verdict: "{VERDICT}{NEWLINE} { | |
294 | /* cut the newline character(s) from the end */ | |
295 | while (yytext[yyleng - 1] == '\r' || yytext[yyleng - 1] == '\n') yyleng--; | |
296 | append_str(yyleng, yytext); | |
297 | /* append a UNIX-style newline character */ | |
298 | append_char('\n'); | |
299 | write_line_buf(); | |
300 | end_testcase(); | |
301 | } | |
302 | ||
303 | {WHITESPACE}+ { | |
304 | if (indent_level > 0) { | |
305 | /* only record the presence of the whitespace in indented blocks */ | |
306 | if (last_token == OTHER) last_token = OTHER_WS; | |
307 | } else { | |
308 | /* copy to output transparently */ | |
309 | append_str(yyleng, yytext); | |
310 | } | |
311 | } | |
312 | ||
313 | {NEWLINE} { | |
314 | if (indent_level > 0) { | |
315 | /* only record the presence of the newline in indented blocks */ | |
316 | if (last_token == OTHER) last_token = OTHER_WS; | |
317 | } else { | |
318 | /* canonize the newline character to UNIX style */ | |
319 | append_char('\n'); | |
320 | } | |
321 | } | |
322 | ||
323 | \{ { | |
324 | if (indent_level > 0) { | |
325 | switch (last_token) { | |
326 | case OPEN_BRACE: | |
327 | case COMMA: | |
328 | /* start a new line for the nested block */ | |
329 | append_indentation(); | |
330 | break; | |
331 | default: | |
332 | /* separate the brace from the previous token */ | |
333 | append_char(' '); | |
334 | } | |
335 | } | |
336 | append_char('{'); | |
337 | indent_level++; | |
338 | last_token = OPEN_BRACE; | |
339 | } | |
340 | ||
341 | \} { | |
342 | if (indent_level > 0) { | |
343 | indent_level--; | |
344 | if (last_token == OPEN_BRACE) { | |
345 | /* add a space to an empty block */ | |
346 | append_char(' '); | |
347 | } else { | |
348 | /* start a newline for the closing brace */ | |
349 | append_indentation(); | |
350 | } | |
351 | last_token = CLOSE_BRACE; | |
352 | } | |
353 | append_char('}'); | |
354 | } | |
355 | ||
356 | , { | |
357 | append_char(','); | |
358 | if (indent_level > 0) last_token = COMMA; | |
359 | } | |
360 | ||
361 | \" { | |
362 | if (indent_level > 0) indent_before_other_token(); | |
363 | append_char('"'); | |
364 | BEGIN(SC_cstring); | |
365 | } | |
366 | ||
367 | \' { | |
368 | if (indent_level > 0) indent_before_other_token(); | |
369 | append_char('\''); | |
370 | BEGIN(SC_binstring); | |
371 | } | |
372 | ||
373 | "char"{WHITESPACE}*\( { | |
374 | if (indent_level > 0) indent_before_other_token(); | |
375 | /* convert whitespaces to canonical form */ | |
376 | append_str(5, "char("); | |
377 | BEGIN(SC_quadruple); | |
378 | } | |
379 | ||
380 | . { | |
381 | if (indent_level > 0) indent_before_other_token(); | |
382 | append_char(yytext[0]); | |
383 | } | |
384 | ||
385 | <SC_cstring> | |
386 | { | |
387 | \"\" append_str(2, "\"\""); /* escaped quotation mark */ | |
388 | \" { | |
389 | /* end of the string */ | |
390 | append_char('"'); | |
391 | BEGIN(INITIAL); | |
392 | } | |
393 | } | |
394 | ||
395 | <SC_binstring>\' { | |
396 | /* end of the string */ | |
397 | append_char('\''); | |
398 | BEGIN(INITIAL); | |
399 | } | |
400 | ||
401 | <SC_cstring,SC_binstring> | |
402 | { | |
403 | \\{NEWLINE} append_str(2, "\\\n"); /* canonize escaped newline characters */ | |
404 | {NEWLINE} append_char('\n'); /* canonize simple newline characters */ | |
405 | \\. append_str(yyleng, yytext); /* copy escaped characters */ | |
406 | . append_char(yytext[0]); /* copy simple characters */ | |
407 | } | |
408 | ||
409 | <SC_quadruple> | |
410 | { | |
411 | \) { | |
412 | append_char(')'); | |
413 | BEGIN(INITIAL); | |
414 | } | |
415 | ({WHITESPACE}|{NEWLINE})+ /* ignore all whitespaces and newlines */ | |
416 | , append_str(2, ", "); /* append a space after the comma */ | |
417 | . append_char(yytext[0]); /* copy simple characters */ | |
418 | } | |
419 | ||
420 | %% | |
421 | ||
422 | static void usage(void) | |
423 | { | |
424 | fprintf(stderr, | |
3abe9331 | 425 | "usage: %s [-i n] [-o outfile] [-s] [-n] [file.log] ...\n" |
970ed795 EL |
426 | " or %s -v\n" |
427 | "\n" | |
428 | "OPTIONS:\n" | |
429 | " -i n: set the depth of each indentation to n " | |
430 | "characters\n" | |
431 | " -o outfile: write the formatted log into file outfile\n" | |
432 | " -s: place the logs of each test case into separate " | |
433 | "files\n" | |
3abe9331 | 434 | " -n newline and tab control characters are not modified\n" |
970ed795 EL |
435 | " -v: print version\n", |
436 | program_name, program_name); | |
437 | } | |
438 | ||
439 | int main(int argc, char *argv[]) | |
440 | { | |
441 | int c; | |
3abe9331 | 442 | int iflag = 0, oflag = 0, sflag = 0, vflag = 0, errflag = 0, nflag = 0; |
970ed795 EL |
443 | #ifdef LICENSE |
444 | license_struct lstr; | |
445 | #endif | |
446 | program_name = argv[0]; | |
447 | output_file = stdout; | |
3abe9331 | 448 | if(argc == 1){ |
449 | errflag = 1; | |
450 | } | |
451 | while ((c = getopt(argc, argv, "i:o:snv")) != -1 && !errflag) { | |
970ed795 EL |
452 | switch (c) { |
453 | case 'i': { | |
454 | unsigned int int_arg; | |
455 | if (iflag || vflag) errflag = 1; | |
456 | if (sscanf(optarg, "%u", &int_arg) != 1) { | |
457 | fprintf(stderr, "%s: invalid indentation depth: %s\n", | |
458 | program_name, optarg); | |
459 | return EXIT_FAILURE; | |
460 | } | |
461 | indent_depth = int_arg; | |
462 | iflag = 1; | |
463 | break; } | |
464 | case 'o': | |
465 | if (oflag || vflag) errflag = 1; | |
466 | else output_file = open_file(optarg, "w"); | |
467 | oflag = 1; | |
468 | break; | |
469 | case 's': | |
470 | if (sflag || vflag) errflag = 1; | |
471 | sflag = 1; | |
472 | break; | |
3abe9331 | 473 | case 'n': |
474 | if(vflag) errflag = 1; | |
475 | format_tab_newline = 0; | |
476 | nflag = 1; | |
477 | break; | |
970ed795 | 478 | case 'v': |
3abe9331 | 479 | if (iflag || oflag || sflag || nflag) errflag = 1; |
970ed795 EL |
480 | vflag = 1; |
481 | break; | |
482 | default: | |
483 | errflag = 1; | |
484 | } | |
485 | } | |
486 | if (errflag) { | |
487 | usage(); | |
488 | return EXIT_FAILURE; | |
489 | } else if (vflag) { | |
490 | fputs("Log Formatter for the TTCN-3 Test Executor\n" | |
491 | "Product number: " PRODUCT_NUMBER "\n" | |
492 | "Build date: " __DATE__ " " __TIME__ "\n" | |
493 | "Compiled with: " C_COMPILER_VERSION "\n\n" | |
494 | COPYRIGHT_STRING "\n\n", stderr); | |
495 | #ifdef LICENSE | |
496 | print_license_info(); | |
497 | #endif | |
498 | return EXIT_SUCCESS; | |
499 | } | |
500 | #ifdef LICENSE | |
501 | init_openssl(); | |
502 | load_license(&lstr); | |
503 | if (!verify_license(&lstr)) { | |
504 | free_license(&lstr); | |
505 | free_openssl(); | |
506 | exit(EXIT_FAILURE); | |
507 | } | |
508 | if (!check_feature(&lstr, FEATURE_LOGFORMAT)) { | |
509 | fputs("The license key does not allow the formatting of log files.\n", | |
510 | stderr); | |
511 | return EXIT_FAILURE; | |
512 | } | |
513 | free_license(&lstr); | |
514 | free_openssl(); | |
515 | #endif | |
516 | separate_files = sflag; | |
517 | yyout = output_file; | |
518 | init_line_buf(); | |
519 | if (optind >= argc) { | |
520 | yyin = stdin; | |
521 | yylex(); | |
522 | write_line_buf(); | |
523 | end_testcase(); | |
524 | } else { | |
525 | for ( ; optind < argc; optind++) { | |
526 | yyin = open_file(argv[optind], "r"); | |
527 | yylex(); | |
528 | fclose(yyin); | |
529 | write_line_buf(); | |
530 | end_testcase(); | |
531 | } | |
532 | } | |
533 | free_line_buf(); | |
534 | if (oflag) fclose(output_file); | |
535 | free_testcases(); | |
536 | check_mem_leak(program_name); | |
537 | return EXIT_SUCCESS; | |
538 | } |