1 : /*****************************************************************************
2 : * myerror.c
3 : *
4 : * DESCRIPTION
5 : * This file contains the code to handle error messages. Instead of simply
6 : * printing the error to stdio, it allocates some memory and stores the
7 : * message in it. This is so that one can pass the error message back to
8 : * Tcl/Tk or another GUI program when there is no stdio.
9 : * In addition a version of sprintf is provided which allocates memory for
10 : * the calling routine, so that one doesn't have to guess the maximum bounds
11 : * of the message.
12 : *
13 : * HISTORY
14 : * 9/2002 Arthur Taylor (MDL / RSIS): Created.
15 : * 12/2002 Rici Yu, Fangyu Chi, Mark Armstrong, & Tim Boyer
16 : * (RY,FC,MA,&TB): Code Review 2.
17 : * 12/2005 AAT Added myWarn routines.
18 : *
19 : * NOTES
20 : * See Kernighan & Ritchie C book (2nd edition) page 156.
21 : *****************************************************************************
22 : */
23 : #include <stdarg.h>
24 : #include <stdlib.h>
25 : #include <string.h>
26 : #include "myassert.h"
27 : #include "myerror.h"
28 : #ifdef MEMWATCH
29 : #include "memwatch.h"
30 : #endif
31 :
32 : /*****************************************************************************
33 : * AllocSprintf() -- Arthur Taylor / MDL (Review 12/2002)
34 : *
35 : * PURPOSE
36 : * Based on minprintf (see K&R C book (2nd edition) page 156. This code
37 : * tries to provide some of the functionality of sprintf, while at the same
38 : * time it handles the memory allocation.
39 : * In addition, it provides a %S option, which allows one to pass in an
40 : * array of strings, and get back a comma delimited string.
41 : *
42 : * ARGUMENTS
43 : * Ptr = An array of data that is of size LenBuff. (Input/Output)
44 : * LenBuff = The allocated length of Ptr. (Input/Output)
45 : * fmt = Format similar to the one used by sprintf to define how to
46 : * print the message (Input)
47 : * ap = argument list initialized by a call to va_start. Contains the
48 : * data needed by fmt. (Input)
49 : *
50 : * RETURNS: void
51 : *
52 : * 9/2002 Arthur Taylor (MDL/RSIS): Created.
53 : * 12/2002 (RY,FC,MA,&TB): Code Review.
54 : * 12/2002 AAT: Fixed the mallocSprintf ("") error.
55 : * 2/2003 AAT: increased bufpart[80] to bufpart[330] because the largest
56 : * 64 bit double is: +1.7E+308, and I want 20 "slots" for stuff
57 : * after the decimal place. There is the possibility of "Long
58 : * doubles" (80 bits) which would have a max of: +3.4E+4932, but
59 : * that is excessive for now.
60 : * 2/2004 AAT: if lenBuff != 0, switch from ipos-- to strlen (buffer);
61 : * 3/2004 AAT: Added %c option.
62 : * 11/2005 AAT: Added %e option.
63 : * 1/2006 AAT: Found a bug with multiple errSprintf. Doesn't seem to be
64 : * able to handle lenBuff > strlen(buffer) when procedure is
65 : * first called. Something like format = "aaa%s", lenBuff = 3,
66 : * buff = 'n' would result in 'naaa__<string>', instead of
67 : * 'naaa<string>'. Simple solution set lenBuff = strlen (buff).
68 : * better solution: Maybe calculate correct place for ipos
69 : * before switch.
70 : *
71 : * NOTES
72 : * Supported formats:
73 : * %0.4f => float, double
74 : * %03d %ld %10ld => int, sInt4.
75 : * %s => Null terminated char string. (no range specification)
76 : * %S => take a char ** and turn it into a comma delimited string.
77 : *
78 : * Assumes that no individual float or int will be more than 80 characters
79 : * Assumes that no % option is more than 20 char.
80 : *****************************************************************************
81 : */
82 56 : static void AllocSprintf (char **Ptr, size_t *LenBuff, const char *fmt,
83 : va_list ap)
84 : {
85 56 : char *buffer = *Ptr; /* Local copy of Ptr. */
86 56 : size_t lenBuff = *LenBuff; /* Local copy of LenBuff. */
87 : const char *p; /* Points to % char in % option. */
88 : const char *p1; /* Points to end of % option. */
89 : char bufpart[330]; /* Used for formating the int / float options. */
90 : char format[20]; /* Used to store the % option. */
91 : char *sval; /* For pulling strings off va_list. */
92 : char **Sval; /* For pulling lists of strings off va_list. */
93 : size_t slen; /* Length of used part of temp. */
94 : char f_inLoop; /* Flag to state whether we got into %S , loop. */
95 : char flag; /* If they have a l,L,h in string. */
96 : /* size_t ipos = *LenBuff; *//* The current index to start storing data. */
97 : size_t ipos; /* The current index to start storing data. */
98 : int c_type; /* Used when handling %c option. */
99 :
100 : myAssert (sizeof (char) == 1);
101 :
102 56 : if ((fmt == NULL) || (strlen (fmt) == 0)) {
103 0 : return;
104 : }
105 56 : p = fmt;
106 : /* If lenBuff = 0, then make room for the '\0' character. */
107 56 : if (lenBuff == 0) {
108 56 : lenBuff++;
109 56 : buffer = (char *) realloc ((void *) buffer, lenBuff);
110 : /* Added following 1 line on 1/2006 */
111 56 : ipos = 0;
112 : } else {
113 : /* Added following 3 lines on 1/2006 */
114 0 : myAssert (lenBuff >= strlen (buffer) + 1);
115 0 : lenBuff = strlen (buffer) + 1;
116 0 : ipos = lenBuff - 1;
117 : /* ipos = strlen (buffer); */
118 : }
119 268 : while (p < fmt + strlen (fmt)) {
120 188 : p1 = p;
121 188 : p = strchr (p1, '%');
122 : /* Handle simple case when no more % in format string. */
123 188 : if (p == NULL) {
124 : /* No more format strings; copy rest of format and return */
125 32 : lenBuff += strlen (p1);
126 32 : buffer = (char *) realloc ((void *) buffer, lenBuff);
127 32 : strcpy (buffer + ipos, p1);
128 32 : goto done;
129 : }
130 : /* Handle data up to the current % in format string. */
131 156 : lenBuff += p - p1;
132 156 : buffer = (char *) realloc ((void *) buffer, lenBuff);
133 156 : strncpy (buffer + ipos, p1, p - p1);
134 156 : ipos = lenBuff - 1;
135 : /* Start dealing with % of format. */
136 156 : p1 = p + strspn (p + 1, "0123456789.");
137 156 : p1++;
138 : /* p1 points to first letter after %. */
139 156 : switch (*p1) {
140 : case 'h':
141 : case 'l':
142 : case 'L':
143 0 : flag = *p1;
144 0 : p1++;
145 0 : break;
146 : case '\0':
147 : /* Handle improper use of '%' for example: '%##' */
148 0 : lenBuff += p1 - p - 1;
149 0 : buffer = (char *) realloc ((void *) buffer, lenBuff);
150 0 : strncpy (buffer + ipos, p + 1, p1 - p - 1);
151 0 : goto done;
152 : default:
153 156 : flag = ' ';
154 : }
155 156 : if ((p1 - p + 1) > (int) (sizeof (format)) - 1) {
156 : /* Protect against overflow of format string. */
157 0 : lenBuff += p1 - p + 1;
158 0 : buffer = (char *) realloc ((void *) buffer, lenBuff);
159 0 : strncpy (buffer + ipos, p, p1 - p + 1);
160 0 : ipos = lenBuff - 1;
161 : } else {
162 156 : strncpy (format, p, p1 - p + 1);
163 156 : format[p1 - p + 1] = '\0';
164 156 : switch (*p1) {
165 : case 'd':
166 40 : switch (flag) {
167 : case 'l':
168 : case 'L':
169 0 : sprintf (bufpart, format, va_arg (ap, sInt4));
170 0 : break;
171 : /*
172 : * gcc warning for 'h': "..." promotes short int to
173 : * int. Could get rid of 'h' option but decided to
174 : * leave it in since we might have a different
175 : * compiler.
176 : */
177 : /*
178 : case 'h':
179 : sprintf (bufpart, format, va_arg(ap, short int));
180 : break;
181 : */
182 : default:
183 40 : sprintf (bufpart, format, va_arg (ap, int));
184 : }
185 40 : slen = strlen (bufpart);
186 40 : lenBuff += slen;
187 40 : buffer = (char *) realloc ((void *) buffer, lenBuff);
188 40 : strncpy (buffer + ipos, bufpart, slen);
189 40 : ipos = lenBuff - 1;
190 40 : break;
191 : case 'f':
192 0 : sprintf (bufpart, format, va_arg (ap, double));
193 0 : slen = strlen (bufpart);
194 0 : lenBuff += slen;
195 0 : buffer = (char *) realloc ((void *) buffer, lenBuff);
196 0 : strncpy (buffer + ipos, bufpart, slen);
197 0 : ipos = lenBuff - 1;
198 0 : break;
199 : case 'e':
200 0 : sprintf (bufpart, format, va_arg (ap, double));
201 0 : slen = strlen (bufpart);
202 0 : lenBuff += slen;
203 0 : buffer = (char *) realloc ((void *) buffer, lenBuff);
204 0 : strncpy (buffer + ipos, bufpart, slen);
205 0 : ipos = lenBuff - 1;
206 0 : break;
207 : case 'g':
208 0 : sprintf (bufpart, format, va_arg (ap, double));
209 0 : slen = strlen (bufpart);
210 0 : lenBuff += slen;
211 0 : buffer = (char *) realloc ((void *) buffer, lenBuff);
212 0 : strncpy (buffer + ipos, bufpart, slen);
213 0 : ipos = lenBuff - 1;
214 0 : break;
215 : case 'c':
216 0 : c_type = va_arg (ap, int);
217 0 : lenBuff += 1;
218 0 : buffer = (char *) realloc ((void *) buffer, lenBuff);
219 0 : buffer[ipos] = (char) c_type;
220 0 : buffer[ipos + 1] = '\0';
221 0 : ipos = lenBuff - 1;
222 0 : break;
223 : case 's':
224 116 : if ((p1 - p) == 1) {
225 116 : sval = va_arg (ap, char *);
226 : /* printf (":: sval :: '%s'\n", sval);*/
227 116 : slen = strlen (sval);
228 116 : lenBuff += slen;
229 116 : buffer = (char *) realloc ((void *) buffer, lenBuff);
230 116 : strncpy (buffer + ipos, sval, slen);
231 116 : ipos = lenBuff - 1;
232 116 : break;
233 : }
234 : /* Intentionally fall through. */
235 : case 'S':
236 0 : if ((p1 - p) == 1) {
237 0 : f_inLoop = 0;
238 0 : for (Sval = va_arg (ap, char **); *Sval; Sval++) {
239 0 : slen = strlen (*Sval);
240 0 : lenBuff += slen + 1;
241 0 : buffer = (char *) realloc ((void *) buffer, lenBuff);
242 0 : strcpy (buffer + ipos, *Sval);
243 0 : strcat (buffer + ipos + slen, ",");
244 0 : ipos = lenBuff - 1;
245 0 : f_inLoop = 1;
246 : }
247 0 : if (f_inLoop) {
248 0 : lenBuff--;
249 0 : buffer[lenBuff] = '\0';
250 0 : ipos = lenBuff - 1;
251 : }
252 0 : break;
253 : }
254 : /* Intentionally fall through. */
255 : default:
256 0 : lenBuff += p1 - p;
257 0 : buffer = (char *) realloc ((void *) buffer, lenBuff);
258 0 : strncpy (buffer + ipos, p + 1, p1 - p);
259 0 : ipos = lenBuff - 1;
260 : }
261 : }
262 156 : p = p1 + 1;
263 : }
264 : done:
265 56 : buffer[lenBuff - 1] = '\0';
266 56 : *Ptr = buffer;
267 56 : *LenBuff = lenBuff;
268 : }
269 :
270 : /*****************************************************************************
271 : * mallocSprintf() -- Arthur Taylor / MDL (Review 12/2002)
272 : *
273 : * PURPOSE
274 : * This is a front end for AllocSprintf, when you want to malloc memory.
275 : * In other words when the pointer is not pointing to anything in particular.
276 : * It allocates the memory, prints the message, and then sets Ptr to point to
277 : * it.
278 : *
279 : * ARGUMENTS
280 : * Ptr = Place to point to new memory which contains the message (Output)
281 : * fmt = Format similar to the one used by sprintf to define how to print the
282 : * message (Input)
283 : *
284 : * RETURNS: void
285 : *
286 : * 9/2002 Arthur Taylor (MDL/RSIS): Created.
287 : * 12/2002 (RY,FC,MA,&TB): Code Review.
288 : *
289 : * NOTES
290 : * Supported formats: See AllocSprintf
291 : *****************************************************************************
292 : */
293 8 : void mallocSprintf (char **Ptr, const char *fmt, ...)
294 : {
295 : va_list ap; /* Contains the data needed by fmt. */
296 8 : size_t buff_len = 0; /* Allocated length of buffer. */
297 :
298 8 : *Ptr = NULL;
299 8 : if (fmt != NULL) {
300 8 : va_start (ap, fmt); /* make ap point to 1st unnamed arg. */
301 8 : AllocSprintf (Ptr, &buff_len, fmt, ap);
302 8 : va_end (ap); /* clean up when done. */
303 : }
304 8 : }
305 :
306 : /*****************************************************************************
307 : * reallocSprintf() -- Arthur Taylor / MDL (Review 12/2002)
308 : *
309 : * PURPOSE
310 : * This is a front end for AllocSprintf, when you want to realloc memory.
311 : * In other words, the pointer is pointing to NULL, or to some memory that
312 : * you want to tack a message onto the end of. It allocates extra memory,
313 : * and prints the message.
314 : *
315 : * KEY WORDS: "Tack a message onto the end of"
316 : *
317 : * ARGUMENTS
318 : * Ptr = Pointer to memory to add the message to. (Input/Output)
319 : * fmt = Format similar to the one used by sprintf to define how to print the
320 : * message (Input)
321 : *
322 : * RETURNS: void
323 : *
324 : * 9/2002 Arthur Taylor (MDL/RSIS): Created.
325 : * 12/2002 (RY,FC,MA,&TB): Code Review.
326 : *
327 : * NOTES
328 : * Supported formats: See AllocSprintf
329 : *****************************************************************************
330 : */
331 48 : void reallocSprintf (char **Ptr, const char *fmt, ...)
332 : {
333 : va_list ap; /* Contains the data needed by fmt. */
334 : size_t buff_len; /* Allocated length of buffer. */
335 :
336 48 : if (fmt != NULL) {
337 48 : va_start (ap, fmt); /* make ap point to 1st unnamed arg. */
338 48 : if (*Ptr == NULL) {
339 48 : buff_len = 0;
340 : } else {
341 0 : buff_len = strlen (*Ptr) + 1;
342 : }
343 48 : AllocSprintf (Ptr, &buff_len, fmt, ap);
344 48 : va_end (ap); /* clean up when done. */
345 : }
346 48 : }
347 :
348 : /*****************************************************************************
349 : * errSprintf() -- Arthur Taylor / MDL (Review 12/2002)
350 : *
351 : * PURPOSE
352 : * This uses AllocSprintf to generate a message, which it stores in a static
353 : * variable. If it is called with a (NULL), it returns the built up message,
354 : * and resets its pointer to NULL. The idea being that errors can be stacked
355 : * up, and you pop them off when you need to report them. The reporting could
356 : * be done by printing them to stdio, or by passing them back to Tcl/Tk.
357 : * Note: It is the caller's responsibility to free the memory, and it is
358 : * the caller's responsibility to make sure the last call to this is with
359 : * (NULL), or else the memory won't get freed.
360 : *
361 : * ARGUMENTS
362 : * fmt = Format similar to the one used by sprintf to define how to print the
363 : * message (Input)
364 : *
365 : * RETURNS: char *
366 : * if (fmt == NULL) returns built up string
367 : * else returns NULL.
368 : *
369 : * 9/2002 Arthur Taylor (MDL/RSIS): Created.
370 : * 12/2002 (RY,FC,MA,&TB): Code Review.
371 : *
372 : * NOTES
373 : * Supported formats: See AllocSprintf
374 : *****************************************************************************
375 : */
376 : /* Following 2 variables used in both errSprintf and preErrSprintf */
377 : static char *errBuffer = NULL; /* Stores the current built up message. */
378 : static size_t errBuff_len = 0; /* Allocated length of errBuffer. */
379 :
380 6 : char *errSprintf (const char *fmt, ...)
381 : {
382 : va_list ap; /* Contains the data needed by fmt. */
383 : char *ans; /* Pointer to the final message while we reset
384 : * buffer. */
385 :
386 6 : if (fmt == NULL) {
387 6 : ans = errBuffer;
388 6 : errBuffer = NULL;
389 6 : errBuff_len = 0;
390 6 : return ans;
391 : }
392 0 : va_start (ap, fmt); /* make ap point to 1st unnamed arg. */
393 0 : AllocSprintf (&errBuffer, &errBuff_len, fmt, ap);
394 0 : va_end (ap); /* clean up when done. */
395 0 : return NULL;
396 : }
397 :
398 : /*****************************************************************************
399 : * preErrSprintf() -- Arthur Taylor / MDL
400 : *
401 : * PURPOSE
402 : * This uses AllocSprintf to generate a message, which it prepends to the
403 : * static variable used by errSprinf. If it is called with a (NULL), it
404 : * does nothing... Use errSprintf (NULL) to get the message, and reset the
405 : * pointer to NULL.
406 : * The idea here is that we want to prepend calling info when there was an
407 : * error.
408 : * Note: It is the caller's responsibility to free the memory, by
409 : * eventually making one last call to errSprintf (NULL) and freeing the
410 : * returned memory.
411 : *
412 : * ARGUMENTS
413 : * fmt = Format similar to the one used by sprintf to define how to print the
414 : * message (Input)
415 : *
416 : * RETURNS: void
417 : *
418 : * 12/2002 Arthur Taylor (MDL/RSIS): Created.
419 : *
420 : * NOTES
421 : * Supported formats: See AllocSprintf
422 : *****************************************************************************
423 : */
424 0 : void preErrSprintf (const char *fmt, ...)
425 : {
426 0 : char *preBuffer = NULL; /* Stores the prepended message. */
427 0 : size_t preBuff_len = 0; /* Allocated length of preBuffer. */
428 : va_list ap; /* Contains the data needed by fmt. */
429 :
430 : myAssert (sizeof (char) == 1);
431 :
432 0 : if (fmt == NULL) {
433 0 : return;
434 : }
435 0 : va_start (ap, fmt); /* make ap point to 1st unnamed arg. */
436 0 : AllocSprintf (&preBuffer, &preBuff_len, fmt, ap);
437 0 : va_end (ap); /* clean up when done. */
438 :
439 0 : if (errBuff_len != 0) {
440 : /* Increase preBuffer to have enough room for errBuffer */
441 0 : preBuff_len += errBuff_len;
442 0 : preBuffer = (char *) realloc ((void *) preBuffer, preBuff_len);
443 : /* concat errBuffer to end of preBuffer, and free errBuffer */
444 0 : strcat (preBuffer, errBuffer);
445 0 : free (errBuffer);
446 : }
447 : /* Finally point errBuffer to preBuffer, and update errBuff_len. */
448 0 : errBuffer = preBuffer;
449 0 : errBuff_len = preBuff_len;
450 0 : return;
451 : }
452 :
453 : /*****************************************************************************
454 : * _myWarn() -- Arthur Taylor / MDL
455 : *
456 : * PURPOSE
457 : * This is an update to my errSprintf routines. This procedure uses
458 : * AllocSprintf to generate a message, which it stores in a static variable.
459 : * It allows for prepending or appending error messages, and allows one to
460 : * set the error level of a message.
461 : *
462 : * ARGUMENTS
463 : * f_errCode = 0 => append notation msg, 1 => append warning msg
464 : * 2 => append error msg, 3 => prepend notation msg
465 : * 4 => prepend warning msg, 5 => prepend error msg (Input)
466 : * fmt = Format to define how to print the msg (Input)
467 : * ap = The arguments for the message. (Input)
468 : *
469 : * RETURNS: void
470 : *
471 : * 12/2005 Arthur Taylor (MDL): Created.
472 : *
473 : * NOTES:
474 : *****************************************************************************
475 : */
476 : /* Following variables used in the myWarn routines */
477 : static char *warnBuff = NULL; /* Stores the current built up message. */
478 : static size_t warnBuffLen = 0; /* Allocated length of warnBuff. */
479 : static sChar warnLevel = -1; /* Current warning level. */
480 : static uChar warnOutType = 0; /* Output type as set in myWarnSet. */
481 : static uChar warnDetail = 0; /* Detail level as set in myWarnSet. */
482 : static uChar warnFileDetail = 0; /* Detail level as set in myWarnSet. */
483 : static FILE *warnFP = NULL; /* Warn File as set in myWarnSet. */
484 :
485 0 : static void _myWarn (uChar f_errCode, const char *fmt, va_list ap)
486 : {
487 0 : char *buff = NULL; /* Stores the message. */
488 0 : size_t buffLen = 0; /* Allocated length of buff. */
489 0 : uChar f_prepend = 0; /* Flag to prepend (or not) the message. */
490 0 : uChar f_filePrt = 1; /* Flag to print to file. */
491 0 : uChar f_memPrt = 1; /* Flag to print to memory. */
492 :
493 0 : if (fmt == NULL) {
494 0 : return;
495 : }
496 0 : if (f_errCode > 5) {
497 0 : f_errCode = 0;
498 : }
499 0 : if (f_errCode > 2) {
500 0 : f_errCode -= (uChar) 3;
501 0 : f_prepend = 1;
502 : }
503 : /* Update the warning level */
504 0 : if (f_errCode > warnLevel) {
505 0 : warnLevel = f_errCode;
506 : }
507 :
508 : /* Check if the warnDetail level allows this message. */
509 0 : if ((warnOutType >= 4) ||
510 0 : (warnDetail == 2) || ((warnDetail == 1) && (f_errCode < 2))) {
511 0 : f_memPrt = 0;
512 : }
513 0 : if ((warnOutType == 0) ||
514 0 : (warnFileDetail == 2) || ((warnFileDetail == 1) && (f_errCode < 2))) {
515 0 : if (!f_memPrt) {
516 0 : return;
517 : }
518 0 : f_filePrt = 0;
519 : }
520 :
521 0 : AllocSprintf (&buff, &buffLen, fmt, ap);
522 :
523 : /* Handle the file writing. */
524 0 : if (f_filePrt) {
525 0 : fprintf (warnFP, "%s", buff);
526 : }
527 : /* Handle the memory writing. */
528 0 : if (f_memPrt) {
529 0 : if (f_prepend) {
530 0 : if (warnBuffLen != 0) {
531 : /* Add warnBuff to end of buff, and free warnBuff. */
532 0 : buffLen += warnBuffLen;
533 : myAssert (sizeof (char) == 1);
534 0 : buff = (char *) realloc (buff, buffLen);
535 0 : strcat (buff, warnBuff);
536 0 : free (warnBuff);
537 : }
538 : /* Point warnBuff to buff. */
539 0 : warnBuff = buff;
540 0 : warnBuffLen = buffLen;
541 : } else {
542 0 : if (warnBuffLen == 0) {
543 0 : warnBuff = buff;
544 0 : warnBuffLen = buffLen;
545 : } else {
546 0 : warnBuffLen += buffLen;
547 : myAssert (sizeof (char) == 1);
548 0 : warnBuff = (char *) realloc (warnBuff, warnBuffLen);
549 0 : strcat (warnBuff, buff);
550 0 : free (buff);
551 : }
552 : }
553 : }
554 : }
555 :
556 : /*****************************************************************************
557 : * myWarn() -- Arthur Taylor / MDL
558 : *
559 : * PURPOSE
560 : * This does the transformation of the "..." parameters, and calls _myWarn.
561 : * This was broken out when we started to implement myWarnRet, so we had two
562 : * ways to call _myWarn. A complicated way (myWarnRet), and a simpler way
563 : * (myWarn). After creating the myWarnW# #defines, thought to depricate use
564 : * of myWarn by making it static. Still need it, because myWarnRet uses it.
565 : *
566 : * ARGUMENTS
567 : * f_errCode = 0 => append notation msg, 1 => append warning msg
568 : * 2 => append error msg, 3 => prepend notation msg
569 : * 4 => prepend warning msg, 5 => prepend error msg (Input)
570 : * fmt = Format to define how to print the msg (Input)
571 : * ... = The actual message arguments. (Input)
572 : *
573 : * RETURNS: void
574 : *
575 : * 12/2005 Arthur Taylor (MDL): Created.
576 : *
577 : * NOTES:
578 : *****************************************************************************
579 : */
580 0 : static void myWarn (uChar f_errCode, const char *fmt, ...)
581 : {
582 : va_list ap; /* Contains the data needed by fmt. */
583 :
584 : /* Create the message in buff. */
585 0 : va_start (ap, fmt); /* make ap point to 1st unnamed arg. */
586 0 : _myWarn (f_errCode, fmt, ap);
587 0 : va_end (ap); /* clean up when done. */
588 0 : }
589 :
590 : /*****************************************************************************
591 : * myWarnRet() -- Arthur Taylor / MDL
592 : *
593 : * PURPOSE
594 : * This does the transformation of the "..." parameters, and calls _myWarn.
595 : * This was created, so that the user could pass in where (file and line
596 : * number) the error took place, and get a uniform handling of the file and
597 : * line numbers. In addition the user could pass in a value for the procedure
598 : * to return, which allows the user to have something like:
599 : * "return myWarnW2 (-1, "foobar\n");"
600 : * Which after the #define is evaluated becomes:
601 : * "return myWarnRet (1, -1, __FILE__, __LINE__, "foobar\n");
602 : *
603 : * Without myWarnRet, one would need something like:
604 : * "myWarn (1, "(%s line %d) foobar\n", __FILE__, __LINE__);"
605 : * "return (-1);
606 : * Trying to come up with a #define to make that easier on the user was
607 : * difficult. The first attempt was:
608 : * #define myWarnLine myWarn(1, "(%s, line %d) " __FILE__, __LINE__); myWarn
609 : * but this had difficulties with "if () myWarnLine" since it became two
610 : * statements, which could confuse the use of {}. A better solition was:
611 : * #define myWarnLineW1(f) myWarnLine (1, __FILE__, __LINE__, f)
612 : * Particularly since the user didn't have to remember that Warn is flag of 1,
613 : * and error is flag of 2. Since I already had to create myWarnW# #defines,
614 : * it was easy to add the user specified return values.
615 : *
616 : * ARGUMENTS
617 : * f_errCode = 0 => append notation msg, 1 => append warning msg
618 : * 2 => append error msg, 3 => prepend notation msg
619 : * 4 => prepend warning msg, 5 => prepend error msg (Input)
620 : * appErrCode = User defined error code for myWarnRet to return.
621 : * file = Filename that the call to myWarnRet was in. (Input)
622 : * If NULL, then it skips the __FILE__, __LINE__ print routine.
623 : * lineNum = Line number of call to myWarnRet. (Input)
624 : * fmt = Format to define how to print the msg (Input)
625 : * ... = The actual message arguments. (Input)
626 : *
627 : * RETURNS: int
628 : * The value of appErrCode.
629 : *
630 : * 12/2005 Arthur Taylor (MDL): Created.
631 : *
632 : * NOTES:
633 : * Is in "Quiet" mode if "file" is NULL (no __FILE__, __LINE__ prints)
634 : *****************************************************************************
635 : */
636 0 : int myWarnRet (uChar f_errCode, int appErrCode, const char *file,
637 : int lineNum, const char *fmt, ...)
638 : {
639 : va_list ap; /* Contains the data needed by fmt. */
640 :
641 0 : if (fmt != NULL) {
642 0 : if (file != NULL) {
643 0 : myWarn (f_errCode, "(%s, line %d) ", file, lineNum);
644 : }
645 : /* Create the message in buff. */
646 0 : va_start (ap, fmt); /* make ap point to 1st unnamed arg. */
647 0 : _myWarn (f_errCode, fmt, ap);
648 0 : va_end (ap); /* clean up when done. */
649 0 : } else if (file != NULL) {
650 0 : myWarn (f_errCode, "(%s, line %d)\n", file, lineNum);
651 : }
652 0 : return appErrCode;
653 : }
654 :
655 : /*****************************************************************************
656 : * myWarnSet() -- Arthur Taylor / MDL
657 : *
658 : * PURPOSE
659 : * This sets warnOutType, warnDetail, and warnFile for myWarn.
660 : *
661 : * ARGUMENTS
662 : * f_outType = 0 => memory, 1 => memory + stdout, 2 => memory + stderr,
663 : * 3 => memory + warnFile, 4 => stdout, 5 => stderr,
664 : * 6 => warnFile. (Input)
665 : * f_detail = 0 => report all, 1 => report errors, 2 => silent. (Input)
666 : * f_fileDetail = 0 => report all, 1 => report errors, 2 => silent. (Input)
667 : * warnFile = An already opened alternate file to log errors to. (Input)
668 : *
669 : * RETURNS: void
670 : *
671 : * 12/2005 Arthur Taylor (MDL): Created.
672 : *
673 : * NOTES:
674 : * The reason someone may want memory + warnFile is so that they can log the
675 : * errors in a logFile, but still have something come to stdout.
676 : *****************************************************************************
677 : */
678 0 : void myWarnSet (uChar f_outType, uChar f_detail, uChar f_fileDetail,
679 : FILE *warnFile)
680 : {
681 0 : if (f_outType > 6) {
682 0 : f_outType = 0;
683 : }
684 0 : if (f_detail > 2) {
685 0 : f_detail = 0;
686 : }
687 0 : warnOutType = f_outType;
688 0 : warnDetail = f_detail;
689 0 : warnFileDetail = f_fileDetail;
690 0 : if ((f_outType == 1) || (f_outType == 4)) {
691 0 : warnFP = stdout;
692 0 : } else if ((f_outType == 2) || (f_outType == 5)) {
693 0 : warnFP = stderr;
694 0 : } else if ((f_outType == 3) || (f_outType == 6)) {
695 0 : if (warnFile == NULL) {
696 0 : warnFP = stderr;
697 : } else {
698 0 : warnFP = warnFile;
699 : }
700 : } else {
701 0 : warnFP = NULL;
702 : }
703 0 : }
704 :
705 : /*****************************************************************************
706 : * myWarnClear() -- Arthur Taylor / MDL
707 : *
708 : * PURPOSE
709 : * This clears the warning stack, returns what is on there in msg, resets
710 : * the memory to NULL, and returns the error code.
711 : *
712 : * ARGUMENTS
713 : * msg = Whatever has been written to the warning memory
714 : * (NULL, or allocated memory) (Out)
715 : * f_closeFile = flag to close the warnFile or not (Input)
716 : *
717 : * RETURNS: sChar
718 : * -1 means no messages in msg (msg should be null)
719 : * 0 means upto notation msg in msg, but msg should not be null.
720 : * 1 means upto warning messages in msg, msg should not be null.
721 : * 2 means upto error messages in msg, msg should not be null.
722 : *
723 : * 12/2005 Arthur Taylor (MDL): Created.
724 : *
725 : * NOTES:
726 : *****************************************************************************
727 : */
728 0 : sChar myWarnClear (char **msg, uChar f_closeFile)
729 : {
730 : sChar ans;
731 :
732 0 : *msg = warnBuff;
733 0 : warnBuff = NULL;
734 0 : warnBuffLen = 0;
735 0 : ans = warnLevel;
736 0 : warnLevel = -1;
737 0 : if (f_closeFile) {
738 0 : fclose (warnFP);
739 : }
740 0 : return ans;
741 : }
742 :
743 : /*****************************************************************************
744 : * myWarnNotEmpty() -- Arthur Taylor / MDL
745 : *
746 : * PURPOSE
747 : * This returns whether the warning message is null or not.
748 : *
749 : * ARGUMENTS
750 : *
751 : * RETURNS: uChar
752 : * 0 => msg == null, 1 => msg != null
753 : *
754 : * 12/2005 Arthur Taylor (MDL): Created.
755 : *
756 : * NOTES:
757 : *****************************************************************************
758 : */
759 0 : uChar myWarnNotEmpty ()
760 : {
761 0 : return (uChar) ((warnBuff != NULL) ? 1 : 0);
762 : }
763 :
764 : /*****************************************************************************
765 : * myWarnLevel() -- Arthur Taylor / MDL
766 : *
767 : * PURPOSE
768 : * This returns the status of the warnLevel.
769 : *
770 : * ARGUMENTS
771 : *
772 : * RETURNS: sChar
773 : * -1 means no messages in msg (msg should be null)
774 : * 0 means upto notation msg in msg, but msg should not be null.
775 : * 1 means upto warning messages in msg, msg should not be null.
776 : * 2 means upto error messages in msg, msg should not be null.
777 : *
778 : * 12/2005 Arthur Taylor (MDL): Created.
779 : *
780 : * NOTES:
781 : *****************************************************************************
782 : */
783 0 : sChar myWarnLevel ()
784 : {
785 0 : return warnLevel;
786 : }
787 :
788 : #ifdef TEST_MYERROR
789 : /*****************************************************************************
790 : * The following 2 procedures are included only to test myerror.c, and only
791 : * if TEST_MYERROR is defined.
792 : *****************************************************************************
793 : */
794 :
795 : /*****************************************************************************
796 : * checkAns() -- Arthur Taylor / MDL (Review 12/2002)
797 : *
798 : * PURPOSE
799 : * To verify that a test gives the expected result.
800 : *
801 : * ARGUMENTS
802 : * ptr = The results of the test. (Input)
803 : * Ans = An array of correct answers. (Input)
804 : * test = Which test we are checking. (Input)
805 : *
806 : * RETURNS: void
807 : *
808 : * 9/2002 Arthur Taylor (MDL/RSIS): Created.
809 : * 12/2002 (RY,FC,MA,&TB): Code Review.
810 : *
811 : * NOTES
812 : *****************************************************************************
813 : */
814 : static void checkAns (char *ptr, char **Ans, int test)
815 : {
816 : if (ptr == NULL) {
817 : printf ("-----Check test (%d)--(ptr == NULL)-----\n", test);
818 : return;
819 : }
820 : if (strcmp (ptr, Ans[test]) != 0) {
821 : printf ("-----Failed test %d-------\n", test);
822 : printf ("%s %d =?= %s %d\n", ptr, strlen (ptr),
823 : Ans[test], strlen (Ans[test]));
824 : } else {
825 : printf ("passed test %d\n", test);
826 : }
827 : }
828 :
829 : /*****************************************************************************
830 : * main() -- Arthur Taylor / MDL (Review 12/2002)
831 : *
832 : * PURPOSE
833 : * To test reallocSprint, mallocSprint, and errSprintf, to make sure that
834 : * they pass certain basic tests. I will be adding more tests, as more bugs
835 : * are found, and features added.
836 : *
837 : * ARGUMENTS
838 : * argc = The number of arguments on the command line. (Input)
839 : * argv = The arguments on the command line. (Input)
840 : *
841 : * RETURNS: int
842 : *
843 : * 9/2002 Arthur Taylor (MDL/RSIS): Created.
844 : * 12/2002 (RY,FC,MA,&TB): Code Review.
845 : *
846 : * NOTES
847 : *****************************************************************************
848 : */
849 : int main (int argc, char **argv)
850 : {
851 : char *ptr;
852 : uChar warn;
853 : static char *Cmd[] = { "configure", "inquire", "convert", NULL };
854 : sInt4 li_temp = 100000L;
855 : short int sect = 5;
856 : char varName[] = "Helium is a gas";
857 : sInt4 lival = 22;
858 : char unit[] = "km", sval[] = "ans";
859 : double dval = 2.71828;
860 :
861 : char *buffer = NULL;
862 : short int ssect = 0;
863 : char vvarName[] = "DataType";
864 : sInt4 llival = 0;
865 : char ssval[] = "Meteorological products";
866 :
867 : static char *Ans[] = { "S0 | DataType | 0 (Meteorological products)\n",
868 : "<testing>", "<05><3.1415><D><20>",
869 : "<configure,inquire,convert> ?options?",
870 : "100000", "25.123", "02s", "01234567890123456789012345",
871 : "25.123,05, hello world",
872 : "This is a test 5... Here I am\n",
873 : "Parse error Section 0\nErrorERROR: Problems opening c:--goober for "
874 : "write.Projection code requires Earth with Rad = 6367.47 not "
875 : "6400.010000",
876 : "ERROR IS1 not labeled correctly. 5000000\n"
877 : "Should be 1196575042 2 25\nERROR IS0 has unexpected values: "
878 : "100000 100000 100000\n",
879 : "S5 | Helium is a gas | 22 (ans)\nS5 | Helium is a gas | 22\n"
880 : "S5 | Helium is a gas | 22 (ans (km))\nS5 | Helium is a gas | ans\n"
881 : "S5 | Helium is a gas | 2.718280\nS5 | Helium is a gas | "
882 : "2.718280 (km)\n",
883 : "ERROR IS1 not labeled correctly. 5000000\n"
884 : "Should be 1196575042 2 25\nERROR IS0 has unexpected values: "
885 : "100000 100000 100000\n",
886 : "5.670000e+001"
887 : };
888 :
889 : /* Test -2. (See if it can handle blank). */
890 : mallocSprintf (&ptr, "");
891 : free (ptr);
892 : ptr = NULL;
893 :
894 : mallocSprintf (&ptr, " ");
895 : free (ptr);
896 : ptr = NULL;
897 :
898 :
899 : /* Test -1. (see if checkAns is ok) */
900 : ptr = errSprintf (NULL);
901 : checkAns (ptr, Ans, -1);
902 :
903 : /* Test 0 */
904 : reallocSprintf (&buffer, "S%d | %s | %ld (%s)\n", ssect, vvarName,
905 : llival, ssval);
906 : checkAns (buffer, Ans, 0);
907 : free (buffer);
908 :
909 : /* Test 1. */
910 : ptr = NULL;
911 : reallocSprintf (&ptr, "<testing>");
912 : checkAns (ptr, Ans, 1);
913 : free (ptr);
914 :
915 : /* Test 2. */
916 : ptr = NULL;
917 : reallocSprintf (&ptr, "<%02d><%.4f><%D><%ld>", 5, 3.1415, 20, 24);
918 : checkAns (ptr, Ans, 2);
919 : free (ptr);
920 :
921 : /* Test 3. */
922 : ptr = NULL;
923 : reallocSprintf (&ptr, "<%S> ?options?", Cmd);
924 : checkAns (ptr, Ans, 3);
925 : free (ptr);
926 :
927 : /* Test 4. */
928 : ptr = NULL;
929 : reallocSprintf (&ptr, "%ld", li_temp);
930 : checkAns (ptr, Ans, 4);
931 : free (ptr);
932 :
933 : /* Test 5. */
934 : ptr = NULL;
935 : reallocSprintf (&ptr, "%.3f", 25.1234);
936 : checkAns (ptr, Ans, 5);
937 : free (ptr);
938 :
939 : /* Test 6. */
940 : ptr = NULL;
941 : reallocSprintf (&ptr, "%02s", 25.1234);
942 : checkAns (ptr, Ans, 6);
943 : free (ptr);
944 :
945 : /* Test 7. */
946 : ptr = NULL;
947 : reallocSprintf (&ptr, "%01234567890123456789012345");
948 : checkAns (ptr, Ans, 7);
949 : free (ptr);
950 :
951 : /* Test 8. */
952 : mallocSprintf (&ptr, "%.3f", 25.1234);
953 : reallocSprintf (&ptr, ",%02d", 5);
954 : reallocSprintf (&ptr, ", %s", "hello world");
955 : checkAns (ptr, Ans, 8);
956 : free (ptr);
957 : ptr = NULL;
958 :
959 : /* Test 9. */
960 : errSprintf ("This is a test %d... ", 5);
961 : errSprintf ("Here I am\n");
962 : ptr = errSprintf (NULL);
963 : checkAns (ptr, Ans, 9);
964 : free (ptr);
965 :
966 : /* Test 10. */
967 : errSprintf ("Parse error Section 0\n%s", "Error");
968 : errSprintf ("ERROR: Problems opening %s for write.", "c:--goober");
969 : errSprintf ("Projection code requires Earth with Rad = 6367.47 not %f",
970 : 6400.01);
971 : ptr = errSprintf (NULL);
972 : checkAns (ptr, Ans, 10);
973 : free (ptr);
974 :
975 : /* Test 11. */
976 : errSprintf ("ERROR IS1 not labeled correctly. %ld\n", 5000000L);
977 : errSprintf ("Should be %ld %d %ld\n", 1196575042L, 2, 25);
978 : errSprintf ("ERROR IS0 has unexpected values: %ld %ld %ld\n", li_temp,
979 : li_temp, li_temp);
980 : ptr = errSprintf (NULL);
981 : checkAns (ptr, Ans, 11);
982 : free (ptr);
983 :
984 : /* Test 12. */
985 : ptr = NULL;
986 : reallocSprintf (&ptr, "S%d | %s | %ld (%s)\n", sect, varName, lival, sval);
987 : reallocSprintf (&ptr, "S%d | %s | %ld\n", sect, varName, lival);
988 : reallocSprintf (&ptr, "S%d | %s | %ld (%s (%s))\n", sect, varName, lival,
989 : sval, unit);
990 : reallocSprintf (&ptr, "S%d | %s | %s\n", sect, varName, sval);
991 : reallocSprintf (&ptr, "S%d | %s | %f\n", sect, varName, dval);
992 : reallocSprintf (&ptr, "S%d | %s | %f (%s)\n", sect, varName, dval, unit);
993 : checkAns (ptr, Ans, 12);
994 : free (ptr);
995 :
996 : /* Test 13. */
997 : preErrSprintf ("Should be %ld %d %ld\n", 1196575042L, 2, 25);
998 : errSprintf ("ERROR IS0 has unexpected values: %ld %ld %ld\n", li_temp,
999 : li_temp, li_temp);
1000 : preErrSprintf ("ERROR IS1 not labeled correctly. %ld\n", 5000000L);
1001 : ptr = errSprintf (NULL);
1002 : checkAns (ptr, Ans, 13);
1003 : free (ptr);
1004 :
1005 : /* Test 14. */
1006 : ptr = NULL;
1007 : reallocSprintf (&ptr, "%e", 56.7);
1008 : checkAns (ptr, Ans, 14);
1009 : free (ptr);
1010 :
1011 : myWarnSet (1, 0, 1, NULL);
1012 : myWarnW2 (0, "This is a test of Warn\n");
1013 : myWarnE2 (0, "This is a test of Err\n");
1014 : myWarnQ2 (0, "This is a quiet note\n");
1015 : myWarnPW2 (0, "This is a test2 of Error\n");
1016 : myWarnW4 (0, "This is a test of WarnLnW3 %d %d\n", 10, 20);
1017 : myWarnE3 (0, "This is a test of WarnLnE2 %d\n", 10);
1018 : printf ("\tTest myWarnRet: %d\n", myWarnW1 (-1));
1019 : printf ("\tTest myWarnRet: %d\n", myWarnE2 (-2, "Hello nurse\n"));
1020 : if (myWarnNotEmpty ()) {
1021 : ptr = NULL;
1022 : warn = myWarnClear (&ptr, 0);
1023 : printf ("WarnLevel=%d\n%s", warn, ptr);
1024 : free (ptr);
1025 : }
1026 :
1027 : return 0;
1028 : }
1029 : #endif
|