| 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 | /////////////////////////////////////////////////////////////////////////////// |
| 8 | /************************** |
| 9 | Log filter for TTCNv3 |
| 10 | written by Gabor Tatarka |
| 11 | **************************/ |
| 12 | #include <stdio.h> |
| 13 | #include <stdlib.h> |
| 14 | #include <unistd.h> |
| 15 | #include <string.h> |
| 16 | #include <errno.h> |
| 17 | #include "../common/version_internal.h" |
| 18 | |
| 19 | #ifdef LICENSE |
| 20 | #include "../common/license.h" |
| 21 | #endif |
| 22 | |
| 23 | #define Fail -1 |
| 24 | #define False 0 |
| 25 | #define True 1 |
| 26 | |
| 27 | #define ERR_NONE 0 |
| 28 | #define ERR_WRITE 1 |
| 29 | #define ERR_INVALID_LOG 2 |
| 30 | |
| 31 | /*filter flag values:*/ |
| 32 | #define Write 1 |
| 33 | #define DontWrite 2 |
| 34 | /*0 is: act as Wothers*/ |
| 35 | |
| 36 | #define TimeLength 15 |
| 37 | #define SecondLength 9 |
| 38 | #define DateTimeLength 27 |
| 39 | #define MaxTimeStampLength 27 |
| 40 | #define BufferSize 512 |
| 41 | #define YYYYMONDD 1 /*format of Date: year/month/day*/ |
| 42 | |
| 43 | static const char * const MON[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
| 44 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; |
| 45 | |
| 46 | #define MaxType 15 |
| 47 | typedef enum {/*If you add new types, add it to the end of the list, and |
| 48 | also add it to EventTypeNames! ET_Unknown has no representation |
| 49 | in text, and it must not be changed.Also change MaxType!*/ |
| 50 | ET_Unknown=-1,ET_Error,ET_Warning,ET_Portevent,ET_Timerop, |
| 51 | ET_Verdictop,ET_Defaultop,ET_Action,ET_Testcase,ET_Function, |
| 52 | ET_User,ET_Statictics,ET_Parallel,ET_Matching,ET_Debug, |
| 53 | ET_Executor |
| 54 | } EventTypes; |
| 55 | static const char * const EventTypeNames[] = { |
| 56 | "ERROR","WARNING","PORTEVENT","TIMEROP","VERDICTOP","DEFAULTOP", |
| 57 | "ACTION","TESTCASE","FUNCTION","USER","STATISTICS","PARALLEL", |
| 58 | "MATCHING","DEBUG","EXECUTOR" |
| 59 | }; |
| 60 | static int Wothers=Write; |
| 61 | static int Wflags[MaxType]; |
| 62 | |
| 63 | static int IsSecond(const char *str)/*Is timestamp format Seconds*/ |
| 64 | { |
| 65 | int a; |
| 66 | if(*str<'0'||*str>'9')return False;/*first digit*/ |
| 67 | while(*str>='0'&&*str<='9')str++;/*other digits*/ |
| 68 | if(*str!='.')return False;/* '.' */ |
| 69 | for(a=0;a<6;a++)if(*str<'0'||*str>'9')return False;/*microseconds(6 digits)*/ |
| 70 | return True; |
| 71 | } |
| 72 | |
| 73 | static int IsSecond2_6(const char *str)/*does string contain sec(2).usec(6)*/ |
| 74 | { |
| 75 | int a; |
| 76 | for(a=0;a<SecondLength;a++) { |
| 77 | if(a==2) { |
| 78 | if(*str=='.') { |
| 79 | str++;continue; |
| 80 | } else return False; |
| 81 | } |
| 82 | if(*str<'0'||*str>'9')return False; |
| 83 | str++; |
| 84 | } |
| 85 | return True; |
| 86 | } |
| 87 | |
| 88 | static int IsTime(const char *str)/*Is timestamp format Time*/ |
| 89 | { |
| 90 | int a; |
| 91 | if(False==IsSecond2_6(str+6))return False; |
| 92 | for(a=0;a<6;a++){ |
| 93 | if(a==2||a==5) { |
| 94 | if(*str==':') { |
| 95 | str++;continue; |
| 96 | } else return False; |
| 97 | } |
| 98 | if(*str<'0'||*str>'9')return False; |
| 99 | str++; |
| 100 | } |
| 101 | return True; |
| 102 | } |
| 103 | |
| 104 | #ifdef YYYYMONDD /*Date format: year/month/day*/ |
| 105 | # define FIRST_LEN 4 |
| 106 | # define THIRD_LEN 2 |
| 107 | #else /*Date format: day/month/year*/ |
| 108 | # define FIRST_LEN 2 |
| 109 | # define THIRD_LEN 4 |
| 110 | #endif |
| 111 | |
| 112 | static int IsDateTime(const char *str)/*is timestamp format Date/Time*/ |
| 113 | { |
| 114 | int a,b; |
| 115 | if(False==IsTime(str+12))return False; |
| 116 | for(a=0;a<FIRST_LEN;a++) {/*YYYY or DD*/ |
| 117 | if(*str<'0'||*str>'9')return False; |
| 118 | str++; |
| 119 | } |
| 120 | if(*str!='/')return False;/* '/' */ |
| 121 | str++; |
| 122 | for(a=0,b=0;a<12;a++)if(0==strncmp(str,MON[a],3)){b=1;break;}/*MON*/ |
| 123 | if(!b)return False; |
| 124 | str+=3; |
| 125 | if(*str!='/')return False;/* '/' */ |
| 126 | str++; |
| 127 | for(a=0;a<THIRD_LEN;a++) {/*DD or YYYY*/ |
| 128 | if(*str<'0'||*str>'9')return False; |
| 129 | str++; |
| 130 | } |
| 131 | return True; |
| 132 | } |
| 133 | |
| 134 | static int ValidTS(const char *str)/*is timestamp of event valid*/ |
| 135 | { |
| 136 | if(True==IsSecond(str))return True; |
| 137 | if(True==IsTime(str))return True; |
| 138 | if(True==IsDateTime(str))return True; |
| 139 | return False; |
| 140 | } |
| 141 | |
| 142 | static EventTypes get_event_type(char *buf) |
| 143 | { |
| 144 | int a; |
| 145 | char *ptr1,*ptr2; |
| 146 | ptr1=buf+strlen(buf); |
| 147 | for(a=0;a<MaxType;a++) { |
| 148 | ptr2=strstr(buf,EventTypeNames[a]); |
| 149 | if(ptr2<ptr1&&ptr2!=NULL) { |
| 150 | ptr1=ptr2; |
| 151 | if(*(ptr2+strlen(EventTypeNames[a]))==' '&& *(ptr2-1)==' ') |
| 152 | return a; |
| 153 | } |
| 154 | } |
| 155 | return ET_Unknown; |
| 156 | } |
| 157 | |
| 158 | static int ProcessFile(FILE *out,FILE *in) /*filter infile to outfile*/ |
| 159 | { |
| 160 | int b=0; |
| 161 | char line_buffer[BufferSize]; |
| 162 | int last_event=0; |
| 163 | EventTypes current_event_type=ET_Unknown; |
| 164 | while(1) { |
| 165 | line_buffer[0]='\0'; |
| 166 | if(NULL==fgets(line_buffer,sizeof(line_buffer),in)) last_event=1; |
| 167 | else if(ValidTS(line_buffer)) { |
| 168 | current_event_type=get_event_type(line_buffer); |
| 169 | b++; |
| 170 | } |
| 171 | if(current_event_type==ET_Unknown && Wothers==Write) { |
| 172 | if(0>fprintf(out,"%s",line_buffer))return ERR_WRITE; |
| 173 | } else if(Wflags[current_event_type]==Write || |
| 174 | (Wflags[current_event_type]==0 && Wothers==Write)) { |
| 175 | if(0>fprintf(out,"%s",line_buffer))return ERR_WRITE; |
| 176 | } |
| 177 | if(last_event)break; |
| 178 | } |
| 179 | if(!b)return ERR_INVALID_LOG; |
| 180 | else return ERR_NONE; |
| 181 | } |
| 182 | |
| 183 | static void Usage(const char *progname) |
| 184 | { |
| 185 | fprintf(stderr, |
| 186 | "Usage: %s [-o outfile] [infile.log] [eventtype+|-] [...]\n" |
| 187 | " or %s option\n" |
| 188 | "options:\n" |
| 189 | " -o outfile: write merged logs into file outfile\n" |
| 190 | " -v: print version\n" |
| 191 | " -h: print help (usage)\n" |
| 192 | " -l: list event types\n" |
| 193 | "If there is no outfile specified output is stdout.\n" |
| 194 | "If infile is omitted input is stdin.\n" |
| 195 | "Including and excluding event types at the same time is not allowed.\n" |
| 196 | "\n",progname,progname); |
| 197 | } |
| 198 | |
| 199 | static void ListTypes(void) |
| 200 | { |
| 201 | int a; |
| 202 | fprintf(stderr,"Event types:\n"); |
| 203 | for(a=0;a<MaxType;a++)fprintf(stderr,"\t%s\n",EventTypeNames[a]); |
| 204 | } |
| 205 | |
| 206 | int main(int argc,char *argv[]) |
| 207 | { |
| 208 | int a,b,c; |
| 209 | FILE *outfile = NULL, *infile = NULL; |
| 210 | char *outfile_name=NULL,*infile_name=NULL; |
| 211 | char tmp[20]; |
| 212 | int vflag=0,oflag=0,hflag=0,lflag=0,plusflag=0,minusflag=0; |
| 213 | #ifdef LICENSE |
| 214 | license_struct lstr; |
| 215 | #endif |
| 216 | while ((c = getopt(argc, argv, "lhvo:")) != -1) { |
| 217 | switch (c) { |
| 218 | case 'o':/*set outfile*/ |
| 219 | outfile_name=optarg; |
| 220 | oflag = 1; |
| 221 | break; |
| 222 | case 'v':/*print version*/ |
| 223 | vflag=1; |
| 224 | break; |
| 225 | case 'h':/*print help (usage)*/ |
| 226 | hflag=1; |
| 227 | break; |
| 228 | case 'l':/*list event types*/ |
| 229 | lflag=1; |
| 230 | break; |
| 231 | default: |
| 232 | Usage(argv[0]); |
| 233 | return EXIT_FAILURE; |
| 234 | } |
| 235 | } |
| 236 | if (oflag + vflag + hflag + lflag > 1) { |
| 237 | Usage(argv[0]); |
| 238 | return EXIT_FAILURE; |
| 239 | } else if (vflag) { |
| 240 | fputs("Log Filter for the TTCN-3 Test Executor\n" |
| 241 | "Product number: " PRODUCT_NUMBER "\n" |
| 242 | "Build date: " __DATE__ " " __TIME__ "\n" |
| 243 | "Compiled with: " C_COMPILER_VERSION "\n\n" |
| 244 | COPYRIGHT_STRING "\n\n", stderr); |
| 245 | #ifdef LICENSE |
| 246 | print_license_info(); |
| 247 | #endif |
| 248 | return EXIT_SUCCESS; |
| 249 | } else if (hflag) { |
| 250 | Usage(argv[0]); |
| 251 | return EXIT_SUCCESS; |
| 252 | } else if(lflag) { |
| 253 | ListTypes(); |
| 254 | return EXIT_SUCCESS; |
| 255 | } |
| 256 | #ifdef LICENSE |
| 257 | init_openssl(); |
| 258 | load_license(&lstr); |
| 259 | if (!verify_license(&lstr)) { |
| 260 | free_license(&lstr); |
| 261 | free_openssl(); |
| 262 | exit(EXIT_FAILURE); |
| 263 | } |
| 264 | if (!check_feature(&lstr, FEATURE_LOGFORMAT)) { |
| 265 | fputs("The license key does not allow the filtering of log files.\n", |
| 266 | stderr); |
| 267 | return 2; |
| 268 | } |
| 269 | free_license(&lstr); |
| 270 | free_openssl(); |
| 271 | #endif |
| 272 | /* |
| 273 | switches: -v -o -h -l |
| 274 | filter parameters: parameter+ or parameter- |
| 275 | */ |
| 276 | for(a=0;a<MaxType;a++)Wflags[a]=0; |
| 277 | for(a=1;a<argc;a++) { |
| 278 | if(*argv[a]=='-'){if(*(argv[a]+1)=='o')a++;continue;}/*switch*/ |
| 279 | if(*(argv[a]+strlen(argv[a])-1)=='-') {/*type to ignore*/ |
| 280 | for(b=0,c=0;b<MaxType;b++) |
| 281 | if(0==strncmp(EventTypeNames[b],argv[a], |
| 282 | strlen(EventTypeNames[b]))&&strlen(EventTypeNames[b])== |
| 283 | strlen(argv[a])-1) { |
| 284 | Wflags[b]=DontWrite; |
| 285 | c=1; |
| 286 | } |
| 287 | if(!c) {/*Undefined type*/ |
| 288 | strncpy(tmp,argv[a],sizeof(tmp)-1); |
| 289 | if(strlen(argv[a])>sizeof(tmp)-1) |
| 290 | for(c=2;c<5;c++)tmp[sizeof(tmp)-c]='.'; |
| 291 | else tmp[strlen(argv[a])-1]='\0'; |
| 292 | tmp[sizeof(tmp)-1]='\0'; |
| 293 | if(strlen(tmp))fprintf(stderr,"Warning: %s is not a valid " |
| 294 | "event-type name.\n",tmp); |
| 295 | else fprintf(stderr,"Warning: `-\' without an event-type " |
| 296 | "name.\n"); |
| 297 | } |
| 298 | Wothers=Write; |
| 299 | minusflag=1; |
| 300 | continue; |
| 301 | } |
| 302 | if(*(argv[a]+strlen(argv[a])-1)=='+') {/*type to write out*/ |
| 303 | for(b=0,c=0;b<MaxType;b++) |
| 304 | if(0==strncmp(EventTypeNames[b],argv[a], |
| 305 | strlen(EventTypeNames[b]))&&strlen(EventTypeNames[b])== |
| 306 | strlen(argv[a])-1) { |
| 307 | Wflags[b]=Write; |
| 308 | c=1; |
| 309 | } |
| 310 | if(!c) {/*Undefined type*/ |
| 311 | strncpy(tmp,argv[a],sizeof(tmp)-1); |
| 312 | if(strlen(argv[a])>sizeof(tmp)-1) |
| 313 | for(c=2;c<5;c++)tmp[sizeof(tmp)-c]='.'; |
| 314 | else tmp[strlen(argv[a])-1]='\0'; |
| 315 | tmp[sizeof(tmp)-1]='\0'; |
| 316 | if(strlen(tmp))fprintf(stderr,"Warning: %s is not a valid " |
| 317 | "event-type name.\n",tmp); |
| 318 | else fprintf(stderr,"Warning: `+\' without an event-type " |
| 319 | "name.\n"); |
| 320 | } |
| 321 | Wothers=DontWrite; |
| 322 | plusflag=1; |
| 323 | continue; |
| 324 | } |
| 325 | if(infile_name!=NULL) {/*only one input file at once*/ |
| 326 | fprintf(stderr,"Error: more than one input file specified.\n"); |
| 327 | return EXIT_FAILURE; |
| 328 | } |
| 329 | infile_name=argv[a]; |
| 330 | } |
| 331 | if(minusflag&&plusflag) {/*type1+ and type2- at the same time could cause |
| 332 | types that are not defined what to do with, to act based on the |
| 333 | last filter definition. Thus it is not allowed.*/ |
| 334 | fprintf(stderr,"Error: include and exclude at the same time.\n"); |
| 335 | return EXIT_FAILURE; |
| 336 | } |
| 337 | if(infile_name==NULL)infile=stdin;/*if no infile specified use stdin*/ |
| 338 | else { |
| 339 | infile=fopen(infile_name,"r"); |
| 340 | if(infile==NULL) { |
| 341 | fprintf(stderr,"Error opening %s : %s\n",infile_name, |
| 342 | strerror(errno)); |
| 343 | return EXIT_FAILURE; |
| 344 | } |
| 345 | } |
| 346 | if(oflag) { |
| 347 | outfile=fopen(outfile_name,"w"); |
| 348 | if(outfile==NULL) { |
| 349 | fprintf(stderr,"Error creating %s : %s\n",outfile_name, |
| 350 | strerror(errno)); |
| 351 | return EXIT_FAILURE; |
| 352 | } |
| 353 | } else outfile=stdout;/*if no outfile specified use stdout*/ |
| 354 | a=ProcessFile(outfile,infile);/*filter infile to outfile*/ |
| 355 | if(a==ERR_INVALID_LOG) { |
| 356 | if(infile_name!=NULL)fprintf(stderr,"Error: the file %s is not a valid " |
| 357 | "log file.\n",infile_name); |
| 358 | else fprintf(stderr,"Error: invalid format received from standard " |
| 359 | "input.\n"); |
| 360 | return EXIT_FAILURE; |
| 361 | } else if(a==ERR_WRITE) { |
| 362 | if(errno)fprintf(stderr,"Error writing to output: %s\n", |
| 363 | strerror(errno)); |
| 364 | else fprintf(stderr,"Error writing to output\n"); |
| 365 | return EXIT_FAILURE; |
| 366 | } |
| 367 | return EXIT_SUCCESS; |
| 368 | } |
| 369 | |