1
/** \file report.c report function for noninteractive utilities
2
 *
3
 * For license terms, see the file COPYING in this directory.
4
 *
5
 * This code is distantly descended from the error.c module written by
6
 * David MacKenzie <djm@gnu.ai.mit.edu>.  It was redesigned and
7
 * rewritten by Dave Bodenstab, then redesigned again by ESR, then
8
 * bludgeoned into submission for SunOS 4.1.3 by Chris Cheyney
9
 * <cheyney@netcom.com>.  It works even when the return from
10
 * vprintf(3) is unreliable.
11
 */
12
13
#ifdef HAVE_CONFIG_H
14
# include "config.h"
15
#endif
16
#include <stdio.h>
17
#include <errno.h>
18
#include <string.h>
19
#if defined(HAVE_SYSLOG)
20
#include <syslog.h>
21
#endif
22
#include "i18n.h"
23
#include "fetchmail.h"
24
25
#if defined(HAVE_VPRINTF) || defined(HAVE_DOPRNT) || defined(_LIBC) || defined(HAVE_STDARG_H)
26
# if HAVE_STDARG_H
27
#  include <stdarg.h>
28
#  define VA_START(args, lastarg) va_start(args, lastarg)
29
# else
30
#  include <varargs.h>
31
#  define VA_START(args, lastarg) va_start(args)
32
# endif
33
#else
34
# define va_alist a1, a2, a3, a4, a5, a6, a7, a8
35
# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
36
#endif
37
38
#define MALLOC(n)	xmalloc(n)	
39
#define REALLOC(n,s)	xrealloc(n,s)	
40
41
/* Used by report_build() and report_complete() to accumulate partial messages.
42
 */
43
static unsigned int partial_message_size = 0;
44
static unsigned int partial_message_size_used = 0;
45
static char *partial_message;
46
static int partial_suppress_tag = 0;
47
48
static unsigned unbuffered;
49
static unsigned int use_syslog;
50
51
#ifdef _LIBC
52
/* In the GNU C library, there is a predefined variable for this.  */
53
54
# define program_name program_invocation_name
55
# include <errno.h>
56
57
#else
58
59
# if !HAVE_STRERROR && !defined(strerror)
60
char *strerror (int errnum)
61
{
62
    extern char *sys_errlist[];
63
    extern int sys_nerr;
64
65
    if (errnum > 0 && errnum <= sys_nerr)
66
	return sys_errlist[errnum];
67
    return GT_("Unknown system error");
68
}
69
# endif	/* HAVE_STRERROR */
70
#endif	/* _LIBC */
71
72
/* Print the program name and error message MESSAGE, which is a printf-style
73
   format string with optional args. */
74
/* VARARGS */
75
void
76
#ifdef HAVE_STDARG_H
77
report (FILE *errfp, const char *message, ...)
78
#else
79
report (FILE *errfp, message, va_alist)
80
     const char *message;
81
     va_dcl
82
#endif
83
{
84
#ifdef VA_START
85
    va_list args;
86
#endif
87
88
    /* If a partially built message exists, print it now so it's not lost.  */
89
    if (partial_message_size_used != 0)
90
    {
91
	partial_message_size_used = 0;
92
	report (errfp, GT_("%s (log message incomplete)\n"), partial_message);
93
    }
94
95
#if defined(HAVE_SYSLOG)
96
    if (use_syslog)
97
    {
98
	int priority;
99
100
#ifdef VA_START
101
	VA_START (args, message);
102
#endif
103
	priority = (errfp == stderr) ? LOG_ERR : LOG_INFO;
104
105
#ifdef HAVE_VSYSLOG
106
	vsyslog (priority, message, args);
107
#else
108
	{
109
	    char *a1 = va_arg(args, char *);
110
	    char *a2 = va_arg(args, char *);
111
	    char *a3 = va_arg(args, char *);
112
	    char *a4 = va_arg(args, char *);
113
	    char *a5 = va_arg(args, char *);
114
	    char *a6 = va_arg(args, char *);
115
	    char *a7 = va_arg(args, char *);
116
	    char *a8 = va_arg(args, char *);
117
	    syslog (priority, message, a1, a2, a3, a4, a5, a6, a7, a8);
118
	}
119
#endif
120
121
#ifdef VA_START
122
	va_end(args);
123
#endif
124
    }
125
    else /* i. e. not using syslog */
126
#endif
127
    {
128
	if ( *message == '\n' )
129
	{
130
	    fputc( '\n', errfp );
131
	    ++message;
132
	}
133
	if (!partial_suppress_tag)
134
		fprintf (errfp, "%s: ", program_name);
135
	partial_suppress_tag = 0;
136
137
#ifdef VA_START
138
	VA_START (args, message);
139
# if defined(HAVE_VPRINTF) || defined(_LIBC)
140
	vfprintf (errfp, message, args);
141
# else
142
	_doprnt (message, args, errfp);
143
# endif
144
	va_end (args);
145
#else
146
	fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
147
#endif
148
	fflush (errfp);
149
    }
150
}
151
152
/**
153
 * Configure the report module. The output is set according to
154
 * \a mode.
155
 */
