|
1 /* |
|
2 * Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 #include "stdafx.h" |
|
22 #include <time.h> |
|
23 #include <stdlib.h> |
|
24 #include <ctype.h> |
|
25 #include <string.h> |
|
26 #include "cReporter.h" |
|
27 |
|
28 |
|
29 /***************************************************************************** |
|
30 * Reporter preparation |
|
31 * |
|
32 * Sets member strings to a known state |
|
33 *****************************************************************************/ |
|
34 |
|
35 void |
|
36 Reporter::prepare ( void ) |
|
37 { |
|
38 int i; |
|
39 |
|
40 #ifdef RPT_DEBUG_PROG |
|
41 // set filename contents to garbage value |
|
42 for (i=0;i<CPO_MAX_FILENAME_LEN;i++) |
|
43 { |
|
44 filename [ i ] = CPO_GARBAGE_CHAR; |
|
45 } |
|
46 |
|
47 // set program name contents to garbage value |
|
48 for (i=0;i<RPT_MAX_PROGRAM_LEN;i++) |
|
49 { |
|
50 progname [ i ] = CPO_GARBAGE_CHAR; |
|
51 } |
|
52 #endif |
|
53 |
|
54 for (i=0;i<RPT_MAX_LIST_ENTRIES;i++) |
|
55 { |
|
56 messageList [ i ] = (char *)0; |
|
57 } |
|
58 |
|
59 fileRead = 0; |
|
60 pCurrent = (va_list) 0; |
|
61 prefix = 0; |
|
62 state = 0; |
|
63 hFile = (HANDLE) 0; |
|
64 filename [ 0 ] = '\0'; |
|
65 progname [ 0 ] = '\0'; |
|
66 } |
|
67 |
|
68 /***************************************************************************** |
|
69 * Reporter setup |
|
70 * |
|
71 * Checks the output streams. |
|
72 * Sets the prefix mask. |
|
73 * Stores a copy of the application name. |
|
74 * Puts a message into the logging file that the Reporter is initialised. |
|
75 *****************************************************************************/ |
|
76 |
|
77 CPO_BOOL |
|
78 Reporter::setup ( const char * appName, |
|
79 const BITFIELD prefixMask, |
|
80 const unsigned long ulOpenAttributes ) |
|
81 { |
|
82 // check for valid output destinations |
|
83 if ( !( state & RPT_FILE ) && |
|
84 !( state & RPT_STDOUT ) && |
|
85 !( state & RPT_STDERR ) ) |
|
86 { |
|
87 return ( CPO_FALSE ); |
|
88 } |
|
89 |
|
90 // check for valid mask |
|
91 if ( !( prefixMask & RPT_MSG_TYPE ) && |
|
92 !( prefixMask & RPT_APPNAME ) && |
|
93 !( prefixMask & RPT_DATE ) && |
|
94 !( prefixMask & RPT_TIME ) ) |
|
95 { |
|
96 prefix = RPT_MSG_ONLY; |
|
97 } |
|
98 else |
|
99 { |
|
100 prefix = prefixMask; |
|
101 } |
|
102 |
|
103 // get the actual program name and make sure it is valid |
|
104 extractName ( progname, appName, RPT_MAX_PROGRAM_LEN ); |
|
105 validateText ( progname, RPT_MAX_PROGRAM_LEN ); |
|
106 if ( !( *progname ) ) |
|
107 { |
|
108 return ( CPO_FALSE ); |
|
109 } |
|
110 |
|
111 if ( state & RPT_FILE ) |
|
112 { |
|
113 // open output file |
|
114 if ( !openFile ( ulOpenAttributes ) ) |
|
115 { |
|
116 state -= RPT_FILE; |
|
117 if ( !( state & RPT_OUTPUT_MASK ) ) |
|
118 { |
|
119 return ( CPO_FALSE ); |
|
120 } |
|
121 } |
|
122 } |
|
123 |
|
124 // set flags to show everything ok |
|
125 state |= CPO_CONNECTED; |
|
126 state |= CPO_ACTIVATED; |
|
127 |
|
128 return ( CPO_TRUE ); |
|
129 } |
|
130 |
|
131 /***************************************************************************** |
|
132 * Resolve a code to a message and build it. |
|
133 *****************************************************************************/ |
|
134 |
|
135 void |
|
136 Reporter::actOnCode ( int code, char messType ) |
|
137 { |
|
138 // must be initialised and activated |
|
139 if ( ( state & CPO_CONNECTED ) && |
|
140 ( state & CPO_ACTIVATED ) && |
|
141 code >= 0 && |
|
142 code < RPT_MAX_LIST_ENTRIES ) |
|
143 { |
|
144 if ( messageList [ code ] ) |
|
145 { |
|
146 buildMessage ( messageList [ code ], messType ); |
|
147 } |
|
148 else |
|
149 { |
|
150 char tmpMsg [ 128 ]; |
|
151 (void) sprintf ( tmpMsg, "[ No message text for code %d ]", code ); |
|
152 buildMessage ( tmpMsg, messType ); |
|
153 } |
|
154 } |
|
155 } |
|
156 |
|
157 /***************************************************************************** |
|
158 * Generate a filename using the application name and current date. |
|
159 *****************************************************************************/ |
|
160 |
|
161 void |
|
162 Reporter::generateFilename ( char * validName, const char * appName ) |
|
163 { |
|
164 /* Construct a filename for the logging to be written to. |
|
165 * Filename is of the format x[xxx]9988.rpt where : |
|
166 * |
|
167 * xxxx = up to first 4 chars of calling program's name |
|
168 * 99 = 2 digit day of month |
|
169 * 88 = 2 digit month of year |
|
170 */ |
|
171 |
|
172 const int PROG_LEN = 4; |
|
173 time_t curtime; |
|
174 struct tm *tminfo; |
|
175 |
|
176 // get filename from path |
|
177 extractName ( buffer, appName, PROG_LEN ); |
|
178 |
|
179 (void) time ( &curtime ); |
|
180 tminfo = localtime ( &curtime ); |
|
181 |
|
182 (void) sprintf ( validName, |
|
183 "%s%02d%02d.RPT", |
|
184 buffer, |
|
185 tminfo->tm_mday, |
|
186 tminfo->tm_mon + 1); |
|
187 |
|
188 #ifdef RPT_DEBUG_PROG |
|
189 (void) fprintf ( stdout, "filename [%s] generated from [%s]\n", |
|
190 validName, appName ); |
|
191 (void) fflush ( stdout ); |
|
192 #endif |
|
193 } |
|
194 |
|
195 /***************************************************************************** |
|
196 * Extract 'length' characters of the actual filename from the path. |
|
197 *****************************************************************************/ |
|
198 |
|
199 void |
|
200 Reporter::extractName ( char * file, const char * path, const int length ) |
|
201 { |
|
202 char *ptr; |
|
203 |
|
204 // find the last backslash |
|
205 ptr = strrchr ( path, '\\' ); |
|
206 |
|
207 if ( !ptr ) |
|
208 { |
|
209 strncpy ( file, path, length ); |
|
210 } |
|
211 else |
|
212 { |
|
213 strncpy ( file, (++ptr), length ); |
|
214 } |
|
215 |
|
216 *( file + length ) = '\0'; |
|
217 } |
|
218 |
|
219 /**************************************************************************** |
|
220 * Open the file or print a message if we can't |
|
221 *****************************************************************************/ |
|
222 |
|
223 int |
|
224 Reporter::openFile ( unsigned long ulOpenAttributes ) |
|
225 { |
|
226 #ifdef UNICODE |
|
227 TCHAR uFilename[CPO_MAX_FILENAME_LEN + 1]; |
|
228 |
|
229 // Convert to UNICODE. |
|
230 if (!MultiByteToWideChar(CP_ACP, // conversion type |
|
231 0, // flags |
|
232 filename, // source |
|
233 -1, // length |
|
234 uFilename, // dest |
|
235 CPO_MAX_FILENAME_LEN)) // length |
|
236 { |
|
237 return ( CPO_FALSE ); |
|
238 } |
|
239 |
|
240 hFile = CreateFile(uFilename, |
|
241 GENERIC_WRITE, |
|
242 FILE_SHARE_READ | FILE_SHARE_WRITE, |
|
243 NULL, |
|
244 ulOpenAttributes, |
|
245 FILE_ATTRIBUTE_NORMAL, |
|
246 NULL); |
|
247 #else |
|
248 hFile = CreateFile(filename, |
|
249 GENERIC_WRITE, |
|
250 FILE_SHARE_READ | FILE_SHARE_WRITE, |
|
251 NULL, |
|
252 ulOpenAttributes, |
|
253 FILE_ATTRIBUTE_NORMAL, |
|
254 NULL); |
|
255 #endif |
|
256 |
|
257 if (hFile == INVALID_HANDLE_VALUE) |
|
258 { |
|
259 // char szTemp[500] = {0}; |
|
260 // sprintf(szTemp, "WARNING : [%s] could not be opened. File output disabled", filename); |
|
261 // MessageBox(NULL, szTemp, NULL, MB_OK); |
|
262 return ( CPO_FALSE ); |
|
263 } |
|
264 |
|
265 // move to the end if a file already exists |
|
266 SetFilePointer(hFile, 0, 0, FILE_END); |
|
267 |
|
268 #ifdef RPT_DEBUG_PROG |
|
269 (void) fprintf ( stdout, "file [%s] opened\n", filename ); |
|
270 (void) fflush ( stdout ); |
|
271 #endif |
|
272 |
|
273 return ( CPO_TRUE ); |
|
274 } |
|
275 |
|
276 /***************************************************************************** |
|
277 * Construct the parts of the complete message. |
|
278 *****************************************************************************/ |
|
279 |
|
280 void |
|
281 Reporter::buildMessage ( const char * message, const char messType ) |
|
282 { |
|
283 char prefixInfo [ RPT_MAX_MSG_LEN + 1 ]; |
|
284 char finalStr [ RPT_OPEN_MSG_LEN + RPT_MAX_MSG_LEN + 1 ]; |
|
285 int maxlen; |
|
286 |
|
287 // get prefix information |
|
288 switch ( messType ) |
|
289 { |
|
290 case RPT_TEXT: |
|
291 case RPT_MSG: |
|
292 *( prefixInfo ) = '\0'; |
|
293 break; |
|
294 case RPT_HEADER: |
|
295 addHeaderInfo ( prefixInfo ); |
|
296 break; |
|
297 default: |
|
298 addLogInfo ( prefixInfo, messType ); |
|
299 break; |
|
300 } |
|
301 |
|
302 // parse arguments passed with message |
|
303 (void) vsprintf ( buffer, message, pCurrent ); |
|
304 |
|
305 // get max length of output string |
|
306 if ( messType == RPT_DEBUG || messType == RPT_MSG || |
|
307 messType == RPT_ERROR || messType == RPT_TEXT ) |
|
308 { |
|
309 maxlen = RPT_OPEN_MSG_LEN - (int) strlen ( prefixInfo ) - 1; |
|
310 } |
|
311 else |
|
312 { |
|
313 maxlen = RPT_MAX_MSG_LEN - (int) strlen ( prefixInfo ) - 1; |
|
314 } |
|
315 |
|
316 // make sure we are dealing with a valid message of correct length |
|
317 validateText ( buffer, maxlen ); |
|
318 |
|
319 // generate the output string |
|
320 (void) sprintf ( finalStr, |
|
321 "%s%s", |
|
322 prefixInfo, |
|
323 buffer ); |
|
324 |
|
325 if ( messType == RPT_TEXT ) |
|
326 { |
|
327 (void) strcpy ( buffer, finalStr ); |
|
328 } |
|
329 else |
|
330 { |
|
331 writeToStream ( finalStr ); |
|
332 } |
|
333 } |
|
334 |
|
335 /***************************************************************************** |
|
336 * Validate the supplied string for zero length and unprintable chars up to |
|
337 * length bytes. |
|
338 * |
|
339 * If unprintable chars are found embedded in the text, the text will be |
|
340 * truncated at that point. |
|
341 * |
|
342 * If the text is longer than the specified length, it will be truncated at |
|
343 * that length. |
|
344 * |
|
345 * If the text is empty or the 1st char is unprintable, the function will |
|
346 * insert replacement 'invalid' text. |
|
347 *****************************************************************************/ |
|
348 |
|
349 void |
|
350 Reporter::validateText ( char * text, const int length ) |
|
351 { |
|
352 int len = (int) strlen ( text ); |
|
353 if ( len > length ) |
|
354 { |
|
355 len = length; |
|
356 } |
|
357 |
|
358 // check for empty message or unprintable 1st char |
|
359 if ( len && isprint ( text [ 0 ] ) ) |
|
360 { |
|
361 // check message for unprintable characters |
|
362 for (int i=1;i<len;i++) |
|
363 { |
|
364 if (text [ i ] != '\t') |
|
365 { |
|
366 if ( ! ( isprint ( text [ i ] ) ) ) |
|
367 { |
|
368 break; |
|
369 } |
|
370 } |
|
371 } |
|
372 |
|
373 text [ i ] = '\0'; |
|
374 } |
|
375 else |
|
376 { |
|
377 (void) strcpy ( text, "####" ); |
|
378 } |
|
379 } |
|
380 |
|
381 /***************************************************************************** |
|
382 * Generate the prefix information to prepend to a 'write' message. |
|
383 * |
|
384 * NOTE: No checks are done to ensure prefix info is not too small for |
|
385 * allocated string. If more information is prepended in the future, |
|
386 * this will have to be implemented. |
|
387 *****************************************************************************/ |
|
388 |
|
389 void |
|
390 Reporter::addHeaderInfo ( char * prefixInfo ) |
|
391 { |
|
392 time_t curTime; |
|
393 |
|
394 // create the time string |
|
395 (void) time ( &curTime ); |
|
396 (void) strftime ( buffer, |
|
397 sizeof ( buffer ), |
|
398 "%a %b %d %Y %H:%M:%S", |
|
399 localtime ( &curTime ) ); |
|
400 |
|
401 // append the program name |
|
402 (void) sprintf ( prefixInfo, |
|
403 "%s [%s] ", |
|
404 buffer, |
|
405 progname ); |
|
406 } |
|
407 |
|
408 /***************************************************************************** |
|
409 * Generate the prefix information to prepend to the message. |
|
410 * If msg type is RPT_ERROR, info is generated regardless of prefix setting. |
|
411 * |
|
412 * NOTE: No checks are done to ensure prefix info is not too small for |
|
413 * allocated string. If more information is prepended in the future, |
|
414 * this will have to be implemented. |
|
415 *****************************************************************************/ |
|
416 |
|
417 void |
|
418 Reporter::addLogInfo ( char * prefixInfo, const char msgType ) |
|
419 { |
|
420 int bytesUsed = 0; |
|
421 time_t curTime; |
|
422 *( prefixInfo ) = '\0'; |
|
423 |
|
424 if ( prefix == RPT_MSG_ONLY) |
|
425 { |
|
426 return; |
|
427 } |
|
428 |
|
429 #ifdef RPT_DEBUG_PROG |
|
430 // fill prefix string to check for memory leaks |
|
431 for (int i=1;i<RPT_MAX_MSG_LEN;i++) |
|
432 { |
|
433 *( prefixInfo + i ) = CPO_GARBAGE_CHAR; |
|
434 } |
|
435 #endif |
|
436 |
|
437 if ( ( prefix & RPT_MSG_TYPE ) ) |
|
438 { |
|
439 *( prefixInfo ) = msgType; |
|
440 *( prefixInfo + 1 ) = ' '; |
|
441 *( prefixInfo + 2 ) = '\0'; |
|
442 |
|
443 bytesUsed += 2; |
|
444 } |
|
445 |
|
446 if ( ( prefix & RPT_APPNAME ) ) |
|
447 { |
|
448 (void) sprintf ( prefixInfo + bytesUsed, "%s ", progname ); |
|
449 bytesUsed += (int) strlen ( progname ) + 1; |
|
450 } |
|
451 |
|
452 if ( ( prefix & RPT_DATE ) ) |
|
453 { |
|
454 const int sublen = 5; |
|
455 |
|
456 // create the date string |
|
457 (void) time ( &curTime ); |
|
458 (void) strftime ( buffer, |
|
459 sizeof ( buffer ), |
|
460 "%d/%m", |
|
461 localtime ( &curTime ) ); |
|
462 |
|
463 (void) sprintf ( prefixInfo + bytesUsed, "%s ", buffer ); |
|
464 bytesUsed += ( sublen + 1 ); |
|
465 } |
|
466 |
|
467 if ( ( prefix & RPT_TIME ) ) |
|
468 { |
|
469 const int sublen = 8; |
|
470 |
|
471 // create the time string |
|
472 (void) time ( &curTime ); |
|
473 (void) strftime ( buffer, |
|
474 sizeof ( buffer ), |
|
475 "%H:%M:%S", |
|
476 localtime ( &curTime ) ); |
|
477 |
|
478 (void) sprintf ( prefixInfo + bytesUsed, "%s ", buffer ); |
|
479 bytesUsed += ( sublen + 1 ); |
|
480 } |
|
481 |
|
482 // null terminate to be sure |
|
483 *( prefixInfo + bytesUsed ) = '\0'; |
|
484 } |
|
485 |
|
486 /***************************************************************************** |
|
487 * Send text to appropriate stream(s). |
|
488 *****************************************************************************/ |
|
489 |
|
490 void |
|
491 Reporter::writeToStream ( const char * str ) |
|
492 { |
|
493 #ifdef RPT_DEBUG_PROG |
|
494 (void) fprintf ( stdout, "output [%s]\n", str ); |
|
495 (void) fflush ( stdout ); |
|
496 #endif |
|
497 |
|
498 // write output string to file |
|
499 if ( state & RPT_FILE ) |
|
500 { |
|
501 unsigned long lBytesWritten; |
|
502 |
|
503 // need to seek to end of file in case other handles have written in the meantime |
|
504 SetFilePointer(hFile, 0, NULL, FILE_END); |
|
505 |
|
506 WriteFile(hFile, str, strlen(str), &lBytesWritten, NULL); |
|
507 WriteFile(hFile, "\r\n", 2, &lBytesWritten, NULL); |
|
508 |
|
509 // in case other processes are using this file to log |
|
510 FlushFileBuffers(hFile); |
|
511 } |
|
512 |
|
513 // write output string to standard output |
|
514 if ( state & RPT_STDOUT ) |
|
515 { |
|
516 (void) fprintf ( stdout, "%s\n", str ); |
|
517 (void) fflush ( stdout ); |
|
518 } |
|
519 |
|
520 // write output string to standard error |
|
521 if ( state & RPT_STDERR ) |
|
522 { |
|
523 (void) fprintf ( stderr, "%s\n", str ); |
|
524 (void) fflush ( stderr ); |
|
525 } |
|
526 } |
|
527 |
|
528 /***************************************************************************** |
|
529 * Close the output file stream. |
|
530 *****************************************************************************/ |
|
531 |
|
532 void |
|
533 Reporter::closeFile ( void ) |
|
534 { |
|
535 CloseHandle(hFile); |
|
536 |
|
537 #ifdef RPT_DEBUG_PROG |
|
538 (void) fprintf ( stdout, "file [%s] closed\n", filename ); |
|
539 (void) fflush ( stdout ); |
|
540 #endif |
|
541 } |
|
542 |
|
543 /*************************************************************************** |
|
544 * Open the message file, then read in contents, processing a line at a |
|
545 * time, putting the current line into global buffer string. |
|
546 ***************************************************************************/ |
|
547 |
|
548 void |
|
549 Reporter::readMessageFile ( const char * file ) |
|
550 { |
|
551 FILE * finptr; |
|
552 |
|
553 // open initialisation file |
|
554 if ( ( finptr = fopen ( file, "r" ) ) == NULL ) |
|
555 { |
|
556 return; |
|
557 } |
|
558 |
|
559 // read each line |
|
560 while ( ( fgets ( buffer, RPT_MAX_LINE_LEN, finptr ) ) != NULL ) |
|
561 { |
|
562 if ( *( buffer ) != '\n' && *( buffer ) != '#' ) |
|
563 { |
|
564 addMessage ( ); |
|
565 } |
|
566 } |
|
567 |
|
568 (void) fclose ( finptr ); |
|
569 } |
|
570 |
|
571 /*************************************************************************** |
|
572 * Split the line of text into code and message and allocate memory in |
|
573 * list and store it. |
|
574 ***************************************************************************/ |
|
575 |
|
576 void |
|
577 Reporter::addMessage ( void ) |
|
578 { |
|
579 char *pCode; |
|
580 char *pText; |
|
581 int code; |
|
582 |
|
583 // extract the code and text |
|
584 pCode = strtok ( buffer, ":" ); |
|
585 pText = strtok ( NULL, "\n" ); |
|
586 |
|
587 // check for legitimate values |
|
588 if ( !pCode || !pText ) |
|
589 { |
|
590 return; |
|
591 } |
|
592 |
|
593 code = atoi ( pCode ); |
|
594 if ( code > RPT_MAX_LIST_ENTRIES || messageList [ code ] ) |
|
595 { |
|
596 return; |
|
597 } |
|
598 |
|
599 // jump any spaces in front of text |
|
600 while ( *( pText ) == ' ' || *( pText ) == '\t' ) |
|
601 { |
|
602 pText++; |
|
603 } |
|
604 |
|
605 // copy string |
|
606 // messageList [ code ] = new char ( strlen ( pText ) + 1 ); |
|
607 messageList [ code ] = |
|
608 (char *) malloc ( sizeof ( char ) * ( strlen ( pText ) + 1 ) ); |
|
609 |
|
610 (void) strcpy ( messageList [ code ], pText ); |
|
611 |
|
612 #ifdef RPT_DEBUG_PROG |
|
613 // (void) fprintf ( stdout, "code [%s] text [%s] entry [%s] len %d\n", |
|
614 // pCode, pText, messageList [ code ], strlen ( pText ) ); |
|
615 // (void) fflush ( stdout ); |
|
616 #endif |
|
617 |
|
618 // let object know there is at least one message entry |
|
619 fileRead = 1; |
|
620 } |
|
621 |
|
622 /****************************************************************************/ |
|
623 |