- portability: vsnprintf/snprintf wrappers for those without (#34657).
[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 /*@access rpmlogRec @*/
11
12 static int nrecs = 0;
13 static /*@only@*/ /*@null@*/ rpmlogRec recs = NULL;
14
15 int rpmlogGetNrecs(void)
16 {
17     return nrecs;
18 }
19
20 int rpmlogCode(void)
21 {
22     if (nrecs > 0)
23         return recs[nrecs-1].code;
24     return -1;
25 }
26
27
28 const char * rpmlogMessage(void)
29 {
30     if (nrecs > 0)
31         return recs[nrecs-1].message;
32     return _("(no error)");
33 }
34
35 void rpmlogPrint(FILE *f)
36 {
37     int i;
38
39     if (f == NULL)
40         f = stderr;
41
42     for (i = 0; i < nrecs; i++) {
43         rpmlogRec rec = recs + i;
44         if (rec->message && *rec->message)
45             fprintf(f, "    %s", rec->message);
46     }
47 }
48
49 void rpmlogClose (void)
50 {
51     int i;
52
53     for (i = 0; i < nrecs; i++) {
54         rpmlogRec rec = recs + i;
55         if (rec->message) {
56             free((void *)rec->message);
57             rec->message = NULL;
58         }
59     }
60     free(recs);
61     recs = NULL;
62     nrecs = 0;
63 }
64
65 void rpmlogOpen (/*@unused@*/ const char *ident, /*@unused@*/ int option,
66                 /*@unused@*/ int facility)
67 {
68 }
69
70 static int rpmlogMask = RPMLOG_UPTO( RPMLOG_NOTICE );
71 static /*@unused@*/ int rpmlogFacility = RPMLOG_USER;
72
73 int rpmlogSetMask (int mask)
74 {
75     int omask = rpmlogMask;
76     if (mask)
77         rpmlogMask = mask;
78     return omask;
79 }
80
81 static /*@null@*/ rpmlogCallback _rpmlogCallback = NULL;
82
83 rpmlogCallback rpmlogSetCallback(rpmlogCallback cb)
84 {
85     rpmlogCallback ocb = _rpmlogCallback;
86     _rpmlogCallback = cb;
87     return ocb;
88 }
89
90 static char *rpmlogMsgPrefix[] = {
91     N_("fatal error: "),/*!< RPMLOG_EMERG */
92     N_("fatal error: "),/*!< RPMLOG_ALERT */
93     N_("fatal error: "),/*!< RPMLOG_CRIT */
94     N_("error: "),      /*!< RPMLOG_ERR */
95     N_("warning: "),    /*!< RPMLOG_WARNING */
96     "",                 /*!< RPMLOG_NOTICE */
97     "",                 /*!< RPMLOG_INFO */
98     "D: ",              /*!< RPMLOG_DEBUG */
99 };
100
101 #if !defined(HAVE_VSNPRINTF)
102 static inline int vsnprintf(char * buf, /*@unused@*/ int nb,
103         const char * fmt, va_list ap)
104 {
105     return vsprintf(buf, fmt, ap);
106 }
107 #endif
108
109 static void vrpmlog (unsigned code, const char *fmt, va_list ap)
110 {
111     int pri = RPMLOG_PRI(code);
112     int mask = RPMLOG_MASK(pri);
113     /*@unused@*/ int fac = RPMLOG_FAC(code);
114     char *msgbuf, *msg;
115     int msgnb = BUFSIZ, nb;
116     FILE * msgout = stderr;
117     rpmlogRec rec;
118
119     if ((mask & rpmlogMask) == 0)
120         return;
121
122     msgbuf = xmalloc(msgnb);
123     *msgbuf = '\0';
124
125     /* Allocate a sufficently large buffer for output. */
126     while (1) {
127         /*@-unrecog@*/
128         nb = vsnprintf(msgbuf, msgnb, fmt, ap);
129         /*@=unrecog@*/
130         if (nb > -1 && nb < msgnb)
131             break;
132         if (nb > -1)            /* glibc 2.1 */
133             msgnb = nb+1;
134         else                    /* glibc 2.0 */
135             msgnb *= 2;
136         msgbuf = xrealloc(msgbuf, msgnb);
137     }
138     msgbuf[msgnb - 1] = '\0';
139     msg = msgbuf;
140
141     /* Save copy of all messages at warning (or below == "more important"). */
142     if (pri <= RPMLOG_WARNING) {
143
144         if (recs == NULL)
145             recs = xmalloc((nrecs+2) * sizeof(*recs));
146         else
147             recs = xrealloc(recs, (nrecs+2) * sizeof(*recs));
148         recs[nrecs+1].code = 0;
149         recs[nrecs+1].message = NULL;
150         rec = recs + nrecs;
151         ++nrecs;
152
153         rec->code = code;
154         rec->message = msgbuf;
155         msgbuf = NULL;
156
157         if (_rpmlogCallback) {
158             _rpmlogCallback();
159             if (msgbuf)
160                 free(msgbuf);
161             return;     /* XXX Preserve legacy rpmError behavior. */
162         }
163     }
164
165     /* rpmMessage behavior */
166
167     switch (pri) {
168     case RPMLOG_INFO:
169     case RPMLOG_NOTICE:
170         msgout = stdout;
171         break;
172
173     case RPMLOG_EMERG:
174     case RPMLOG_ALERT:
175     case RPMLOG_CRIT:
176     case RPMLOG_ERR: /* XXX Legacy rpmError behavior used stdout w/o prefix. */
177     case RPMLOG_WARNING:
178     case RPMLOG_DEBUG:
179         break;
180     }
181
182     /* Silly FORTRAN-like carriage control. */
183     if (*msg == '+')
184         msg++;
185     else if (rpmlogMsgPrefix[pri] && *rpmlogMsgPrefix[pri])
186         fputs(_(rpmlogMsgPrefix[pri]), msgout);
187
188     fputs(msg, msgout);
189     fflush(msgout);
190     if (msgbuf)
191         free(msgbuf);
192     if (pri <= RPMLOG_CRIT)
193         exit(EXIT_FAILURE);
194 }
195
196 void rpmlog (int code, const char *fmt, ...)
197 {
198     va_list ap;
199
200     va_start(ap, fmt);
201     vrpmlog(code, fmt, ap);
202     va_end(ap);
203 }
204
205 int rpmErrorCode(void)
206 {
207     return rpmlogCode();
208 }
209
210 const char * rpmErrorString(void)
211 {
212     return rpmlogMessage();
213 }
214
215 rpmlogCallback rpmErrorSetCallback(rpmlogCallback cb)
216 {
217     return rpmlogSetCallback(cb);
218 }