Add boundsread annotations throughout, enable +bounds checking.
[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     }
183     msgbuf[msgnb - 1] = '\0';
184     msg = msgbuf;
185
186     /* Save copy of all messages at warning (or below == "more important"). */
187     /*@-branchstate@*/
188     if (pri <= RPMLOG_WARNING) {
189
190         if (recs == NULL)
191             recs = xmalloc((nrecs+2) * sizeof(*recs));
192         else
193             recs = xrealloc(recs, (nrecs+2) * sizeof(*recs));
194         recs[nrecs].code = code;
195         recs[nrecs].message = msg = xrealloc(msgbuf, strlen(msgbuf)+1);
196         msgbuf = NULL;          /* XXX don't free at exit. */
197         recs[nrecs+1].code = 0;
198         recs[nrecs+1].message = NULL;
199         ++nrecs;
200
201         if (_rpmlogCallback) {
202             /*@-noeffectuncon@*/ /* FIX: useless callback */
203             _rpmlogCallback();
204             /*@=noeffectuncon@*/
205             return;     /* XXX Preserve legacy rpmError behavior. */
206         }
207     }
208     /*@=branchstate@*/
209 /*@=boundswrite@*/
210
211     /* rpmMessage behavior */
212
213     switch (pri) {
214     case RPMLOG_INFO:
215     case RPMLOG_NOTICE:
216         msgout = stdout;
217         break;
218
219     case RPMLOG_EMERG:
220     case RPMLOG_ALERT:
221     case RPMLOG_CRIT:
222     case RPMLOG_ERR: /* XXX Legacy rpmError behavior used stdout w/o prefix. */
223     case RPMLOG_WARNING:
224     case RPMLOG_DEBUG:
225         break;
226     }
227
228     if (rpmlogMsgPrefix[pri] && *rpmlogMsgPrefix[pri])
229         (void) fputs(_(rpmlogMsgPrefix[pri]), msgout);
230
231     (void) fputs(msg, msgout);
232     (void) fflush(msgout);
233     msgbuf = _free(msgbuf);
234     if (pri <= RPMLOG_CRIT)
235         exit(EXIT_FAILURE);
236 }
237 /*@=compmempass =nullstate@*/
238 /*@=modfilesys@*/
239
240 void rpmlog (int code, const char *fmt, ...)
241 {
242     va_list ap;
243
244     va_start(ap, fmt);
245     /*@-internalglobs@*/ /* FIX: shrug */
246     vrpmlog(code, fmt, ap);
247     /*@=internalglobs@*/
248     va_end(ap);
249 }
250
251 int rpmErrorCode(void)
252 {
253     return rpmlogCode();
254 }
255
256 const char * rpmErrorString(void)
257 {
258     return rpmlogMessage();
259 }
260
261 rpmlogCallback rpmErrorSetCallback(rpmlogCallback cb)
262 {
263     return rpmlogSetCallback(cb);
264 }
265 /*@=boundsread@*/