156
void report_init(int mode /** 0: regular output, 1: unbuffered output, -1: syslog */)
157
{
158
    switch(mode)
159
    {
160
    case 0:			/* errfp, buffered */
161
    default:
162
	unbuffered = FALSE;
163
	use_syslog = FALSE;
164
	break;
165
166
    case 1:			/* errfp, unbuffered */
167
	unbuffered = TRUE;
168
	use_syslog = FALSE;
169
	break;
170
171
#ifdef HAVE_SYSLOG
172
    case -1:			/* syslogd */
173
	unbuffered = FALSE;
174
	use_syslog = TRUE;
175
	break;
176
#endif /* HAVE_SYSLOG */
177
    }
178
}
179
180
/* Build an report message by appending MESSAGE, which is a printf-style
181
   format string with optional args, to the existing report message (which may
182
   be empty.)  The completed report message is finally printed (and reset to
183
   empty) by calling report_complete().
184
   If an intervening call to report() occurs when a partially constructed
185
   message exists, then, in an attempt to keep the messages in their proper
186
   sequence, the partial message will be printed as-is (with a trailing 
187
   newline) before report() prints its message. */
188
/* VARARGS */
189
190
static void rep_ensuresize(void) {
191
    /* Make an initial guess for the size of any single message fragment.  */
192
    if (partial_message_size == 0)
193
    {
194
	partial_message_size_used = 0;
195
	partial_message_size = 2048;
196
	partial_message = (char *)MALLOC (partial_message_size);
197
    }
198
    else
199
	if (partial_message_size - partial_message_size_used < 1024)
200
	{
201
	    partial_message_size += 2048;
202
	    partial_message = (char *)REALLOC (partial_message, partial_message_size);
203
	}
204
}
205
206
#ifdef HAVE_STDARG_H
207
static void report_vbuild(const char *message, va_list args)
208
{
209
    int n;
210
211
    for ( ; ; )
212
    {
213
	/*
214
	 * args has to be initialized before every call of vsnprintf(), 
215
	 * because vsnprintf() invokes va_arg macro and thus args is 
216
	 * undefined after the call.
217
	 */
218
	n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used,
219
		       message, args);
220
221
	/* output error, f. i. EILSEQ */
222
	if (n < 0) break;
223
224
	if (n >= 0
225
	    && (unsigned)n < partial_message_size - partial_message_size_used)
226
        {
227
	    partial_message_size_used += n;
228
	    break;
229
	}
230
231
	partial_message_size += 2048;
232
	partial_message = (char *)REALLOC (partial_message, partial_message_size);
233
    }
234
}
235
#endif
236
237
void
238
#ifdef HAVE_STDARG_H
239
report_build (FILE *errfp, const char *message, ...)
240
#else
241
report_build (FILE *errfp, message, va_alist)
242
     const char *message;
243
     va_dcl
