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  * dss open/close/library/method implementation
23  *
24  * Glenn Fowler
25  * AT&T Research
26  */
27 
28 static const char usage[] =
29 "[-1ls5P?\n@(#)$Id: dss library (AT&T Research) 2012-09-04 $\n]"
30 USAGE_LICENSE
31 "[+PLUGIN?\findex\f]"
32 "[+DESCRIPTION?The \bdss\b default method provides types, global "
33     "variables, and a schema available for all other methods.]"
34 "{\fvariables\f}"
35 "[+?The schema is a pure XML (tags "
36     "only) file that specifies the \bdss\b method and optional field value "
37     "maps and constraints. Public schemas are usually placed in a "
38     "\b../lib/dss\b sibling directory on \b$PATH\b. The supported tags are:]"
39 "{"
40 ;
41 
42 #include "dsshdr.h"
43 
44 #include <dlldefs.h>
45 #include <pzip.h>
46 #include <stak.h>
47 
48 typedef Dsslib_t* (*Dsslib_f)(const char*, Dssdisc_t*);
49 
50 static const char	id[] = DSS_ID;
51 
52 static Dssstate_t	state;
53 
54 #define DSS_MEM_file		1
55 #define DSS_MEM_format		2
56 #define DSS_MEM_length		3
57 #define DSS_MEM_offset		4
58 #define DSS_MEM_queried		5
59 #define DSS_MEM_record		6
60 #define DSS_MEM_selected	7
61 
62 static Cxvariable_t dss_mem_struct[] =
63 {
64 CXV("file",     "string", DSS_MEM_file,     "Current record file name.")
65 CXV("format",   "string", DSS_MEM_format,   "Current record format.")
66 CXV("length",   "number", DSS_MEM_length,   "Current record length (always 0 for some formats.)")
67 CXV("offset",   "number", DSS_MEM_offset,   "Current record offset (always 0 for some formats.).")
68 CXV("queried",  "number", DSS_MEM_queried,  "Current queried record count.")
69 CXV("record",   "number", DSS_MEM_record,   "Current record number.")
70 CXV("selected", "number", DSS_MEM_selected, "Current selected record count.")
71 {0}
72 };
73 
74 /*
75  * find and open file for read
76  */
77 
78 Sfio_t*
dssfind(const char * name,const char * suffix,Dssflags_t flags,char * path,size_t size,Dssdisc_t * disc)79 dssfind(const char* name, const char* suffix, Dssflags_t flags, char* path, size_t size, Dssdisc_t* disc)
80 {
81 	Sfio_t*		sp;
82 
83 	if (!suffix)
84 		suffix = id;
85 	if (*name == ':')
86 	{
87 		sfsprintf(path, size, "%s", "schema-string");
88 		sp = sfnew(NiL, (char*)name + 1, strlen(name) - 1, -1, SF_READ|SF_STRING);
89 	}
90 	else if (!pathfind(name, id, suffix, path, size))
91 	{
92 		if ((flags & DSS_VERBOSE) && disc->errorf)
93 			(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "%s: %s file not found", name, suffix);
94 		return 0;
95 	}
96 	else
97 		sp = sfopen(NiL, path, "r");
98 	if (!sp)
99 	{
100 		if ((flags & DSS_VERBOSE) && disc->errorf)
101 			(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "%s: cannot read %s file", path, suffix);
102 		return 0;
103 	}
104 	return sp;
105 }
106 
107 /*
108  * load tags file
109  */
110 
111 static Dssmeth_t*
loadtags(const char * name,const char * suffix,Dssdisc_t * disc,Dssmeth_t * meth)112 loadtags(const char* name, const char* suffix, Dssdisc_t* disc, Dssmeth_t* meth)
113 {
114 	Sfio_t*		sp;
115 	char		path[PATH_MAX];
116 
117 	if (streq(name, DSS_ID))
118 		return meth;
119 	if (!(sp = dssfind(name, suffix, DSS_VERBOSE, path, sizeof(path), disc)))
120 		return 0;
121 	return dsstags(sp, path, 1, 0, disc, meth);
122 }
123 
124 /*
125  * dss identf
126  */
127 
128 static int
dssidentf(Dssfile_t * file,void * buf,size_t size,Dssdisc_t * disc)129 dssidentf(Dssfile_t* file, void* buf, size_t size, Dssdisc_t* disc)
130 {
131 	register char*	s;
132 	register char*	e;
133 
134 	s = (char*)buf;
135 	e = s + size;
136 	while (s < e && isspace(*s))
137 		s++;
138 	if (*s++ != '<')
139 		return 0;
140 	if (*s == '!')
141 		while (s < e && *s++ != '!');
142 	else
143 		while (s < e && isalpha(*s))
144 			s++;
145 	return s < e && *s == '>';
146 }
147 
148 /*
149  * dss openf
150  */
151 
152 static int
dssopenf(Dssfile_t * file,Dssdisc_t * disc)153 dssopenf(Dssfile_t* file, Dssdisc_t* disc)
154 {
155 	return 0;
156 }
157 
158 /*
159  * dss readf
160  */
161 
162 static int
dssreadf(Dssfile_t * file,Dssrecord_t * record,Dssdisc_t * disc)163 dssreadf(Dssfile_t* file, Dssrecord_t* record, Dssdisc_t* disc)
164 {
165 	return 0;
166 }
167 
168 /*
169  * dss writef
170  */
171 
172 static int
dsswritef(Dssfile_t * file,Dssrecord_t * record,Dssdisc_t * disc)173 dsswritef(Dssfile_t* file, Dssrecord_t* record, Dssdisc_t* disc)
174 {
175 	return 0;
176 }
177 
178 /*
179  * dss seekf
180  */
181 
182 static Sfoff_t
dssseekf(Dssfile_t * file,Sfoff_t offset,Dssdisc_t * disc)183 dssseekf(Dssfile_t* file, Sfoff_t offset, Dssdisc_t* disc)
184 {
185 	return 0;
186 }
187 
188 /*
189  * dss closef
190  */
191 
192 static int
dssclosef(Dssfile_t * file,Dssdisc_t * disc)193 dssclosef(Dssfile_t* file, Dssdisc_t* disc)
194 {
195 	return 0;
196 }
197 
198 static Dssformat_t	dss_format =
199 {
200 	&id[0],
201 	"pseudo-format that treats all files as /dev/null",
202 	CXH,
203 	dssidentf,
204 	dssopenf,
205 	dssreadf,
206 	dsswritef,
207 	dssseekf,
208 	dssclosef
209 };
210 
211 static Dssmeth_t*	dssmethf(const char*, const char*, const char*, Dssdisc_t*, Dssmeth_t*);
212 
213 static Dssmeth_t	dss_method =
214 {
215 	&id[0],
216 	"meta-method that specifies a method, value maps and constraints",
217 	CXH,
218 	dssmethf
219 };
220 
221 /*
222  * dss methf
223  */
224 
225 static Dssmeth_t*
dssmethf(const char * name,const char * options,const char * schema,Dssdisc_t * disc,Dssmeth_t * meth)226 dssmethf(const char* name, const char* options, const char* schema, Dssdisc_t* disc, Dssmeth_t* meth)
227 {
228 	Sfio_t*		up;
229 	char*		us;
230 	Tagdisc_t	tagdisc;
231 
232 	if (options)
233 	{
234 		if (!(up = sfstropen()))
235 		{
236 			if (disc->errorf)
237 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
238 			return 0;
239 		}
240 		sfprintf(up, "%s", usage);
241 		taginit(&tagdisc, disc->errorf);
242 		if (tagusage(dss_tags, up, &tagdisc))
243 		{
244 			sfclose(up);
245 			return 0;
246 		}
247 		sfputc(up, '}');
248 		sfputc(up, '\n');
249 		if (!(us = sfstruse(up)))
250 		{
251 			sfclose(up);
252 			return 0;
253 		}
254 		dss_method.data = dss_mem_struct;
255 		state.global = &dss_method;
256 		for (;;)
257 		{
258 			switch (optstr(options, us))
259 			{
260 			case '?':
261 				if (disc->errorf)
262 					(*disc->errorf)(NiL, disc, ERROR_USAGE|4, "%s", opt_info.arg);
263 				return 0;
264 			case ':':
265 				if (disc->errorf)
266 					(*disc->errorf)(NiL, disc, 2, "%s", opt_info.arg);
267 				return 0;
268 			}
269 			break;
270 		}
271 		state.global = 0;
272 		sfclose(up);
273 	}
274 	if (schema)
275 		return loadtags(schema, NiL, disc, meth);
276 	dtinsert(meth->formats, &dss_format);
277 	return meth;
278 }
279 
280 #include "dss-compress.h"
281 #include "dss-count.h"
282 #include "dss-null.h"
283 #include "dss-print.h"
284 #include "dss-return.h"
285 #include "dss-scan.h"
286 #include "dss-write.h"
287 
288 static Cxquery_t	queries[] =
289 {
290 	QUERY_compress,
291 	QUERY_count,
292 	QUERY_null,
293 	QUERY_print,
294 	QUERY_return,
295 	QUERY_scan,
296 	QUERY_write,
297 	{0},
298 };
299 
300 static Dsslib_t		dss_library =
301 {
302 	&id[0],
303 	"dss method",
304 	CXH,
305 	0,
306 	&dss_method,
307 	0,
308 	0,
309 	0,
310 	0,
311 	&queries[0],
312 	0,
313 	0,
314 	0
315 };
316 
317 /*
318  * initialize library given name and dlopen() handle
319  */
320 
321 static Dsslib_t*
init(void * dll,Dsslib_t * lib,const char * path,Dssflags_t flags,Dssdisc_t * disc)322 init(void* dll, Dsslib_t* lib, const char* path, Dssflags_t flags, Dssdisc_t* disc)
323 {
324 	Dsslib_f	libf;
325 	char		buf[64];
326 
327 	/*
328 	 * check for the Dsslib_t* function
329 	 */
330 
331 	if (dll)
332 	{
333 		sfsprintf(buf, sizeof(buf), "%s_lib", id);
334 		lib = (libf = (Dsslib_f)dlllook(dll, buf)) ? (*libf)(path, disc) : (Dsslib_t*)0;
335 	}
336 	if (lib)
337 	{
338 		if (!(lib->path = (const char*)strdup(path)))
339 		{
340 			if (disc && disc->errorf)
341 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
342 			return 0;
343 		}
344 		if (!dtsearch(state.cx->libraries, lib))
345 			dtinsert(state.cx->libraries, lib);
346 		return lib;
347 	}
348 	if ((flags & DSS_VERBOSE) && disc && disc->errorf)
349 		(*disc->errorf)(NiL, disc, 2, "%s: %s: initialization function not found in library", path, buf);
350 	return 0;
351 }
352 
353 /*
354  * open and return library info for name
355  * name==0 scans for all related libraries on $PATH
356  */
357 
358 Dsslib_t*
dsslib(const char * name,Dssflags_t flags,Dssdisc_t * disc)359 dsslib(const char* name, Dssflags_t flags, Dssdisc_t* disc)
360 {
361 	register Dsslib_t*	lib;
362 	Dllscan_t*		dls;
363 	Dllent_t*		dle;
364 	void*			dll;
365 	Dllnames_t		names;
366 
367 	dssstate(disc);
368 	if (!name)
369 	{
370 		if (!state.scanned)
371 		{
372 			state.scanned++;
373 			if (dtsize(state.cx->libraries) == 1 && (dls = dllsopen(id, NiL, NiL)))
374 			{
375 				while (dle = dllsread(dls))
376 					if (dll = dlopen(dle->path, RTLD_LAZY))
377 						init(dll, NiL, dle->path, 0, disc);
378 					else if (disc && disc->errorf)
379 						(*disc->errorf)(NiL, disc, 1, "%s: %s", dle->path, dlerror());
380 				dllsclose(dls);
381 			}
382 		}
383 		return (Dsslib_t*)dtfirst(state.cx->libraries);
384 	}
385 	if (!dllnames(id, name, &names))
386 		return 0;
387 	if ((lib = (Dsslib_t*)dtmatch(state.cx->libraries, names.base)) ||
388 	    (lib = (Dsslib_t*)dll_lib(&names, DSS_PLUGIN_VERSION, (flags & DSS_VERBOSE) ? (Error_f)disc->errorf : (Error_f)0, disc)))
389 		init(NiL, lib, names.path, flags|DSS_VERBOSE, disc);
390 	return lib;
391 }
392 
393 /*
394  * add lib tables
395  */
396 
397 int
dssadd(register Dsslib_t * lib,Dssdisc_t * disc)398 dssadd(register Dsslib_t* lib, Dssdisc_t* disc)
399 {
400 	register int	i;
401 	int		schema;
402 
403 	if (lib->header.flags & CX_INITIALIZED)
404 		return 0;
405 	lib->header.flags |= CX_INITIALIZED;
406 	if (lib->libraries)
407 		for (i = 0; lib->libraries[i]; i++)
408 			if (!dssload(lib->libraries[i], disc))
409 				return -1;
410 	if (lib->types)
411 	{
412 		schema = -1;
413 		for (i = 0; lib->types[i].name; i++)
414 		{
415 			if (cxaddtype(NiL, &lib->types[i], disc))
416 				return -1;
417 			if (lib->types[i].header.flags & CX_SCHEMA)
418 				schema = i;
419 		}
420 		for (i = 0; i <= schema; i++)
421 			if ((lib->types[i].header.flags & (CX_INITIALIZED|CX_SCHEMA)) == CX_SCHEMA)
422 			{
423 				lib->types[i].header.flags |= CX_INITIALIZED;
424 				lib->types[i].member->members = state.cx->variables;
425 			}
426 	}
427 	if (lib->callouts)
428 		for (i = 0; lib->callouts[i].callout; i++)
429 			if (cxaddcallout(NiL, &lib->callouts[i], disc))
430 				return -1;
431 	if (lib->recodes)
432 		for (i = 0; lib->recodes[i].recode; i++)
433 			if (cxaddrecode(NiL, &lib->recodes[i], disc))
434 				return -1;
435 	if (lib->maps)
436 		for (i = 0; lib->maps[i]; i++)
437 			if (cxaddmap(NiL, lib->maps[i], disc))
438 				return -1;
439 	if (lib->queries)
440 		for (i = 0; lib->queries[i].name; i++)
441 			if (cxaddquery(NiL, &lib->queries[i], disc))
442 				return -1;
443 	if (lib->constraints)
444 		for (i = 0; lib->constraints[i].name; i++)
445 			if (cxaddconstraint(NiL, &lib->constraints[i], disc))
446 				return -1;
447 	if (lib->edits)
448 		for (i = 0; lib->edits[i].name; i++)
449 			if (cxaddedit(NiL, &lib->edits[i], disc))
450 				return -1;
451 	if (!dtsearch(state.cx->libraries, lib))
452 		dtinsert(state.cx->libraries, lib);
453 	return 0;
454 }
455 
456 /*
457  * find and add library name
458  */
459 
460 Dsslib_t*
dssload(const char * name,Dssdisc_t * disc)461 dssload(const char* name, Dssdisc_t* disc)
462 {
463 	Dsslib_t*	lib;
464 
465 	if (!(lib = dsslib(name, DSS_VERBOSE, disc)))
466 		return 0;
467 	return dssadd(lib, disc) ? 0 : lib;
468 }
469 
470 /*
471  * return the input location string for data
472  * suitable for errorf
473  */
474 
475 static char*
location(Cx_t * cx,void * data,Cxdisc_t * disc)476 location(Cx_t* cx, void* data, Cxdisc_t* disc)
477 {
478 	register Dssfile_t*	file = DSSRECORD(data)->file;
479 	char*			path;
480 	char*			sep;
481 	char*			loc;
482 	char*			nxt;
483 	char*			end;
484 	size_t			n;
485 
486 	if (!file)
487 		return "";
488 	if (path = strrchr(file->path, '/'))
489 		path++;
490 	else
491 		path = file->path;
492 	n = strlen(path) + 3;
493 	sep = ": ";
494 	if (file->count || file->offset)
495 	{
496 		sep = ", ";
497 		n += 64;
498 	}
499 	loc = nxt = fmtbuf(n);
500 	end = loc + n;
501 	nxt += sfsprintf(nxt, end - nxt, "%s%s", path, sep);
502 	if (file->count || file->offset)
503 		sfsprintf(nxt, end - nxt, "%s %I*u, %s %I*d: ", ERROR_translate(NiL, NiL, id, "record"), sizeof(file->count), file->count, ERROR_translate(NiL, NiL, id, "offset"), sizeof(file->offset), file->offset);
504 	return loc;
505 }
506 
507 static int
dss_mem_get(Cx_t * cx,Cxinstruction_t * pc,Cxoperand_t * r,Cxoperand_t * a,Cxoperand_t * b,void * data,Cxdisc_t * disc)508 dss_mem_get(Cx_t* cx, Cxinstruction_t* pc, Cxoperand_t* r, Cxoperand_t* a, Cxoperand_t* b, void* data, Cxdisc_t* disc)
509 {
510 	Dssfile_t*	file = DSSRECORD(data)->file;
511 
512 	switch (pc->data.variable->index)
513 	{
514 	case DSS_MEM_file:
515 		r->value.string.data = (char*)file->path;
516 		r->value.string.size = strlen(file->path);
517 		break;
518 	case DSS_MEM_format:
519 		r->value.string.data = (char*)file->format->name;
520 		r->value.string.size = strlen(file->format->name);
521 		break;
522 	case DSS_MEM_length:
523 		r->value.number = file->length;
524 		break;
525 	case DSS_MEM_offset:
526 		r->value.number = file->offset;
527 		break;
528 	case DSS_MEM_queried:
529 		r->value.number = cx->expr ? cx->expr->parent->queried : 0;
530 		break;
531 	case DSS_MEM_record:
532 		r->value.number = file->count;
533 		break;
534 	case DSS_MEM_selected:
535 		r->value.number = cx->expr ? cx->expr->parent->selected : 0;
536 		break;
537 	default:
538 		return -1;
539 	}
540 	return 0;
541 }
542 
543 static Cxmember_t	dss_mem =
544 {
545 	dss_mem_get,
546 	0,
547 	(Dt_t*)&dss_mem_struct[0],
548 	CX_VIRTUAL
549 };
550 
551 static Cxtype_t		dss_type[] =
552 {
553 	{ DSS_ID "_s", "Global state struct.", CXH, (Cxtype_t*)"void", 0, 0, 0, 0, 0, 0, 0, {0}, 0, &dss_mem },
554 	{ DSS_ID "_t", "Global state.", CXH, (Cxtype_t*)DSS_ID "_s" },
555 };
556 
557 static Cxvariable_t	dss_var[] =
558 {
559 	CXV(".",	DSS_ID "_t",	0,	"Global State.")
560 	CXV(DSS_ID,	DSS_ID "_t",	0,	"Global State.")
561 };
562 
563 /*
564  * open a dss session
565  */
566 
567 Dss_t*
dssopen(Dssflags_t flags,Dssflags_t test,Dssdisc_t * disc,Dssmeth_t * meth)568 dssopen(Dssflags_t flags, Dssflags_t test, Dssdisc_t* disc, Dssmeth_t* meth)
569 {
570 	register Dss_t*		dss;
571 	register Vmalloc_t*	vm;
572 	Cxvariable_t*		var;
573 	Dsslib_t*		lib;
574 	int			i;
575 
576 	if (!disc)
577 		return 0;
578 	dssstate(disc);
579 	if (!meth)
580 	{
581 		/*
582 		 * find the first user library that defines a method
583 		 */
584 
585 		lib = (Dsslib_t*)dtfirst(state.cx->libraries);
586 		while ((lib = (Dsslib_t*)dtnext(state.cx->libraries, lib)) && !lib->meth);
587 		if (lib)
588 			meth = lib->meth;
589 		else
590 		{
591 			if (!(flags & DSS_QUIET) && disc->errorf)
592 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "a method must be specified");
593 			return 0;
594 		}
595 	}
596 	if (!(vm = vmopen(Vmdcheap, Vmbest, 0)))
597 	{
598 		if (disc->errorf)
599 			(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
600 		return 0;
601 	}
602 	if (!(dss = vmnewof(vm, 0, Dss_t, 1, 0)))
603 	{
604 		if (disc->errorf)
605 			(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
606 		vmclose(vm);
607 		return 0;
608 	}
609 	if (!disc->loadf)
610 		disc->loadf = dssload;
611 	if (!disc->locationf)
612 		disc->locationf = location;
613 	dss->id = id;
614 	dss->vm = vm;
615 	dss->disc = disc;
616 	if (!meth->cx || (meth->flags & DSS_BASE))
617 		meth = dssmethinit(NiL, NiL, NiL, disc, meth);
618 	dss->meth = meth;
619 	dss->flags = flags;
620 	dss->test = test;
621 	dss->state = &state;
622 	if (!(dss->cx = cxscope(NiL, meth->cx, flags & DSS_CX_FLAGS, test, disc)) || disc->map && !loadtags(disc->map, "map", disc, meth))
623 		goto bad;
624 	dss->cx->caller = dss;
625 	if (meth->openf && (*meth->openf)(dss, dss->disc))
626 		goto bad;
627 	for (var = (Cxvariable_t*)dtfirst(dss->cx->variables); var; var = (Cxvariable_t*)dtnext(dss->cx->variables, var))
628 		if (var->format.map)
629 			var->format.map->header.flags |= CX_REFERENCED;
630 	for (i = 0; i < elementsof(dss_type); i++)
631 		if (!cxtype(NiL, dss_type[i].name, disc) && cxaddtype(NiL, &dss_type[i], disc))
632 			goto bad;
633 	for (i = 0; i < elementsof(dss_var); i++)
634 	{
635 		if (!(var = vmnewof(vm, 0, Cxvariable_t, 1, 0)))
636 			goto bad;
637 		*var = dss_var[i];
638 		var->header.flags |= CX_INITIALIZED;
639 		if (cxaddvariable(dss->cx, var, disc))
640 			goto bad;
641 	}
642 	return state.dss = dss;
643  bad:
644 	dssclose(dss);
645 	return 0;
646 }
647 
648 /*
649  * close a dss session
650  */
651 
652 int
dssclose(register Dss_t * dss)653 dssclose(register Dss_t* dss)
654 {
655 	int	r;
656 
657 	if (!dss)
658 		return -1;
659 	if (dss->meth->closef)
660 		r = (*dss->meth->closef)(dss, dss->disc);
661 	else
662 		r = 0;
663 	if (dss == state.dss)
664 		state.dss = 0;
665 	if (!dss->vm)
666 		r = -1;
667 	else
668 		vmclose(dss->vm);
669 	return r;
670 }
671 
672 /*
673  * initialize method given pointer
674  * this is a library private global for dssmeth() and dssstatic()
675  */
676 
677 Dssmeth_t*
dssmethinit(const char * name,const char * options,const char * schema,Dssdisc_t * disc,Dssmeth_t * meth)678 dssmethinit(const char* name, const char* options, const char* schema, Dssdisc_t* disc, Dssmeth_t* meth)
679 {
680 	Dssmeth_t*	ometh;
681 	Opt_t		opt;
682 
683 	if (meth->flags & DSS_BASE)
684 	{
685 		if (!(ometh = newof(0, Dssmeth_t, 1, 0)))
686 		{
687 			if (disc->errorf)
688 				(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
689 			return 0;
690 		}
691 		*ometh = *meth;
692 		meth = ometh;
693 		meth->flags &= ~DSS_BASE;
694 		meth->cx = 0;
695 	}
696 	if (!meth->cx && !(meth->cx = cxopen(0, 0, disc)))
697 		return 0;
698 	if (!meth->formats && !(meth->formats = dtopen(&state.cx->namedisc, Dtoset)))
699 	{
700 		cxclose(meth->cx);
701 		if (disc->errorf)
702 			(*disc->errorf)(NiL, disc, ERROR_SYSTEM|2, "out of space");
703 		return 0;
704 	}
705 	if (name)
706 	{
707 		if (meth->methf)
708 		{
709 			ometh = meth;
710 			opt = opt_info;
711 			state.cx->header = (Cxheader_t*)meth;
712 			state.meth = meth;
713 			meth = (*meth->methf)(name, options, schema, disc, meth);
714 			opt_info = opt;
715 			if (!meth)
716 				return 0;
717 			if (meth != ometh)
718 				meth->reference++;
719 		}
720 		else if (options)
721 			return 0;
722 	}
723 	return state.meth = meth;
724 }
725 
726 /*
727  * return method given name
728  */
729 
730 Dssmeth_t*
dssmeth(const char * name,Dssdisc_t * disc)731 dssmeth(const char* name, Dssdisc_t* disc)
732 {
733 	register char*	s;
734 	const char*	options;
735 	const char*	schema;
736 	Dsslib_t*	lib;
737 	Dssmeth_t*	meth;
738 	char		buf[1024];
739 	char		path[1024];
740 
741 	buf[sfsprintf(buf, sizeof(buf) - 1, "%s", name)] = 0;
742 	options = schema = 0;
743 	for (s = buf; *s; s++)
744 		if (*s == ',' || *s == '\t' || *s == '\r' || *s == '\n')
745 		{
746 			if (!options)
747 			{
748 				*s++ = 0;
749 				options = (char*)s;
750 			}
751 		}
752 		else if (*s == ':')
753 		{
754 			*s++ = 0;
755 			schema = name + (s - buf);
756 			break;
757 		}
758 	name = (const char*)buf;
759 	if (!*name)
760 		name = id;
761 	if (!(meth = (Dssmeth_t*)dtmatch(state.cx->methods, name)))
762 	{
763 		if (pathfind(name, id, id, path, sizeof(path)))
764 		{
765 			meth = &dss_method;
766 			name = id;
767 			schema = path;
768 		}
769 		else if (!(lib = dsslib(name, 0, disc)) || !(meth = lib->meth) || dssadd(lib, disc))
770 			return 0;
771 	}
772 	return dssmethinit(name, options, schema, disc, meth);
773 }
774 
775 /*
776  * return initialized global state pointer
777  */
778 
779 Dssstate_t*
dssstate(Dssdisc_t * disc)780 dssstate(Dssdisc_t* disc)
781 {
782 	if (!state.initialized && !state.initialized++)
783 	{
784 		error(-1, "%s", fmtident(usage));
785 		state.cx = cxstate(disc);
786 		dtinsert(state.cx->libraries, &dss_library);
787 		if (dssadd(&dss_library, disc))
788 			error(ERROR_PANIC, "%s library initialization error", id);
789 	}
790 	return &state;
791 }
792 
793 /*
794  * return 1 if expr contains a query
795  */
796 
797 static int
hasquery(register Dssexpr_t * expr)798 hasquery(register Dssexpr_t* expr)
799 {
800 	do
801 	{
802 		if (!expr->query->prog)
803 			return 1;
804 		if (expr->pass && hasquery(expr->pass))
805 			return 1;
806 		if (expr->fail && hasquery(expr->fail))
807 			return 1;
808 		if (expr->group && hasquery(expr->group))
809 			return 1;
810 	} while (expr = expr->next);
811 	return 0;
812 }
813 
814 /*
815  * apply expression with optional head and tail queries to files in argv
816  */
817 
818 int
dssrun(Dss_t * dss,const char * expression,const char * head,const char * tail,char ** argv)819 dssrun(Dss_t* dss, const char* expression, const char* head, const char* tail, char** argv)
820 {
821 	register Dssexpr_t*	x;
822 	Dssexpr_t*		expr;
823 	Dssexpr_t*		xh;
824 	Dssexpr_t*		xt;
825 	int			errors;
826 	int			r;
827 
828 	errors = error_info.errors;
829 	if (!expression || !*expression || *expression == '-' && !*(expression + 1))
830 		expression = tail ? tail : "{write}";
831 	if (!(expr = dsscomp(dss, expression, NiL)))
832 		return -1;
833 	xh = xt = 0;
834 	r = -1;
835 	if (expression == tail)
836 		tail = 0;
837 	else if (!tail && !hasquery(expr))
838 		tail = "{write}";
839 	if (tail)
840 	{
841 		if (!(xt = dsscomp(dss, tail, NiL)))
842 			goto bad;
843 		if (xt->query->beg == null_beg)
844 		{
845 			dssfree(dss, xt);
846 			xt = 0;
847 		}
848 	}
849 	for (x = expr; x->group; x = x->group);
850 	if (!x->query->head)
851 	{
852 		if (!head)
853 			head = "{scan}";
854 		if (!(xh = dsscomp(dss, head, NiL)))
855 			goto bad;
856 		if (!xh->query->head)
857 		{
858 			if (dss->disc->errorf)
859 				(*dss->disc->errorf)(dss, dss->disc, 2, "%s: not a head query", head);
860 			goto bad;
861 		}
862 		xh->files = argv;
863 	}
864 	else if (head)
865 	{
866 		if (dss->disc->errorf)
867 			(*dss->disc->errorf)(dss, dss->disc, 2, "%s: expression already has %s head", head, x->query->name);
868 		goto bad;
869 	}
870 	else
871 		x->files = argv;
872 	if (xh || xt)
873 	{
874 		if (expr->pass || expr->fail || expr->next)
875 		{
876 			if (!(x = vmnewof(expr->vm, 0, Cxexpr_t, 1, sizeof(Cxquery_t))))
877 			{
878 				if (dss->disc->errorf)
879 					(*dss->disc->errorf)(dss, dss->disc, ERROR_SYSTEM|2, "out of space");
880 				goto bad;
881 			}
882 			x->vm = expr->vm;
883 			x->done = expr->done;
884 			x->stack = expr->stack;
885 			x->op = expr->op;
886 			x->query = (Cxquery_t*)(x + 1);
887 			x->group = expr;
888 			expr = x;
889 		}
890 		if (xt)
891 		{
892 			expr->pass = xt;
893 			xt->parent = expr;
894 		}
895 		if (xh)
896 		{
897 			x = xh->pass = expr;
898 			expr = xh;
899 			xh = x;
900 		}
901 	}
902 	if (expr->pass)
903 		expr->pass->parent = expr->pass;
904 	if (dss->test & 0x00000100)
905 		dsslist(dss, expr, sfstdout);
906 	if (dssbeg(dss, expr) || dssend(dss, expr))
907 		goto bad;
908 	dssfree(dss, expr);
909 	r = error_info.errors != errors ? -1 : 0;
910  bad:
911 	if (xh)
912 		dssfree(dss, xh);
913 	if (xt)
914 		dssfree(dss, xt);
915 	dssfree(dss, expr);
916 	return r;
917 }
918