1 /*++
2 
3 Copyright (c) 2004, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution.  The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8 
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12 Module Name:
13 
14   EfiUtilityMsgs.c
15 
16 Abstract:
17 
18   EFI tools utility functions to display warning, error, and informational
19   messages.
20 
21 --*/
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 
28 #include "Tiano.h"
29 #include "EfiUtilityMsgs.h"
30 
31 #define MAX_LINE_LEN  200
32 
33 //
34 // Declare module globals for keeping track of the the utility's
35 // name and other settings.
36 //
37 static STATUS mStatus                 = STATUS_SUCCESS;
38 static INT8   mUtilityName[50]        = { 0 };
39 static UINT32 mDebugMsgMask           = 0;
40 static INT8   *mSourceFileName        = NULL;
41 static UINT32 mSourceFileLineNum      = 0;
42 static UINT32 mErrorCount             = 0;
43 static UINT32 mWarningCount           = 0;
44 static UINT32 mMaxErrors              = 0;
45 static UINT32 mMaxWarnings            = 0;
46 static UINT32 mMaxWarningsPlusErrors  = 0;
47 static INT8   mPrintLimitsSet         = 0;
48 
49 static
50 void
51 PrintMessage (
52   INT8    *Type,
53   INT8    *FileName,
54   UINT32  LineNumber,
55   UINT32  MessageCode,
56   INT8    *Text,
57   INT8    *MsgFmt,
58   va_list List
59   );
60 
61 static
62 void
63 PrintLimitExceeded (
64   VOID
65   );
66 
67 void
Error(INT8 * FileName,UINT32 LineNumber,UINT32 MessageCode,INT8 * Text,INT8 * MsgFmt,...)68 Error (
69   INT8    *FileName,
70   UINT32  LineNumber,
71   UINT32  MessageCode,
72   INT8    *Text,
73   INT8    *MsgFmt,
74   ...
75   )
76 /*++
77 
78 Routine Description:
79   Prints an error message.
80 
81 Arguments:
82   All arguments are optional, though the printed message may be useless if
83   at least something valid is not specified.
84 
85   FileName - name of the file or application. If not specified, then the
86              utilty name (as set by the utility calling SetUtilityName()
87              earlier) is used. Otherwise "Unknown utility" is used.
88 
89   LineNumber - the line number of error, typically used by parsers. If the
90                utility is not a parser, then 0 should be specified. Otherwise
91                the FileName and LineNumber info can be used to cause
92                MS Visual Studio to jump to the error.
93 
94   MessageCode - an application-specific error code that can be referenced in
95               other documentation.
96 
97   Text        - the text in question, typically used by parsers.
98 
99   MsgFmt - the format string for the error message. Can contain formatting
100            controls for use with the varargs.
101 
102 Returns:
103   None.
104 
105 Notes:
106   We print the following (similar to the Warn() and Debug()
107   W
108   Typical error/warning message format:
109 
110   bin\VfrCompile.cpp(330) : error C2660: 'AddVfrDataStructField' : function does not take 2 parameters
111 
112   BUGBUG -- these three utility functions are almost identical, and
113   should be modified to share code.
114 
115   Visual Studio does not find error messages with:
116 
117      " error :"
118      " error 1:"
119      " error c1:"
120      " error 1000:"
121      " error c100:"
122 
123   It does find:
124      " error c1000:"
125 --*/
126 {
127   va_list List;
128   //
129   // If limits have been set, then check that we have not exceeded them
130   //
131   if (mPrintLimitsSet) {
132     //
133     // See if we've exceeded our total count
134     //
135     if (mMaxWarningsPlusErrors != 0) {
136       if (mErrorCount + mWarningCount > mMaxWarningsPlusErrors) {
137         PrintLimitExceeded ();
138         return ;
139       }
140     }
141     //
142     // See if we've exceeded our error count
143     //
144     if (mMaxErrors != 0) {
145       if (mErrorCount > mMaxErrors) {
146         PrintLimitExceeded ();
147         return ;
148       }
149     }
150   }
151 
152   mErrorCount++;
153   va_start (List, MsgFmt);
154   PrintMessage ("error", FileName, LineNumber, MessageCode, Text, MsgFmt, List);
155   va_end (List);
156   //
157   // Set status accordingly
158   //
159   if (mStatus < STATUS_ERROR) {
160     mStatus = STATUS_ERROR;
161   }
162 }
163 
164 void
ParserError(UINT32 MessageCode,INT8 * Text,INT8 * MsgFmt,...)165 ParserError (
166   UINT32  MessageCode,
167   INT8    *Text,
168   INT8    *MsgFmt,
169   ...
170   )
171 /*++
172 
173 Routine Description:
174   Print a parser error, using the source file name and line number
175   set by a previous call to SetParserPosition().
176 
177 Arguments:
178   MessageCode   - application-specific error code
179   Text          - text to print in the error message
180   MsgFmt        - format string to print at the end of the error message
181 
182 Returns:
183   NA
184 
185 --*/
186 {
187   va_list List;
188   //
189   // If limits have been set, then check them
190   //
191   if (mPrintLimitsSet) {
192     //
193     // See if we've exceeded our total count
194     //
195     if (mMaxWarningsPlusErrors != 0) {
196       if (mErrorCount + mWarningCount > mMaxWarningsPlusErrors) {
197         PrintLimitExceeded ();
198         return ;
199       }
200     }
201     //
202     // See if we've exceeded our error count
203     //
204     if (mMaxErrors != 0) {
205       if (mErrorCount > mMaxErrors) {
206         PrintLimitExceeded ();
207         return ;
208       }
209     }
210   }
211 
212   mErrorCount++;
213   va_start (List, MsgFmt);
214   PrintMessage ("error", mSourceFileName, mSourceFileLineNum, MessageCode, Text, MsgFmt, List);
215   va_end (List);
216   //
217   // Set status accordingly
218   //
219   if (mStatus < STATUS_ERROR) {
220     mStatus = STATUS_ERROR;
221   }
222 }
223 
224 void
ParserWarning(UINT32 ErrorCode,INT8 * OffendingText,INT8 * MsgFmt,...)225 ParserWarning (
226   UINT32  ErrorCode,
227   INT8    *OffendingText,
228   INT8    *MsgFmt,
229   ...
230   )
231 /*++
232 
233 Routine Description:
234   Print a parser warning, using the source file name and line number
235   set by a previous call to SetParserPosition().
236 
237 Arguments:
238   ErrorCode     - application-specific error code
239   OffendingText - text to print in the warning message
240   MsgFmt        - format string to print at the end of the warning message
241 
242 Returns:
243   NA
244 
245 --*/
246 {
247   va_list List;
248   //
249   // If limits have been set, then check them
250   //
251   if (mPrintLimitsSet) {
252     //
253     // See if we've exceeded our total count
254     //
255     if (mMaxWarningsPlusErrors != 0) {
256       if (mErrorCount + mWarningCount > mMaxWarningsPlusErrors) {
257         PrintLimitExceeded ();
258         return ;
259       }
260     }
261     //
262     // See if we've exceeded our warning count
263     //
264     if (mMaxWarnings != 0) {
265       if (mWarningCount > mMaxWarnings) {
266         PrintLimitExceeded ();
267         return ;
268       }
269     }
270   }
271 
272   mWarningCount++;
273   va_start (List, MsgFmt);
274   PrintMessage ("warning", mSourceFileName, mSourceFileLineNum, ErrorCode, OffendingText, MsgFmt, List);
275   va_end (List);
276   //
277   // Set status accordingly
278   //
279   if (mStatus < STATUS_WARNING) {
280     mStatus = STATUS_WARNING;
281   }
282 }
283 
284 void
Warning(INT8 * FileName,UINT32 LineNumber,UINT32 MessageCode,INT8 * Text,INT8 * MsgFmt,...)285 Warning (
286   INT8    *FileName,
287   UINT32  LineNumber,
288   UINT32  MessageCode,
289   INT8    *Text,
290   INT8    *MsgFmt,
291   ...
292   )
293 /*++
294 
295 Routine Description:
296   Print a warning message.
297 
298 Arguments:
299   FileName    - name of the file where the warning was detected, or the name
300                 of the application that detected the warning
301 
302   LineNumber  - the line number where the warning was detected (parsers).
303                 0 should be specified if the utility is not a parser.
304 
305   MessageCode - an application-specific warning code that can be referenced in
306                 other documentation.
307 
308   Text        - the text in question (parsers)
309 
310   MsgFmt      - the format string for the warning message. Can contain formatting
311                 controls for use with varargs.
312 
313 Returns:
314   None.
315 
316 --*/
317 {
318   va_list List;
319   //
320   // If limits have been set, then check them
321   //
322   if (mPrintLimitsSet) {
323     //
324     // See if we've exceeded our total count
325     //
326     if (mMaxWarningsPlusErrors != 0) {
327       if (mErrorCount + mWarningCount > mMaxWarningsPlusErrors) {
328         PrintLimitExceeded ();
329         return ;
330       }
331     }
332     //
333     // See if we've exceeded our warning count
334     //
335     if (mMaxWarnings != 0) {
336       if (mWarningCount > mMaxWarnings) {
337         PrintLimitExceeded ();
338         return ;
339       }
340     }
341   }
342 
343   mWarningCount++;
344   va_start (List, MsgFmt);
345   PrintMessage ("warning", FileName, LineNumber, MessageCode, Text, MsgFmt, List);
346   va_end (List);
347   //
348   // Set status accordingly
349   //
350   if (mStatus < STATUS_WARNING) {
351     mStatus = STATUS_WARNING;
352   }
353 }
354 
355 void
DebugMsg(INT8 * FileName,UINT32 LineNumber,UINT32 MsgMask,INT8 * Text,INT8 * MsgFmt,...)356 DebugMsg (
357   INT8    *FileName,
358   UINT32  LineNumber,
359   UINT32  MsgMask,
360   INT8    *Text,
361   INT8    *MsgFmt,
362   ...
363   )
364 /*++
365 
366 Routine Description:
367   Print a warning message.
368 
369 Arguments:
370   FileName    - typically the name of the utility printing the debug message, but
371                 can be the name of a file being parsed.
372 
373   LineNumber  - the line number in FileName (parsers)
374 
375   MsgMask     - an application-specific bitmask that, in combination with mDebugMsgMask,
376                 determines if the debug message gets printed.
377 
378   Text        - the text in question (parsers)
379 
380   MsgFmt      - the format string for the debug message. Can contain formatting
381                 controls for use with varargs.
382 
383 Returns:
384   None.
385 
386 --*/
387 {
388   va_list List;
389   //
390   // If the debug mask is not applicable, then do nothing.
391   //
392   if ((MsgMask != 0) && ((mDebugMsgMask & MsgMask) == 0)) {
393     return ;
394   }
395 
396   va_start (List, MsgFmt);
397   PrintMessage ("debug", FileName, LineNumber, 0, Text, MsgFmt, List);
398   va_end (List);
399 }
400 
401 static
402 void
PrintMessage(INT8 * Type,INT8 * FileName,UINT32 LineNumber,UINT32 MessageCode,INT8 * Text,INT8 * MsgFmt,va_list List)403 PrintMessage (
404   INT8    *Type,
405   INT8    *FileName,
406   UINT32  LineNumber,
407   UINT32  MessageCode,
408   INT8    *Text,
409   INT8    *MsgFmt,
410   va_list List
411   )
412 /*++
413 
414 Routine Description:
415   Worker routine for all the utility printing services. Prints the message in
416   a format that Visual Studio will find when scanning build outputs for
417   errors or warnings.
418 
419 Arguments:
420   Type        - "warning" or "error" string to insert into the message to be
421                 printed. The first character of this string (converted to uppercase)
422                 is used to preceed the MessageCode value in the output string.
423 
424   FileName    - name of the file where the warning was detected, or the name
425                 of the application that detected the warning
426 
427   LineNumber  - the line number where the warning was detected (parsers).
428                 0 should be specified if the utility is not a parser.
429 
430   MessageCode - an application-specific warning code that can be referenced in
431                 other documentation.
432 
433   Text        - part of the message to print
434 
435   MsgFmt      - the format string for the message. Can contain formatting
436                 controls for use with varargs.
437   List        - the variable list.
438 
439 Returns:
440   None.
441 
442 Notes:
443   If FileName == NULL then this utility will use the string passed into SetUtilityName().
444 
445   LineNumber is only used if the caller is a parser, in which case FileName refers to the
446   file being parsed.
447 
448   Text and MsgFmt are both optional, though it would be of little use calling this function with
449   them both NULL.
450 
451   Output will typically be of the form:
452     <FileName>(<LineNumber>) : <Type> <Type[0]><MessageCode>: <Text> : <MsgFmt>
453 
454     Parser (LineNumber != 0)
455       VfrCompile.cpp(330) : error E2660: AddVfrDataStructField : function does not take 2 parameters
456     Generic utility (LineNumber == 0)
457       UtilityName : error E1234 : Text string : MsgFmt string and args
458 
459 --*/
460 {
461   INT8  Line[MAX_LINE_LEN];
462   INT8  Line2[MAX_LINE_LEN];
463   INT8  *Cptr;
464   //
465   // If given a filename, then add it (and the line number) to the string.
466   // If there's no filename, then use the program name if provided.
467   //
468   if (FileName != NULL) {
469     Cptr = FileName;
470   } else if (mUtilityName[0] != 0) {
471     Cptr = mUtilityName;
472   } else {
473     Cptr = "Unknown utility";
474   }
475 
476   strcpy (Line, Cptr);
477   if (LineNumber != 0) {
478     sprintf (Line2, "(%d)", LineNumber);
479     strcat (Line, Line2);
480   }
481   //
482   // Have to print an error code or Visual Studio won't find the
483   // message for you. It has to be decimal digits too.
484   //
485   sprintf (Line2, " : %s %c%04d", Type, toupper (Type[0]), MessageCode);
486   strcat (Line, Line2);
487   fprintf (stdout, "%s", Line);
488   //
489   // If offending text was provided, then print it
490   //
491   if (Text != NULL) {
492     fprintf (stdout, ": %s ", Text);
493   }
494   //
495   // Print formatted message if provided
496   //
497   if (MsgFmt != NULL) {
498     vsprintf (Line2, MsgFmt, List);
499     fprintf (stdout, ": %s", Line2);
500   }
501 
502   fprintf (stdout, "\n");
503 }
504 
505 void
ParserSetPosition(INT8 * SourceFileName,UINT32 LineNum)506 ParserSetPosition (
507   INT8    *SourceFileName,
508   UINT32  LineNum
509   )
510 /*++
511 
512 Routine Description:
513   Set the position in a file being parsed. This can be used to
514   print error messages deeper down in a parser.
515 
516 Arguments:
517   SourceFileName - name of the source file being parsed
518   LineNum        - line number of the source file being parsed
519 
520 Returns:
521   NA
522 
523 --*/
524 {
525   mSourceFileName     = SourceFileName;
526   mSourceFileLineNum  = LineNum;
527 }
528 
529 void
SetUtilityName(INT8 * UtilityName)530 SetUtilityName (
531   INT8    *UtilityName
532   )
533 /*++
534 
535 Routine Description:
536   All printed error/warning/debug messages follow the same format, and
537   typically will print a filename or utility name followed by the error
538   text. However if a filename is not passed to the print routines, then
539   they'll print the utility name if you call this function early in your
540   app to set the utility name.
541 
542 Arguments:
543   UtilityName  -  name of the utility, which will be printed with all
544                   error/warning/debug messags.
545 
546 Returns:
547   NA
548 
549 --*/
550 {
551   //
552   // Save the name of the utility in our local variable. Make sure its
553   // length does not exceed our buffer.
554   //
555   if (UtilityName != NULL) {
556     if (strlen (UtilityName) >= sizeof (mUtilityName)) {
557       Error (UtilityName, 0, 0, "application error", "utility name length exceeds internal buffer size");
558       strncpy (mUtilityName, UtilityName, sizeof (mUtilityName) - 1);
559       mUtilityName[sizeof (mUtilityName) - 1] = 0;
560       return ;
561     } else {
562       strcpy (mUtilityName, UtilityName);
563     }
564   } else {
565     Error (NULL, 0, 0, "application error", "SetUtilityName() called with NULL utility name");
566   }
567 }
568 
569 STATUS
GetUtilityStatus(VOID)570 GetUtilityStatus (
571   VOID
572   )
573 /*++
574 
575 Routine Description:
576   When you call Error() or Warning(), this module keeps track of it and
577   sets a local mStatus to STATUS_ERROR or STATUS_WARNING. When the utility
578   exits, it can call this function to get the status and use it as a return
579   value.
580 
581 Arguments:
582   None.
583 
584 Returns:
585   Worst-case status reported, as defined by which print function was called.
586 
587 --*/
588 {
589   return mStatus;
590 }
591 
592 void
SetDebugMsgMask(UINT32 DebugMask)593 SetDebugMsgMask (
594   UINT32  DebugMask
595   )
596 /*++
597 
598 Routine Description:
599   Set the debug printing mask. This is used by the DebugMsg() function
600   to determine when/if a debug message should be printed.
601 
602 Arguments:
603   DebugMask  - bitmask, specific to the calling application
604 
605 Returns:
606   NA
607 
608 --*/
609 {
610   mDebugMsgMask = DebugMask;
611 }
612 
613 void
SetPrintLimits(UINT32 MaxErrors,UINT32 MaxWarnings,UINT32 MaxWarningsPlusErrors)614 SetPrintLimits (
615   UINT32  MaxErrors,
616   UINT32  MaxWarnings,
617   UINT32  MaxWarningsPlusErrors
618   )
619 /*++
620 
621 Routine Description:
622   Set the limits of how many errors, warnings, and errors+warnings
623   we will print.
624 
625 Arguments:
626   MaxErrors       - maximum number of error messages to print
627   MaxWarnings     - maximum number of warning messages to print
628   MaxWarningsPlusErrors
629                   - maximum number of errors+warnings to print
630 
631 Returns:
632   NA
633 
634 --*/
635 {
636   mMaxErrors              = MaxErrors;
637   mMaxWarnings            = MaxWarnings;
638   mMaxWarningsPlusErrors  = MaxWarningsPlusErrors;
639   mPrintLimitsSet         = 1;
640 }
641 
642 static
643 void
PrintLimitExceeded(VOID)644 PrintLimitExceeded (
645   VOID
646   )
647 {
648   static INT8 mPrintLimitExceeded = 0;
649   //
650   // If we've already printed the message, do nothing. Otherwise
651   // temporarily increase our print limits so we can pass one
652   // more message through.
653   //
654   if (mPrintLimitExceeded == 0) {
655     mPrintLimitExceeded++;
656     mMaxErrors++;
657     mMaxWarnings++;
658     mMaxWarningsPlusErrors++;
659     Error (NULL, 0, 0, "error/warning print limit exceeded", NULL);
660     mMaxErrors--;
661     mMaxWarnings--;
662     mMaxWarningsPlusErrors--;
663   }
664 }
665 
666 #if 0
667 void
668 TestUtilityMessages (
669   VOID
670   )
671 {
672   char *ArgStr = "ArgString";
673   int  ArgInt;
674 
675   ArgInt  = 0x12345678;
676   //
677   // Test without setting utility name
678   //
679   fprintf (stdout, "* Testing without setting utility name\n");
680   fprintf (stdout, "** Test debug message not printed\n");
681   DebugMsg (NULL, 0, 0x00000001, NULL, NULL);
682   fprintf (stdout, "** Test warning with two strings and two args\n");
683   Warning (NULL, 0, 1234, "Text1", "Text2 %s 0x%X", ArgStr, ArgInt);
684   fprintf (stdout, "** Test error with two strings and two args\n");
685   Warning (NULL, 0, 1234, "Text1", "Text2 %s 0x%X", ArgStr, ArgInt);
686   fprintf (stdout, "** Test parser warning with nothing\n");
687   ParserWarning (0, NULL, NULL);
688   fprintf (stdout, "** Test parser error with nothing\n");
689   ParserError (0, NULL, NULL);
690   //
691   // Test with utility name set now
692   //
693   fprintf (stdout, "** Testingin with utility name set\n");
694   SetUtilityName ("MyUtilityName");
695   //
696   // Test debug prints
697   //
698   SetDebugMsgMask (2);
699   fprintf (stdout, "** Test debug message with one string\n");
700   DebugMsg (NULL, 0, 0x00000002, "Text1", NULL);
701   fprintf (stdout, "** Test debug message with one string\n");
702   DebugMsg (NULL, 0, 0x00000002, NULL, "Text2");
703   fprintf (stdout, "** Test debug message with two strings\n");
704   DebugMsg (NULL, 0, 0x00000002, "Text1", "Text2");
705   fprintf (stdout, "** Test debug message with two strings and two args\n");
706   DebugMsg (NULL, 0, 0x00000002, "Text1", "Text2 %s 0x%X", ArgStr, ArgInt);
707   //
708   // Test warning prints
709   //
710   fprintf (stdout, "** Test warning with no strings\n");
711   Warning (NULL, 0, 1234, NULL, NULL);
712   fprintf (stdout, "** Test warning with one string\n");
713   Warning (NULL, 0, 1234, "Text1", NULL);
714   fprintf (stdout, "** Test warning with one string\n");
715   Warning (NULL, 0, 1234, NULL, "Text2");
716   fprintf (stdout, "** Test warning with two strings and two args\n");
717   Warning (NULL, 0, 1234, "Text1", "Text2 %s 0x%X", ArgStr, ArgInt);
718   //
719   // Test error prints
720   //
721   fprintf (stdout, "** Test error with no strings\n");
722   Error (NULL, 0, 1234, NULL, NULL);
723   fprintf (stdout, "** Test error with one string\n");
724   Error (NULL, 0, 1234, "Text1", NULL);
725   fprintf (stdout, "** Test error with one string\n");
726   Error (NULL, 0, 1234, NULL, "Text2");
727   fprintf (stdout, "** Test error with two strings and two args\n");
728   Error (NULL, 0, 1234, "Text1", "Text2 %s 0x%X", ArgStr, ArgInt);
729   //
730   // Test parser prints
731   //
732   fprintf (stdout, "** Test parser errors\n");
733   ParserSetPosition (__FILE__, __LINE__ + 1);
734   ParserError (1234, NULL, NULL);
735   ParserSetPosition (__FILE__, __LINE__ + 1);
736   ParserError (1234, "Text1", NULL);
737   ParserSetPosition (__FILE__, __LINE__ + 1);
738   ParserError (1234, NULL, "Text2");
739   ParserSetPosition (__FILE__, __LINE__ + 1);
740   ParserError (1234, "Text1", "Text2");
741   ParserSetPosition (__FILE__, __LINE__ + 1);
742   ParserError (1234, "Text1", "Text2 %s 0x%X", ArgStr, ArgInt);
743 
744   fprintf (stdout, "** Test parser warnings\n");
745   ParserSetPosition (__FILE__, __LINE__ + 1);
746   ParserWarning (4321, NULL, NULL);
747   ParserSetPosition (__FILE__, __LINE__ + 1);
748   ParserWarning (4321, "Text1", NULL);
749   ParserSetPosition (__FILE__, __LINE__ + 1);
750   ParserWarning (4321, NULL, "Text2");
751   ParserSetPosition (__FILE__, __LINE__ + 1);
752   ParserWarning (4321, "Text1", "Text2");
753   ParserSetPosition (__FILE__, __LINE__ + 1);
754   ParserWarning (4321, "Text1", "Text2 %s 0x%X", ArgStr, ArgInt);
755 }
756 #endif
757