244
#endif
245
{
246
#ifdef VA_START
247
    va_list args;
248
#else
249
    int n;
250
#endif
251
252
    rep_ensuresize();
253
254
#if defined(VA_START)
255
    VA_START(args, message);
256
    report_vbuild(message, args);
257
    va_end(args);
258
#else
259
    for ( ; ; )
260
    {
261
	n = snprintf (partial_message + partial_message_size_used,
262
		      partial_message_size - partial_message_size_used,
263
		      message, a1, a2, a3, a4, a5, a6, a7, a8);
264
265
	/* output error, f. i. EILSEQ */
266
	if (n < 0) break;
267
268
	if (n >= 0
269
	    && (unsigned)n < partial_message_size - partial_message_size_used)
270
        {
271
	    partial_message_size_used += n;
272
	    break;
273
	}
274
275
	partial_message_size += 2048;
276
	partial_message = REALLOC (partial_message, partial_message_size);
277
    }
278
#endif
279
280
    if (unbuffered && partial_message_size_used != 0)
281
    {
282
	partial_message_size_used = 0;
283
	fputs(partial_message, errfp);
284
    }
285
}
286
287
void report_flush(FILE *errfp)
288
{
289
    if (partial_message_size_used != 0)
290
    {
291
	partial_message_size_used = 0;
292
	report(errfp, "%s", partial_message);
293
	partial_suppress_tag = 1;
294
    }
295
}
296
297
/* Complete a report message by appending MESSAGE, which is a printf-style
298
   format string with optional args, to the existing report message (which may
299
   be empty.)  The completed report message is then printed (and reset to
300
   empty.) */
301
/* VARARGS */
302
void
303
#ifdef HAVE_STDARG_H
304
report_complete (FILE *errfp, const char *message, ...)
305
#else
306
report_complete (FILE *errfp, message, va_alist)
307
     const char *message;
308
     va_dcl
309
#endif
310
{
311
#ifdef VA_START
312
    va_list args;
313
#endif
314
315
    rep_ensuresize();
316
317
#if defined(VA_START)
318
    VA_START(args, message);
319
    report_vbuild(message, args);
320
    va_end(args);
321
#else
322
    report_build(errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
323
#endif
324
325
    /* Finally... print it.  */
326
    partial_message_size_used = 0;
327
328
    if (unbuffered)
329
    {
330
	fputs(partial_message, errfp);
331
	fflush (errfp);
332
    }
333
    else
334
	report(errfp, "%s", partial_message);
335
}
336
337
/* Sometimes we want to have at most one error per line.  This
338
   variable controls whether this mode is selected or not.  */
339
static int error_one_per_line;
340
341
/* If errnum is nonzero, print its corresponding system error message. */
342
void
343
#ifdef HAVE_STDARG_H
344
report_at_line (FILE *errfp, int errnum, const char *file_name,
345
	       unsigned int line_number, const char *message, ...)
346
#else
347
report_at_line (FILE *errfp, errnum, file_name, line_number, message, va_alist)
348
     int errnum;
349
     const char *file_name;
350
     unsigned int line_number;
351
     const char *message;
352
     va_dcl
353
#endif
354
{
355
#ifdef VA_START
356
    va_list args;
357
#endif
358
359
    if (error_one_per_line)
360
    {
361
	static const char *old_file_name;
362
	static unsigned int old_line_number;
363
364
	if (old_line_number == line_number &&
365
	    (file_name == old_file_name || !strcmp (old_file_name, file_name)))
366
	    /* Simply return and print nothing.  */
367
	    return;
368
369
	old_file_name = file_name;
370
	old_line_number = line_number;
371
    }
372
373
    fflush (errfp);
374
    if ( *message == '\n' )
375
    {
376
	fputc( '\n', errfp );
377
	++message;
378
    }
379
    fprintf (errfp, "%s:", program_name);
380
381
    if (file_name != NULL)
382
	fprintf (errfp, "%s:%u: ", file_name, line_number);
383
384
#ifdef VA_START
385
    VA_START (args, message);
386
# if defined(HAVE_VPRINTF) || defined(_LIBC)
387
    vfprintf (errfp, message, args);
388
# else
389
    _doprnt (message, args, errfp);
390
# endif
391
    va_end (args);
392
#else
393
    fprintf (errfp, message, a1, a2, a3, a4, a5, a6, a7, a8);
394
#endif
395
396
    if (errnum)
397
	fprintf (errfp, ": %s", strerror (errnum));
398
    putc ('\n', errfp);
399
    fflush (errfp);
400
}