1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 2002-2013 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 open/close and global initialization
23  *
24  * Glenn Fowler
25  * AT&T Research
26  */
27 
28 #include "cxlib.h"
29 
30 #include <ccode.h>
31 
32 typedef struct Type_s
33 {
34 	Cxtype_t**	state;
35 	Cxtype_t	type;
36 } Type_t;
37 
38 static Cxstate_t	state;
39 static Cxtable_t	table;
40 
41 static const char	name_bool[] = "bool";
42 static const char	name_buffer[] = "buffer";
43 static const char	name_number[] = "number";
44 static const char	name_pointer[] = "pointer";
45 static const char	name_reference[] = "reference";
46 static const char	name_string[] = "string";
47 static const char	name_type[] = "type_t";
48 static const char	name_void[] = "void";
49 
50 static void*
match_string_comp(Cx_t * cx,Cxtype_t * st,Cxtype_t * pt,Cxvalue_t * pv,Cxdisc_t * disc)51 match_string_comp(Cx_t* cx, Cxtype_t* st, Cxtype_t* pt, Cxvalue_t* pv, Cxdisc_t* disc)
52 {
53 	regex_t*	re;
54 
55 	if (!cxisstring(pt))
56 	{
57 		if (disc->errorf)
58 			(*disc->errorf)(NiL, disc, 2, "%s: match requires %s pattern", st->name, st->name);
59 		return 0;
60 	}
61 	if (!(re = newof(0, regex_t, 1, 0)))
62 	{
63 		if (disc->errorf)
64 			(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
65 		return 0;
66 	}
67 	re->re_disc = &cx->redisc;
68 	if (regcomp(re, pv->string.data, REG_AUGMENTED|REG_DISCIPLINE))
69 	{
70 		free(re);
71 		return 0;
72 	}
73 	return re;
74 }
75 
76 static int
match_string_exec(Cx_t * cx,void * data,Cxtype_t * st,Cxvalue_t * sv,Cxdisc_t * disc)77 match_string_exec(Cx_t* cx, void* data, Cxtype_t* st, Cxvalue_t* sv, Cxdisc_t* disc)
78 {
79 	int	i;
80 
81 	if ((i = regnexec((regex_t*)data, sv->string.data, sv->string.size, 0, NiL, 0)) && i != REG_NOMATCH)
82 		i = -1;
83 	return i == 0;
84 }
85 
86 static int
match_string_free(Cx_t * cx,void * data,Cxdisc_t * disc)87 match_string_free(Cx_t* cx, void* data, Cxdisc_t* disc)
88 {
89 	if (data)
90 	{
91 		regfree((regex_t*)data);
92 		free(data);
93 	}
94 	return 0;
95 }
96 
97 static Cxmatch_t	match_string =
98 {
99 	"regex",
100 	"Matches on this type treat the pattern as a regex(3) pattern string.",
101 	CXH,
102 	match_string_comp,
103 	match_string_exec,
104 	match_string_free
105 };
106 
107 /*
108  * default void externalf
109  */
110 
111 static ssize_t
void_external(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxvalue_t * value,char * buf,size_t size,Cxdisc_t * disc)112 void_external(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxvalue_t* value, char* buf, size_t size, Cxdisc_t* disc)
113 {
114 	ssize_t	i;
115 
116 	if (!format)
117 		return 0;
118 	if (format->width > size)
119 		return format->width;
120 	for (i = 0; i < format->width; i++)
121 		buf[i] = 0;
122 	return i;
123 }
124 
125 /*
126  * default void internalf
127  */
128 
129 static ssize_t
void_internal(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxoperand_t * ret,const char * buf,size_t size,Vmalloc_t * vm,Cxdisc_t * disc)130 void_internal(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxoperand_t* ret, const char* buf, size_t size, Vmalloc_t* vm, Cxdisc_t* disc)
131 {
132 	ret->value.number = 0;
133 	return format ? format->width : 0;
134 }
135 
136 /*
137  * default number externalf
138  */
139 
140 static ssize_t
number_external(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxvalue_t * value,char * buf,size_t size,Cxdisc_t * disc)141 number_external(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxvalue_t* value, char* buf, size_t size, Cxdisc_t* disc)
142 {
143 	ssize_t	n;
144 	char*	f;
145 	char	fmt[16];
146 
147 	if (CXDETAILS(details, format, type, 0))
148 		n = sfsprintf(buf, size, details, (intmax_t)value->number);
149 	else if (value->number == (intmax_t)value->number || (format->flags & CX_INTEGER))
150 	{
151 		f = fmt;
152 		*f++ = '%';
153 		if (format)
154 		{
155 			if (format->width)
156 			{
157 				if (format->fill > 0)
158 					*f++ = format->fill;
159 				f += sfsprintf(f, sizeof(fmt) - (f - fmt), "%d", format->width);
160 			}
161 			*f++ = 'l';
162 			*f++ = 'l';
163 			*f++ = (format->flags & CX_UNSIGNED) ? 'u' : 'd';
164 		}
165 		else
166 		{
167 			*f++ = 'l';
168 			*f++ = 'l';
169 			*f++ = 'd';
170 		}
171 		*f = 0;
172 		n = sfsprintf(buf, size, fmt, (intmax_t)value->number);
173 	}
174 	else if (format->width)
175 	{
176 		int	w;
177 
178 		w = format->width - ((value->number < 0) ? 2 : 1);
179 		n = sfsprintf(buf, size, "%#.*I*g", w, sizeof(value->number), value->number);
180 		if (n != w)
181 		{
182 			w += w - n;
183 			n = sfsprintf(buf, size, "%#.*I*g", w, sizeof(value->number), value->number);
184 		}
185 	}
186 	else
187 		n = sfsprintf(buf, size, "%1.15I*g", sizeof(value->number), value->number);
188 	if (n < 0)
189 		return -1;
190 	if (n > size)
191 		return size ? 2 * size : 32;
192 	return n;
193 }
194 
195 /*
196  * default number internalf
197  */
198 
199 static ssize_t
number_internal(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxoperand_t * ret,const char * buf,size_t size,Vmalloc_t * vm,Cxdisc_t * disc)200 number_internal(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxoperand_t* ret, const char* buf, size_t size, Vmalloc_t* vm, Cxdisc_t* disc)
201 {
202 	Cxunsigned_t	m;
203 	char*		e;
204 
205 	if (size == 0)
206 	{
207 		ret->value.number = 0;
208 		return 0;
209 	}
210 	if (format && (format->flags & CX_UNSIGNED))
211 		ret->value.number = (Cxinteger_t)strntoull(buf, size, &e, format->base);
212 	else if (format && format->base)
213 		ret->value.number = strntoll(buf, size, &e, format->base);
214 	else
215 		ret->value.number = strntod(buf, size, &e);
216 	if (e != ((char*)buf + size) && *buf)
217 	{
218 		if (format && format->map && !cxstr2num(cx, format, buf, size, &m))
219 		{
220 			ret->value.number = (Cxinteger_t)m;
221 			return size;
222 		}
223 		if (disc->errorf && !(cx->flags & CX_QUIET))
224 			(*disc->errorf)(cx, disc, 1, "%s: invalid number [(buf+size)=%p e=%p%s base=%d size=%d]", fmtquote(buf, NiL, NiL, size, 0), (char*)buf+size, e, (format && (format->flags & CX_UNSIGNED)) ? " unsigned" : "", format ? format->base : 0, size);
225 	}
226 	return e - (char*)buf;
227 }
228 
229 /*
230  * default bool externalf
231  */
232 
233 static ssize_t
bool_external(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxvalue_t * value,char * buf,size_t size,Cxdisc_t * disc)234 bool_external(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxvalue_t* value, char* buf, size_t size, Cxdisc_t* disc)
235 {
236 	return sfsprintf(buf, size, "%c", value->number ? '1' : '0');
237 }
238 
239 /*
240  * default bool internalf
241  */
242 
243 static ssize_t
bool_internal(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxoperand_t * ret,const char * buf,size_t size,Vmalloc_t * vm,Cxdisc_t * disc)244 bool_internal(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxoperand_t* ret, const char* buf, size_t size, Vmalloc_t* vm, Cxdisc_t* disc)
245 {
246 	Cxunsigned_t	m;
247 	char*		e;
248 
249 	if (size == 0)
250 	{
251 		ret->value.number = 0;
252 		return 0;
253 	}
254 	ret->value.number = (Cxinteger_t)strntoull(buf, size, &e, format->base);
255 	if (e != ((char*)buf + size) && *buf)
256 	{
257 		if (format && format->map && !cxstr2num(cx, format, buf, size, &m))
258 		{
259 			ret->value.number = (Cxinteger_t)m;
260 			return size;
261 		}
262 		if (disc->errorf && !(cx->flags & CX_QUIET))
263 			(*disc->errorf)(cx, disc, 1, "%s: invalid bool [(buf+size)=%p e=%p%s base=%d size=%d]", fmtquote(buf, NiL, NiL, size, 0), (char*)buf+size, e, (format && (format->flags & CX_UNSIGNED)) ? " unsigned" : "", format ? format->base : 0, size);
264 	}
265 	return e - (char*)buf;
266 }
267 
268 /*
269  * default string externalf
270  */
271 
272 static ssize_t
string_external(Cx_t * cx,Cxtype_t * type,const char * details,register Cxformat_t * format,register Cxvalue_t * value,char * buf,size_t size,Cxdisc_t * disc)273 string_external(Cx_t* cx, Cxtype_t* type, const char* details, register Cxformat_t* format, register Cxvalue_t* value, char* buf, size_t size, Cxdisc_t* disc)
274 {
275 	if (format && format->width)
276 	{
277 		if (format->width > size)
278 			return format->width;
279 		if (format->width <= value->string.size)
280 			memcpy(buf, value->string.data, format->width);
281 		else
282 		{
283 			memcpy(buf, value->string.data, value->string.size);
284 			memset(buf + value->string.size, format->fill >= 0 ? format->fill : 0, format->width - value->string.size);
285 		}
286 		return format->width;
287 	}
288 	else
289 	{
290 		if (value->string.size > size)
291 			return value->string.size;
292 		memcpy(buf, value->string.data, value->string.size);
293 		return value->string.size;
294 	}
295 }
296 
297 /*
298  * default string internalf
299  */
300 
301 static ssize_t
string_internal(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxoperand_t * ret,const char * buf,size_t size,Vmalloc_t * vm,Cxdisc_t * disc)302 string_internal(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxoperand_t* ret, const char* buf, size_t size, Vmalloc_t* vm, Cxdisc_t* disc)
303 {
304 	char*	s;
305 
306 	ret->value.string.data = (char*)buf;
307 	if ((!format || !(format->flags & CX_NUL) && format->fill <= 0) && (s = memchr(buf, 0, size)))
308 		size = s - (char*)buf;
309 	return ret->value.string.size = size;
310 }
311 
312 /*
313  * default buffer externalf -- mime base64 encode
314  */
315 
316 static ssize_t
buffer_external(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxvalue_t * value,char * buf,size_t size,Cxdisc_t * disc)317 buffer_external(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxvalue_t* value, char* buf, size_t size, Cxdisc_t* disc)
318 {
319 	register unsigned char*	t;
320 	register unsigned char*	f;
321 	register unsigned char*	e;
322 	register int		v;
323 	int			z;
324 
325 	static const char	hex[] = "0123456789abcdefg";
326 
327 	switch (details ? *details : 0)
328 	{
329 	case 0:
330 	case 'b':
331 	case 'm':
332 		return base64encode(value->buffer.data, value->buffer.size, NiL, buf, size, NiL);
333 	case 'h':
334 	case 'x':
335 		z = value->buffer.size;
336 		f = (unsigned char*)value->buffer.data;
337 		e = f + z;
338 		z *= 2;
339 		if (size < z)
340 			return z;
341 		t = (unsigned char*)buf;
342 		while (f < e)
343 		{
344 			v = *f++;
345 			*t++ = hex[v >> 4];
346 			*t++ = hex[v & 0xf];
347 		}
348 		return z;
349 	}
350 	if (cx->disc->errorf)
351 		(*cx->disc->errorf)(cx, cx->disc, ERROR_SYSTEM|2, "%s: unknown buffer representation details", details);
352 	return -1;
353 }
354 
355 /*
356  * default buffer internalf -- mime base64 decode
357  */
358 
359 static ssize_t
buffer_internal(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxoperand_t * ret,const char * buf,size_t size,Vmalloc_t * vm,Cxdisc_t * disc)360 buffer_internal(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxoperand_t* ret, const char* buf, size_t size, Vmalloc_t* vm, Cxdisc_t* disc)
361 {
362 	void*	t;
363 	size_t	n;
364 	ssize_t	r;
365 
366 	n = (size * 3) / 4 + 1;
367 	if (!vm)
368 		vm = Vmregion;
369 	if (!(t = vmnewof(vm, 0, unsigned char, n, 0)))
370 	{
371 		if (disc->errorf)
372 			(*disc->errorf)(cx, disc, ERROR_SYSTEM|2, "out of space");
373 		return -1;
374 	}
375 	if ((r = base64decode(buf, size, NiL, t, n, NiL)) > n)
376 		vmfree(vm, t);
377 	else
378 	{
379 		ret->value.buffer.data = t;
380 		ret->value.buffer.size = r;
381 	}
382 	return r;
383 }
384 
385 /*
386  * type externalf
387  */
388 
389 static ssize_t
type_external(Cx_t * cx,Cxtype_t * type,const char * details,register Cxformat_t * format,register Cxvalue_t * value,char * buf,size_t size,Cxdisc_t * disc)390 type_external(Cx_t* cx, Cxtype_t* type, const char* details, register Cxformat_t* format, register Cxvalue_t* value, char* buf, size_t size, Cxdisc_t* disc)
391 {
392 	size_t	n;
393 
394 	type = (Cxtype_t*)value->pointer;
395 	if ((n = strlen(type->name)) <= size)
396 		memcpy(buf, type->name, n);
397 	return n;
398 }
399 
400 /*
401  * default string internalf
402  */
403 
404 static ssize_t
type_internal(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxoperand_t * ret,const char * buf,size_t size,Vmalloc_t * vm,Cxdisc_t * disc)405 type_internal(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxoperand_t* ret, const char* buf, size_t size, Vmalloc_t* vm, Cxdisc_t* disc)
406 {
407 	return -1;
408 }
409 
410 #define BT(r,n,s,d,e,i,m)	{s,{n,d,CXH,0,0,e,i,r,0,0,0,{0},m},},
411 
412 /*
413  * NOTE: void must be first
414  */
415 
416 static Type_t types[] =
417 {
418 BT(CX_void,	&name_void[0],	   &state.type_void,	"No value. May be used for padding.", void_external, void_internal, 0)
419 BT(CX_number,	&name_number[0],   &state.type_number,	"An integral or floating point number.", number_external, number_internal, 0)
420 BT(CX_string,	&name_string[0],   &state.type_string,	"A string. The format details string specifies quoting and C style character escape processing: \bquote\b[=\achar\a] quotes string with \achar\a (\b\"\b) as the begin and end quote; \bendquote\b=\achar\a changes the \bquote\b end to \achar\a; \bshell\b[=\abeg\a] quotes using shell conventions and \abeg\a (\b$'\b) as the quote begin; \bopt\b performs \bquote\b and \bshell\b quoting only when necessary; \bescape\b assumes that escape processing has already been performed; \bwide\b does not escape characters with the bit 8 set; \bexpand\b=\amask\a expands escaped characters according to \amask\a which may be a \b,\b or \b|\b separated list of \ball\b: expand all escaped chars, \bchar\b expands 7 bit character escapes, \bline\b expands \b\\r\b and \b\\n\b escapes, \bwide\b expands wide character escapes, \bnocr\b eliminates \b\\r\b, and \bnonl\b eliminates \b\\n\b.", string_external, string_internal, &match_string)
421 BT(CX_reference,&name_reference[0],&state.type_reference,"A referenced type.", 0,0,0)
422 BT(CX_buffer,	&name_buffer[0],   &state.type_buffer,	"A separately allocated sized buffer. The external representation is a newline separated base64 mime encoding.", buffer_external, buffer_internal, 0)
423 BT(CX_bool,	&name_bool[0],   &state.type_bool,	"An boolean value: 0==false, 1==true.", bool_external, bool_internal, 0)
424 BT(CX_type,	&name_type[0],   &state.type_type_t,	"A type.", type_external, type_internal, 0)
425 BT(CX_pointer,	&name_pointer[0],  0,			"A generic pointer.", 0,0,0)
426 };
427 
428 #define CX_bool_t	((Cxtype_t*)&name_bool[0])
429 #define CX_buffer_t	((Cxtype_t*)&name_buffer[0])
430 #define CX_number_t	((Cxtype_t*)&name_number[0])
431 #define CX_pointer_t	((Cxtype_t*)&name_pointer[0])
432 #define CX_reference_t	((Cxtype_t*)&name_reference[0])
433 #define CX_string_t	((Cxtype_t*)&name_string[0])
434 #define CX_type_t	((Cxtype_t*)&name_type[0])
435 #define CX_void_t	((Cxtype_t*)&name_void[0])
436 
437 static int
op_call_V(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)438 op_call_V(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
439 {
440 	return (*pc->data.variable->function)(cx, pc->data.variable, r, b + (pc->pp + 1), -pc->pp, data, disc);
441 }
442 
443 static int
op_nop_V(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)444 op_nop_V(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
445 {
446 	return 0;
447 }
448 
449 static int
op_end_V(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)450 op_end_V(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
451 {
452 	/*
453 	 * NOTE: this special case error return breaks out of the
454 	 *       cxeval() execute() inner loop and is not recorded
455 	 *	 as an error
456 	 */
457 
458 	return -1;
459 }
460 
461 static int
op_ref_V(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)462 op_ref_V(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
463 {
464 	return (r->value.variable = cxvariable(cx, b->value.string.data, a->type, disc)) ? 0 : -1;
465 }
466 
467 static int
op_sc_V(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)468 op_sc_V(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
469 {
470 	if ((pc->op == CX_SC0) == (b->value.number == 0))
471 	{
472 		cx->jump = (int)pc->data.number;
473 		return 1;
474 	}
475 	return 0;
476 }
477 
478 static int
op_const_V(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)479 op_const_V(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
480 {
481 	r->value = pc->data;
482 	return 0;
483 }
484 
485 static int
op_tst_V(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)486 op_tst_V(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
487 {
488 	if (cx->disc->errorf)
489 		(*cx->disc->errorf)(cx, cx->disc, 2, "CX_TST not implemented yet");
490 	return -1;
491 }
492 
493 static int
op_add_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)494 op_add_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
495 {
496 	r->value.number = a->value.number + b->value.number;
497 	return 0;
498 }
499 
500 static int
op_sub_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)501 op_sub_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
502 {
503 	r->value.number = a->value.number - b->value.number;
504 	return 0;
505 }
506 
507 static int
op_mpy_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)508 op_mpy_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
509 {
510 	r->value.number = a->value.number * b->value.number;
511 	return 0;
512 }
513 
514 static int
op_div_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)515 op_div_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
516 {
517 	if (b->value.number == 0.0)
518 	{
519 		if (cx->disc->errorf)
520 			(*cx->disc->errorf)(cx, cx->disc, 2, "divide by 0");
521 		return -1;
522 	}
523 	r->value.number = a->value.number / b->value.number;
524 	return 0;
525 }
526 
527 static int
op_mod_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)528 op_mod_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
529 {
530 	if (b->value.number < 1.0)
531 	{
532 		if (cx->disc->errorf)
533 			(*cx->disc->errorf)(cx, cx->disc, 2, "modulus by number < 1.0");
534 		return -1;
535 	}
536 	r->value.number = a->value.number / b->value.number;
537 	r->value.number = (Cxnumber_t)(((Cxinteger_t)a->value.number) % ((Cxinteger_t)b->value.number));
538 	return 0;
539 }
540 
541 static int
op_and_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)542 op_and_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
543 {
544 	r->value.number = (Cxnumber_t)(((Cxinteger_t)a->value.number) & ((Cxinteger_t)b->value.number));
545 	return 0;
546 }
547 
548 static int
op_or_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)549 op_or_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
550 {
551 	r->value.number = (Cxnumber_t)(((Cxinteger_t)a->value.number) | ((Cxinteger_t)b->value.number));
552 	return 0;
553 }
554 
555 static int
op_xor_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)556 op_xor_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
557 {
558 	r->value.number = (Cxnumber_t)(((Cxinteger_t)a->value.number) ^ ((Cxinteger_t)b->value.number));
559 	return 0;
560 }
561 
562 static int
op_andand_L(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)563 op_andand_L(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
564 {
565 	r->value.number = a->value.number > 0 && b->value.number > 0;
566 	return 0;
567 }
568 
569 static int
op_oror_L(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)570 op_oror_L(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
571 {
572 	r->value.number = a->value.number > 0 || b->value.number > 0;
573 	return 0;
574 }
575 
576 static int
op_lsh_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)577 op_lsh_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
578 {
579 	r->value.number = (Cxnumber_t)(((Cxinteger_t)a->value.number) << ((int)b->value.number));
580 	return 0;
581 }
582 
583 static int
op_rsh_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)584 op_rsh_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
585 {
586 	r->value.number = (Cxnumber_t)(((Cxinteger_t)a->value.number) >> ((int)b->value.number));
587 	return 0;
588 }
589 
590 static int
op_inv_L(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)591 op_inv_L(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
592 {
593 	r->value.number = (Cxnumber_t)(~((Cxinteger_t)a->value.number));
594 	return 0;
595 }
596 
597 static int
op_log_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)598 op_log_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
599 {
600 	r->value.number = b->value.number > 0.0;
601 	return 0;
602 }
603 
604 static int
op_not_L(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)605 op_not_L(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
606 {
607 	r->value.number = b->value.number == 0.0;
608 	return 0;
609 }
610 
611 static int
op_uplus_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)612 op_uplus_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
613 {
614 	r->value.number = b->value.number;
615 	return 0;
616 }
617 
618 static int
op_uminus_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)619 op_uminus_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
620 {
621 	r->value.number = -b->value.number;
622 	return 0;
623 }
624 
625 static int
op_lt_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)626 op_lt_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
627 {
628 	r->value.number = a->value.number < b->value.number;
629 	return 0;
630 }
631 
632 static int
op_le_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)633 op_le_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
634 {
635 	r->value.number = a->value.number <= b->value.number;
636 	return 0;
637 }
638 
639 static int
op_eq_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)640 op_eq_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
641 {
642 	r->value.number = a->value.number == b->value.number;
643 	return 0;
644 }
645 
646 static int
op_ne_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)647 op_ne_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
648 {
649 	r->value.number = a->value.number != b->value.number;
650 	return 0;
651 }
652 
653 static int
op_ge_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)654 op_ge_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
655 {
656 	r->value.number = a->value.number >= b->value.number;
657 	return 0;
658 }
659 
660 static int
op_gt_N(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)661 op_gt_N(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
662 {
663 	r->value.number = a->value.number > b->value.number;
664 	return 0;
665 }
666 
667 static int
op_log_S(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)668 op_log_S(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
669 {
670 	r->value.number = b->value.string.size > 0 && b->value.string.data[0] != 0;
671 	return 0;
672 }
673 
674 static int
op_lt_S(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)675 op_lt_S(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
676 {
677 	int	c;
678 
679 	r->value.number = (c = strncmp(a->value.string.data, b->value.string.data, CXMIN(a->value.string.size, b->value.string.size))) < 0 || c == 0 && a->value.string.size < b->value.string.size;
680 	return 0;
681 }
682 
683 static int
op_le_S(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)684 op_le_S(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
685 {
686 	int	c;
687 
688 	r->value.number = (c = strncmp(a->value.string.data, b->value.string.data, CXMIN(a->value.string.size, b->value.string.size))) < 0 || c == 0 && a->value.string.size == b->value.string.size;
689 	return 0;
690 }
691 
692 static int
op_eq_S(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)693 op_eq_S(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
694 {
695 	r->value.number = a->value.string.size == b->value.string.size && strncmp(a->value.string.data, b->value.string.data, a->value.string.size) == 0;
696 	return 0;
697 }
698 
699 static int
op_ne_S(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)700 op_ne_S(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
701 {
702 	r->value.number = a->value.string.size != b->value.string.size || strncmp(a->value.string.data, b->value.string.data, a->value.string.size) != 0;
703 	return 0;
704 }
705 
706 static int
op_ge_S(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)707 op_ge_S(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
708 {
709 	int	c;
710 
711 	r->value.number = (c = strncmp(a->value.string.data, b->value.string.data, CXMIN(a->value.string.size, b->value.string.size))) > 0 || c == 0 && a->value.string.size > b->value.string.size;
712 	return 0;
713 }
714 
715 static int
op_gt_S(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)716 op_gt_S(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
717 {
718 	int	c;
719 
720 	r->value.number = (c = strncmp(a->value.string.data, b->value.string.data, CXMIN(a->value.string.size, b->value.string.size))) > 0 || c == 0 && a->value.string.size > b->value.string.size;
721 	return 0;
722 }
723 
724 static int
op_not_S(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)725 op_not_S(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
726 {
727 	r->value.number = *b->value.string.data == 0;
728 	return 0;
729 }
730 
731 static int
op_lt_B(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)732 op_lt_B(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
733 {
734 	int	c;
735 
736 	r->value.number = (c = memcmp(a->value.buffer.data, b->value.buffer.data, CXMIN(a->value.buffer.size, b->value.buffer.size))) < 0 || c == 0 && a->value.buffer.size < b->value.buffer.size;
737 	return 0;
738 }
739 
740 static int
op_le_B(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)741 op_le_B(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
742 {
743 	int	c;
744 
745 	r->value.number = (c = memcmp(a->value.buffer.data, b->value.buffer.data, CXMIN(a->value.buffer.size, b->value.buffer.size))) < 0 || c == 0 && a->value.buffer.size == b->value.buffer.size;
746 	return 0;
747 }
748 
749 static int
op_eq_B(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)750 op_eq_B(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
751 {
752 	r->value.number = a->value.buffer.size == b->value.buffer.size && memcmp(a->value.buffer.data, b->value.buffer.data, a->value.buffer.size) == 0;
753 	return 0;
754 }
755 
756 static int
op_ne_B(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)757 op_ne_B(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
758 {
759 	r->value.number = a->value.buffer.size != b->value.buffer.size || memcmp(a->value.buffer.data, b->value.buffer.data, a->value.buffer.size) != 0;
760 	return 0;
761 }
762 
763 static int
op_ge_B(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)764 op_ge_B(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
765 {
766 	int	c;
767 
768 	r->value.number = (c = memcmp(a->value.buffer.data, b->value.buffer.data, CXMIN(a->value.buffer.size, b->value.buffer.size))) > 0 || c == 0 && a->value.buffer.size > b->value.buffer.size;
769 	return 0;
770 }
771 
772 static int
op_gt_B(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)773 op_gt_B(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
774 {
775 	int	c;
776 
777 	r->value.number = (c = memcmp(a->value.buffer.data, b->value.buffer.data, CXMIN(a->value.buffer.size, b->value.buffer.size))) > 0 || c == 0 && a->value.buffer.size > b->value.buffer.size;
778 	return 0;
779 }
780 
781 static int
op_log_B(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)782 op_log_B(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
783 {
784 	r->value.number = b->value.buffer.data && b->value.buffer.size;
785 	return 0;
786 }
787 
788 static int
op_not_B(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)789 op_not_B(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
790 {
791 	r->value.number = b->value.buffer.size == 0;
792 	return 0;
793 }
794 
795 static int
op_eq_T(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)796 op_eq_T(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
797 {
798 	r->value.number = a->value.type == b->value.type;
799 	return 0;
800 }
801 
802 static int
op_ne_T(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)803 op_ne_T(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
804 {
805 	r->value.number = a->type != b->type;
806 	return 0;
807 }
808 
809 static int
op_cast_SN(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)810 op_cast_SN(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
811 {
812 	char*	e;
813 
814 	r->value.number = strtod(b->value.string.data, &e);
815 	if (*e && cx->disc->errorf)
816 		(*cx->disc->errorf)(cx, cx->disc, 2, "%s: invalid number", b->value.string.data);
817 	return 0;
818 }
819 
820 static int
op_cast_BL(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)821 op_cast_BL(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
822 {
823 	r->value.number = b->value.buffer.data && b->value.buffer.size;
824 	return 0;
825 }
826 
827 static int
op_cast_SL(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)828 op_cast_SL(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
829 {
830 	r->value.number = b->value.string.data && b->value.string.size && *b->value.string.data;
831 	return 0;
832 }
833 
834 static int
op_cast_BN(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)835 op_cast_BN(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
836 {
837 	r->value.number = !!b->value.buffer.data;
838 	return 0;
839 }
840 
841 static int
op_cast_BS(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)842 op_cast_BS(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
843 {
844 	r->value.string.data = b->value.buffer.data ? "1" : "0";
845 	r->value.string.size = 1;
846 	return 0;
847 }
848 
849 static int
op_match(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)850 op_match(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
851 {
852 	int	i;
853 
854 	if ((i = (*a->type->match->execf)(cx, pc->data.pointer, a->type, &a->value, disc)) < 0)
855 		return i;
856 	r->value.number = i == 1;
857 	return 0;
858 }
859 
860 static int
op_nomatch(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)861 op_nomatch(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
862 {
863 	int	i;
864 
865 	if ((i = (*a->type->match->execf)(cx, pc->data.pointer, a->type, &a->value, disc)) < 0)
866 		return i;
867 	r->value.number = i == 0;
868 	return 0;
869 }
870 
871 static int
re_match(Cx_t * cx,Cxexpr_t * expr,Cxinstruction_t * x,Cxinstruction_t * a,Cxinstruction_t * b,void * data,Cxdisc_t * disc)872 re_match(Cx_t* cx, Cxexpr_t* expr, Cxinstruction_t* x, Cxinstruction_t* a, Cxinstruction_t* b, void* data, Cxdisc_t* disc)
873 {
874 	if (!(x->data.pointer = (*a->type->match->compf)(cx, a->type, b->type, &b->data, disc)))
875 		return -1;
876 	if (a->type->match->freef && cxatfree(cx, expr, a->type->match->freef, x->data.pointer))
877 	{
878 		(*a->type->match->freef)(cx, x->data.pointer, disc);
879 		return -1;
880 	}
881 	b->type = state.type_void;
882 	x->callout = x->op == CX_MATCH ? op_match : op_nomatch;
883 	return 0;
884 }
885 
886 static Cxcallout_t callouts[] =
887 {
888 
889 CXC(CX_CALL,	CX_void_t,	CX_void_t,	op_call_V,	0)
890 CXC(CX_NOP,	CX_void_t,	CX_void_t,	op_nop_V,	0)
891 CXC(CX_POP,	CX_void_t,	CX_void_t,	op_nop_V,	0)
892 CXC(CX_REF,	CX_reference_t,	CX_void_t,	op_const_V,	0)
893 CXC(CX_REF,	CX_string_t,	CX_void_t,	op_ref_V,	0)
894 CXC(CX_SC0,	CX_bool_t,	CX_void_t,	op_sc_V,	0)
895 CXC(CX_SC1,	CX_bool_t,	CX_void_t,	op_sc_V,	0)
896 CXC(CX_SC0,	CX_number_t,	CX_void_t,	op_sc_V,	0)
897 CXC(CX_SC1,	CX_number_t,	CX_void_t,	op_sc_V,	0)
898 CXC(CX_TST,	CX_void_t,	CX_void_t,	op_tst_V,	0)
899 CXC(CX_END,	CX_void_t,	CX_void_t,	op_end_V,	0)
900 
901 CXC(CX_NUM,	CX_number_t,	CX_void_t,	op_const_V,	0)
902 CXC(CX_STR,	CX_string_t,	CX_void_t,	op_const_V,	0)
903 CXC(CX_NUM,	CX_type_t,	CX_void_t,	op_const_V,	0)
904 
905 CXC(CX_ADD,	CX_number_t,	CX_number_t,	op_add_N,	0)
906 CXC(CX_SUB,	CX_number_t,	CX_number_t,	op_sub_N,	0)
907 CXC(CX_MPY,	CX_number_t,	CX_number_t,	op_mpy_N,	0)
908 CXC(CX_DIV,	CX_number_t,	CX_number_t,	op_div_N,	0)
909 CXC(CX_MOD,	CX_number_t,	CX_number_t,	op_mod_N,	0)
910 CXC(CX_AND,	CX_number_t,	CX_number_t,	op_and_N,	0)
911 CXC(CX_OR,	CX_number_t,	CX_number_t,	op_or_N,	0)
912 CXC(CX_XOR,	CX_number_t,	CX_number_t,	op_xor_N,	0)
913 
914 CXC(CX_ANDAND,	CX_bool_t,	CX_bool_t,	op_andand_L,	0)
915 CXC(CX_OROR,	CX_bool_t,	CX_bool_t,	op_oror_L,	0)
916 CXC(CX_INV,	CX_bool_t,	CX_void_t,	op_inv_L,	0)
917 CXC(CX_NOT,	CX_bool_t,	CX_void_t,	op_not_L,	0)
918 
919 CXC(CX_ANDAND,	CX_number_t,	CX_number_t,	op_andand_L,	0)
920 CXC(CX_OROR,	CX_number_t,	CX_number_t,	op_oror_L,	0)
921 CXC(CX_INV,	CX_number_t,	CX_void_t,	op_inv_L,	0)
922 CXC(CX_NOT,	CX_number_t,	CX_void_t,	op_not_L,	0)
923 
924 CXC(CX_LSH,	CX_number_t,	CX_number_t,	op_lsh_N,	0)
925 CXC(CX_RSH,	CX_number_t,	CX_number_t,	op_rsh_N,	0)
926 
927 CXC(CX_UPLUS,	CX_number_t,	CX_void_t,	op_uplus_N,	0)
928 CXC(CX_UMINUS,	CX_number_t,	CX_void_t,	op_uminus_N,	0)
929 
930 CXC(CX_LT,	CX_number_t,	CX_number_t,	op_lt_N,	0)
931 CXC(CX_LE,	CX_number_t,	CX_number_t,	op_le_N,	0)
932 CXC(CX_EQ,	CX_number_t,	CX_number_t,	op_eq_N,	0)
933 CXC(CX_NE,	CX_number_t,	CX_number_t,	op_ne_N,	0)
934 CXC(CX_GE,	CX_number_t,	CX_number_t,	op_ge_N,	0)
935 CXC(CX_GT,	CX_number_t,	CX_number_t,	op_gt_N,	0)
936 
937 CXC(CX_LT,	CX_string_t,	CX_string_t,	op_lt_S,	0)
938 CXC(CX_LE,	CX_string_t,	CX_string_t,	op_le_S,	0)
939 CXC(CX_EQ,	CX_string_t,	CX_string_t,	op_eq_S,	0)
940 CXC(CX_NE,	CX_string_t,	CX_string_t,	op_ne_S,	0)
941 CXC(CX_GE,	CX_string_t,	CX_string_t,	op_ge_S,	0)
942 CXC(CX_GT,	CX_string_t,	CX_string_t,	op_gt_S,	0)
943 
944 CXC(CX_LT,	CX_buffer_t,	CX_buffer_t,	op_lt_B,	0)
945 CXC(CX_LE,	CX_buffer_t,	CX_buffer_t,	op_le_B,	0)
946 CXC(CX_EQ,	CX_buffer_t,	CX_buffer_t,	op_eq_B,	0)
947 CXC(CX_NE,	CX_buffer_t,	CX_buffer_t,	op_ne_B,	0)
948 CXC(CX_GE,	CX_buffer_t,	CX_buffer_t,	op_ge_B,	0)
949 CXC(CX_GT,	CX_buffer_t,	CX_buffer_t,	op_gt_B,	0)
950 
951 CXC(CX_EQ,	CX_type_t,	CX_type_t,	op_eq_T,	0)
952 CXC(CX_NE,	CX_type_t,	CX_type_t,	op_ne_T,	0)
953 
954 CXC(CX_CAST,	CX_string_t,	CX_bool_t,	op_cast_SL,	0)
955 CXC(CX_CAST,	CX_buffer_t,	CX_bool_t,	op_cast_BL,	0)
956 CXC(CX_CAST,	CX_string_t,	CX_number_t,	op_cast_SN,	0)
957 CXC(CX_CAST,	CX_buffer_t,	CX_number_t,	op_cast_BN,	0)
958 CXC(CX_CAST,	CX_buffer_t,	CX_string_t,	op_cast_BS,	0)
959 
960 };
961 
962 size_t
cxsizeof(Cx_t * cx,Cxvariable_t * var,Cxtype_t * type,Cxvalue_t * value)963 cxsizeof(Cx_t* cx, Cxvariable_t* var, Cxtype_t* type, Cxvalue_t* value)
964 {
965 	size_t		size;
966 
967 	if (var->array)
968 		size = var->array->size;
969 	else
970 		do
971 		{
972 			if (size = type->size)
973 				break;
974 			switch (type->representation)
975 			{
976 			case CX_buffer:
977 				if (size = value->buffer.size)
978 				{
979 					if (value->buffer.elements)
980 						size = value->buffer.elements;
981 					else if (type->element)
982 						size /= type->element;
983 				}
984 				break;
985 			case CX_number:
986 			case CX_pointer:
987 				size = 8;
988 				break;
989 			case CX_string:
990 				size = value->string.size;
991 				break;
992 			default:
993 				continue;
994 			}
995 			break;
996 		} while (type = type->base);
997 	return size;
998 }
999 
1000 static int
cx_edit_B(Cx_t * cx,Cxvariable_t * var,Cxoperand_t * ret,Cxoperand_t * arg,int n,void * data,Cxdisc_t * disc)1001 cx_edit_B(Cx_t* cx, Cxvariable_t* var, Cxoperand_t* ret, Cxoperand_t* arg, int n, void* data, Cxdisc_t* disc)
1002 {
1003 	Cxedit_t*	edit;
1004 
1005 	if (!(edit = cxedit(cx, arg[0].value.string.data, disc)))
1006 		return -1;
1007 	ret->value = arg[1].value;
1008 	return cxsub(cx, edit, ret);
1009 }
1010 
1011 static int
cx_sizeof_B(Cx_t * cx,Cxvariable_t * var,Cxoperand_t * ret,Cxoperand_t * arg,int n,void * data,Cxdisc_t * disc)1012 cx_sizeof_B(Cx_t* cx, Cxvariable_t* var, Cxoperand_t* ret, Cxoperand_t* arg, int n, void* data, Cxdisc_t* disc)
1013 {
1014 	ret->value.number = cxsizeof(cx, var, arg->type, &arg->value);
1015 	return 0;
1016 }
1017 
1018 static int
cx_typeof_B(Cx_t * cx,Cxvariable_t * var,Cxoperand_t * ret,Cxoperand_t * arg,int n,void * data,Cxdisc_t * disc)1019 cx_typeof_B(Cx_t* cx, Cxvariable_t* var, Cxoperand_t* ret, Cxoperand_t* arg, int n, void* data, Cxdisc_t* disc)
1020 {
1021 	ret->value.type = arg->type;
1022 	return 0;
1023 }
1024 
1025 static Cxvariable_t builtins[] =
1026 {
1027 CXF("edit",		"string",	cx_edit_B,	"string,string",
1028 			"Returns the result of applying the ed(1) style substitute expression"
1029 			" in the first argument to the second argument.")
1030 CXF("sizeof",		"number",	cx_sizeof_B,	"*",
1031 			"Returns the size of the \avariable\a argument;"
1032 			" the size of an array variable is the number of elements,"
1033 			" otherwise the size is in bytes.")
1034 CXF("typeof",		"type_t",	cx_typeof_B,	"*",
1035 			"Returns the type of the \avariable\a argument"
1036 			" for comparison with other types.")
1037 };
1038 
1039 /*
1040  * open a cx session
1041  */
1042 
1043 Cx_t*
cxopen(Cxflags_t flags,Cxflags_t test,Cxdisc_t * disc)1044 cxopen(Cxflags_t flags, Cxflags_t test, Cxdisc_t* disc)
1045 {
1046 	register Cx_t*		cx;
1047 	register Vmalloc_t*	vm;
1048 	register Vmalloc_t*	em;
1049 	register Vmalloc_t*	rm;
1050 
1051 	if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) || !(em = vmopen(Vmdcheap, Vmlast, 0)) || !(rm = vmopen(Vmdcheap, Vmlast, 0)))
1052 	{
1053 		if (vm)
1054 		{
1055 			vmclose(vm);
1056 			if (em)
1057 				vmclose(em);
1058 		}
1059 		if (disc->errorf)
1060 			(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1061 		return 0;
1062 	}
1063 	if (!(cx = vmnewof(vm, 0, Cx_t, 1, 0)) || !(cx->cvtbuf = vmnewof(vm, 0, char, cx->cvtsiz = CX_CVT, 0)))
1064 	{
1065 		if (disc->errorf)
1066 			(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1067 		goto bad;
1068 	}
1069 	cx->state = cxstate(disc);
1070 	cx->table = &table;
1071 	cx->id = CX_ID;
1072 	cx->vm = vm;
1073 	cx->em = em;
1074 	cx->rm = rm;
1075 	cx->disc = disc;
1076 	cx->flags = flags;
1077 	cx->test = test;
1078 	cx->redisc.re_version = REG_VERSION;
1079 	cx->redisc.re_flags = REG_NOFREE;
1080 	cx->redisc.re_errorf = (regerror_t)disc->errorf;
1081 	if (!(cx->buf = sfstropen()) || !(cx->tp = sfstropen()))
1082 	{
1083 		cxclose(cx);
1084 		return 0;
1085 	}
1086 	cx->scoped = 1;
1087 	if (!(flags & CX_SCOPE))
1088 	{
1089 		cx->op = sfstdout;
1090 		if (!(cx->fields = dtnew(cx->vm, &state.listdisc, Dtqueue)) || !(cx->buf = sfstropen()) || !(cx->tp = sfstropen()))
1091 		{
1092 			cxclose(cx);
1093 			return 0;
1094 		}
1095 		cx->callouts = state.callouts;
1096 		cx->constraints = state.constraints;
1097 		cx->edits = state.edits;
1098 		cx->maps = state.maps;
1099 		cx->queries = state.queries;
1100 		cx->recodes = state.recodes;
1101 		cx->types = state.types;
1102 		cx->variables = state.variables;
1103 	}
1104 	cx->ctype = state.ctype;
1105 	return cx;
1106  bad:
1107 	vmclose(vm);
1108 	vmclose(em);
1109 	vmclose(rm);
1110 	return 0;
1111 }
1112 
1113 /*
1114  * scope control
1115  */
1116 
1117 Cx_t*
cxscope(Cx_t * top,Cx_t * bot,Cxflags_t flags,Cxflags_t test,Cxdisc_t * disc)1118 cxscope(Cx_t* top, Cx_t* bot, Cxflags_t flags, Cxflags_t test, Cxdisc_t* disc)
1119 {
1120 	if (!top)
1121 	{
1122 		if (!(top = cxopen(CX_SCOPE|flags, test, disc)))
1123 			return 0;
1124 		top->op = sfstdout;
1125 	}
1126 	if (top->scoped != 1)
1127 	{
1128 		if (top->disc->errorf)
1129 			(*top->disc->errorf)(NiL, top->disc, 2, "cannot change active scope");
1130 		return 0;
1131 	}
1132 	if (bot)
1133 	{
1134 		/*
1135 		 * scope top on bot
1136 		 */
1137 
1138 		if (top->scope)
1139 		{
1140 			if (top->disc->errorf)
1141 				(*top->disc->errorf)(NiL, top->disc, 2, "already scoped");
1142 			return 0;
1143 		}
1144 		if (top->view & CX_VIEW_callouts)
1145 			dtview(top->callouts, bot->callouts);
1146 		else
1147 			top->callouts = bot->callouts;
1148 		if (top->view & CX_VIEW_constraints)
1149 			dtview(top->constraints, bot->constraints);
1150 		else
1151 			top->constraints = bot->constraints;
1152 		if (top->view & CX_VIEW_edits)
1153 			dtview(top->edits, bot->edits);
1154 		else
1155 			top->edits = bot->edits;
1156 		if (top->view & CX_VIEW_fields)
1157 			dtview(top->fields, bot->fields);
1158 		else
1159 			top->fields = bot->fields;
1160 		if (top->view & CX_VIEW_maps)
1161 			dtview(top->maps, bot->maps);
1162 		else
1163 			top->maps = bot->maps;
1164 		if (top->view & CX_VIEW_queries)
1165 			dtview(top->queries, bot->queries);
1166 		else
1167 			top->queries = bot->queries;
1168 		if (top->view & CX_VIEW_recodes)
1169 			dtview(top->recodes, bot->recodes);
1170 		else
1171 			top->recodes = bot->recodes;
1172 		if (top->view & CX_VIEW_types)
1173 			dtview(top->types, bot->types);
1174 		else
1175 			top->types = bot->types;
1176 		if (top->view & CX_VIEW_variables)
1177 			dtview(top->variables, bot->variables);
1178 		else
1179 			top->variables = bot->variables;
1180 		bot->scoped++;
1181 		top->scope = bot;
1182 		bot = top;
1183 	}
1184 	else if (bot = top->scope)
1185 	{
1186 		/*
1187 		 * pop the scope
1188 		 */
1189 
1190 		if (top->view & CX_VIEW_callouts)
1191 			dtview(top->callouts, NiL);
1192 		if (top->view & CX_VIEW_constraints)
1193 			dtview(top->constraints, NiL);
1194 		if (top->view & CX_VIEW_edits)
1195 			dtview(top->edits, NiL);
1196 		if (top->view & CX_VIEW_fields)
1197 			dtview(top->fields, NiL);
1198 		if (top->view & CX_VIEW_maps)
1199 			dtview(top->maps, NiL);
1200 		if (top->view & CX_VIEW_queries)
1201 			dtview(top->queries, NiL);
1202 		if (top->view & CX_VIEW_recodes)
1203 			dtview(top->recodes, NiL);
1204 		if (top->view & CX_VIEW_types)
1205 			dtview(top->types, NiL);
1206 		if (top->view & CX_VIEW_variables)
1207 			dtview(top->variables, NiL);
1208 		top->scope = 0;
1209 		bot->scoped--;
1210 	}
1211 	else
1212 		bot = top;
1213 	return bot;
1214 }
1215 
1216 /*
1217  * close a cx session
1218  */
1219 
1220 int
cxclose(register Cx_t * cx)1221 cxclose(register Cx_t* cx)
1222 {
1223 	if (!cx)
1224 		return -1;
1225 	if (cx->scope)
1226 		cxscope(cx, NiL, 0, 0, cx->disc);
1227 	if (--cx->scoped <= 0)
1228 	{
1229 		if (cx->view & CX_VIEW_callouts)
1230 			dtclose(cx->callouts);
1231 		if (cx->view & CX_VIEW_constraints)
1232 			dtclose(cx->constraints);
1233 		if (cx->view & CX_VIEW_edits)
1234 			dtclose(cx->edits);
1235 		if (cx->view & CX_VIEW_maps)
1236 			dtclose(cx->maps);
1237 		if (cx->view & CX_VIEW_queries)
1238 			dtclose(cx->queries);
1239 		if (cx->view & CX_VIEW_recodes)
1240 			dtclose(cx->recodes);
1241 		if (cx->view & CX_VIEW_types)
1242 			dtclose(cx->types);
1243 		if (cx->view & CX_VIEW_fields)
1244 			dtclose(cx->fields);
1245 		if (cx->view & CX_VIEW_variables)
1246 			dtclose(cx->variables);
1247 		if (cx->scope)
1248 			cx->scope->scoped--;
1249 		if (cx->buf)
1250 			sfclose(cx->buf);
1251 		if (cx->tp)
1252 			sfclose(cx->tp);
1253 		if (cx->em)
1254 			vmclose(cx->em);
1255 		if (cx->rm)
1256 			vmclose(cx->rm);
1257 		if (cx->vm)
1258 			vmclose(cx->vm);
1259 	}
1260 	return 0;
1261 }
1262 
1263 /*
1264  * add a type
1265  */
1266 
1267 int
cxaddtype(Cx_t * cx,register Cxtype_t * type,Cxdisc_t * disc)1268 cxaddtype(Cx_t* cx, register Cxtype_t* type, Cxdisc_t* disc)
1269 {
1270 	char*		base;
1271 	Cxvariable_t*	v;
1272 	Cxvariable_t*	member;
1273 	Dt_t*		dict;
1274 	Cxtype_t*	copy;
1275 	Cxrecode_t*	re;
1276 	int		i;
1277 
1278 	if (cx)
1279 	{
1280 		disc = cx->disc;
1281 		if (cx->view & CX_VIEW_types)
1282 			dict = cx->types;
1283 		else if (!(dict = dtnew(cx->vm, &state.namedisc, Dtoset)))
1284 		{
1285 			if (disc->errorf)
1286 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1287 			return -1;
1288 		}
1289 		else
1290 		{
1291 			dtview(dict, cx->types);
1292 			cx->types = dict;
1293 			cx->view |= CX_VIEW_types;
1294 		}
1295 		if (!(copy = vmnewof(cx->vm, 0, Cxtype_t, 1, 0)))
1296 		{
1297 			if (disc->errorf)
1298 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1299 			return -1;
1300 		}
1301 		*copy = *type;
1302 		type = copy;
1303 	}
1304 	else
1305 		dict = state.types;
1306 	if (dtsearch(dict, type))
1307 	{
1308 		if (disc->errorf)
1309 			(*disc->errorf)(NiL, disc, 2, "%s: type already defined", type->name);
1310 		return -1;
1311 	}
1312 	dtinsert(dict, type);
1313 	if (!(type->header.flags & CX_NORMALIZED))
1314 	{
1315 		type->header.flags |= CX_NORMALIZED;
1316 		if ((base = (char*)type->base) && !(type->base = cxtype(cx, base, disc)))
1317 		{
1318 			if (disc->errorf)
1319 				(*disc->errorf)(NiL, disc, 2, "%s: unknown base type", base);
1320 			return -1;
1321 		}
1322 		if ((base = (char*)type->fundamental) && !(type->fundamental = cxtype(cx, base, disc)))
1323 		{
1324 			if (disc->errorf)
1325 				(*disc->errorf)(NiL, disc, 2, "%s: unknown fundamental type", base);
1326 			return -1;
1327 		}
1328 		if (type->member)
1329 		{
1330 			if (!type->member->getf)
1331 			{
1332 				if (disc->errorf)
1333 					(*disc->errorf)(NiL, disc, 2, "%s: no member get function", type->name);
1334 				return -1;
1335 			}
1336 			if (!(member = (Cxvariable_t*)type->member->members))
1337 			{
1338 				if (disc->errorf)
1339 					(*disc->errorf)(NiL, disc, 2, "%s: no member table", type->name);
1340 				return -1;
1341 			}
1342 			if (type->header.flags & CX_SCHEMA)
1343 				type->header.flags |= CX_REFERENCED;
1344 			else if (!(type->member->members = cx ? dtnew(cx->vm, &state.namedisc, Dtoset) : dtopen(&state.namedisc, Dtoset)))
1345 			{
1346 				if (disc->errorf)
1347 					(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1348 				return -1;
1349 			}
1350 			else
1351 				for (i = 0; member->name; member++)
1352 				{
1353 					v = member;
1354 					if (!(v->header.flags & CX_NORMALIZED))
1355 					{
1356 						v->header.flags |= CX_NORMALIZED;
1357 						if ((base = (char*)v->type) && !(v->type = cxtype(cx, base, disc)))
1358 						{
1359 							if (disc->errorf)
1360 								(*disc->errorf)(NiL, disc, 2, "%s: unknown type", base);
1361 							return -1;
1362 						}
1363 						v->member = type;
1364 					}
1365 					v->header.index = i++;
1366 					dtinsert(type->member->members, v);
1367 				}
1368 		}
1369 		if (type->generic)
1370 			for (i = 0; base = (char*)type->generic[i]; i++)
1371 				if (!(type->generic[i] = cxtype(cx, base, disc)))
1372 				{
1373 					if (disc->errorf)
1374 						(*disc->errorf)(NiL, disc, 2, "%s: unknown type", base);
1375 					return -1;
1376 				}
1377 	}
1378 	if (!(type->header.flags & CX_INITIALIZED))
1379 	{
1380 		type->header.flags |= CX_INITIALIZED;
1381 		if (type->fundamental)
1382 		{
1383 			if (type->base)
1384 				type->representation = type->base->representation;
1385 		}
1386 		else if (!type->base)
1387 			type->fundamental = type;
1388 		else
1389 		{
1390 			type->fundamental = type->base->fundamental;
1391 			type->representation = type->base->representation;
1392 		}
1393 		if (type->match)
1394 		{
1395 			if (!(re = newof(0, Cxrecode_t, 1, 0)))
1396 			{
1397 				if (disc->errorf)
1398 					(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1399 				return -1;
1400 			}
1401 			re->header.flags = CX_NORMALIZED;
1402 			re->op.code = CX_MATCH;
1403 			re->op.type1 = type;
1404 			re->op.type2 = state.type_void;
1405 			re->recode = re_match;
1406 			if (cxaddrecode(cx, re, disc))
1407 				return -1;
1408 		}
1409 		if (type->initf && !(type->data = (*type->initf)(type, disc)))
1410 			return -1;
1411 	}
1412 	return 0;
1413 }
1414 
1415 /*
1416  * return type given name
1417  */
1418 
1419 Cxtype_t*
cxtype(Cx_t * cx,const char * name,Cxdisc_t * disc)1420 cxtype(Cx_t* cx, const char* name, Cxdisc_t* disc)
1421 {
1422 	register char*	s;
1423 	register char*	lib;
1424 	Cxtype_t*	t;
1425 	size_t		n;
1426 
1427 	if ((s = strchr(name, ':')) && *++s == ':')
1428 	{
1429 		n = s - (char*)name;
1430 		lib = fmtbuf(n);
1431 		memcpy(lib, name, --n);
1432 		lib[n] = 0;
1433 		name = (const char*)s + 1;
1434 	}
1435 	else
1436 		lib = (char*)name;
1437 	if (!(t = (Cxtype_t*)dtmatch(cx ? cx->types : state.types, name)) && disc->loadf && (*disc->loadf)(lib, disc))
1438 		t = (Cxtype_t*)dtmatch(cx ? cx->types : state.types, name);
1439 	return t;
1440 }
1441 
1442 /*
1443  * add an op callout
1444  */
1445 
1446 int
cxaddcallout(Cx_t * cx,register Cxcallout_t * callout,Cxdisc_t * disc)1447 cxaddcallout(Cx_t* cx, register Cxcallout_t* callout, Cxdisc_t* disc)
1448 {
1449 	char*		name;
1450 	Dt_t*		dict;
1451 	Cxcallout_t*	copy;
1452 
1453 	if (cx)
1454 	{
1455 		disc = cx->disc;
1456 		if (cx->view & CX_VIEW_callouts)
1457 			dict = cx->callouts;
1458 		else if (!(dict = dtnew(cx->vm, &state.codedisc, Dtoset)))
1459 		{
1460 			if (disc->errorf)
1461 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1462 			return -1;
1463 		}
1464 		else
1465 		{
1466 			dtview(dict, cx->callouts);
1467 			cx->callouts = dict;
1468 			cx->view |= CX_VIEW_callouts;
1469 		}
1470 		if (!(copy = vmnewof(cx->vm, 0, Cxcallout_t, 1, 0)))
1471 		{
1472 			if (disc->errorf)
1473 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1474 			return -1;
1475 		}
1476 		*copy = *callout;
1477 		callout = copy;
1478 	}
1479 	else if (callout->op.code == CX_GET || callout->op.code == CX_SET || callout->op.code == CX_DEL || callout->op.code == CX_RET)
1480 	{
1481 		if (disc->errorf)
1482 			(*disc->errorf)(NiL, disc, 2, "%s: callout must be local", cxcodename(callout->op.code));
1483 		return -1;
1484 	}
1485 	else
1486 		dict = state.callouts;
1487 	if (!(callout->header.flags & CX_NORMALIZED))
1488 	{
1489 		callout->header.flags |= CX_NORMALIZED;
1490 		if (!(name = (char*)callout->op.type1))
1491 			callout->op.type1 = state.type_void;
1492 		else if (!(callout->op.type1 = cxtype(cx, name, disc)))
1493 		{
1494 			if (disc->errorf)
1495 				(*disc->errorf)(NiL, disc, 2, "%s: unknown type", name);
1496 			return -1;
1497 		}
1498 		if (!(name = (char*)callout->op.type2))
1499 			callout->op.type2 = state.type_void;
1500 		else if (!(callout->op.type2 = cxtype(cx, name, disc)))
1501 		{
1502 			if (disc->errorf)
1503 				(*disc->errorf)(NiL, disc, 2, "%s: unknown type", name);
1504 			return -1;
1505 		}
1506 	}
1507 	if (!(copy = (Cxcallout_t*)dtinsert(dict, callout)) || copy->callout != callout->callout)
1508 	{
1509 		if (disc->errorf)
1510 			(*disc->errorf)(NiL, disc, 2, "callout initialization error");
1511 		return -1;
1512 	}
1513 	return 0;
1514 }
1515 
1516 /*
1517  * return callout given <code,type1,type2>
1518  */
1519 
1520 Cxcallout_f
cxcallout(Cx_t * cx,int code,Cxtype_t * type1,Cxtype_t * type2,Cxdisc_t * disc)1521 cxcallout(Cx_t* cx, int code, Cxtype_t* type1, Cxtype_t* type2, Cxdisc_t* disc)
1522 {
1523 	Cxcallout_t*	callout;
1524 	Cxop_t		op;
1525 
1526 	memset(&op, 0, sizeof(op));
1527 	op.code = code;
1528 	if (!(op.type1 = type1))
1529 		op.type1 = state.type_void;
1530 	if (!(op.type2 = type2))
1531 		op.type2 = state.type_void;
1532 	while (!(callout = (Cxcallout_t*)dtmatch(cx ? cx->callouts : state.callouts, &op)))
1533 	{
1534 		if (op.code == CX_NOMATCH)
1535 			op.code = CX_MATCH;
1536 		else if (op.type2 == state.type_void)
1537 			return 0;
1538 		else
1539 			op.type2 = state.type_void;
1540 	}
1541 	return callout->callout;
1542 }
1543 
1544 /*
1545  * add a query
1546  */
1547 
1548 int
cxaddquery(Cx_t * cx,Cxquery_t * query,Cxdisc_t * disc)1549 cxaddquery(Cx_t* cx, Cxquery_t* query, Cxdisc_t* disc)
1550 {
1551 	Dt_t*		dict;
1552 	Cxquery_t*	copy;
1553 
1554 	if (cx)
1555 	{
1556 		disc = cx->disc;
1557 		if (cx->view & CX_VIEW_queries)
1558 			dict = cx->queries;
1559 		else if (!(dict = dtnew(cx->vm, &state.namedisc, Dtoset)))
1560 		{
1561 			if (disc->errorf)
1562 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1563 			return -1;
1564 		}
1565 		else
1566 		{
1567 			dtview(dict, cx->queries);
1568 			cx->queries = dict;
1569 			cx->view |= CX_VIEW_queries;
1570 		}
1571 		if (!(copy = vmnewof(cx->vm, 0, Cxquery_t, 1, 0)))
1572 		{
1573 			if (disc->errorf)
1574 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1575 			return -1;
1576 		}
1577 		*copy = *query;
1578 		query = copy;
1579 	}
1580 	else
1581 		dict = state.queries;
1582 	if (dtsearch(dict, query))
1583 	{
1584 		if (disc->errorf)
1585 			(*disc->errorf)(NiL, disc, 2, "%s: query already defined", query->name);
1586 		return -1;
1587 	}
1588 	dtinsert(dict, query);
1589 	return 0;
1590 }
1591 
1592 /*
1593  * return query given name
1594  */
1595 
1596 Cxquery_t*
cxquery(Cx_t * cx,const char * name,Cxdisc_t * disc)1597 cxquery(Cx_t* cx, const char* name, Cxdisc_t* disc)
1598 {
1599 	register char*	s;
1600 	register char*	lib;
1601 	Cxquery_t*	q;
1602 	size_t		n;
1603 
1604 	if ((s = strchr(name, ':')) && *++s == ':')
1605 	{
1606 		n = s - (char*)name;
1607 		lib = fmtbuf(n);
1608 		memcpy(lib, name, --n);
1609 		lib[n] = 0;
1610 		name = (const char*)s + 1;
1611 	}
1612 	else
1613 		lib = (char*)name;
1614 	if (!(q = (Cxquery_t*)dtmatch(cx ? cx->queries : state.queries, name)) && disc->loadf && (*disc->loadf)(lib, disc))
1615 		q = (Cxquery_t*)dtmatch(cx ? cx->queries : state.queries, name);
1616 	return q;
1617 }
1618 
1619 /*
1620  * return function given name
1621  */
1622 
1623 Cxvariable_t*
cxfunction(Cx_t * cx,const char * name,Cxdisc_t * disc)1624 cxfunction(Cx_t* cx, const char* name, Cxdisc_t* disc)
1625 {
1626 	register char*	s;
1627 	register char*	p;
1628 	Cxvariable_t*	f;
1629 	Cxlib_t*	lib;
1630 	int		i;
1631 
1632 	if (!cx)
1633 	{
1634 		if (disc->errorf)
1635 			(*disc->errorf)(NiL, disc, 2, "%s: function must be local", name);
1636 		return 0;
1637 	}
1638 	if (f = (Cxvariable_t*)dtmatch(cx->variables, name))
1639 	{
1640 		if (f->function)
1641 			return f;
1642 		if (disc->errorf)
1643 			(*disc->errorf)(NiL, disc, 2, "%s: not a function", name);
1644 		return 0;
1645 	}
1646 	if (!(s = strchr(name, ':')) || *++s != ':')
1647 	{
1648 		if (disc->errorf)
1649 			(*disc->errorf)(NiL, disc, 2, "%s: function must be local", name);
1650 		return 0;
1651 	}
1652 	i = s - (char*)name;
1653 	p = fmtbuf(i);
1654 	memcpy(p, name, --i);
1655 	p[i] = 0;
1656 	if (!disc->loadf || !(lib = (*disc->loadf)(p, disc)))
1657 		return 0;
1658 	if (lib->functions)
1659 		for (i = 0; lib->functions[i].name; i++)
1660 			if (cxaddvariable(cx, &lib->functions[i], disc))
1661 				return 0;
1662 	if (f = (Cxvariable_t*)dtmatch(cx->variables, name))
1663 	{
1664 		if (f->function)
1665 			return f;
1666 		if (disc->errorf)
1667 			(*disc->errorf)(NiL, disc, 2, "%s: not a function", name);
1668 	}
1669 	else
1670 	{
1671 		if (disc->errorf)
1672 			(*disc->errorf)(NiL, disc, 2, "%s: undefined function", name);
1673 		return 0;
1674 	}
1675 	return 0;
1676 }
1677 
1678 /*
1679  * add a map
1680  */
1681 
1682 int
cxaddmap(Cx_t * cx,Cxmap_t * map,Cxdisc_t * disc)1683 cxaddmap(Cx_t* cx, Cxmap_t* map, Cxdisc_t* disc)
1684 {
1685 	Dt_t*		dict;
1686 	Cxmap_t*	copy;
1687 
1688 	if (cx)
1689 	{
1690 		disc = cx->disc;
1691 		if (cx->view & CX_VIEW_maps)
1692 			dict = cx->maps;
1693 		if (!(dict = dtnew(cx->vm, &state.namedisc, Dtoset)))
1694 		{
1695 			if (disc->errorf)
1696 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1697 			return -1;
1698 		}
1699 		else
1700 		{
1701 			dtview(dict, cx->maps);
1702 			cx->maps = dict;
1703 			cx->view |= CX_VIEW_maps;
1704 		}
1705 		if (!(copy = vmnewof(cx->vm, 0, Cxmap_t, 1, 0)))
1706 		{
1707 			if (disc->errorf)
1708 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1709 			return -1;
1710 		}
1711 		*copy = *map;
1712 		map = copy;
1713 	}
1714 	else
1715 		dict = state.maps;
1716 	if (dtsearch(dict, map))
1717 	{
1718 		if (disc->errorf)
1719 			(*disc->errorf)(NiL, disc, 2, "%s: map already defined", map->name);
1720 		return -1;
1721 	}
1722 	if (cxinitmap(map, disc))
1723 		return -1;
1724 	dtinsert(dict, map);
1725 	return 0;
1726 }
1727 
1728 /*
1729  * return map given name
1730  */
1731 
1732 Cxmap_t*
cxmap(Cx_t * cx,const char * name,Cxdisc_t * disc)1733 cxmap(Cx_t* cx, const char* name, Cxdisc_t* disc)
1734 {
1735 	return (Cxmap_t*)dtmatch(cx ? cx->maps : state.maps, name);
1736 }
1737 
1738 /*
1739  * add an op recode
1740  */
1741 
1742 int
cxaddrecode(Cx_t * cx,register Cxrecode_t * recode,Cxdisc_t * disc)1743 cxaddrecode(Cx_t* cx, register Cxrecode_t* recode, Cxdisc_t* disc)
1744 {
1745 	Cxrecode_t*	o;
1746 	char*		name;
1747 	Dt_t*		dict;
1748 	Cxrecode_t*	copy;
1749 
1750 	if (cx)
1751 	{
1752 		disc = cx->disc;
1753 		if (cx->view & CX_VIEW_recodes)
1754 			dict = cx->recodes;
1755 		else if (!(dict = dtnew(cx->vm, &state.codedisc, Dtoset)))
1756 		{
1757 			if (disc->errorf)
1758 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1759 			return -1;
1760 		}
1761 		else
1762 		{
1763 			dtview(dict, cx->recodes);
1764 			cx->recodes = dict;
1765 			cx->view |= CX_VIEW_recodes;
1766 		}
1767 		if (!(copy = vmnewof(cx->vm, 0, Cxrecode_t, 1, 0)))
1768 		{
1769 			if (disc->errorf)
1770 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1771 			return -1;
1772 		}
1773 		*copy = *recode;
1774 		recode = copy;
1775 	}
1776 	else if (recode->op.code == CX_GET || recode->op.code == CX_SET || recode->op.code == CX_DEL || recode->op.code == CX_RET)
1777 	{
1778 		if (disc->errorf)
1779 			(*disc->errorf)(NiL, disc, 2, "%s: recode must be local", cxcodename(recode->op.code));
1780 		return -1;
1781 	}
1782 	else
1783 		dict = state.recodes;
1784 	if (!(recode->header.flags & CX_NORMALIZED))
1785 	{
1786 		recode->header.flags |= CX_NORMALIZED;
1787 		if ((name = (char*)recode->op.type1) && !(recode->op.type1 = cxtype(cx, name, disc)))
1788 		{
1789 			if (disc->errorf)
1790 				(*disc->errorf)(NiL, disc, 2, "%s: unknown type", name);
1791 			return -1;
1792 		}
1793 		if ((name = (char*)recode->op.type2) && !(recode->op.type2 = cxtype(cx, name, disc)))
1794 		{
1795 			if (disc->errorf)
1796 				(*disc->errorf)(NiL, disc, 2, "%s: unknown type", name);
1797 			return -1;
1798 		}
1799 	}
1800 	if (!(o = (Cxrecode_t*)dtsearch(dict, recode)) || o != recode && dtdelete(dict, o))
1801 		dtinsert(dict, recode);
1802 	return 0;
1803 }
1804 
1805 /*
1806  * return recode given <code,type1,type2>
1807  */
1808 
1809 Cxrecode_f
cxrecode(Cx_t * cx,int code,Cxtype_t * type1,Cxtype_t * type2,Cxdisc_t * disc)1810 cxrecode(Cx_t* cx, int code, Cxtype_t* type1, Cxtype_t* type2, Cxdisc_t* disc)
1811 {
1812 	Cxrecode_t*	recode;
1813 	Cxop_t		op;
1814 
1815 	switch (code)
1816 	{
1817 	case CX_NOMATCH:
1818 		code = CX_MATCH;
1819 		/*FALLTHROUGH*/
1820 	case CX_MATCH:
1821 		type2 = 0;
1822 		break;
1823 	}
1824 	memset(&op, 0, sizeof(op));
1825 	op.code = code;
1826 	if (!(op.type1 = type1))
1827 		op.type1 = state.type_void;
1828 	if (!(op.type2 = type2))
1829 		op.type2 = state.type_void;
1830 	return (recode = (Cxrecode_t*)dtmatch(cx ? cx->recodes : state.recodes, &op)) ? recode->recode : (Cxrecode_f)0;
1831 }
1832 
1833 /*
1834  * add an edit
1835  */
1836 
1837 int
cxaddedit(Cx_t * cx,register Cxedit_t * edit,Cxdisc_t * disc)1838 cxaddedit(Cx_t* cx, register Cxedit_t* edit, Cxdisc_t* disc)
1839 {
1840 	Dt_t*		dict;
1841 	Cxedit_t*	copy;
1842 
1843 	if (cx)
1844 	{
1845 		disc = cx->disc;
1846 		if (cx->view & CX_VIEW_edits)
1847 			dict = cx->edits;
1848 		else if (!(dict = dtnew(cx->vm, &state.namedisc, Dtoset)))
1849 		{
1850 			if (disc->errorf)
1851 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1852 			return -1;
1853 		}
1854 		else
1855 		{
1856 			dtview(dict, cx->edits);
1857 			cx->edits = dict;
1858 			cx->view |= CX_VIEW_edits;
1859 		}
1860 		if (!(copy = vmnewof(cx->vm, 0, Cxedit_t, 1, 0)))
1861 		{
1862 			if (disc->errorf)
1863 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1864 			return -1;
1865 		}
1866 		*copy = *edit;
1867 		edit = copy;
1868 	}
1869 	else
1870 		dict = state.edits;
1871 	if (dtsearch(dict, edit))
1872 	{
1873 		if (disc->errorf)
1874 			(*disc->errorf)(NiL, disc, 2, "%s: edit already defined", edit->name);
1875 		return -1;
1876 	}
1877 	dtinsert(dict, edit);
1878 	if (!(edit->header.flags & CX_INITIALIZED))
1879 	{
1880 		edit->header.flags |= CX_INITIALIZED;
1881 		if (edit->initf && !(edit->data = (*edit->initf)(edit, disc)))
1882 			return -1;
1883 	}
1884 	return 0;
1885 }
1886 
1887 /*
1888  * return edit given name
1889  * optional substitute expression instantiated
1890  */
1891 
1892 Cxedit_t*
cxedit(Cx_t * cx,const char * data,Cxdisc_t * disc)1893 cxedit(Cx_t* cx, const char* data, Cxdisc_t* disc)
1894 {
1895 	Cxedit_t*	e;
1896 	Cxedit_t*	o;
1897 	char*		s;
1898 
1899 	e = (Cxedit_t*)dtmatch(cx ? cx->edits : state.edits, data);
1900 	if (isalpha(*data))
1901 	{
1902 		if (!e)
1903 		{
1904 			if (disc->errorf)
1905 				(*disc->errorf)(NiL, disc, 2, "%s: edit not defined", data);
1906 			return 0;
1907 		}
1908 		o = e;
1909 		if (!(e = cx ? vmnewof(cx->vm, 0, Cxedit_t, 1, 0) : newof(0, Cxedit_t, 1, 0)))
1910 		{
1911 			if (disc->errorf)
1912 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1913 			return 0;
1914 		}
1915 		e->name = o->name;
1916 		e->description = o->description;
1917 		e->initf = o->initf;
1918 		e->num2strf = o->num2strf;
1919 		e->str2numf = o->str2numf;
1920 	}
1921 	else if (e)
1922 		return e;
1923 	else
1924 	{
1925 		if (!(e = cx ? vmnewof(cx->vm, 0, Cxedit_t, 1, strlen(data) + 1) : newof(0, Cxedit_t, 1, strlen(data) + 1)))
1926 		{
1927 			if (disc->errorf)
1928 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1929 			return 0;
1930 		}
1931 		e->redisc.re_version = REG_VERSION;
1932 		e->redisc.re_errorf = (regerror_t)disc->errorf;
1933 		if (cx)
1934 		{
1935 			e->redisc.re_flags = REG_NOFREE;
1936 			e->redisc.re_resizef = (regresize_t)vmgetmem;
1937 			e->redisc.re_resizehandle = cx->vm;
1938 		}
1939 		e->re.re_disc = &e->redisc;
1940 		s = (char*)data;
1941 		if (regcomp(&e->re, s, REG_DELIMITED|REG_LENIENT|REG_NULL))
1942 			return 0;
1943 		s += e->re.re_npat;
1944 		if (regsubcomp(&e->re, s, NiL, 0, 0))
1945 			return 0;
1946 		s += e->re.re_npat;
1947 		e->re.re_npat = s - (char*)data;
1948 		if (*s && disc->errorf)
1949 			(*disc->errorf)(NiL, disc, 1, "invalid character after substitution: %s", s);
1950 		strcpy((char*)(e->name = (const char*)(e + 1)), data);
1951 		if (cx && cxaddedit(cx, e, disc))
1952 			return 0;
1953 	}
1954 	return e;
1955 }
1956 
1957 /*
1958  * add a constraint
1959  */
1960 
1961 int
cxaddconstraint(Cx_t * cx,register Cxconstraint_t * constraint,Cxdisc_t * disc)1962 cxaddconstraint(Cx_t* cx, register Cxconstraint_t* constraint, Cxdisc_t* disc)
1963 {
1964 	Dt_t*		dict;
1965 	Cxconstraint_t*	copy;
1966 
1967 	if (cx)
1968 	{
1969 		disc = cx->disc;
1970 		if (cx->view & CX_VIEW_constraints)
1971 			dict = cx->constraints;
1972 		else if (!(dict = dtnew(cx->vm, &state.namedisc, Dtoset)))
1973 		{
1974 			if (disc->errorf)
1975 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1976 			return -1;
1977 		}
1978 		else
1979 		{
1980 			dtview(dict, cx->constraints);
1981 			cx->constraints = dict;
1982 			cx->view |= CX_VIEW_constraints;
1983 		}
1984 		if (!(copy = vmnewof(cx->vm, 0, Cxconstraint_t, 1, 0)))
1985 		{
1986 			if (disc->errorf)
1987 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
1988 			return -1;
1989 		}
1990 		*copy = *constraint;
1991 		constraint = copy;
1992 	}
1993 	else
1994 		dict = state.constraints;
1995 	if (dtsearch(dict, constraint))
1996 	{
1997 		if (disc->errorf)
1998 			(*disc->errorf)(NiL, disc, 2, "%s: constraint already defined", constraint->name);
1999 		return -1;
2000 	}
2001 	if (!(constraint->header.flags & CX_INITIALIZED))
2002 	{
2003 		constraint->header.flags |= CX_INITIALIZED;
2004 		if (constraint->initf && !(constraint->data = (*constraint->initf)(constraint, disc)))
2005 			return -1;
2006 	}
2007 	dtinsert(dict, constraint);
2008 	return 0;
2009 }
2010 
2011 /*
2012  * return constraint given name
2013  */
2014 
2015 Cxconstraint_t*
cxconstraint(Cx_t * cx,const char * name,Cxdisc_t * disc)2016 cxconstraint(Cx_t* cx, const char* name, Cxdisc_t* disc)
2017 {
2018 	return (Cxconstraint_t*)dtmatch(cx ? cx->constraints : state.constraints, name);
2019 }
2020 
2021 /*
2022  * mark type CX_REFERENCED
2023  */
2024 
2025 static void
referenced(register Cxtype_t * type)2026 referenced(register Cxtype_t* type)
2027 {
2028 	register Cxvariable_t*	mp;
2029 
2030 	if (!(type->header.flags & CX_REFERENCED))
2031 	{
2032 		type->header.flags |= CX_REFERENCED;
2033 		if (type->base)
2034 			referenced(type->base);
2035 		if (type->member)
2036 			for (mp = (Cxvariable_t*)dtfirst(type->member->members); mp; mp = (Cxvariable_t*)dtnext(type->member->members, mp))
2037 				referenced(mp->type);
2038 	}
2039 }
2040 
2041 /*
2042  * add a variable
2043  */
2044 
2045 int
cxaddvariable(register Cx_t * cx,register Cxvariable_t * variable,Cxdisc_t * disc)2046 cxaddvariable(register Cx_t* cx, register Cxvariable_t* variable, Cxdisc_t* disc)
2047 {
2048 	Dt_t*	dict;
2049 	Cx_t*	sx;
2050 	char*	name;
2051 
2052 	if (cx)
2053 	{
2054 		disc = cx->disc;
2055 		if (cx->view & CX_VIEW_variables)
2056 			dict = cx->variables;
2057 		else if (!(dict = dtnew(cx->vm, &state.namedisc, Dtoset)))
2058 		{
2059 			if (disc->errorf)
2060 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
2061 			return -1;
2062 		}
2063 		else
2064 		{
2065 			dtview(dict, cx->variables);
2066 			cx->variables = dict;
2067 			cx->view |= CX_VIEW_variables;
2068 		}
2069 	}
2070 	else
2071 		dict = state.variables;
2072 	if (!variable)
2073 		return 0;
2074 	if (dtsearch(dict, variable))
2075 	{
2076 		if (disc->errorf)
2077 			(*disc->errorf)(NiL, disc, 2, "%s: variable already defined", variable->name);
2078 		return -1;
2079 	}
2080 	if (!(variable->header.flags & CX_NORMALIZED))
2081 	{
2082 		variable->header.flags |= CX_NORMALIZED;
2083 		if ((name = (char*)variable->type) && !(variable->type = cxtype(cx, name, disc)))
2084 		{
2085 			if (disc->errorf)
2086 				(*disc->errorf)(NiL, disc, 2, "%s: %s: unknown type", variable->name, name);
2087 			return -1;
2088 		}
2089 	}
2090 	dtinsert(dict, variable);
2091 	if (cx)
2092 	{
2093 		if (sx = cx->scope)
2094 		{
2095 			variable->header.index = sx->index++;
2096 			cx->index = sx->index;
2097 		}
2098 		else
2099 			variable->header.index = cx->index++;
2100 	}
2101 	if (!(variable->header.flags & CX_INITIALIZED))
2102 	{
2103 		variable->header.flags |= CX_INITIALIZED;
2104 		referenced(variable->type);
2105 		if (cx)
2106 			dtinsert(cx->fields, variable);
2107 	}
2108 	return 0;
2109 }
2110 
2111 /*
2112  * return variable given name
2113  */
2114 
2115 Cxvariable_t*
cxvariable(Cx_t * cx,const char * name,register Cxtype_t * m,Cxdisc_t * disc)2116 cxvariable(Cx_t* cx, const char* name, register Cxtype_t* m, Cxdisc_t* disc)
2117 {
2118 	register char*	s;
2119 	register char*	t;
2120 	Cxvariable_t*	v;
2121 	Dt_t*		dict;
2122 	Cxreference_t*	ref;
2123 	Cxreference_t*	head;
2124 	Cxreference_t*	tail;
2125 
2126 	if (!cx)
2127 	{
2128 		if (disc->errorf)
2129 			(*disc->errorf)(NiL, disc, 2, "%s: variable must be local", name);
2130 		return 0;
2131 	}
2132 	disc = cx->disc;
2133 	if (!m || !m->member)
2134 	{
2135 		if (!(v = (Cxvariable_t*)dtmatch(cx->variables, name)))
2136 		{
2137 			if (!(t = strchr(name, '.')))
2138 			{
2139 				if (disc->errorf)
2140 					(*disc->errorf)(NiL, disc, 2, "%s: undefined variable", name);
2141 				return 0;
2142 			}
2143 
2144 			/*
2145 			 * cxparse() never gets here
2146 			 */
2147 
2148 			strcpy(s = fmtbuf(strlen(name) + 1), name);
2149 			t = s + (t - (char*)name);
2150 			m = 0;
2151 			dict = cx->variables;
2152 			head = 0;
2153 			for (;;)
2154 			{
2155 				if (t)
2156 					*t++ = 0;
2157 				v = (Cxvariable_t*)dtmatch(dict, *s ? s : ".");
2158 				if (!v)
2159 				{
2160 					if (disc->errorf)
2161 					{
2162 						if (m)
2163 							(*disc->errorf)(NiL, disc, 2, "%s: not a member of %s", s, m->name);
2164 
2165 						else
2166 							(*disc->errorf)(NiL, disc, 2, "%s: undefined variable", s);
2167 					}
2168 					return 0;
2169 				}
2170 				if (!(ref = newof(0, Cxreference_t, 1, 0)))
2171 				{
2172 					if (disc->errorf)
2173 						(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
2174 					return 0;
2175 				}
2176 				ref->variable = v;
2177 				if (m)
2178 					ref->member = m->member;
2179 				if (head)
2180 					tail->next = ref;
2181 				else
2182 					head = ref;
2183 				tail = ref;
2184 				if (!(s = t))
2185 					break;
2186 				if ((!(m = v->type) || !m->member || !(dict = m->member->members)) &&
2187 				    (!(m = v->type->base) || !m->member || !(dict = m->member->members)))
2188 				{
2189 					if (disc->errorf)
2190 						(*disc->errorf)(NiL, disc, 2, "%s: struct or union variable expected",  v->name);
2191 					return 0;
2192 				}
2193 				t = strchr(t, '.');
2194 			}
2195 			if (!(v = newof(0, Cxvariable_t, 1, strlen(name) + 1)))
2196 			{
2197 				if (disc->errorf)
2198 					(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
2199 				return 0;
2200 			}
2201 			strcpy((char*)(v->name = (const char*)(v + 1)), name);
2202 			v->reference = head;
2203 			v->type = tail->variable->type;
2204 			dtinsert(cx->variables, v);
2205 		}
2206 	}
2207 	else if (!(v = (Cxvariable_t*)dtmatch(m->member->members, name)))
2208 	{
2209 		if (disc->errorf)
2210 			(*disc->errorf)(cx, disc, 2, "%s: not a member of %s", name, m->name);
2211 		return 0;
2212 	}
2213 	return v;
2214 }
2215 
2216 /*
2217  * cast var value to type
2218  * if var==0 or data==0 then the value is already in ret
2219  */
2220 
2221 int
cxcast(Cx_t * cx,Cxoperand_t * ret,Cxvariable_t * var,Cxtype_t * type,void * data,const char * format)2222 cxcast(Cx_t* cx, Cxoperand_t* ret, Cxvariable_t* var, Cxtype_t* type, void* data, const char* format)
2223 {
2224 	Cxinstruction_t	x;
2225 	Cxoperand_t	val;
2226 	Cxreference_t*	ref;
2227 	Cxtype_t*	from;
2228 	unsigned char*	map;
2229 	Cxbuf_t*	cvt;
2230 	char*		s;
2231 	void*		d;
2232 	ssize_t		n;
2233 	Cxunsigned_t	m;
2234 	int		c;
2235 	Cxoperand_t	a;
2236 	Cxoperand_t	b;
2237 	Cxexternal_f	e;
2238 
2239 	x.callout = 0;
2240 	if (x.data.variable = var)
2241 	{
2242 		from = var->type;
2243 		if (!type)
2244 			type = from;
2245 		if (data)
2246 		{
2247 			if (!cx->getf && !(cx->getf = cxcallout(cx, CX_GET, 0, 0, cx->disc)))
2248 			{
2249 				if (cx->disc->errorf)
2250 					(*cx->disc->errorf)(NiL, cx->disc, 3, "%s: cx CX_GET callout must be defined", var->name);
2251 				return -1;
2252 			}
2253 			a.type = state.type_string;
2254 			a.refs = 0;
2255 			a.value.string.size = (a.value.string.data = (char*)format) ? strlen(format) : 0;
2256 			ret->type = var->type;
2257 			ret->value.number = 0;
2258 			if (ref = var->reference)
2259 			{
2260 				b.type = var->type;
2261 				b.refs = 0;
2262 				b.value.number = 0;
2263 				x.data.variable = ref->variable;
2264 				x.type = ret->type = ref->variable->type;
2265 				if (var->name != var->type->name && (*cx->getf)(cx, &x, ret, &a, &b, data, cx->disc))
2266 					return -1;
2267 				while (ref = ref->next)
2268 				{
2269 					b.type = x.data.variable->type;
2270 					x.data.variable = ref->variable;
2271 					ret->type = x.data.variable->type;
2272 					if ((*ref->member->getf)(cx, &x, ret, &a, &b, (ref->member->flags & CX_VIRTUAL) ? data : (void*)0, cx->disc))
2273 						return -1;
2274 				}
2275 			}
2276 			else if ((*cx->getf)(cx, &x, ret, &a, NiL, data, cx->disc))
2277 				return -1;
2278 			if (type == state.type_void)
2279 				return 0;
2280 			from = ret->type;
2281 		}
2282 	}
2283 	else
2284 		from = 0;
2285 	if (var && var->format.map && var->format.map->part && var->format.map->part->edit)
2286 		cxsuball(cx, var->format.map->part, ret);
2287 	if ((var && (c = var->format.code) || (c = type->format.code)) &&
2288 	    c != CC_NATIVE &&
2289 	    (CCCONVERT(c) || !(type->format.flags & CX_BINARY) && (c = CCOP(c, CC_NATIVE))) &&
2290 	    (map = ccmap(CCIN(c), CCOUT(c))))
2291 	{
2292 		if (ret->value.string.size > cx->ccsiz)
2293 		{
2294 			n = roundof(ret->value.string.size + 1, CX_CVT);
2295 			if (!(cx->ccbuf = vmoldof(cx->vm, cx->ccbuf, char, n, 0)))
2296 			{
2297 				if (cx->disc->errorf)
2298 					(*cx->disc->errorf)(NiL, cx->disc, ERROR_SYSTEM|2, "out of space");
2299 				return -1;
2300 			}
2301 			cx->ccsiz = n;
2302 		}
2303 		ret->value.string.data = (char*)ccmapcpy(map, cx->ccbuf, ret->value.string.data, ret->value.string.size);
2304 	}
2305 	if (ret->type->representation != type->representation)
2306 	{
2307 		val = *ret;
2308 		if (cxisstring(type))
2309 		{
2310 			if (!var || !cxisnumber(from) || !var->format.map || format || cxnum2str(cx, &var->format, (Cxinteger_t)ret->value.number, &s))
2311 			{
2312 				if (!(e = ret->type->externalf) && var && var->type->member)
2313 					e = cxmembers;
2314 				if (e)
2315 				{
2316 					if (!(cvt = cx->cvt))
2317 					{
2318 						if (!cx->top)
2319 						{
2320 							if (!(cx->top = vmnewof(cx->vm, 0, Cxbuf_t, 1, 0)) || !(cx->top->data = vmoldof(cx->vm, 0, char, CX_CVT, 0)))
2321 							{
2322 								if (cx->disc->errorf)
2323 									(*cx->disc->errorf)(NiL, cx->disc, ERROR_SYSTEM|2, "out of space");
2324 								return -1;
2325 							}
2326 							cx->top->size = CX_CVT;
2327 						}
2328 						cx->cvt = cx->top;
2329 					}
2330 					else
2331 					{
2332 						if (!cx->cvt->next)
2333 						{
2334 							if (!(cx->cvt->next = vmnewof(cx->vm, 0, Cxbuf_t, 1, 0)) || !(cx->cvt->next->data = vmoldof(cx->vm, 0, char, CX_CVT, 0)))
2335 							{
2336 								if (cx->disc->errorf)
2337 									(*cx->disc->errorf)(NiL, cx->disc, ERROR_SYSTEM|2, "out of space");
2338 								return -1;
2339 							}
2340 							cx->cvt->next->size = CX_CVT;
2341 						}
2342 						cx->cvt = cx->cvt->next;
2343 					}
2344 					while ((n = (*e)(cx, ret->type, format, var ? &var->format : (Cxformat_t*)0, &ret->value, cx->cvt->data, cx->cvt->size, cx->disc)) > cx->cvt->size)
2345 					{
2346 						n = roundof(n + 1, CX_CVT);
2347 						if (!(cx->cvt->data = vmoldof(cx->vm, cx->cvt->data, char, n, 0)))
2348 						{
2349 							if (cx->disc->errorf)
2350 								(*cx->disc->errorf)(NiL, cx->disc, ERROR_SYSTEM|2, "out of space");
2351 							cx->cvt = cvt;
2352 							return -1;
2353 						}
2354 						cx->cvt->size = n;
2355 					}
2356 					if (n < 0)
2357 					{
2358 						cx->cvt = cvt;
2359 						return -1;
2360 					}
2361 					ret->value.string.data = cx->cvt->data;
2362 					ret->value.string.size = n;
2363 					cx->cvt = cvt;
2364 				}
2365 				else if (!cxisnumber(ret->type))
2366 					goto bad;
2367 				else
2368 					ret->value.string.size = strlen(ret->value.string.data = sfprints("%Lg", val.value.number));
2369 			}
2370 			else
2371 				ret->value.string.size = strlen(ret->value.string.data = s);
2372 		}
2373 		else if (var && data)
2374 			goto bad;
2375 		else if (cxisnumber(type) && var && cxisstring(from) && var->format.map && !cxstr2num(cx, &var->format, val.value.string.data, val.value.string.size, &m))
2376 			ret->value.number = (Cxinteger_t)m;
2377 #if 0
2378 		else if (ret->type->representation != type->representation)
2379 			goto bad;
2380 #endif
2381 		else if (!type->internalf || (*type->internalf)(cx, type, format, var ? &var->format : (Cxformat_t*)0, ret, val.value.string.data, val.value.string.size, cx->em, cx->disc) < 0)
2382 			goto bad;
2383 		ret->type = type;
2384 	}
2385 	return 0;
2386  bad:
2387 	if (((x.callout = cxcallout(cx, CX_CAST, ret->type, type, cx->disc)) || ret->type->fundamental && (x.callout = cxcallout(cx, CX_CAST, ret->type->fundamental, type, cx->disc))) && !(*x.callout)(cx, &x, ret, NiL, &val, data, cx->disc))
2388 		return 0;
2389 	if (cx->disc->errorf)
2390 		(*cx->disc->errorf)(cx, cx->disc, 2, "cannot cast %s to %s", ret->type->name, type->name);
2391 	return -1;
2392 }
2393 
2394 /*
2395  * generic struct type externalf that lists defined members in ksh93u compound notation
2396  */
2397 
2398 ssize_t
cxmembers(Cx_t * cx,Cxtype_t * type,const char * details,Cxformat_t * format,Cxvalue_t * value,char * buf,size_t size,Cxdisc_t * disc)2399 cxmembers(Cx_t* cx, Cxtype_t* type, const char* details, Cxformat_t* format, Cxvalue_t* value, char* buf, size_t size, Cxdisc_t* disc)
2400 {
2401 	Cxvariable_t*		v;
2402 	unsigned char*		p;
2403 	unsigned char*		e;
2404 	char*			b;
2405 	char*			m;
2406 	char*			s;
2407 	size_t			n;
2408 	int			i;
2409 	Cxoperand_t		o;
2410 	Cxinstruction_t		x;
2411 
2412 	if (!type->member || !value->buffer.data)
2413 		return 0;
2414 	b = buf;
2415 	m = b + size;
2416 	b += sfsprintf(b, m - b, "( ");
2417 	for (v = (Cxvariable_t*)dtfirst(type->member->members); v; v = (Cxvariable_t*)dtnext(type->member->members, v))
2418 	{
2419 		if (!v->type->generic)
2420 		{
2421 			x.data.variable = v;
2422 			o.type = v->type;
2423 			o.value.buffer.data = value->buffer.data;
2424 			if (!type->member->getf(cx, &x, &o, NiL, NiL, NiL, disc))
2425 			{
2426 				switch (cxrepresentation(v->type))
2427 				{
2428 				case CX_buffer:
2429 					if (!o.value.buffer.size)
2430 						continue;
2431 					p = (unsigned char*)o.value.buffer.data;
2432 					e = p + o.value.buffer.size;
2433 					while (p < e && !*p)
2434 						p++;
2435 					if (p >= e)
2436 						continue;
2437 					break;
2438 				case CX_number:
2439 					if (!o.value.number)
2440 						continue;
2441 					break;
2442 				case CX_string:
2443 					if (!o.value.string.size)
2444 						continue;
2445 					break;
2446 				case CX_void:
2447 					continue;
2448 				}
2449 				if (!cxcast(cx, &o, v, cx->state->type_string, NiL, NiL))
2450 					b += sfsprintf(b, m > b ? m - b : 0, "%s=%s ", v->name, o.value.string.data);
2451 			}
2452 		}
2453 	}
2454 	if ((n = b - buf) <= 2)
2455 		n = 0;
2456 	else
2457 	{
2458 		b += sfsprintf(b, m > b ? m - b : 0, ")");
2459 		n++;
2460 		if (n >= size)
2461 			return n + 1;
2462 		*b = 0;
2463 	}
2464 	return n;
2465 }
2466 
2467 /*
2468  * initialize the global state
2469  */
2470 
2471 static void
initialize(Cxdisc_t * disc)2472 initialize(Cxdisc_t* disc)
2473 {
2474 	register int		i;
2475 
2476 	static const char	cx_alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_$";
2477 	static const char	cx_digit[] = "0123456789";
2478 	static const char	cx_float[] = "_.#";
2479 	static const char	cx_space[] = " \f\n\t\r\v";
2480 
2481 	if (!state.initialized++)
2482 	{
2483 		table.opcode['+'] = CX_ADD;
2484 		table.opcode['&'] = CX_AND;
2485 		table.opcode['/'] = CX_DIV;
2486 		table.opcode['>'] = CX_GT;
2487 		table.opcode['~'] = CX_INV;
2488 		table.opcode['<'] = CX_LT;
2489 		table.opcode['%'] = CX_MOD;
2490 		table.opcode['*'] = CX_MPY;
2491 		table.opcode['!'] = CX_NOT;
2492 		table.opcode['|'] = CX_OR;
2493 		table.opcode['='] = CX_SET;
2494 		table.opcode['-'] = CX_SUB;
2495 		table.opcode['^'] = CX_XOR;
2496 
2497 		table.comparison[CX_LOG] =
2498 		table.comparison[CX_LT] =
2499 		table.comparison[CX_LE] =
2500 		table.comparison[CX_EQ] =
2501 		table.comparison[CX_NE] =
2502 		table.comparison[CX_MATCH] =
2503 		table.comparison[CX_NOMATCH] =
2504 		table.comparison[CX_GE] =
2505 		table.comparison[CX_GT] =	1;
2506 
2507 		table.logical[CX_ANDAND] =
2508 		table.logical[CX_LOG] =
2509 		table.logical[CX_OROR] =
2510 		table.logical[CX_NOT] =		1;
2511 
2512 		table.precedence[CX_INV] =
2513 		table.precedence[CX_LOG] =
2514 		table.precedence[CX_NOT] =
2515 		table.precedence[CX_UMINUS] =
2516 		table.precedence[CX_UPLUS] =	15;
2517 		table.precedence[CX_DIV] =
2518 		table.precedence[CX_MOD] =
2519 		table.precedence[CX_MPY] =	14;
2520 		table.precedence[CX_ADD] =
2521 		table.precedence[CX_SUB] =	13;
2522 		table.precedence[CX_LSH] =
2523 		table.precedence[CX_RSH] =	12;
2524 		table.precedence[CX_GE] =
2525 		table.precedence[CX_GT] =
2526 		table.precedence[CX_LE] =
2527 		table.precedence[CX_LT] =	11;
2528 		table.precedence[CX_EQ] =
2529 		table.precedence[CX_NE] =
2530 		table.precedence[CX_MATCH] =
2531 		table.precedence[CX_NOMATCH] =	10;
2532 		table.precedence[CX_AND] =	9;
2533 		table.precedence[CX_XOR] =	8;
2534 		table.precedence[CX_AND] =
2535 		table.precedence[CX_OR] =	7;
2536 		table.precedence[CX_ANDAND] =	6;
2537 		table.precedence[CX_OROR] =	5;
2538 		table.precedence[CX_TST] =	4;
2539 		table.precedence[CX_SET] =	3;
2540 		table.precedence[CX_PAREN] =	2;
2541 		table.precedence[CX_CALL] =	1;
2542 
2543 		state.codedisc.key = offsetof(Cxcodeheader_t, op);
2544 		state.codedisc.size = sizeof(Cxop_t);
2545 		state.codedisc.link = offsetof(Cxcodeheader_t, header.link);
2546 		state.listdisc.key = offsetof(Cxlistheader_t, name);
2547 		state.listdisc.size = -1;
2548 		state.listdisc.link = offsetof(Cxlistheader_t, header.list);
2549 		state.namedisc.key = offsetof(Cxnameheader_t, name);
2550 		state.namedisc.size = -1;
2551 		state.namedisc.link = offsetof(Cxnameheader_t, header.link);
2552 
2553 		if (!(state.libraries = dtopen(&state.namedisc, Dtqueue)) ||
2554 		    !(state.methods = dtopen(&state.namedisc, Dtoset)) ||
2555 		    !(state.types = dtopen(&state.namedisc, Dtoset)) ||
2556 		    !(state.callouts = dtopen(&state.codedisc, Dtoset)) ||
2557 		    !(state.recodes = dtopen(&state.codedisc, Dtoset)) ||
2558 		    !(state.maps = dtopen(&state.namedisc, Dtoset)) ||
2559 		    !(state.queries = dtopen(&state.namedisc, Dtoset)) ||
2560 		    !(state.constraints = dtopen(&state.namedisc, Dtoset)) ||
2561 		    !(state.edits = dtopen(&state.namedisc, Dtoset)) ||
2562 		    !(state.variables = dtopen(&state.namedisc, Dtoset)))
2563 		{
2564 			if (disc->errorf)
2565 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
2566 			goto panic;
2567 		}
2568 
2569 		for (i = 0; i < elementsof(types); i++)
2570 		{
2571 			if (cxaddtype(NiL, &types[i].type, disc))
2572 				goto panic;
2573 			if (types[i].state)
2574 				*types[i].state = cxtype(NiL, types[i].type.name, disc);
2575 		}
2576 		for (i = 0; i < elementsof(callouts); i++)
2577 			if (cxaddcallout(NiL, &callouts[i], disc))
2578 				goto panic;
2579 		for (i = 0; i < elementsof(builtins); i++)
2580 			if (cxaddvariable(NiL, &builtins[i], disc))
2581 				goto panic;
2582 
2583 		for (i = 0; i < (sizeof(cx_alpha) - 1); i++)
2584 			state.ctype[cx_alpha[i]] |= CX_CTYPE_ALPHA;
2585 		for (i = 0; i < (sizeof(cx_digit) - 1); i++)
2586 			state.ctype[cx_digit[i]] |= CX_CTYPE_DIGIT;
2587 		for (i = 0; i < (sizeof(cx_float) - 1); i++)
2588 			state.ctype[cx_float[i]] |= CX_CTYPE_FLOAT;
2589 		for (i = 0; i < (sizeof(cx_space) - 1); i++)
2590 			state.ctype[cx_space[i]] |= CX_CTYPE_SPACE;
2591 	}
2592 	return;
2593  panic:
2594 	error(ERROR_PANIC, "%s library initialization error", CX_ID);
2595 }
2596 
2597 /*
2598  * return initialized global state pointer
2599  */
2600 
2601 Cxstate_t*
cxstate(Cxdisc_t * disc)2602 cxstate(Cxdisc_t* disc)
2603 {
2604 	if (!state.initialized)
2605 		initialize(disc);
2606 	return &state;
2607 }
2608 
2609 /*
2610  * return the input location (path,record,offset) or the empty string
2611  */
2612 
2613 char*
cxlocation(Cx_t * cx,void * data)2614 cxlocation(Cx_t* cx, void* data)
2615 {
2616 	char*	s;
2617 
2618 	return cx->disc->locationf && (s = (*cx->disc->locationf)(cx, data, cx->disc)) ? s : "";
2619 }
2620 
2621 /*
2622  * for when only a 0-terminated string will do
2623  * copy n bytes of s to cvt buffer and 0-terminate it
2624  * pointer to cvt buf returned
2625  * data ok until the next cvtbuf() call
2626  */
2627 
2628 char*
cxcvt(register Cx_t * cx,const char * s,size_t n)2629 cxcvt(register Cx_t* cx, const char* s, size_t n)
2630 {
2631 	if (cx->cvtsiz <= n || !cx->cvtbuf)
2632 	{
2633 		cx->cvtsiz = roundof(n + 1, CX_CVT);
2634 		if (!(cx->cvtbuf = vmoldof(cx->vm, cx->cvtbuf, char, cx->cvtsiz, 0)))
2635 		{
2636 			if (cx->disc->errorf)
2637 				(*cx->disc->errorf)(NiL, cx->disc, ERROR_SYSTEM|2, "out of space");
2638 			return (char*)s;
2639 		}
2640 	}
2641 	memcpy(cx->cvtbuf, s, n);
2642 	cx->cvtbuf[n] = 0;
2643 	return cx->cvtbuf;
2644 }
2645