1 /******************************************************************************
2 * Copyright (c) 2000-2015 Ericsson Telecom AB
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 ******************************************************************************/
18 #include "../common/memory.h"
19 #include "../common/version_internal.h"
22 #include "../common/license.h"
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;
29 static int format_tab_newline = 1;
30 static int replaced_tab_newline = 0;
32 static size_t indent_level = 0;
33 static enum { OPEN_BRACE, CLOSE_BRACE, COMMA, OTHER, OTHER_WS }
36 static char *line_buf = NULL;
37 static size_t buf_size = 0, buf_len = 0;
38 #define MIN_BUFSIZE 1024
40 static void init_line_buf(void)
42 line_buf = (char *)Malloc(MIN_BUFSIZE);
43 buf_size = MIN_BUFSIZE;
47 static void enlarge_line_buf(size_t size_incr)
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;
57 static void free_line_buf(void)
65 static void append_str(size_t chunk_size, const char *chunk_ptr)
67 enlarge_line_buf(chunk_size);
68 memcpy(line_buf + buf_len, chunk_ptr, chunk_size);
69 buf_len += chunk_size;
72 static void append_char(char c)
74 if (buf_size <= buf_len) {
76 line_buf = (char *)Realloc(line_buf, buf_size);
78 line_buf[buf_len++] = c;
81 static void append_indentation(void)
83 if (indent_depth > 0) {
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;
92 /* no indentation is made */
97 static void indent_before_other_token(void)
102 /* start a new line after an opening brace or comma */
103 append_indentation();
107 /* add a single space as separator between the previous token or block */
115 static void write_failure(void)
117 fprintf(stderr, "%s: writing to output file failed: %s\n",
118 program_name, strerror(errno));
124 str_replace ( const char *string, const char *substr, const char *replacement ){
129 int length_diff = strlen(substr) - strlen(replacement);
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);
135 while ( (tok = strstr ( head, substr ))){
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 ){
141 fprintf(stderr, "Failed to allocate memory.\n");
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 );
158 static void write_line_buf(void)
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);
167 if (fwrite(line_buf, buf_len-replaced_tab_newline, 1, yyout) != 1) write_failure();
168 /* append a newline character if it is missing from the end
169 * (e.g. because of EOF) */
170 if (line_buf[buf_len - replaced_tab_newline - 1] != '\n'){
171 if (putc('\n', yyout) == EOF) write_failure();
173 replaced_tab_newline = 0;
175 if (buf_size > MIN_BUFSIZE && buf_size > 2 * buf_len) {
176 /* reset the buffer size if a shorter one is enough */
178 line_buf = (char *)Malloc(MIN_BUFSIZE);
179 buf_size = MIN_BUFSIZE;
186 static FILE *open_file(const char *path, const char *mode)
188 FILE *fp = fopen(path, mode);
190 fprintf(stderr, "%s: cannot open file %s for %s: %s\n",
191 program_name, path, mode[0] == 'r' ? "reading" : "writing",
198 static int in_testcase = 0;
199 static size_t n_testcases = 0;
200 static char **testcases = NULL;
202 static void begin_testcase(const char *testcase_name)
204 if (separate_files) {
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");
215 testcases = (char **)Realloc(testcases, (n_testcases + 1) *
217 testcases[n_testcases - 1] = mcopystr(testcase_name);
218 yyout = open_file(testcase_name, "w");
222 static void free_testcases(void)
225 for (i = 0; i < n_testcases; i++) Free(testcases[i]);
231 static void end_testcase(void)
245 NUMBER 0|([1-9][0-9]*)
247 IDENTIFIER [a-zA-Z][a-zA-Z0-9_]*
249 VERDICT none|pass|inconc|fail|error
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]
259 TIMESTAMP1 {NUMBER}\.{MICROSEC}
260 TIMESTAMP2 {HOUR}\:{MIN}\:{SEC}\.{MICROSEC}
261 TIMESTAMP3 {YEAR}\/{MONTH}\/{DATE}" "{TIMESTAMP2}
263 TIMESTAMP {TIMESTAMP1}|{TIMESTAMP2}|{TIMESTAMP3}
265 %x SC_cstring SC_binstring SC_quadruple
268 /* start parsing of a new file from the initial state */
272 /* recognize the start of a new entry in all states in order to recover from
273 * unterminated strings */
275 append_str(yyleng, yytext);
279 "Test case "{IDENTIFIER}" started."{NEWLINE} {
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 */
286 /* find the end of testcase identifier */
287 for (i = 10; yytext[i] != ' '; i++);
289 begin_testcase(yytext + 10);
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 */
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;
308 /* copy to output transparently */
309 append_str(yyleng, yytext);
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;
318 /* canonize the newline character to UNIX style */
324 if (indent_level > 0) {
325 switch (last_token) {
328 /* start a new line for the nested block */
329 append_indentation();
332 /* separate the brace from the previous token */
338 last_token = OPEN_BRACE;
342 if (indent_level > 0) {
344 if (last_token == OPEN_BRACE) {
345 /* add a space to an empty block */
348 /* start a newline for the closing brace */
349 append_indentation();
351 last_token = CLOSE_BRACE;
358 if (indent_level > 0) last_token = COMMA;
362 if (indent_level > 0) indent_before_other_token();
368 if (indent_level > 0) indent_before_other_token();
373 "char"{WHITESPACE}*\( {
374 if (indent_level > 0) indent_before_other_token();
375 /* convert whitespaces to canonical form */
376 append_str(5, "char(");
381 if (indent_level > 0) indent_before_other_token();
382 append_char(yytext[0]);
387 \"\" append_str(2, "\"\""); /* escaped quotation mark */
389 /* end of the string */
396 /* end of the string */
401 <SC_cstring,SC_binstring>
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 */
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 */
422 static void usage(void)
425 "usage: %s [-i n] [-o outfile] [-s] [-n] [file.log] ...\n"
429 " -i n: set the depth of each indentation to n "
431 " -o outfile: write the formatted log into file outfile\n"
432 " -s: place the logs of each test case into separate "
434 " -n newline and tab control characters are not modified\n"
435 " -v: print version\n",
436 program_name, program_name);
439 int main(int argc, char *argv[])
442 int iflag = 0, oflag = 0, sflag = 0, vflag = 0, errflag = 0, nflag = 0;
446 program_name = argv[0];
447 output_file = stdout;
451 while ((c = getopt(argc, argv, "i:o:snv")) != -1 && !errflag) {
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);
461 indent_depth = int_arg;
465 if (oflag || vflag) errflag = 1;
466 else output_file = open_file(optarg, "w");
470 if (sflag || vflag) errflag = 1;
474 if(vflag) errflag = 1;
475 format_tab_newline = 0;
479 if (iflag || oflag || sflag || nflag) errflag = 1;
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);
496 print_license_info();
503 if (!verify_license(&lstr)) {
508 if (!check_feature(&lstr, FEATURE_LOGFORMAT)) {
509 fputs("The license key does not allow the formatting of log files.\n",
516 separate_files = sflag;
519 if (optind >= argc) {
525 for ( ; optind < argc; optind++) {
526 yyin = open_file(argv[optind], "r");
534 if (oflag) fclose(output_file);
536 check_mem_leak(program_name);