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