1 /******************************************************************************
2 * Copyright (c) 2000-2014 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;
30 static size_t indent_level = 0;
31 static enum { OPEN_BRACE, CLOSE_BRACE, COMMA, OTHER, OTHER_WS }
34 static char *line_buf = NULL;
35 static size_t buf_size = 0, buf_len = 0;
36 #define MIN_BUFSIZE 1024
38 static void init_line_buf(void)
40 line_buf = (char *)Malloc(MIN_BUFSIZE);
41 buf_size = MIN_BUFSIZE;
45 static void enlarge_line_buf(size_t size_incr)
47 size_t new_buf_size = buf_size;
48 while (buf_len + size_incr > new_buf_size) new_buf_size *= 2;
49 if (new_buf_size > buf_size) {
50 line_buf = (char *)Realloc(line_buf, new_buf_size);
51 buf_size = new_buf_size;
55 static void free_line_buf(void)
63 static void append_str(size_t chunk_size, const char *chunk_ptr)
65 enlarge_line_buf(chunk_size);
66 memcpy(line_buf + buf_len, chunk_ptr, chunk_size);
67 buf_len += chunk_size;
70 static void append_char(char c)
72 if (buf_size <= buf_len) {
74 line_buf = (char *)Realloc(line_buf, buf_size);
76 line_buf[buf_len++] = c;
79 static void append_indentation(void)
81 if (indent_depth > 0) {
83 if (indent_level > 0) {
84 size_t nof_spaces = indent_level * indent_depth;
85 enlarge_line_buf(nof_spaces);
86 memset(line_buf + buf_len, ' ', nof_spaces);
87 buf_len += nof_spaces;
90 /* no indentation is made */
95 static void indent_before_other_token(void)
100 /* start a new line after an opening brace or comma */
101 append_indentation();
105 /* add a single space as separator between the previous token or block */
113 static void write_failure(void)
115 fprintf(stderr, "%s: writing to output file failed: %s\n",
116 program_name, strerror(errno));
120 static void write_line_buf(void)
123 if (fwrite(line_buf, buf_len, 1, yyout) != 1) write_failure();
124 /* append a newline character if it is missing from the end
125 * (e.g. because of EOF) */
126 if (line_buf[buf_len - 1] != '\n')
127 if (putc('\n', yyout) == EOF) write_failure();
129 if (buf_size > MIN_BUFSIZE && buf_size > 2 * buf_len) {
130 /* reset the buffer size if a shorter one is enough */
132 line_buf = (char *)Malloc(MIN_BUFSIZE);
133 buf_size = MIN_BUFSIZE;
140 static FILE *open_file(const char *path, const char *mode)
142 FILE *fp = fopen(path, mode);
144 fprintf(stderr, "%s: cannot open file %s for %s: %s\n",
145 program_name, path, mode[0] == 'r' ? "reading" : "writing",
152 static int in_testcase = 0;
153 static size_t n_testcases = 0;
154 static char **testcases = NULL;
156 static void begin_testcase(const char *testcase_name)
158 if (separate_files) {
160 if (in_testcase) fclose(yyout);
161 else in_testcase = 1;
162 for (i = 0; i < n_testcases; i++) {
163 if (!strcmp(testcase_name, testcases[i])) {
164 yyout = open_file(testcase_name, "a");
169 testcases = (char **)Realloc(testcases, (n_testcases + 1) *
171 testcases[n_testcases - 1] = mcopystr(testcase_name);
172 yyout = open_file(testcase_name, "w");
176 static void free_testcases(void)
179 for (i = 0; i < n_testcases; i++) Free(testcases[i]);
185 static void end_testcase(void)
199 NUMBER 0|([1-9][0-9]*)
201 IDENTIFIER [a-zA-Z][a-zA-Z0-9_]*
203 VERDICT none|pass|inconc|fail|error
206 MONTH Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec
207 DATE [0-2][0-9]|3[01]
208 HOUR [01][0-9]|2[0-3]
213 TIMESTAMP1 {NUMBER}\.{MICROSEC}
214 TIMESTAMP2 {HOUR}\:{MIN}\:{SEC}\.{MICROSEC}
215 TIMESTAMP3 {YEAR}\/{MONTH}\/{DATE}" "{TIMESTAMP2}
217 TIMESTAMP {TIMESTAMP1}|{TIMESTAMP2}|{TIMESTAMP3}
219 %x SC_cstring SC_binstring SC_quadruple
222 /* start parsing of a new file from the initial state */
226 /* recognize the start of a new entry in all states in order to recover from
227 * unterminated strings */
229 append_str(yyleng, yytext);
233 "Test case "{IDENTIFIER}" started."{NEWLINE} {
235 /* cut the newline character(s) from the end */
236 while (yytext[yyleng - 1] == '\r' || yytext[yyleng - 1] == '\n') yyleng--;
237 append_str(yyleng, yytext);
238 /* append a UNIX-style newline character */
240 /* find the end of testcase identifier */
241 for (i = 10; yytext[i] != ' '; i++);
243 begin_testcase(yytext + 10);
247 "Test case "{IDENTIFIER}" finished. Verdict: "{VERDICT}{NEWLINE} {
248 /* cut the newline character(s) from the end */
249 while (yytext[yyleng - 1] == '\r' || yytext[yyleng - 1] == '\n') yyleng--;
250 append_str(yyleng, yytext);
251 /* append a UNIX-style newline character */
258 if (indent_level > 0) {
259 /* only record the presence of the whitespace in indented blocks */
260 if (last_token == OTHER) last_token = OTHER_WS;
262 /* copy to output transparently */
263 append_str(yyleng, yytext);
268 if (indent_level > 0) {
269 /* only record the presence of the newline in indented blocks */
270 if (last_token == OTHER) last_token = OTHER_WS;
272 /* canonize the newline character to UNIX style */
278 if (indent_level > 0) {
279 switch (last_token) {
282 /* start a new line for the nested block */
283 append_indentation();
286 /* separate the brace from the previous token */
292 last_token = OPEN_BRACE;
296 if (indent_level > 0) {
298 if (last_token == OPEN_BRACE) {
299 /* add a space to an empty block */
302 /* start a newline for the closing brace */
303 append_indentation();
305 last_token = CLOSE_BRACE;
312 if (indent_level > 0) last_token = COMMA;
316 if (indent_level > 0) indent_before_other_token();
322 if (indent_level > 0) indent_before_other_token();
327 "char"{WHITESPACE}*\( {
328 if (indent_level > 0) indent_before_other_token();
329 /* convert whitespaces to canonical form */
330 append_str(5, "char(");
335 if (indent_level > 0) indent_before_other_token();
336 append_char(yytext[0]);
341 \"\" append_str(2, "\"\""); /* escaped quotation mark */
343 /* end of the string */
350 /* end of the string */
355 <SC_cstring,SC_binstring>
357 \\{NEWLINE} append_str(2, "\\\n"); /* canonize escaped newline characters */
358 {NEWLINE} append_char('\n'); /* canonize simple newline characters */
359 \\. append_str(yyleng, yytext); /* copy escaped characters */
360 . append_char(yytext[0]); /* copy simple characters */
369 ({WHITESPACE}|{NEWLINE})+ /* ignore all whitespaces and newlines */
370 , append_str(2, ", "); /* append a space after the comma */
371 . append_char(yytext[0]); /* copy simple characters */
376 static void usage(void)
379 "usage: %s [-i n] [-o outfile] [-s] [file.log] ...\n"
383 " -i n: set the depth of each indentation to n "
385 " -o outfile: write the formatted log into file outfile\n"
386 " -s: place the logs of each test case into separate "
388 " -v: print version\n",
389 program_name, program_name);
392 int main(int argc, char *argv[])
395 int iflag = 0, oflag = 0, sflag = 0, vflag = 0, errflag = 0;
399 program_name = argv[0];
400 output_file = stdout;
401 while ((c = getopt(argc, argv, "i:o:sv")) != -1) {
404 unsigned int int_arg;
405 if (iflag || vflag) errflag = 1;
406 if (sscanf(optarg, "%u", &int_arg) != 1) {
407 fprintf(stderr, "%s: invalid indentation depth: %s\n",
408 program_name, optarg);
411 indent_depth = int_arg;
415 if (oflag || vflag) errflag = 1;
416 else output_file = open_file(optarg, "w");
420 if (sflag || vflag) errflag = 1;
424 if (iflag || oflag || sflag) errflag = 1;
435 fputs("Log Formatter for the TTCN-3 Test Executor\n"
436 "Product number: " PRODUCT_NUMBER "\n"
437 "Build date: " __DATE__ " " __TIME__ "\n"
438 "Compiled with: " C_COMPILER_VERSION "\n\n"
439 COPYRIGHT_STRING "\n\n", stderr);
441 print_license_info();
448 if (!verify_license(&lstr)) {
453 if (!check_feature(&lstr, FEATURE_LOGFORMAT)) {
454 fputs("The license key does not allow the formatting of log files.\n",
461 separate_files = sflag;
464 if (optind >= argc) {
470 for ( ; optind < argc; optind++) {
471 yyin = open_file(argv[optind], "r");
479 if (oflag) fclose(output_file);
481 check_mem_leak(program_name);