optimizations and fixes in log watcher repeat count processing
[public/netxms.git] / src / libnxlp / parser.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Log Parsing Library
4 ** Copyright (C) 2003-2016 Raden Solutions
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published by
8 ** the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: parser.cpp
21 **
22 **/
23
24 #include "libnxlp.h"
25 #include <expat.h>
26
27 /**
28 * Context state texts
29 */
30 static const TCHAR *s_states[] = { _T("MANUAL"), _T("AUTO"), _T("INACTIVE") };
31
32 /**
33 * XML parser state codes
34 */
35 #define XML_STATE_INIT -1
36 #define XML_STATE_END -2
37 #define XML_STATE_ERROR -255
38 #define XML_STATE_PARSER 0
39 #define XML_STATE_RULES 1
40 #define XML_STATE_RULE 2
41 #define XML_STATE_MATCH 3
42 #define XML_STATE_EVENT 4
43 #define XML_STATE_FILE 5
44 #define XML_STATE_ID 6
45 #define XML_STATE_LEVEL 7
46 #define XML_STATE_SOURCE 8
47 #define XML_STATE_CONTEXT 9
48 #define XML_STATE_MACROS 10
49 #define XML_STATE_MACRO 11
50 #define XML_STATE_DESCRIPTION 12
51
52 /**
53 * XML parser state for creating LogParser object from XML
54 */
55 struct XML_PARSER_STATE
56 {
57 LogParser *parser;
58 int state;
59 String regexp;
60 String event;
61 String file;
62 StringList files;
63 ObjectArray<int> encodings;
64 String id;
65 String level;
66 String source;
67 String context;
68 String description;
69 int contextAction;
70 String ruleContext;
71 int numEventParams;
72 String errorText;
73 String macroName;
74 String macro;
75 bool invertedRule;
76 bool breakFlag;
77 int repeatCount;
78 int repeatInterval;
79 bool resetRepeat;
80
81 XML_PARSER_STATE() : encodings(1, 1, true)
82 {
83 state = -1;
84 parser = NULL;
85 invertedRule = false;
86 breakFlag = false;
87 contextAction = CONTEXT_SET_AUTOMATIC;
88 numEventParams = 0;
89 repeatCount = 0;
90 repeatInterval = 0;
91 resetRepeat = true;
92 }
93 };
94
95 /**
96 * Parser default constructor
97 */
98 LogParser::LogParser()
99 {
100 m_numRules = 0;
101 m_rules = NULL;
102 m_cb = NULL;
103 m_userArg = NULL;
104 m_name = NULL;
105 m_fileName = NULL;
106 m_fileEncoding = LP_FCP_ACP;
107 m_eventNameList = NULL;
108 m_eventResolver = NULL;
109 m_thread = INVALID_THREAD_HANDLE;
110 m_recordsProcessed = 0;
111 m_recordsMatched = 0;
112 m_processAllRules = false;
113 m_traceLevel = 0;
114 m_traceCallback = NULL;
115 _tcscpy(m_status, LPS_INIT);
116 #ifdef _WIN32
117 m_marker = NULL;
118 #endif
119 }
120
121 /**
122 * Parser copy constructor
123 */
124 LogParser::LogParser(LogParser *src)
125 {
126 m_numRules = src->m_numRules;
127 m_rules = (LogParserRule **)malloc(sizeof(LogParserRule *) * m_numRules);
128 for(int i = 0; i < m_numRules; i++)
129 m_rules[i] = new LogParserRule(src->m_rules[i], this);
130
131 m_cb = src->m_cb;
132 m_userArg = src->m_userArg;
133 m_name = (src->m_name != NULL) ? _tcsdup(src->m_name) : NULL;
134 m_fileName = (src->m_fileName != NULL) ? _tcsdup(src->m_fileName) : NULL;
135 m_fileEncoding = src->m_fileEncoding;
136
137 if (src->m_eventNameList != NULL)
138 {
139 int count;
140 for(count = 0; src->m_eventNameList[count].text != NULL; count++);
141 m_eventNameList = (count > 0) ? (CODE_TO_TEXT *)nx_memdup(src->m_eventNameList, sizeof(CODE_TO_TEXT) * (count + 1)) : NULL;
142 }
143 else
144 {
145 m_eventNameList = NULL;
146 }
147
148 m_eventResolver = src->m_eventResolver;
149 m_thread = INVALID_THREAD_HANDLE;
150 m_recordsProcessed = 0;
151 m_recordsMatched = 0;
152 m_processAllRules = src->m_processAllRules;
153 m_traceLevel = src->m_traceLevel;
154 m_traceCallback = src->m_traceCallback;
155 _tcscpy(m_status, LPS_INIT);
156 #ifdef _WIN32
157 m_marker = _tcsdup_ex(src->m_marker);
158 #endif
159 }
160
161 /**
162 * Destructor
163 */
164 LogParser::~LogParser()
165 {
166 int i;
167
168 for(i = 0; i < m_numRules; i++)
169 delete m_rules[i];
170 safe_free(m_rules);
171 safe_free(m_name);
172 safe_free(m_fileName);
173 #ifdef _WIN32
174 safe_free(m_marker);
175 #endif
176 }
177
178 /**
179 * Trace
180 */
181 void LogParser::trace(int level, const TCHAR *format, ...)
182 {
183 va_list args;
184
185 if ((m_traceCallback == NULL) || (level > m_traceLevel))
186 return;
187
188 va_start(args, format);
189 m_traceCallback(format, args);
190 va_end(args);
191 }
192
193 /**
194 * Add rule
195 */
196 bool LogParser::addRule(LogParserRule *rule)
197 {
198 bool isOK;
199
200 isOK = rule->isValid();
201 if (isOK)
202 {
203 m_rules = (LogParserRule **)realloc(m_rules, sizeof(LogParserRule *) * (m_numRules + 1));
204 m_rules[m_numRules++] = rule;
205 }
206 else
207 {
208 delete rule;
209 }
210 return isOK;
211 }
212
213 /**
214 * Create and add rule
215 */
216 bool LogParser::addRule(const TCHAR *regexp, UINT32 eventCode, const TCHAR *eventName, int numParams, int repeatInterval, int repeatCount, bool resetRepeat)
217 {
218 return addRule(new LogParserRule(this, regexp, eventCode, eventName, numParams, repeatInterval, repeatCount, resetRepeat));
219 }
220
221 /**
222 * Check context
223 */
224 const TCHAR *LogParser::checkContext(LogParserRule *rule)
225 {
226 const TCHAR *state;
227
228 if (rule->getContext() == NULL)
229 {
230 trace(5, _T(" rule has no context"));
231 return s_states[CONTEXT_SET_MANUAL];
232 }
233
234 state = m_contexts.get(rule->getContext());
235 if (state == NULL)
236 {
237 trace(5, _T(" context '%s' inactive, rule should be skipped"), rule->getContext());
238 return NULL; // Context inactive, don't use this rule
239 }
240
241 if (!_tcscmp(state, s_states[CONTEXT_CLEAR]))
242 {
243 trace(5, _T(" context '%s' inactive, rule should be skipped"), rule->getContext());
244 return NULL;
245 }
246 else
247 {
248 trace(5, _T(" context '%s' active (mode=%s)"), rule->getContext(), state);
249 return state;
250 }
251 }
252
253 /**
254 * Match log record
255 */
256 bool LogParser::matchLogRecord(bool hasAttributes, const TCHAR *source, UINT32 eventId,
257 UINT32 level, const TCHAR *line, UINT32 objectId)
258 {
259 int i;
260 const TCHAR *state;
261 bool matched = false;
262
263 if (hasAttributes)
264 trace(5, _T("Match event: source=\"%s\" id=%u level=%d text=\"%s\""), source, eventId, level, line);
265 else
266 trace(5, _T("Match line: \"%s\""), line);
267
268 m_recordsProcessed++;
269 for(i = 0; i < m_numRules; i++)
270 {
271 trace(6, _T("checking rule %d \"%s\""), i + 1, m_rules[i]->getDescription());
272 if ((state = checkContext(m_rules[i])) != NULL)
273 {
274 bool ruleMatched = hasAttributes ?
275 m_rules[i]->matchEx(source, eventId, level, line, m_cb, objectId, m_userArg) :
276 m_rules[i]->match(line, m_cb, objectId, m_userArg);
277 if (ruleMatched)
278 {
279 trace(5, _T("rule %d \"%s\" matched"), i + 1, m_rules[i]->getDescription());
280 if (!matched)
281 m_recordsMatched++;
282
283 // Update context
284 if (m_rules[i]->getContextToChange() != NULL)
285 {
286 m_contexts.set(m_rules[i]->getContextToChange(), s_states[m_rules[i]->getContextAction()]);
287 trace(5, _T("rule %d \"%s\": context %s set to %s"), i + 1, m_rules[i]->getDescription(), m_rules[i]->getContextToChange(), s_states[m_rules[i]->getContextAction()]);
288 }
289
290 // Set context of this rule to inactive if rule context mode is "automatic reset"
291 if (!_tcscmp(state, s_states[CONTEXT_SET_AUTOMATIC]))
292 {
293 m_contexts.set(m_rules[i]->getContext(), s_states[CONTEXT_CLEAR]);
294 trace(5, _T("rule %d \"%s\": context %s cleared because it was set to automatic reset mode"),
295 i + 1, m_rules[i]->getDescription(), m_rules[i]->getContext());
296 }
297 matched = true;
298 if (!m_processAllRules || m_rules[i]->getBreakFlag())
299 break;
300 }
301 }
302 }
303 if (i < m_numRules)
304 trace(5, _T("processing stopped at rule %d \"%s\"; result = %s"), i + 1,
305 m_rules[i]->getDescription(), matched ? _T("true") : _T("false"));
306 else
307 trace(5, _T("Processing stopped at end of rules list; result = %s"), matched ? _T("true") : _T("false"));
308 return matched;
309 }
310
311 /**
312 * Match simple log line
313 */
314 bool LogParser::matchLine(const TCHAR *line, UINT32 objectId)
315 {
316 return matchLogRecord(false, NULL, 0, 0, line, objectId);
317 }
318
319 /**
320 * Match log event (text with additional attributes - source, severity, event id)
321 */
322 bool LogParser::matchEvent(const TCHAR *source, UINT32 eventId, UINT32 level, const TCHAR *line, UINT32 objectId)
323 {
324 return matchLogRecord(true, source, eventId, level, line, objectId);
325 }
326
327 /**
328 * Set associated file name
329 */
330 void LogParser::setFileName(const TCHAR *name)
331 {
332 safe_free(m_fileName);
333 m_fileName = (name != NULL) ? _tcsdup(name) : NULL;
334 if (m_name == NULL)
335 m_name = _tcsdup(name); // Set parser name to file name
336 }
337
338 /**
339 * Set parser name
340 */
341 void LogParser::setName(const TCHAR *name)
342 {
343 safe_free(m_name);
344 m_name = _tcsdup((name != NULL) ? name : CHECK_NULL(m_fileName));
345 }
346
347 /**
348 * Add macro
349 */
350 void LogParser::addMacro(const TCHAR *name, const TCHAR *value)
351 {
352 m_macros.set(name, value);
353 }
354
355 /**
356 * Get macro
357 */
358 const TCHAR *LogParser::getMacro(const TCHAR *name)
359 {
360 const TCHAR *value;
361
362 value = m_macros.get(name);
363 return CHECK_NULL_EX(value);
364 }
365
366 /**
367 * Create parser configuration from XML - callback for element start
368 */
369 static void StartElement(void *userData, const char *name, const char **attrs)
370 {
371 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
372
373 if (!strcmp(name, "parser"))
374 {
375 ps->state = XML_STATE_PARSER;
376 ps->parser->setProcessAllFlag(XMLGetAttrBoolean(attrs, "processAll", false));
377 ps->parser->setTraceLevel(XMLGetAttrInt(attrs, "trace", 0));
378 const char *name = XMLGetAttr(attrs, "name");
379 if (name != NULL)
380 {
381 #ifdef UNICODE
382 WCHAR *wname = WideStringFromUTF8String(name);
383 ps->parser->setName(wname);
384 free(wname);
385 #else
386 ps->parser->setName(name);
387 #endif
388 }
389 }
390 else if (!strcmp(name, "file"))
391 {
392 ps->state = XML_STATE_FILE;
393 const char *encoding = XMLGetAttr(attrs, "encoding");
394 if (encoding != NULL)
395 {
396 if (!stricmp(encoding, "acp") || (*encoding == 0))
397 {
398 ps->encodings.add(new int(LP_FCP_ACP));
399 }
400 else if (!stricmp(encoding, "utf8") || !stricmp(encoding, "utf-8"))
401 {
402 ps->encodings.add(new int(LP_FCP_UTF8));
403 }
404 else if (!stricmp(encoding, "ucs2") || !stricmp(encoding, "ucs-2") || !stricmp(encoding, "utf-16"))
405 {
406 ps->encodings.add(new int(LP_FCP_UCS2));
407 }
408 else if (!stricmp(encoding, "ucs4") || !stricmp(encoding, "ucs-4") || !stricmp(encoding, "utf-32"))
409 {
410 ps->encodings.add(new int(LP_FCP_UCS4));
411 }
412 else
413 {
414 ps->errorText = _T("Invalid file encoding");
415 ps->state = XML_STATE_ERROR;
416 }
417 }
418 else
419 {
420 ps->encodings.add(new int(LP_FCP_ACP));
421 }
422 }
423 else if (!strcmp(name, "macros"))
424 {
425 ps->state = XML_STATE_MACROS;
426 }
427 else if (!strcmp(name, "macro"))
428 {
429 const char *name;
430
431 ps->state = XML_STATE_MACRO;
432 name = XMLGetAttr(attrs, "name");
433 #ifdef UNICODE
434 ps->macroName = L"";
435 ps->macroName.appendMBString(name, strlen(name), CP_UTF8);
436 #else
437 ps->macroName = CHECK_NULL_A(name);
438 #endif
439 ps->macro = NULL;
440 }
441 else if (!strcmp(name, "rules"))
442 {
443 ps->state = XML_STATE_RULES;
444 }
445 else if (!strcmp(name, "rule"))
446 {
447 ps->regexp = NULL;
448 ps->invertedRule = false;
449 ps->event = NULL;
450 ps->context = NULL;
451 ps->contextAction = CONTEXT_SET_AUTOMATIC;
452 ps->description = NULL;
453 ps->id = NULL;
454 ps->source = NULL;
455 ps->level = NULL;
456 #ifdef UNICODE
457 ps->ruleContext = L"";
458 const char *context = XMLGetAttr(attrs, "context");
459 if (context != NULL)
460 ps->ruleContext.appendMBString(context, strlen(context), CP_UTF8);
461 #else
462 ps->ruleContext = XMLGetAttr(attrs, "context");
463 #endif
464 ps->breakFlag = XMLGetAttrBoolean(attrs, "break", false);
465 ps->state = XML_STATE_RULE;
466 ps->numEventParams = 0;
467 }
468 else if (!strcmp(name, "match"))
469 {
470 ps->state = XML_STATE_MATCH;
471 ps->invertedRule = XMLGetAttrBoolean(attrs, "invert", false);
472 ps->resetRepeat = XMLGetAttrBoolean(attrs, "reset", true);
473 ps->repeatCount = XMLGetAttrInt(attrs, "repeatCount", 0);
474 ps->repeatInterval = XMLGetAttrInt(attrs, "repeatInterval", 0);
475 }
476 else if (!strcmp(name, "id") || !strcmp(name, "facility"))
477 {
478 ps->state = XML_STATE_ID;
479 }
480 else if (!strcmp(name, "level") || !strcmp(name, "severity"))
481 {
482 ps->state = XML_STATE_LEVEL;
483 }
484 else if (!strcmp(name, "source") || !strcmp(name, "tag"))
485 {
486 ps->state = XML_STATE_SOURCE;
487 }
488 else if (!strcmp(name, "event"))
489 {
490 ps->numEventParams = XMLGetAttrUINT32(attrs, "params", 0);
491 ps->state = XML_STATE_EVENT;
492 }
493 else if (!strcmp(name, "context"))
494 {
495 const char *action;
496
497 ps->state = XML_STATE_CONTEXT;
498
499 action = XMLGetAttr(attrs, "action");
500 if (action == NULL)
501 action = "set";
502
503 if (!strcmp(action, "set"))
504 {
505 const char *mode;
506
507 mode = XMLGetAttr(attrs, "reset");
508 if (mode == NULL)
509 mode = "auto";
510
511 if (!strcmp(mode, "auto"))
512 {
513 ps->contextAction = CONTEXT_SET_AUTOMATIC;
514 }
515 else if (!strcmp(mode, "manual"))
516 {
517 ps->contextAction = CONTEXT_SET_MANUAL;
518 }
519 else
520 {
521 ps->errorText = _T("Invalid context reset mode");
522 ps->state = XML_STATE_ERROR;
523 }
524 }
525 else if (!strcmp(action, "clear"))
526 {
527 ps->contextAction = CONTEXT_CLEAR;
528 }
529 else
530 {
531 ps->errorText = _T("Invalid context action");
532 ps->state = XML_STATE_ERROR;
533 }
534 }
535 else if (!strcmp(name, "description"))
536 {
537 ps->state = XML_STATE_DESCRIPTION;
538 }
539 else
540 {
541 ps->state = XML_STATE_ERROR;
542 }
543 }
544
545 /**
546 * Create parser configuration from XML - callback for element end
547 */
548 static void EndElement(void *userData, const char *name)
549 {
550 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
551
552 if (!strcmp(name, "parser"))
553 {
554 ps->state = XML_STATE_END;
555 }
556 else if (!strcmp(name, "file"))
557 {
558 ps->files.add(ps->file);
559 ps->file = _T("");
560 ps->state = XML_STATE_PARSER;
561 }
562 else if (!strcmp(name, "macros"))
563 {
564 ps->state = XML_STATE_PARSER;
565 }
566 else if (!strcmp(name, "macro"))
567 {
568 ps->parser->addMacro(ps->macroName, ps->macro);
569 ps->state = XML_STATE_MACROS;
570 }
571 else if (!strcmp(name, "rules"))
572 {
573 ps->state = XML_STATE_PARSER;
574 }
575 else if (!strcmp(name, "rule"))
576 {
577 UINT32 eventCode;
578 const TCHAR *eventName = NULL;
579 TCHAR *eptr;
580 LogParserRule *rule;
581
582 ps->event.trim();
583 eventCode = _tcstoul(ps->event, &eptr, 0);
584 if (*eptr != 0)
585 {
586 eventCode = ps->parser->resolveEventName(ps->event, 0);
587 if (eventCode == 0)
588 {
589 eventName = (const TCHAR *)ps->event;
590 }
591 }
592
593 if (ps->regexp.isEmpty())
594 ps->regexp = _T(".*");
595 rule = new LogParserRule(ps->parser, (const TCHAR *)ps->regexp, eventCode, eventName, ps->numEventParams, ps->repeatInterval, ps->repeatCount, ps->resetRepeat);
596 if (!ps->ruleContext.isEmpty())
597 rule->setContext(ps->ruleContext);
598 if (!ps->context.isEmpty())
599 {
600 rule->setContextToChange(ps->context);
601 rule->setContextAction(ps->contextAction);
602 }
603
604 if (!ps->description.isEmpty())
605 rule->setDescription(ps->description);
606
607 if (!ps->source.isEmpty())
608 rule->setSource(ps->source);
609
610 if (!ps->level.isEmpty())
611 rule->setLevel(_tcstoul(ps->level, NULL, 0));
612
613 if (!ps->id.isEmpty())
614 {
615 UINT32 start, end;
616 TCHAR *eptr;
617
618 start = _tcstoul(ps->id, &eptr, 0);
619 if (*eptr == 0)
620 {
621 end = start;
622 }
623 else /* TODO: add better error handling */
624 {
625 while(!_istdigit(*eptr))
626 eptr++;
627 end = _tcstoul(eptr, NULL, 0);
628 }
629 rule->setIdRange(start, end);
630 }
631
632 rule->setInverted(ps->invertedRule);
633 rule->setBreakFlag(ps->breakFlag);
634
635 ps->parser->addRule(rule);
636 ps->state = XML_STATE_RULES;
637 }
638 else if (!strcmp(name, "match"))
639 {
640 ps->state = XML_STATE_RULE;
641 }
642 else if (!strcmp(name, "id") || !strcmp(name, "facility"))
643 {
644 ps->state = XML_STATE_RULE;
645 }
646 else if (!strcmp(name, "level") || !strcmp(name, "severity"))
647 {
648 ps->state = XML_STATE_RULE;
649 }
650 else if (!strcmp(name, "source") || !strcmp(name, "tag"))
651 {
652 ps->state = XML_STATE_RULE;
653 }
654 else if (!strcmp(name, "event"))
655 {
656 ps->state = XML_STATE_RULE;
657 }
658 else if (!strcmp(name, "context"))
659 {
660 ps->state = XML_STATE_RULE;
661 }
662 else if (!strcmp(name, "description"))
663 {
664 ps->state = XML_STATE_RULE;
665 }
666 }
667
668 /**
669 * Create parser configuration from XML - callback for element data
670 */
671 static void CharData(void *userData, const XML_Char *s, int len)
672 {
673 XML_PARSER_STATE *ps = (XML_PARSER_STATE *)userData;
674
675 switch(ps->state)
676 {
677 case XML_STATE_MATCH:
678 ps->regexp.appendMBString(s, len, CP_UTF8);
679 break;
680 case XML_STATE_ID:
681 ps->id.appendMBString(s, len, CP_UTF8);
682 break;
683 case XML_STATE_LEVEL:
684 ps->level.appendMBString(s, len, CP_UTF8);
685 break;
686 case XML_STATE_SOURCE:
687 ps->source.appendMBString(s, len, CP_UTF8);
688 break;
689 case XML_STATE_EVENT:
690 ps->event.appendMBString(s, len, CP_UTF8);
691 break;
692 case XML_STATE_FILE:
693 ps->file.appendMBString(s, len, CP_UTF8);
694 break;
695 case XML_STATE_CONTEXT:
696 ps->context.appendMBString(s, len, CP_UTF8);
697 break;
698 case XML_STATE_DESCRIPTION:
699 ps->description.appendMBString(s, len, CP_UTF8);
700 break;
701 case XML_STATE_MACRO:
702 ps->macro.appendMBString(s, len, CP_UTF8);
703 break;
704 default:
705 break;
706 }
707 }
708
709 /**
710 * Create parser configuration from XML. Returns array of identical parsers,
711 * one for each <file> tag in source XML. Resulting parsers only differs with file names.
712 */
713 ObjectArray<LogParser> *LogParser::createFromXml(const char *xml, int xmlLen, TCHAR *errorText, int errBufSize, bool (*eventResolver)(const TCHAR *, UINT32 *))
714 {
715 ObjectArray<LogParser> *parsers = NULL;
716
717 XML_Parser parser = XML_ParserCreate(NULL);
718 XML_PARSER_STATE state;
719 state.parser = new LogParser;
720 state.parser->setEventNameResolver(eventResolver);
721 XML_SetUserData(parser, &state);
722 XML_SetElementHandler(parser, StartElement, EndElement);
723 XML_SetCharacterDataHandler(parser, CharData);
724 bool success = (XML_Parse(parser, xml, (xmlLen == -1) ? (int)strlen(xml) : xmlLen, TRUE) != XML_STATUS_ERROR);
725 if (!success && (errorText != NULL))
726 {
727 _sntprintf(errorText, errBufSize, _T("%hs at line %d"),
728 XML_ErrorString(XML_GetErrorCode(parser)),
729 (int)XML_GetCurrentLineNumber(parser));
730 }
731 XML_ParserFree(parser);
732 if (success && (state.state == XML_STATE_ERROR))
733 {
734 if (errorText != NULL)
735 nx_strncpy(errorText, state.errorText, errBufSize);
736 }
737 else if (success)
738 {
739 parsers = new ObjectArray<LogParser>;
740 if (state.files.size() > 0)
741 {
742 for(int i = 0; i < state.files.size(); i++)
743 {
744 LogParser *p = (i > 0) ? new LogParser(state.parser) : state.parser;
745 p->setFileName(state.files.get(i));
746 p->setFileEncoding(*state.encodings.get(i));
747 parsers->add(p);
748 }
749 }
750 else
751 {
752 // It is possible to have parser without <file> tag, syslog parser for example
753 parsers->add(state.parser);
754 }
755 }
756
757 return parsers;
758 }
759
760 /**
761 * Resolve event name
762 */
763 UINT32 LogParser::resolveEventName(const TCHAR *name, UINT32 defVal)
764 {
765 if (m_eventNameList != NULL)
766 {
767 int i;
768
769 for(i = 0; m_eventNameList[i].text != NULL; i++)
770 if (!_tcsicmp(name, m_eventNameList[i].text))
771 return m_eventNameList[i].code;
772 }
773
774 if (m_eventResolver != NULL)
775 {
776 UINT32 val;
777
778 if (m_eventResolver(name, &val))
779 return val;
780 }
781
782 return defVal;
783 }