1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 2002-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * C expression library value map support
23 *
24 * Glenn Fowler
25 * AT&T Research
26 */
27
28 #include "cxlib.h"
29
30 typedef struct Frame_s
31 {
32 struct Frame_s* prev;
33 Dt_t* str2num;
34 } Frame_t;
35
36 /*
37 * dt string ignorecase comparison
38 */
39
40 static int
ignorecase(Dt_t * dt,void * a,void * b,Dtdisc_t * disc)41 ignorecase(Dt_t* dt, void* a, void* b, Dtdisc_t* disc)
42 {
43 return strcasecmp((char*)a, (char*)b);
44 }
45
46 /*
47 * cxinitmap helper
48 */
49
50 static int
initmap(Frame_t * frame,Cxmap_t * map,Cxdisc_t * disc)51 initmap(Frame_t* frame, Cxmap_t* map, Cxdisc_t* disc)
52 {
53 register Cxpart_t* part;
54 register Cxitem_t* item;
55 Frame_t top;
56 Frame_t* fp;
57 int easy;
58 Cxunsigned_t masks;
59
60 static Dtdisc_t num2strdisc;
61 static Dtdisc_t str2numdisc;
62 static Dtdisc_t stricase2numdisc;
63
64 if (map->header.flags & CX_INITIALIZED)
65 return -1;
66 map->header.flags |= CX_INITIALIZED;
67 for (;;)
68 {
69 if (!map->mask)
70 map->mask = ~map->mask;
71 if (!map->map)
72 break;
73 map = map->map;
74 }
75 if (!map->part)
76 return 0;
77 if (map->str2num)
78 {
79 if (!frame)
80 return 0;
81 easy = 0;
82 }
83 else
84 {
85 str2numdisc.link = offsetof(Cxitem_t, str2num);
86 str2numdisc.key = offsetof(Cxitem_t, name);
87 str2numdisc.size = -1;
88 if (map->header.flags & CX_IGNORECASE)
89 {
90 stricase2numdisc = str2numdisc;
91 stricase2numdisc.comparf = ignorecase;
92 map->str2num = dtopen(&stricase2numdisc, Dtoset);
93 }
94 else
95 map->str2num = dtopen(&str2numdisc, Dtoset);
96 if (!map->str2num)
97 {
98 if (disc->errorf)
99 (*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
100 return -1;
101 }
102 top.prev = frame;
103 top.str2num = map->str2num;
104 frame = ⊤
105 easy = 1;
106 }
107 for (part = map->part; part; part = part->next)
108 {
109 if (part->mask)
110 easy = 0;
111 else
112 part->mask = ~part->mask;
113 masks = part->item ? part->item->mask : 0;
114 for (item = part->item; item; item = item->next)
115 {
116 for (fp = frame; fp; fp = fp->prev)
117 dtinsert(fp->str2num, item);
118 if (item->mask != masks)
119 part->flags |= CX_ALL;
120 if (item->mask)
121 easy = 0;
122 else
123 item->mask = ~item->mask;
124 if (item->map)
125 {
126 if (initmap(frame, item->map, disc))
127 return -1;
128 easy = 0;
129 }
130 }
131 }
132 if (easy)
133 {
134 num2strdisc.link = offsetof(Cxitem_t, num2str);
135 num2strdisc.key = offsetof(Cxitem_t, value);
136 num2strdisc.size = sizeof(Cxunsigned_t);
137 if (!(map->num2str = dtopen(&num2strdisc, Dtoset)))
138 {
139 if (disc->errorf)
140 (*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
141 return -1;
142 }
143 part = map->part;
144 map->shift += part->shift;
145 map->mask |= part->mask;
146 for (item = part->item; item; item = item->next)
147 dtinsert(map->num2str, item);
148 }
149 return 0;
150 }
151
152 /*
153 * initialize map
154 */
155
156 int
cxinitmap(Cxmap_t * map,Cxdisc_t * disc)157 cxinitmap(Cxmap_t* map, Cxdisc_t* disc)
158 {
159 return initmap(NiL, map, disc);
160 }
161
162 /*
163 * cxnum2str helper
164 */
165
166 static int
num2str(Cx_t * cx,Cxmap_t * map,Sfio_t * sp,Cxunsigned_t num,int del)167 num2str(Cx_t* cx, Cxmap_t* map, Sfio_t* sp, Cxunsigned_t num, int del)
168 {
169 register Cxpart_t* part;
170 register Cxitem_t* item;
171 register Cxedit_t* edit;
172 Cxunsigned_t n;
173 char* s;
174 int v;
175 int p;
176 int r;
177 regmatch_t match[10];
178 char buf[64];
179
180 for (;;)
181 {
182 num >>= map->shift;
183 num &= map->mask;
184 if (!map->map)
185 break;
186 map = map->map;
187 }
188 if (map->num2str && (item = (Cxitem_t*)dtmatch(map->num2str, &num)))
189 {
190 sfprintf(sp, "%c%s", del, item->name);
191 return 1;
192 }
193 r = 0;
194 for (part = map->part; part; part = part->next)
195 {
196 n = num;
197 n >>= part->shift;
198 n &= part->mask;
199 v = !(part->flags & CX_ALL);
200 p = r;
201 for (item = part->item; item; item = item->next)
202 if ((n & item->mask) == item->value)
203 {
204 if (item->name)
205 {
206 sfprintf(sp, "%c%s", del, item->name);
207 r++;
208 }
209 if (item->map)
210 r += num2str(cx, item->map, sp, n, del);
211 if (v)
212 break;
213 }
214 if (r == p && part->num2str)
215 {
216 buf[0] = 0;
217 for (edit = part->num2str; edit; edit = edit->next)
218 if (!edit->num2strf)
219 {
220 if (!buf[0])
221 sfsprintf(buf, sizeof(buf), "%lld", n);
222 if (!regexec(&edit->re, buf, elementsof(match), match, 0) && !regsubexec(&edit->re, buf, elementsof(match), match))
223 {
224 sfprintf(sp, "%c%s", del, edit->re.re_sub->re_buf);
225 r++;
226 break;
227 }
228 }
229 else if (s = (*edit->num2strf)(cx, n, cx->disc))
230 {
231 sfprintf(sp, "%c%s", del, s);
232 r++;
233 break;
234 }
235 }
236 }
237 return r;
238 }
239
240 /*
241 * map number to string
242 */
243
244 int
cxnum2str(Cx_t * cx,Cxformat_t * format,Cxunsigned_t num,char ** p)245 cxnum2str(Cx_t* cx, Cxformat_t* format, Cxunsigned_t num, char** p)
246 {
247 char* s;
248 int del;
249
250 if (format->map)
251 {
252 if ((del = format->delimiter) == -1)
253 del = '|';
254 if (!num2str(cx, format->map, cx->tp, num, del))
255 return -1;
256 }
257 else
258 sfprintf(cx->tp, "|%I*u", sizeof(num), num);
259 if (!(s = sfstruse(cx->tp)))
260 return -1;
261 if (p)
262 *p = s + 1;
263 return 0;
264 }
265
266 /*
267 * cxstr2num helper
268 */
269
270 static int
str2num(Cx_t * cx,Cxmap_t * map,const char * str,Cxunsigned_t * num)271 str2num(Cx_t* cx, Cxmap_t* map, const char* str, Cxunsigned_t* num)
272 {
273 register Cxpart_t* part;
274 register Cxedit_t* edit;
275 regmatch_t match[10];
276
277 for (part = map->part; part; part = part->next)
278 for (edit = part->str2num; edit; edit = edit->next)
279 if (edit->str2numf)
280 {
281 if (!(*edit->str2numf)(cx, str, strlen(str), num, cx->disc))
282 return 1;
283 }
284 else if (!regexec(&edit->re, str, elementsof(match), match, 0) && !regsubexec(&edit->re, str, elementsof(match), match))
285 {
286 *num = strtoull(edit->re.re_sub->re_buf, NiL, 0);
287 return 1;
288 }
289 return 0;
290 }
291
292 /*
293 * map string to number
294 */
295
296 int
cxstr2num(Cx_t * cx,Cxformat_t * format,const char * str,size_t siz,Cxunsigned_t * p)297 cxstr2num(Cx_t* cx, Cxformat_t* format, const char* str, size_t siz, Cxunsigned_t* p)
298 {
299 register char* s;
300 register char* b;
301 int del;
302 Dt_t* dt;
303 Cxitem_t* item;
304 Cxunsigned_t n;
305 Cxunsigned_t m;
306
307 if (!format->map)
308 return -1;
309 del = format->delimiter;
310 dt = format->map->str2num;
311 n = 0;
312 sfwrite(cx->tp, str, siz);
313 if (!(s = sfstruse(cx->tp)))
314 return -1;
315 while (*s)
316 {
317 for (b = s; *s && *s != del && *s != '|' && *s != '+'; s++);
318 if (*s)
319 *s++ = 0;
320 if (item = (Cxitem_t*)dtmatch(dt, b))
321 n |= item->value;
322 else if (str2num(cx, format->map, b, &m))
323 n |= m;
324 else
325 return -1;
326 }
327 if (p)
328 *p = n;
329 return 0;
330 }
331
332 /*
333 * apply edit substitutions in edit to r
334 */
335
336 int
cxsub(Cx_t * cx,Cxedit_t * edit,Cxoperand_t * r)337 cxsub(Cx_t* cx, Cxedit_t* edit, Cxoperand_t* r)
338 {
339 Cxtype_t* type;
340 regmatch_t match[10];
341
342 if (!cxisstring(r->type))
343 {
344 type = r->type;
345 if (cxcast(cx, r, NiL, cx->state->type_string, NiL, NiL))
346 return -1;
347 }
348 else
349 type = 0;
350 if (!regnexec(&edit->re, r->value.string.data, r->value.string.size, elementsof(match), match, 0) && !regsubexec(&edit->re, r->value.string.data, elementsof(match), match))
351 r->value.string.size = strlen(r->value.string.data = edit->re.re_sub->re_buf);
352 if (type && cxcast(cx, r, NiL, type, NiL, NiL))
353 return -1;
354 return 0;
355 }
356
357 /*
358 * apply edit substitutions in part to r
359 */
360
361 int
cxsuball(Cx_t * cx,Cxpart_t * part,Cxoperand_t * r)362 cxsuball(Cx_t* cx, Cxpart_t* part, Cxoperand_t* r)
363 {
364 while (part)
365 {
366 if (part->edit && cxsub(cx, part->edit, r))
367 return -1;
368 part = part->next;
369 }
370 return 0;
371 }
372