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