1 /******************************************************************************
2 * Copyright (c) 2000-2016 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
14 * Szabo, Janos Zoltan – initial implementation
16 ******************************************************************************/
27 #include "../common/memory.h"
28 #include "../common/version_internal.h"
31 #include "../common/license.h"
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;
38 static int format_tab_newline = 1;
39 static int replaced_tab_newline = 0;
41 static size_t indent_level = 0;
42 static enum { OPEN_BRACE, CLOSE_BRACE, COMMA, OTHER, OTHER_WS }
45 static char *line_buf = NULL;
46 static size_t buf_size = 0, buf_len = 0;
47 #define MIN_BUFSIZE 1024
49 static void init_line_buf(void)
51 line_buf = (char *)Malloc(MIN_BUFSIZE);
52 buf_size = MIN_BUFSIZE;
56 static void enlarge_line_buf(size_t size_incr)
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;
66 static void free_line_buf(void)
74 static void append_str(size_t chunk_size, const char *chunk_ptr)
76 enlarge_line_buf(chunk_size);
77 memcpy(line_buf + buf_len, chunk_ptr, chunk_size);
78 buf_len += chunk_size;
81 static void append_char(char c)
83 if (buf_size <= buf_len) {
85 line_buf = (char *)Realloc(line_buf, buf_size);
87 line_buf[buf_len++] = c;
90 static void append_indentation(void)
92 if (indent_depth > 0) {
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;
101 /* no indentation is made */
106 static void indent_before_other_token(void)
108 switch (last_token) {
111 /* start a new line after an opening brace or comma */
112 append_indentation();
116 /* add a single space as separator between the previous token or block */
124 static void write_failure(void)
126 fprintf(stderr, "%s: writing to output file failed: %s\n",
127 program_name, strerror(errno));
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);
140 str_replace ( const char *string, const int len, const char *substr, const char *replacement ){
145 int length_diff = strlen(substr) - strlen(replacement);
147 /* if either substr or replacement is NULL, duplicate string a let caller handle it */
148 if ( substr == NULL || replacement == NULL ) return my_strndup (string, len);
149 newstr = my_strndup (string, len);
151 while ( (tok = strstr ( head, substr ))){
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 ){
157 fprintf(stderr, "Failed to allocate memory.\n");
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 );
174 static void write_line_buf(void)
177 if(format_tab_newline){
178 char * temp = str_replace(line_buf, buf_len, "\\n", "\n");
179 temp = str_replace(temp, buf_len-replaced_tab_newline, "\\t", "\t");
180 strcpy(line_buf, temp);
183 if (fwrite(line_buf, buf_len-replaced_tab_newline, 1, yyout) != 1) write_failure();
184 /* append a newline character if it is missing from the end
185 * (e.g. because of EOF) */
186 if (line_buf[buf_len - replaced_tab_newline - 1] != '\n'){
187 if (putc('\n', yyout) == EOF) write_failure();
189 replaced_tab_newline = 0;
191 if (buf_size > MIN_BUFSIZE && buf_size > 2 * buf_len) {
192 /* reset the buffer size if a shorter one is enough */
194 line_buf = (char *)Malloc(MIN_BUFSIZE);
195 buf_size = MIN_BUFSIZE;
202 static FILE *open_file(const char *path, const char *mode)
204 FILE *fp = fopen(path, mode);
206 fprintf(stderr, "%s: cannot open file %s for %s: %s\n",
207 program_name, path, mode[0] == 'r' ? "reading" : "writing",
214 static int in_testcase = 0;
215 static size_t n_testcases = 0;
216 static char **testcases = NULL;
218 static void begin_testcase(const char *testcase_name)
220 if (separate_files) {
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");
231 testcases = (char **)Realloc(testcases, (n_testcases + 1) *
233 testcases[n_testcases - 1] = mcopystr(testcase_name);
234 yyout = open_file(testcase_name, "w");
238 static void free_testcases(void)
241 for (i = 0; i < n_testcases; i++) Free(testcases[i]);
247 static void end_testcase(void)
261 NUMBER 0|([1-9][0-9]*)
263 IDENTIFIER [a-zA-Z][a-zA-Z0-9_]*
265 VERDICT none|pass|inconc|fail|error
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]
275 TIMESTAMP1 {NUMBER}\.{MICROSEC}
276 TIMESTAMP2 {HOUR}\:{MIN}\:{SEC}\.{MICROSEC}
277 TIMESTAMP3 {YEAR}\/{MONTH}\/{DATE}" "{TIMESTAMP2}
279 TIMESTAMP {TIMESTAMP1}|{TIMESTAMP2}|{TIMESTAMP3}
281 %x SC_cstring SC_binstring SC_quadruple
284 /* start parsing of a new file from the initial state */
288 /* recognize the start of a new entry in all states in order to recover from
289 * unterminated strings */
291 append_str(yyleng, yytext);
295 "Test case "{IDENTIFIER}" started."{NEWLINE} {
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 */
302 /* find the end of testcase identifier */
303 for (i = 10; yytext[i] != ' '; i++);
305 begin_testcase(yytext + 10);
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 */
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;
324 /* copy to output transparently */
325 append_str(yyleng, yytext);
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;
334 /* canonize the newline character to UNIX style */
340 if (indent_level > 0) {
341 switch (last_token) {
344 /* start a new line for the nested block */
345 append_indentation();
348 /* separate the brace from the previous token */
354 last_token = OPEN_BRACE;
358 if (indent_level > 0) {
360 if (last_token == OPEN_BRACE) {
361 /* add a space to an empty block */
364 /* start a newline for the closing brace */
365 append_indentation();
367 last_token = CLOSE_BRACE;
374 if (indent_level > 0) last_token = COMMA;
378 if (indent_level > 0) indent_before_other_token();
384 if (indent_level > 0) indent_before_other_token();
389 "char"{WHITESPACE}*\( {
390 if (indent_level > 0) indent_before_other_token();
391 /* convert whitespaces to canonical form */
392 append_str(5, "char(");
397 if (indent_level > 0) indent_before_other_token();
398 append_char(yytext[0]);
403 \"\" append_str(2, "\"\""); /* escaped quotation mark */
405 /* end of the string */
412 /* end of the string */
417 <SC_cstring,SC_binstring>
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 */
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 */
438 static void usage(void)
441 "usage: %s [-i n] [-o outfile] [-s] [-n] [file.log] ...\n"
445 " -i n: set the depth of each indentation to n "
447 " -o outfile: write the formatted log into file outfile\n"
448 " -s: place the logs of each test case into separate "
450 " -n newline and tab control characters are not modified\n"
451 " -v: print version\n",
452 program_name, program_name);
455 int main(int argc, char *argv[])
458 int iflag = 0, oflag = 0, sflag = 0, vflag = 0, errflag = 0, nflag = 0;
462 program_name = argv[0];
463 output_file = stdout;
467 while ((c = getopt(argc, argv, "i:o:snv")) != -1 && !errflag) {
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);
477 indent_depth = int_arg;
481 if (oflag || vflag) errflag = 1;
482 else output_file = open_file(optarg, "w");
486 if (sflag || vflag) errflag = 1;
490 if(vflag) errflag = 1;
491 format_tab_newline = 0;
495 if (iflag || oflag || sflag || nflag) errflag = 1;
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);
512 print_license_info();
519 if (!verify_license(&lstr)) {
524 if (!check_feature(&lstr, FEATURE_LOGFORMAT)) {
525 fputs("The license key does not allow the formatting of log files.\n",
532 separate_files = sflag;
535 if (optind >= argc) {
541 for ( ; optind < argc; optind++) {
542 yyin = open_file(argv[optind], "r");
550 if (oflag) fclose(output_file);
552 check_mem_leak(program_name);