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