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 = &top;
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