Add va_end to match va_copy (#74251).
[platform/upstream/rpm.git] / rpmio / rpmlog.c
1 /*@-boundsread@*/
2 /** \ingroup rpmio
3  * \file rpmio/rpmlog.c
4  */
5
6 #include "system.h"
7 #include <stdarg.h>
8 #include "rpmlog.h"
9 #include "debug.h"
10
11 #ifndef va_copy
12 # ifdef __va_copy
13 #  define va_copy(DEST,SRC) __va_copy((DEST),(SRC))
14 # else
15 #  ifdef HAVE_VA_LIST_AS_ARRAY
16 #   define va_copy(DEST,SRC) (*(DEST) = *(SRC))
17 #  else
18 #   define va_copy(DEST,SRC) ((DEST) = (SRC))
19 #  endif
20 # endif
21 #endif
22
23 /*@access rpmlogRec @*/
24
25 /*@unchecked@*/
26 static int nrecs = 0;
27 /*@unchecked@*/
28 static /*@only@*/ /*@null@*/ rpmlogRec recs = NULL;
29
30 /**
31  * Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
32  * @param p             memory to free
33  * @retval              NULL always
34  */
35 /*@unused@*/ static inline /*@null@*/ void *
36 _free(/*@only@*/ /*@null@*/ /*@out@*/ const void * p) /*@modifies p@*/
37 {
38     if (p != NULL)      free((void *)p);
39     return NULL;
40 }
41
42 int rpmlogGetNrecs(void)
43 {
44     return nrecs;
45 }
46
47 int rpmlogCode(void)
48 {
49     if (recs != NULL && nrecs > 0)
50         return recs[nrecs-1].code;
51     return -1;
52 }
53
54
55 const char * rpmlogMessage(void)
56 {
57     if (recs != NULL && nrecs > 0)
58         return recs[nrecs-1].message;
59     return _("(no error)");
60 }
61
62 /*@-modfilesys@*/
63 void rpmlogPrint(FILE *f)
64 {
65     int i;
66
67     if (f == NULL)
68         f = stderr;
69
70     if (recs)
71     for (i = 0; i < nrecs; i++) {
72         rpmlogRec rec = recs + i;
73         if (rec->message && *rec->message)
74             fprintf(f, "    %s", rec->message);
75     }
76 }
77 /*@=modfilesys@*/
78
79 void rpmlogClose (void)
80         /*@globals recs, nrecs @*/
81         /*@modifies recs, nrecs @*/
82 {
83     int i;
84
85     if (recs)
86     for (i = 0; i < nrecs; i++) {
87         rpmlogRec rec = recs + i;
88         rec->message = _free(rec->message);
89     }
90     recs = _free(recs);
91     nrecs = 0;
92 }
93
94 void rpmlogOpen (/*@unused@*/ const char *ident, /*@unused@*/ int option,
95                 /*@unused@*/ int facility)
96 {
97 }
98
99 /*@unchecked@*/
100 static unsigned rpmlogMask = RPMLOG_UPTO( RPMLOG_NOTICE );
101
102 /*@unchecked@*/
103 static /*@unused@*/ unsigned rpmlogFacility = RPMLOG_USER;
104
105 int rpmlogSetMask (int mask)
106         /*@globals rpmlogMask @*/
107         /*@modifies rpmlogMask @*/
108 {
109     int omask = rpmlogMask;
110     if (mask)
111         rpmlogMask = mask;
112     return omask;
113 }
114
115 /*@unchecked@*/
116 static /*@null@*/ rpmlogCallback _rpmlogCallback = NULL;
117
118 rpmlogCallback rpmlogSetCallback(rpmlogCallback cb)
119         /*@globals _rpmlogCallback @*/
120         /*@modifies _rpmlogCallback @*/
121 {
122     rpmlogCallback ocb = _rpmlogCallback;
123     _rpmlogCallback = cb;
124     return ocb;
125 }
126
127 /*@-readonlytrans@*/    /* FIX: double indirection. */
128 /*@observer@*/ /*@unchecked@*/
129 static char *rpmlogMsgPrefix[] = {
130     N_("fatal error: "),/*!< RPMLOG_EMERG */
131     N_("fatal error: "),/*!< RPMLOG_ALERT */
132     N_("fatal error: "),/*!< RPMLOG_CRIT */
133     N_("error: "),      /*!< RPMLOG_ERR */
134     N_("warning: "),    /*!< RPMLOG_WARNING */
135     "",                 /*!< RPMLOG_NOTICE */
136     "",                 /*!< RPMLOG_INFO */
137     "D: ",              /*!< RPMLOG_DEBUG */
138 };
139 /*@=readonlytrans@*/
140
141 #if !defined(HAVE_VSNPRINTF)
142 static inline int vsnprintf(char * buf, /*@unused@*/ int nb,
143         const char * fmt, va_list ap)
144 {
145     return vsprintf(buf, fmt, ap);
146 }
147 #endif
148
149 /*@-modfilesys@*/
150 /*@-compmempass@*/ /* FIX: rpmlogMsgPrefix[] dependent, not unqualified */
151 /*@-nullstate@*/ /* FIX: rpmlogMsgPrefix[] may be NULL */
152 static void vrpmlog (unsigned code, const char *fmt, va_list ap)
153         /*@globals nrecs, recs, internalState @*/
154         /*@modifies nrecs, recs, internalState @*/
155 {
156     unsigned pri = RPMLOG_PRI(code);
157     unsigned mask = RPMLOG_MASK(pri);
158     /*@unused@*/ unsigned fac = RPMLOG_FAC(code);
159     char *msgbuf, *msg;
160     int msgnb = BUFSIZ, nb;
161     FILE * msgout = stderr;
162
163     if ((mask & rpmlogMask) == 0)
164         return;
165
166 /*@-boundswrite@*/
167     msgbuf = xmalloc(msgnb);
168     *msgbuf = '\0';
169
170     /* Allocate a sufficently large buffer for output. */
171     while (1) {
172         va_list apc;
173         /*@-sysunrecog -usedef@*/ va_copy(apc, ap); /*@=sysunrecog =usedef@*/
174         nb = vsnprintf(msgbuf, msgnb, fmt, apc);
175         if (nb > -1 && nb < msgnb)
176             break;
177         if (nb > -1)            /* glibc 2.1 (and later) */
178             msgnb = nb+1;
179         else                    /* glibc 2.0 */
180             msgnb *= 2;
181         msgbuf = xrealloc(msgbuf, msgnb);
182         va_end(apc);
183     }
184     msgbuf[msgnb - 1] = '\0';
185     msg = msgbuf;
186
187     /* Save copy of all messages at warning (or below == "more important"). */
188     /*@-branchstate@*/
189     if (pri <= RPMLOG_WARNING) {
190
191         if (recs == NULL)
192             recs = xmalloc((nrecs+2) * sizeof(*recs));
193         else
194             recs = xrealloc(recs, (nrecs+2) * sizeof(*recs));
195         recs[nrecs].code = code;
196         recs[nrecs].message = msg = xrealloc(msgbuf, strlen(msgbuf)+1);
197         msgbuf = NULL;          /* XXX don't free at exit. */
198         recs[nrecs+1].code = 0;
199         recs[nrecs+1].message = NULL;
200         ++nrecs;
201
202         if (_rpmlogCallback) {
203             /*@-noeffectuncon@*/ /* FIX: useless callback */
204             _rpmlogCallback();
205             /*@=noeffectuncon@*/
206             return;     /* XXX Preserve legacy rpmError behavior. */
207         }
208     }
209     /*@=branchstate@*/
210 /*@=boundswrite@*/
211
212     /* rpmMessage behavior */
213
214     switch (pri) {
215     case RPMLOG_INFO:
216     case RPMLOG_NOTICE:
217         msgout = stdout;
218         break;
219
220     case RPMLOG_EMERG:
221     case RPMLOG_ALERT:
222     case RPMLOG_CRIT:
223     case RPMLOG_ERR: /* XXX Legacy rpmError behavior used stdout w/o prefix. */
224     case RPMLOG_WARNING:
225     case RPMLOG_DEBUG:
226         break;
227     }
228
229     if (rpmlogMsgPrefix[pri] && *rpmlogMsgPrefix[pri])
230         (void) fputs(_(rpmlogMsgPrefix[pri]), msgout);
231
232     (void) fputs(msg, msgout);
233     (void) fflush(msgout);
234     msgbuf = _free(msgbuf);
235     if (pri <= RPMLOG_CRIT)
236         exit(EXIT_FAILURE);
237 }
238 /*@=compmempass =nullstate@*/
239 /*@=modfilesys@*/
240
241 void rpmlog (int code, const char *fmt, ...)
242 {
243     va_list ap;
244
245     va_start(ap, fmt);
246     /*@-internalglobs@*/ /* FIX: shrug */
247     vrpmlog(code, fmt, ap);
248     /*@=internalglobs@*/
249     va_end(ap);
250 }
251
252 int rpmErrorCode(void)
253 {
254     return rpmlogCode();
255 }
256
257 const char * rpmErrorString(void)
258 {
259     return rpmlogMessage();
260 }
261
262 rpmlogCallback rpmErrorSetCallback(rpmlogCallback cb)
263 {
264     return rpmlogSetCallback(cb);
265 }
266 /*@=boundsread@*/