]>
Commit | Line | Data |
---|---|---|
616915dd | 1 | /* |
2 | ** LCLint - annotation-assisted static program checker | |
3 | ** Copyright (C) 1994-2000 University of Virginia, | |
4 | ** Massachusetts Institute of Technology | |
5 | ** | |
6 | ** This program is free software; you can redistribute it and/or modify it | |
7 | ** under the terms of the GNU General Public License as published by the | |
8 | ** Free Software Foundation; either version 2 of the License, or (at your | |
9 | ** option) any later version. | |
10 | ** | |
11 | ** This program is distributed in the hope that it will be useful, but | |
12 | ** WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | ** General Public License for more details. | |
15 | ** | |
16 | ** The GNU General Public License is available from http://www.gnu.org/ or | |
17 | ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, | |
18 | ** MA 02111-1307, USA. | |
19 | ** | |
20 | ** For information on lclint: lclint-request@cs.virginia.edu | |
21 | ** To report a bug: lclint-bug@cs.virginia.edu | |
22 | ** For more information: http://lclint.cs.virginia.edu | |
23 | */ | |
24 | /* | |
25 | ** message.c | |
26 | */ | |
27 | ||
28 | # include "lclintMacros.nf" | |
29 | # include "basic.h" | |
30 | ||
31 | /* patch for linux? solaris? */ | |
32 | ||
33 | static char strbuf[64]; | |
34 | static int modcode; | |
35 | ||
36 | typedef enum | |
37 | { | |
38 | XINVALID, | |
39 | XCHAR, XSTRING, XSTRINGFREE, XTSTRINGFREE, XINT, XFLOAT, XBOOL, XUENTRY, | |
40 | XPERCENT, XCTYPE, XPLURAL, XREPREFIX, XFILELOC | |
41 | } ccode; | |
42 | ||
43 | /* char *s, anytype v */ | |
44 | /*@notfunction@*/ | |
45 | # define GETPRINTF(s,v) (sprintf (strbuf, s, v), mstring_copy (strbuf)) | |
46 | ||
47 | /* | |
48 | ** returns control code indicated by *c, and | |
49 | ** advances *c to next character. | |
50 | */ | |
51 | ||
52 | static ccode | |
53 | identify_control (char **s) | |
54 | { | |
55 | char c; | |
56 | ||
57 | modcode = 0; | |
58 | ||
59 | c = **s; | |
60 | if (c == '\0') | |
61 | { | |
62 | return (XINVALID); | |
63 | } | |
64 | ||
65 | if (c >= '0' && c <= '9') | |
66 | { | |
67 | modcode = getInt (s); | |
68 | } | |
69 | ||
70 | c = **s; | |
71 | ||
72 | (*s)++; | |
73 | ||
74 | /* | |
75 | ** handle single-char codes | |
76 | */ | |
77 | ||
78 | switch (c) | |
79 | { | |
80 | case '%': | |
81 | return (XPERCENT); | |
82 | case 'h': | |
83 | case 'c': | |
84 | return (XCHAR); | |
85 | case 's': | |
86 | return (XSTRING); | |
87 | case 'q': | |
88 | return (XSTRINGFREE); | |
89 | case 'x': | |
90 | return (XSTRINGFREE); | |
91 | case 'd': | |
92 | return (XINT); | |
93 | case 'u': | |
94 | return (XINT); /* unsigned */ | |
95 | case 'w': | |
96 | return (XINT); /* unsigned long */ | |
97 | case 'f': | |
98 | return (XFLOAT); | |
99 | case 'b': | |
100 | return (XBOOL); | |
101 | case 't': | |
102 | return (XCTYPE); | |
103 | case 'l': | |
104 | return (XFILELOC); | |
105 | case 'p': | |
106 | return (XPLURAL); | |
107 | case 'r': | |
108 | return (XREPREFIX); | |
109 | default: | |
110 | llcontbug (message ("Message: invalid code: %h (%s)", c, | |
111 | cstring_fromChars (*s))); | |
112 | return (XINVALID); | |
113 | } | |
114 | } | |
115 | ||
116 | /* | |
117 | ** message | |
118 | ** | |
119 | ** returns a cstring containing the message, as formated by s. | |
120 | ** | |
121 | ** the format codes are similar to printf: | |
122 | ** | |
123 | ** %s cstring (don't free after print) | |
124 | ** %q cstring (free after print) | |
125 | ** %d int | |
126 | ** %f float | |
127 | ** %b bool (uses bool_unparse) | |
128 | ** %u uentry | |
129 | ** %l fileloc | |
130 | ** %t ctype | |
131 | */ | |
132 | ||
133 | ||
134 | # if USEVARARGS | |
135 | cstring | |
136 | message (fmt, va_alist) | |
137 | char *fmt; | |
138 | va_dcl | |
139 | # else | |
140 | /*@messagelike@*/ /*@only@*/ cstring | |
141 | message (/*@temp@*/ char *fmt, ...) | |
142 | # endif | |
143 | { | |
144 | char c; | |
145 | int lastint = 0; | |
146 | char *ret = mstring_createEmpty (); | |
147 | char *ofmt = fmt; | |
148 | va_list pvar; | |
149 | ||
150 | # if USEVARARGS | |
151 | va_start (pvar); | |
152 | # else | |
153 | va_start (pvar, fmt); | |
154 | # endif | |
155 | ||
156 | while ((c = *fmt++) != '\0') | |
157 | { | |
158 | if (c == '%') | |
159 | { | |
160 | /*@-loopswitchbreak@*/ | |
161 | ||
162 | switch (identify_control (&fmt)) | |
163 | { | |
164 | case XPERCENT: | |
165 | { | |
166 | ret = mstring_concatFree1 (ret, "%"); | |
167 | break; | |
168 | } | |
169 | case XCHAR: | |
170 | { | |
171 | /* | |
172 | ** some systems don't handle char va_arg correctly, so it must be | |
173 | ** passed as an int here | |
174 | */ | |
175 | ||
176 | char lc = (char) va_arg (pvar, int); | |
177 | ||
178 | ret = mstring_append (ret, lc); | |
179 | break; | |
180 | } | |
181 | case XSTRING: | |
182 | { | |
183 | cstring s = va_arg (pvar, cstring); | |
184 | ||
185 | if (modcode != 0) | |
186 | { | |
187 | ret = mstring_concatFree (ret, cstring_toCharsSafe | |
188 | (cstring_fill (s, modcode))); | |
189 | } | |
190 | else | |
191 | { | |
192 | if (cstring_isDefined (s)) | |
193 | { | |
194 | ret = mstring_concatFree1 (ret, cstring_toCharsSafe (s)); | |
195 | } | |
196 | } | |
197 | } | |
198 | break; | |
199 | case XSTRINGFREE: | |
200 | case XTSTRINGFREE: | |
201 | { | |
202 | cstring s = va_arg (pvar, cstring); | |
203 | ||
204 | if (modcode != 0) | |
205 | { | |
206 | ret = mstring_concatFree (ret, cstring_toCharsSafe | |
207 | (cstring_fill (s, modcode))); | |
208 | } | |
209 | else | |
210 | { | |
211 | if (cstring_isDefined (s)) | |
212 | { | |
213 | ret = mstring_concatFree | |
214 | (ret, cstring_toCharsSafe (s)); | |
215 | } | |
216 | } | |
217 | } | |
218 | break; | |
219 | case XREPREFIX: | |
220 | lastint = va_arg (pvar, int); | |
221 | ||
222 | if (lastint != 0) | |
223 | { | |
224 | ret = mstring_concatFree1 (ret, "re"); | |
225 | } | |
226 | break; | |
227 | case XPLURAL: | |
228 | if (lastint != 1) | |
229 | { | |
230 | ret = mstring_concatFree1 (ret, "s"); | |
231 | } | |
232 | break; | |
233 | case XINT: | |
234 | lastint = va_arg (pvar, int); | |
235 | ret = mstring_concatFree (ret, GETPRINTF ("%d", lastint)); | |
236 | break; | |
237 | case XFLOAT: | |
238 | ret = mstring_concatFree (ret, GETPRINTF ("%e", va_arg (pvar, double))); | |
239 | break; | |
240 | case XBOOL: | |
241 | ret = mstring_concatFree1 (ret, cstring_toCharsSafe | |
242 | (bool_unparse (va_arg (pvar, bool)))); | |
243 | break; | |
244 | case XUENTRY: | |
245 | ret = mstring_concatFree (ret, cstring_toCharsSafe | |
246 | (uentry_unparse (va_arg (pvar, uentry)))); | |
247 | break; | |
248 | case XCTYPE: | |
249 | /* cannot free ctype_unparse */ | |
250 | ret = mstring_concatFree1 (ret, cstring_toCharsSafe | |
251 | (ctype_unparse (va_arg (pvar, ctype)))); | |
252 | break; | |
253 | case XFILELOC: | |
254 | ret = mstring_concatFree (ret, cstring_toCharsSafe | |
255 | (fileloc_unparse (va_arg (pvar, fileloc)))); | |
256 | break; | |
257 | case XINVALID: | |
258 | default: | |
259 | llcontbug (cstring_makeLiteral ("message: bad control flag")); | |
260 | fprintf (stdout, "\tFormat string: %s", ofmt); | |
261 | } | |
262 | /*@=loopswitchbreak@*/ | |
263 | } | |
264 | else | |
265 | { | |
266 | ret = mstring_append (ret, c); | |
267 | } | |
268 | } | |
269 | ||
270 | va_end (pvar); | |
271 | ||
272 | /* | |
273 | ** cstring_fromChars returns the same storage (exposed) | |
274 | */ | |
275 | ||
276 | /*@-mustfree@*/ return (cstring_fromChars (ret)); /*@=mustfree@*/ | |
277 | } |