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));
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);
131 str_replace ( const char *string, const int len, const char *substr, const char *replacement ){
136 int length_diff = strlen(substr) - strlen(replacement);
138 /* if either substr or replacement is NULL, duplicate string a let caller handle it */
139 if ( substr == NULL || replacement == NULL ) return my_strndup (string, len);
140 newstr = my_strndup (string, len);
142 while ( (tok = strstr ( head, substr ))){
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 ){
148 fprintf(stderr, "Failed to allocate memory.\n");
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 );
165 static void write_line_buf(void)
168 if(format_tab_newline){
169 char * temp = str_replace(line_buf, buf_len, "\\n", "\n");
170 temp = str_replace(temp, buf_len-replaced_tab_newline, "\\t", "\t");
171 strcpy(line_buf, temp);
174 if (fwrite(line_buf, buf_len-replaced_tab_newline, 1, yyout) != 1) write_failure();
175 /* append a newline character if it is missing from the end
176 * (e.g. because of EOF) */
177 if (line_buf[buf_len - replaced_tab_newline - 1] != '\n'){
178 if (putc('\n', yyout) == EOF) write_failure();
180 replaced_tab_newline = 0;
182 if (buf_size > MIN_BUFSIZE && buf_size > 2 * buf_len) {
183 /* reset the buffer size if a shorter one is enough */
185 line_buf = (char *)Malloc(MIN_BUFSIZE);
186 buf_size = MIN_BUFSIZE;
193 static FILE *open_file(const char *path, const char *mode)
195 FILE *fp = fopen(path, mode);
197 fprintf(stderr, "%s: cannot open file %s for %s: %s\n",
198 program_name, path, mode[0] == 'r' ? "reading" : "writing",
205 static int in_testcase = 0;
206 static size_t n_testcases = 0;
207 static char **testcases = NULL;
209 static void begin_testcase(const char *testcase_name)
211 if (separate_files) {
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");
222 testcases = (char **)Realloc(testcases, (n_testcases + 1) *
224 testcases[n_testcases - 1] = mcopystr(testcase_name);
225 yyout = open_file(testcase_name, "w");
229 static void free_testcases(void)
232 for (i = 0; i < n_testcases; i++) Free(testcases[i]);
238 static void end_testcase(void)
252 NUMBER 0|([1-9][0-9]*)
254 IDENTIFIER [a-zA-Z][a-zA-Z0-9_]*
256 VERDICT none|pass|inconc|fail|error
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]
266 TIMESTAMP1 {NUMBER}\.{MICROSEC}
267 TIMESTAMP2 {HOUR}\:{MIN}\:{SEC}\.{MICROSEC}
268 TIMESTAMP3 {YEAR}\/{MONTH}\/{DATE}" "{TIMESTAMP2}
270 TIMESTAMP {TIMESTAMP1}|{TIMESTAMP2}|{TIMESTAMP3}
272 %x SC_cstring SC_binstring SC_quadruple
275 /* start parsing of a new file from the initial state */
279 /* recognize the start of a new entry in all states in order to recover from
280 * unterminated strings */
282 append_str(yyleng, yytext);
286 "Test case "{IDENTIFIER}" started."{NEWLINE} {
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 */
293 /* find the end of testcase identifier */
294 for (i = 10; yytext[i] != ' '; i++);
296 begin_testcase(yytext + 10);
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 */
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;
315 /* copy to output transparently */
316 append_str(yyleng, yytext);
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;
325 /* canonize the newline character to UNIX style */
331 if (indent_level > 0) {
332 switch (last_token) {
335 /* start a new line for the nested block */
336 append_indentation();
339 /* separate the brace from the previous token */
345 last_token = OPEN_BRACE;
349 if (indent_level > 0) {
351 if (last_token == OPEN_BRACE) {
352 /* add a space to an empty block */
355 /* start a newline for the closing brace */
356 append_indentation();
358 last_token = CLOSE_BRACE;
365 if (indent_level > 0) last_token = COMMA;
369 if (indent_level > 0) indent_before_other_token();
375 if (indent_level > 0) indent_before_other_token();
380 "char"{WHITESPACE}*\( {
381 if (indent_level > 0) indent_before_other_token();
382 /* convert whitespaces to canonical form */
383 append_str(5, "char(");
388 if (indent_level > 0) indent_before_other_token();
389 append_char(yytext[0]);
394 \"\" append_str(2, "\"\""); /* escaped quotation mark */
396 /* end of the string */
403 /* end of the string */
408 <SC_cstring,SC_binstring>
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 */
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 */
429 static void usage(void)
432 "usage: %s [-i n] [-o outfile] [-s] [-n] [file.log] ...\n"
436 " -i n: set the depth of each indentation to n "
438 " -o outfile: write the formatted log into file outfile\n"
439 " -s: place the logs of each test case into separate "
441 " -n newline and tab control characters are not modified\n"
442 " -v: print version\n",
443 program_name, program_name);
446 int main(int argc, char *argv[])
449 int iflag = 0, oflag = 0, sflag = 0, vflag = 0, errflag = 0, nflag = 0;
453 program_name = argv[0];
454 output_file = stdout;
458 while ((c = getopt(argc, argv, "i:o:snv")) != -1 && !errflag) {
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);
468 indent_depth = int_arg;
472 if (oflag || vflag) errflag = 1;
473 else output_file = open_file(optarg, "w");
477 if (sflag || vflag) errflag = 1;
481 if(vflag) errflag = 1;
482 format_tab_newline = 0;
486 if (iflag || oflag || sflag || nflag) errflag = 1;
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);
503 print_license_info();
510 if (!verify_license(&lstr)) {
515 if (!check_feature(&lstr, FEATURE_LOGFORMAT)) {
516 fputs("The license key does not allow the formatting of log files.\n",
523 separate_files = sflag;
526 if (optind >= argc) {
532 for ( ; optind < argc; optind++) {
533 yyin = open_file(argv[optind], "r");
541 if (oflag) fclose(output_file);
543 check_mem_leak(program_name);