1 /*
2 * $Id: ErrLog.c 191 2007-03-30 23:26:38Z boote $
3 */
4 /************************************************************************
5 * *
6 * Copyright (C) 2002 *
7 * Internet2 *
8 * All Rights Reserved *
9 * *
10 ************************************************************************/
11 /*
12 * File: ErrLog.c
13 *
14 * Author: Jeff Boote
15 * Internet2
16 *
17 * Date: Tue Apr 23 09:11:05 2002
18 *
19 * Description:
20 * Generic error logging routines.
21 *
22 *
23 * Based on code from UCAR DCS tools. Copyright information
24 * from UCAR follows:
25 *
26 * Copyright 1997 University Corporation for Atmospheric Research,
27 * Scientific Computing Division. All rights reserved.
28 *
29 *
30 * Permission to use, copy, modify and distribute this software
31 * and its documentation for any academic, educational and
32 * scientific research purpose is hereby granted without fee,
33 * provided that the above copyright notice and this permission
34 * notice appear in all copies of this software and its
35 * documentation, and that the software is not sold and/or made
36 * the subject of any commercial activity. Parties interested
37 * in commercial licensing should contact the copyright holder.
38 *
39 */
40 #include <I2util/utilP.h>
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <string.h>
46 #include <errno.h>
47 #include "mach_dep.h"
48
49
50 /*
51 * May support threading in future.
52 */
53 static I2ThreadMutex_T MyMutex = I2PTHREAD_MUTEX_INITIALIZER;
54
55 #define TABLE_SIZE 10
56 #define MSG_BUF_SIZE 10240
57
58 /*
59 * an error table. Error table[0] contains sys_errlist.
60 */
61 typedef struct ErrTable_ {
62 unsigned start, /* starting index for err_list */
63 num; /* num elements in err_list */
64 const char **err_list; /* error messags */
65 } ErrTable;
66
67
68 /*
69 * static variables that contain the file name and line number of the most
70 * recently logged error via a call to I2ErrLocation_().
71 *
72 * N.B. I2ErrLocation_() should never be called directly by a client
73 * of the error module. I2ErrLocation_() is invoked via the macro call
74 * I2ErrLog() which calls I2ErrLocation_() and then immediately calls
75 * I2ErrLogFunction_(). This enables multiple instances of an error
76 * logging session w/out having to maintain the current file and line
77 * number as a separate state variable for each session with some degree
78 * of security. In a threaded environment access to these variables is
79 * locked by I2ErrLocation_() and unlocked by I2ErrLogFunction_(). All of
80 * this is necessary because the only way to automatically generate the
81 * line number and file name information automatically is via and macro
82 * call (as opposed to a function call) and macros don't accept variable
83 * number of arguments
84 */
85 static const char *errorFile = "";
86 static const char *errorDate = "";
87 static int errorLine = 0;
88
89
90 /*
91 * An error handle struct. This struct maintains the state of an
92 * error reporting session created via I2ErrOpen()
93 */
94 typedef struct ErrHandle_ {
95 ErrTable err_tab[TABLE_SIZE]; /* all the error tables */
96 int err_tab_num; /* num error tables */
97 int code; /* error code of last error */
98 const char *program_name; /* name of calling prog */
99 void *data; /* client data */
100 I2ErrLogFuncPtr log_func; /* client loggin func */
101 void *log_func_arg; /* client arg to log func*/
102 I2ErrRetrieveFuncPtr retrieve_func; /* client fetch func */
103 void *retrieve_func_arg; /* client arg to retrieve func*/
104 I2ErrLogResetFuncPtr reset_func; /* reset log_func */
105 } ErrHandle;
106
107 /*
108 * fetch an error message associated with a given error code. Return
109 * an empty string if no matching error message is found.
110 */
get_error(ErrHandle * eh,int error)111 static const char *get_error(ErrHandle *eh, int error)
112 {
113 int i;
114 unsigned int index;
115 unsigned int errnum;
116
117 if(error < 0)
118 return("");
119 errnum = (unsigned int)error;
120
121 if(!eh){
122 return strerror(errnum);
123 }
124
125 for (i=0; i<eh->err_tab_num; i++) {
126 if(eh->err_tab[i].start > errnum)
127 continue;
128 index = errnum - eh->err_tab[i].start;
129 if ((unsigned)errnum >= eh->err_tab[i].start &&
130 index < eh->err_tab[i].num) {
131
132 return(eh->err_tab[i].err_list[index]);
133 }
134 }
135
136 return("");
137 }
138
139 /*
140 * format an error message converting any `%M' tokens to the error
141 * string associated with error message number `code, and any
142 * `%N' tokens to the ascii representation of the value of `code
143 *
144 * `buf' must have enough space to hold the new string
145 */
esnprintf(ErrHandle * eh,char * buf,size_t size_buf,const char * format,int code)146 static int esnprintf(
147 ErrHandle *eh,
148 char *buf,
149 size_t size_buf,
150 const char *format,
151 int code
152 ) {
153 const char *p1;
154 char *p2;
155 size_t size_p2 = size_buf;
156 int len;
157
158
159 for(p1=format, p2=buf; *p1 && (size_p2 > 1); p1++, p2++, size_p2--) {
160 if (*p1 != '%') {
161 *p2 = *p1;
162 continue;
163 }
164 p1++;
165 if (*p1 == 'M') {
166 len =snprintf(p2,size_p2,"%s",get_error(eh,code))-1;
167 p2 += len;
168 size_p2 -= len;
169 }
170 else if (*p1 == 'N') {
171 len = snprintf(p2,size_p2,"%d", code) - 1;
172 p2 += len;
173 size_p2 -= len;
174 }
175 else {
176 *p2++ = '%';
177 size_p2--;
178 *p2 = *p1;
179 }
180 }
181 *p2 = '\0';
182
183 return(strlen(buf));
184 }
185
add_ansiC_errors(ErrHandle * eh)186 void add_ansiC_errors(ErrHandle *eh)
187 {
188 char **sys_errlist; /* list of errors */
189 int errlist_size; /* size of $sys_errlist */
190
191 /*
192 ** Get the list of system errors supported on this platform
193 */
194 sys_errlist = I2GetSysErrList(&errlist_size);
195
196 eh->err_tab_num = 0;
197 (void) I2ErrList(
198 (I2ErrHandle) eh, 0, errlist_size, (const char **) sys_errlist);
199 }
200
201 /*
202 **
203 ** A P I
204 **
205 */
206
207 /*
208 * Function: I2ErrOpen()
209 *
210 * Description: The I2ErrOpen() function starts an error reporting session
211 * and returns an error handle associated with that session.
212 *
213 * The `log_func' argument is a pointer to a logging function
214 * that is to be called whenever an error is logged with the
215 * I2ErrLog() macro. This function may be user-defined or may
216 * be one of the pre-defined functions (I2LogFuncStack_()
217 * or I2LogFuncImmediate_()).
218 *
219 * The `retrieve_func' argument is a pointer to a error
220 * log retrieval function that is invoked to fetch error
221 * messages logged with the function pointed to by
222 * `log_func'. `retrive_func' may be NULL if no such
223 * function exists.
224 *
225 * In Args:
226 *
227 * *program_name A pointer to a character string specifying the
228 * name of the calling program (or any other piece
229 * of information you might wish).
230 *
231 * *log_func A pointer to a user-defined error logging function
232 * as described below.
233 *
234 * *log_func_arg A pointer to user-defined data that is to be passed
235 * to the logging function whenever the logging
236 * function is invoked.
237 *
238 * *retrieve_func A pointer to a user-defined error retrieval function
239 * as described below.
240 *
241 * *ret_func_arg A pointer to user-defined data that is to be passed
242 * to the retrieval function whenever the retrieval
243 * function is invoked.
244 *
245 *
246 * Out Args:
247 *
248 * Return Values: An error handle is returned upon success. Otherwise
249 * NULL is returned and the global variable `errno'
250 * is set accordingly
251 *
252 * Side Effects:
253 *
254 *
255 * The Logging Function
256 *
257 * The logging function pointed to by `log_func' is called indirectly
258 * by the I2ErrLog() macro to log error messages. The `program_name'
259 * parameter points to the array pointed to by the parameter of the
260 * same name passed to I2ErrOpen(). The logging function is also provided
261 * provided with the name of the file from whence I2ErrOpen() was
262 * called, `file', as well as the line number, `line', the date the
263 * file was compiled, `date', the error message to be logged, `msg',
264 * the user-defined logging function argument passed to I2ErrOpen(),
265 * `arg', and a hook for any state context the logging function
266 * needs to maintain, `data'.
267 *
268 * In Args:
269 *
270 * *program_name A pointer to the character string passed to
271 * I2ErrOpen() as its first argument.
272 *
273 * *file A pointer to a string containing the name of the
274 * file from whence I2ErrLog() was called.
275 *
276 * line An integer indicating the line number within
277 * `file' from whence I2ErrLog() was called.
278 *
279 * *date A pointer to a string containing the date (of the
280 * file from which I2ErrLog() was called) was compiled.
281 *
282 * *msg A pointer to string containing the error message
283 * to be logged.
284 *
285 * *arg A pointer to the `log_func_arg' argument passed to
286 * I2ErrOpen()
287 *
288 * **data A pointer to a pointer to be used by the logging
289 * function in any desired manner. `*data' is NULL
290 * unless changed by the logging function
291 *
292 * The Retrieval Function
293 *
294 * The retrieval function pointed to by `retrieve_func' is invoked
295 * whenever the error messages logged by the logging function are
296 * to be retrieved. The retrieval function is passed the same hook,
297 * `data', for maintaining state information as the logging function,
298 * and the user-defined argument, `arg', passed to the I2ErrOpen()
299 * function as `retreive_func_arg'.
300 *
301 * The retrieval function should return a pointer to an area of
302 * dynamically allocated memory containing a character string
303 * representing the error messages logged with previous calls
304 * to the logging function. The caller will free the memory
305 * returned by the retrieval function.
306 *
307 * In Args
308 *
309 * *arg A pointer to the `retrieve_func_arg' argument passed to
310 * I2ErrOpen()
311 *
312 * **data A pointer to a pointer to be used by the retrieval
313 * function in any desired manner. `*data' is NULL
314 * unless changed by the logging function
315 *
316 */
I2ErrOpen(const char * program_name,I2ErrLogFuncPtr log_func,void * log_func_arg,I2ErrRetrieveFuncPtr retrieve_func,void * retrieve_func_arg)317 I2ErrHandle I2ErrOpen(
318 const char *program_name,
319 I2ErrLogFuncPtr log_func,
320 void *log_func_arg,
321 I2ErrRetrieveFuncPtr retrieve_func,
322 void *retrieve_func_arg
323 ) {
324
325 ErrHandle *eh;
326
327 if (! log_func) {
328 errno = EINVAL;
329 return(NULL);
330 }
331
332 if (! (eh = malloc(sizeof(ErrHandle)))) {
333 return(NULL);
334 }
335
336 eh->code = 0;
337 eh->program_name = program_name;
338 eh->log_func = log_func;
339 eh->log_func_arg = log_func_arg;
340 eh->retrieve_func = retrieve_func;
341 eh->retrieve_func_arg = retrieve_func_arg;
342 eh->data = NULL;
343
344
345 /*
346 * add the Standard C Library error messages
347 */
348 add_ansiC_errors(eh);
349
350 return((I2ErrHandle) eh);
351 }
352
353 /*
354 * Function: I2ErrSetResetFunc
355 *
356 * Description:
357 * Used to assign the "reset" function. Not used, so it
358 * was not useful to add to the "open". (Plus, it would
359 * be hard to make "open" backward compat.)
360 *
361 * In Args:
362 *
363 * Out Args:
364 *
365 * Scope:
366 * Returns:
367 * Side Effect:
368 */
I2ErrSetResetFunc(I2ErrHandle dpeh,I2ErrLogResetFuncPtr reset_func)369 void I2ErrSetResetFunc(
370 I2ErrHandle dpeh,
371 I2ErrLogResetFuncPtr reset_func
372 )
373 {
374 ErrHandle *eh = (ErrHandle *) dpeh;
375
376 eh->reset_func = reset_func;
377
378 return;
379 }
380
381 /*
382 * Function: I2ErrReset
383 *
384 * Description:
385 * If there is a "reset_func" assigned to the ErrHandle,
386 * call it.
387 *
388 * In Args:
389 *
390 * Out Args:
391 *
392 * Scope:
393 * Returns:
394 * Side Effect:
395 */
396 I2ErrHandle
I2ErrReset(I2ErrHandle dpeh)397 I2ErrReset(
398 I2ErrHandle dpeh
399 )
400 {
401 ErrHandle *eh = (ErrHandle *) dpeh;
402
403 if(eh->reset_func){
404 if(!(eh->reset_func(eh->log_func_arg, &(eh->data)))){
405 I2ErrClose(dpeh);
406 return NULL;
407 }
408 }
409
410 return dpeh;
411 }
412
413 /*
414 * Function: I2ErrClose()
415 *
416 * Description: This function closes the error handling session associated
417 * with `dpeh'.
418 *
419 * In Args:
420 *
421 * dpeh An error handle returned via a call to I2ErrOpen().
422 *
423 * Out Args:
424 *
425 * Return Values:
426 *
427 * Side Effects:
428 */
I2ErrClose(I2ErrHandle dpeh)429 void I2ErrClose(I2ErrHandle dpeh)
430 {
431 ErrHandle *eh = (ErrHandle *) dpeh;
432
433 if (eh) free(eh);
434 }
435
436 /*
437 * Function: I2ErrRep()
438 *
439 * Description: The I2ErrRep() function calls the client retrieval function
440 * (if one exists) passed to I2ErrOpen() for the error
441 * handling session associated with `dpeh'. I2ErrRep() copies
442 * the program name, followed by a ":", followed by a space,
443 * followed by the character string returned by the client
444 * retrieval function, followed by a newline, to the file
445 * pointer, `fp'. The character string returned by the
446 * retrieval function is then freed with a call to free().
447 *
448 * In Args:
449 *
450 * dpeh An error handle returned via a call to I2ErrOpen().
451 *
452 * fp A pointer to a file stream.
453 *
454 * Out Args:
455 *
456 * Return Values:
457 *
458 * Side Effects:
459 */
I2ErrRep(I2ErrHandle dpeh,FILE * fp)460 void I2ErrRep(
461 I2ErrHandle dpeh,
462 FILE *fp
463 ) {
464 ErrHandle *eh = (ErrHandle *) dpeh;
465 char *msg;
466
467
468 if (! eh) return;
469
470 I2ThreadMutexLock(&MyMutex);
471
472 if (eh->retrieve_func) {
473 msg = eh->retrieve_func(eh->retrieve_func_arg, &(eh->data));
474 (void) fprintf(fp, "%s:\n", eh->program_name);
475 (void) fprintf(fp, "%s\n", msg);
476 free(msg);
477 }
478 I2ThreadMutexUnlock(&MyMutex);
479
480 }
481
482 /*
483 * Function: I2ErrGetMsg()
484 *
485 * Description: The I2ErrGetMsg() function calls the client retrieval function
486 * (if one exists) passed to I2ErrOpen() for the error
487 * handling session associated with `dpeh'. I2ErrGetMsg() returns
488 * to the caller the value returned to it by the client
489 * retrieval function (an address in dynamically allocated memory
490 * of a character string). The caller is responsible for freeing
491 * the memory returned by I2ErrGetMsg().
492 *
493 * In Args:
494 *
495 * dpeh An error handle returned via a call to I2ErrOpen().
496 *
497 * Out Args:
498 *
499 * Return Values: I2ErrGetMsg() returns NULL if no retrieval function
500 * exists.
501 *
502 * Side Effects:
503 */
I2ErrGetMsg(I2ErrHandle dpeh)504 char *I2ErrGetMsg(
505 I2ErrHandle dpeh
506 ) {
507 ErrHandle *eh = (ErrHandle *) dpeh;
508 char *msg;
509
510 I2ThreadMutexLock(&MyMutex);
511
512 if (eh && eh->retrieve_func) {
513 msg = eh->retrieve_func(eh->retrieve_func_arg, &(eh->data));
514 }
515 else {
516 msg = NULL;
517 }
518
519 I2ThreadMutexUnlock(&MyMutex);
520
521 return(msg);
522 }
523
524
525
526 /*
527 * Function: I2ErrGetCode()
528 *
529 * Description: The I2ErrGetCode() function returns the error number currently
530 * associated with the error handle, `dpeh'.
531 *
532 * N.B. the error code returned is the error code stored from
533 * the most *recent* call to I2ErrLog() or I2ErrLogP(). If
534 * you are nesting calls to these functions only the error
535 * code from the outer-most logging function is available - i.e
536 * inner calls get overwritten.
537 *
538 * In Args:
539 *
540 * dpeh An error handle returned via a call to I2ErrOpen().
541 *
542 * Out Args:
543 *
544 * Return Values: I2ErrGetCode() returns an integer.
545 *
546 * Side Effects:
547 */
I2ErrGetCode(I2ErrHandle dpeh)548 int I2ErrGetCode(
549 I2ErrHandle dpeh
550 ) {
551 ErrHandle *eh = (ErrHandle *) dpeh;
552
553 return(eh->code);
554 }
555
556
557
558
559 /*
560 * I2ErrList()
561 *
562 * Adds an error list to the error table. 'start' should be the first
563 * valid error number for this table. The values 0 - 1000 are reserved.
564 * The index into 'err_list' is calculated by subtracting 'start'
565 * from the error number. Thus if you add a list with 'start' equal
566 * to 1001 and later invoke ESprintf with 'code' equal to 1001 the
567 * error message referenced will be 'err_list[0]'
568 *
569 * on entry
570 * start : first valid error number
571 * num : number of elements in 'err_list'.
572 * **err_list : address of error list
573 *
574 * on exit
575 * return : -1 => table full, else OK.
576 */
I2ErrList(I2ErrHandle dpeh,unsigned start,unsigned num,const char ** err_list)577 int I2ErrList(
578 I2ErrHandle dpeh,
579 unsigned start,
580 unsigned num,
581 const char **err_list
582 ) {
583
584 ErrHandle *eh = (ErrHandle *) dpeh;
585
586 if (! eh) return(0);
587
588 if (eh->err_tab_num >= TABLE_SIZE -1) {
589 return(-1); /* table full */
590 }
591
592 eh->err_tab[eh->err_tab_num].start = start;
593 eh->err_tab[eh->err_tab_num].num = num;
594 eh->err_tab[eh->err_tab_num].err_list = err_list;
595 eh->err_tab_num++;
596
597 return(0);
598 }
599
600
601
602 /*
603 * Function: I2ErrLocation_
604 *
605 * Description: The I2ErrLocation_() records the file name, `file', line
606 * number, `line', and compilation date, `date' reported by
607 * the I2ErrLog() macro. This function should never be
608 * called directly by the user himself.
609 *
610 * In Args:
611 *
612 * *file A pointer to a character string containing a file
613 * name.
614 *
615 * *date A pointer to a character string containing the
616 * compiliation date of `file'
617 *
618 * line An integer specifying a line number within the
619 * file, `file'.
620 *
621 * Out Args:
622 *
623 * Return Values:
624 *
625 * Side Effects:
626 *
627 * I2ErrLocation_() writes the global, static variables,
628 * `errorFile', `errorDate', and `errorLine'. In order to
629 * be thread-save this function should lock these variables
630 * before attemping to write them (it does if `THREADS_ENABLE'
631 * is defined). The variables should then
632 * remain locked until unlocked by I2ErrLogFunction() or
633 * I2ErrLogPFunction().
634 */
I2ErrLocation_(const char * file,const char * date,int line)635 void I2ErrLocation_(
636 const char *file,
637 const char *date,
638 int line
639 ) {
640
641 I2ThreadMutexLock(&MyMutex);
642
643 errorFile = file;
644 errorDate = date;
645 errorLine = line;
646 }
647
648 /*
649 * Function: I2ErrLogPFunction_()
650 *
651 * Description: The I2ErrLogPFunction() is identical to the I2ErrLogFunction()
652 * except that it is invoked by the I2LogPErr() macro which
653 * takes an additional error code argument that is passed
654 * on to I2ErrLogPFunction_(), `code'
655 *
656 * `code' may be any valid value of the system global
657 * variable, `errno', or it may be an error code made valid
658 * by a call to I2ErrList().
659 *
660 *
661 *
662 * In Args:
663 *
664 * dpeh An error handle returned via a call to I2ErrOpen().
665 *
666 * code An integer indicating the error number.
667 *
668 * *format A pointer to a character string containing formating
669 * information.
670 *
671 * ... variable arguments as indicated in `format'
672 *
673 * Out Args:
674 *
675 * Return Values:
676 *
677 * Side Effects:
678 */
679 void
I2ErrLogVT(I2ErrHandle dpeh,int level,int code,const char * format,va_list ap)680 I2ErrLogVT(
681 I2ErrHandle dpeh,
682 int level,
683 int code,
684 const char *format,
685 va_list ap
686 )
687 {
688 ErrHandle *eh = (ErrHandle *) dpeh;
689 char new_format[MSG_BUF_SIZE];
690 char buf[MSG_BUF_SIZE];
691 struct I2ErrLogEvent event;
692
693 if(level == I2LOG_NONE)
694 return;
695
696 event.mask = 0;
697
698 if(!code)
699 code = errno;
700 else{
701 event.code = code; event.mask |= I2CODE;
702 }
703
704 (void)esnprintf(eh,new_format,sizeof(new_format),format,code);
705
706 /*
707 * deal with variable args
708 */
709 (void) vsnprintf(buf,sizeof(buf),new_format,ap);
710
711 if(!eh){
712 fwrite(buf,sizeof(char),strlen(buf),stderr);
713 fwrite("\n",sizeof(char),1,stderr);
714 I2ThreadMutexUnlock(&MyMutex);
715 return;
716 }
717
718 eh->code = code;
719 event.name = eh->program_name; event.mask |= I2NAME;
720 event.file = errorFile; event.mask |= I2FILE;
721 event.line = errorLine; event.mask |= I2LINE;
722 event.date = errorDate; event.mask |= I2DATE;
723 event.msg = buf; event.mask |= I2MSG;
724
725 if(level > -1 && level < 8){
726 event.level = level; event.mask |= I2LEVEL;
727 }
728
729 eh->log_func(
730 &event, eh->log_func_arg, &(eh->data)
731 );
732 I2ThreadMutexUnlock(&MyMutex);
733
734 return;
735 }
736
737 void
I2ErrLogTFunction_(I2ErrHandle dpeh,int level,int code,const char * format,...)738 I2ErrLogTFunction_(
739 I2ErrHandle dpeh,
740 int level,
741 int code,
742 const char *format,
743 ...
744 )
745 {
746 va_list ap;
747
748 va_start(ap, format);
749 I2ErrLogVT(dpeh,level,code,format,ap);
750 va_end(ap);
751
752 return;
753 }
754
755 /*
756 * Function: I2ErrLogPFunction_()
757 *
758 * Description: The I2ErrLogPFunction() is identical to the I2ErrLogFunction()
759 * except that it is invoked by the I2LogPErr() macro which
760 * takes an additional error code argument that is passed
761 * on to I2ErrLogPFunction_(), `code'
762 *
763 * `code' may be any valid value of the system global
764 * variable, `errno', or it may be an error code made valid
765 * by a call to I2ErrList().
766 *
767 *
768 *
769 * In Args:
770 *
771 * dpeh An error handle returned via a call to I2ErrOpen().
772 *
773 * code An integer indicating the error number.
774 *
775 * *format A pointer to a character string containing formating
776 * information.
777 *
778 * ... variable arguments as indicated in `format'
779 *
780 * Out Args:
781 *
782 * Return Values:
783 *
784 * Side Effects:
785 */
I2ErrLogPFunction_(I2ErrHandle dpeh,int code,const char * format,...)786 void I2ErrLogPFunction_(
787 I2ErrHandle dpeh,
788 int code,
789 const char *format, ...
790 )
791 {
792 va_list ap;
793
794 va_start(ap, format);
795 I2ErrLogVT(dpeh,-1,code,format,ap);
796 va_end(ap);
797
798 return;
799 }
800
801 /*
802 * Function: I2ErrLogFunction_()
803 *
804 * Description: The I2ErrLogFunction() converts and formats its arguments
805 * under the control of `format'. I2ErrLogFunction_()
806 * then invokes the client-supplied logging function associated
807 * with `dpeh' and passes it the formatted argument string.
808 *
809 * `format' is a character string which accepts an identical
810 * syntax to that of Standard C's *printf family of functions.
811 * Additionaly, `format' may contain instances of the format
812 * specifier, "%M", which are replaced by the UNIX system
813 * error message associated with `errno', and instances of
814 * the specifier "%N", which are replaced with the ascii value
815 * of `errno'.
816 *
817 * I2ErrLogFunction() is never invoked directly by the user.
818 * Instead, it is invoked via the I2ErrLog() macro, which
819 * calls I2ErrLocation_() first, passing it the error location
820 * information.
821 *
822 *
823 *
824 * In Args:
825 *
826 * dpeh An error handle returned via a call to I2ErrOpen().
827 *
828 * *format A pointer to a character string containing formating
829 * information.
830 *
831 * ... variable arguments as indicated in `format'
832 *
833 * Out Args:
834 *
835 * Return Values:
836 *
837 * Side Effects:
838 */
I2ErrLogFunction_(I2ErrHandle dpeh,const char * format,...)839 void I2ErrLogFunction_(
840 I2ErrHandle dpeh,
841 const char *format, ...
842 )
843 {
844 va_list ap;
845
846 va_start(ap, format);
847 I2ErrLogVT(dpeh,-1,0,format,ap);
848 va_end(ap);
849
850 return;
851 }
852