8068c78ea60c33161307abd38b6ff6f4c42bf99e
[idzebra-moved-to-github.git] / data1 / d1_absyn.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 1994-2010 Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #include <stdio.h>
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <yaz/log.h>
26 #include <yaz/oid_db.h>
27 #include <idzebra/data1.h>
28 #include <idzebra/recctrl.h>
29 #include <zebra_xpath.h>
30 #include <d1_absyn.h>
31
32 #define D1_MAX_NESTING  128
33
34 struct data1_hash_table {
35     NMEM nmem;
36     int size;
37     struct data1_hash_entry **ar;
38 };
39
40 struct data1_hash_entry {
41     void *clientData;
42     char *str;
43     struct data1_hash_entry *next;
44 };
45
46 unsigned data1_hash_calc(struct data1_hash_table *ht, const char *str)
47 {
48     unsigned v = 0;
49     assert(str);
50     while (*str)
51     {
52         if (*str >= 'a' && *str <= 'z')
53             v = v*65509 + *str -'a'+10;
54         else if (*str >= 'A' && *str <= 'Z')
55             v = v*65509 + *str -'A'+10;
56         else if (*str >= '0' && *str <= '9')
57             v = v*65509 + *str -'0';
58         str++;
59     }
60     return v % ht->size;
61 }
62
63 struct data1_hash_table *data1_hash_open(int size, NMEM nmem)
64 {
65     int i;
66     struct data1_hash_table *ht = nmem_malloc(nmem, sizeof(*ht));
67     ht->nmem = nmem;
68     ht->size = size;
69     if (ht->size <= 0)
70         ht->size = 29;
71     ht->ar = nmem_malloc(nmem, sizeof(*ht->ar) * ht->size);
72     for (i = 0; i<ht->size; i++)
73         ht->ar[i] = 0;
74     return ht;
75 }
76
77 void data1_hash_insert(struct data1_hash_table *ht, const char *str,
78                        void *clientData, int copy)
79 {
80     char *dstr = copy ? nmem_strdup(ht->nmem, str) : (char*) str;
81     if (strchr(str, '?') || strchr(str, '.'))
82     {
83         int i;
84         for (i = 0; i<ht->size; i++)
85         {
86             struct data1_hash_entry **he = &ht->ar[i];
87             for (; *he && strcmp(str, (*he)->str); he = &(*he)->next)
88                 ;
89             if (!*he)
90             {
91                 *he = nmem_malloc(ht->nmem, sizeof(**he));
92                 (*he)->str = dstr;
93                 (*he)->next = 0;
94             }
95             (*he)->clientData = clientData;
96         }
97     }
98     else
99     {
100         struct data1_hash_entry **he = &ht->ar[data1_hash_calc(ht, str)];
101         for (; *he && strcmp(str, (*he)->str); he = &(*he)->next)
102             ;
103         if (!*he)
104         {
105             *he = nmem_malloc(ht->nmem, sizeof(**he));
106             (*he)->str = dstr;
107             (*he)->next = 0;
108         }
109         (*he)->clientData = clientData;
110     }
111 }
112
113 void *data1_hash_lookup(struct data1_hash_table *ht, const char *str)
114 {
115     struct data1_hash_entry **he = &ht->ar[data1_hash_calc(ht, str)];
116     
117     for (; *he && yaz_matchstr(str, (*he)->str); he = &(*he)->next)
118         ;
119     if (*he)
120         return (*he)->clientData;
121     return 0;
122 }
123
124 struct data1_systag {
125     char *name;
126     char *value;
127     struct data1_systag *next;
128 };
129
130 struct data1_absyn_cache_info 
131 {
132     char *name;
133     data1_absyn *absyn;
134     data1_absyn_cache next;
135 };
136
137 struct data1_attset_cache_info 
138 {
139     char *name;
140     data1_attset *attset;
141     data1_attset_cache next;
142 };
143
144 data1_element *data1_mk_element(data1_handle dh)
145 {
146     data1_element *e = nmem_malloc(data1_nmem_get(dh), sizeof(*e));
147     e->name = 0;
148     e->tag = 0;
149     e->termlists = 0;
150     e->next = e->children = 0;
151     e->sub_name = 0;
152     e->hash = 0;
153     return e;
154 }
155
156 data1_absyn *data1_absyn_search (data1_handle dh, const char *name)
157 {
158     data1_absyn_cache p = *data1_absyn_cache_get (dh);
159
160     while (p)
161     {
162         if (!yaz_matchstr (name, p->name))
163             return p->absyn;
164         p = p->next;
165     }
166     return 0;
167 }
168 /* *ostrich*
169    We need to destroy DFAs, in xp_element (xelm) definitions 
170    pop, 2002-12-13
171 */
172
173 void data1_absyn_destroy (data1_handle dh)
174 {
175     data1_absyn_cache p = *data1_absyn_cache_get (dh);
176     
177     while (p)
178     {
179         data1_absyn *abs = p->absyn;
180         if (abs)
181         {
182             data1_xpelement *xpe = abs->xp_elements;
183             while (xpe) {
184                 yaz_log (YLOG_DEBUG,"Destroy xp element %s",xpe->xpath_expr);
185                 if (xpe->dfa) 
186                     dfa_delete (&xpe->dfa);
187                 xpe = xpe->next;
188             }
189         }
190         p = p->next;
191     }
192 }
193
194
195 void data1_absyn_trav (data1_handle dh, void *handle,
196                        void (*fh)(data1_handle dh, void *h, data1_absyn *a))
197 {
198     data1_absyn_cache p = *data1_absyn_cache_get (dh);
199
200     while (p)
201     {
202         (*fh)(dh, handle, p->absyn);
203         p = p->next;
204     }
205 }
206
207 static data1_absyn *data1_read_absyn(data1_handle dh, const char *file,
208                                      enum DATA1_XPATH_INDEXING en);
209
210 static data1_absyn *data1_absyn_add(data1_handle dh, const char *name,
211                                     enum DATA1_XPATH_INDEXING en)
212 {
213     char fname[512];
214     NMEM mem = data1_nmem_get (dh);
215
216     data1_absyn_cache p = (data1_absyn_cache)nmem_malloc (mem, sizeof(*p));
217     data1_absyn_cache *pp = data1_absyn_cache_get (dh);
218
219     sprintf(fname, "%.500s.abs", name);
220     p->absyn = data1_read_absyn(dh, fname, en);
221     p->name = nmem_strdup(mem, name);
222     p->next = *pp;
223     *pp = p;
224     return p->absyn;
225 }
226
227 data1_absyn *data1_get_absyn (data1_handle dh, const char *name,
228                               enum DATA1_XPATH_INDEXING en)
229 {
230     data1_absyn *absyn;
231
232     if (!(absyn = data1_absyn_search (dh, name)))
233         absyn = data1_absyn_add (dh, name, en);
234     return absyn;
235 }
236
237 data1_attset *data1_attset_search_name (data1_handle dh, const char *name)
238 {
239     data1_attset_cache p = *data1_attset_cache_get (dh);
240
241     while (p)
242     {
243         if (!yaz_matchstr (name, p->name))
244             return p->attset;
245         p = p->next;
246     }
247     return 0;
248 }
249
250 data1_attset *data1_attset_search_id(data1_handle dh, const Odr_oid *oid)
251 {
252     data1_attset_cache p = *data1_attset_cache_get (dh);
253
254     while (p)
255     {
256         if (p->attset->oid && !oid_oidcmp(oid, p->attset->oid))
257             return p->attset;
258         p = p->next;
259     }
260     return 0;
261 }
262
263 data1_attset *data1_attset_add (data1_handle dh, const char *name)
264 {
265     NMEM mem = data1_nmem_get (dh);
266     data1_attset *attset;
267     
268     attset = data1_read_attset (dh, name);
269     if (!attset)
270         yaz_log (YLOG_WARN|YLOG_ERRNO, "Couldn't load attribute set %s", name);
271     else
272     {
273         data1_attset_cache p = (data1_attset_cache)
274             nmem_malloc (mem, sizeof(*p));
275         data1_attset_cache *pp = data1_attset_cache_get (dh);
276         
277         attset->name = p->name = nmem_strdup(mem, name);
278         p->attset = attset;
279         p->next = *pp;
280         *pp = p;
281     }
282     return attset;
283 }
284
285 data1_attset *data1_get_attset (data1_handle dh, const char *name)
286 {
287     data1_attset *attset;
288
289     if (!(attset = data1_attset_search_name (dh, name)))
290         attset = data1_attset_add (dh, name);
291     return attset;
292 }
293
294 data1_esetname *data1_getesetbyname(data1_handle dh, data1_absyn *a,
295                                     const char *name)
296 {
297     data1_esetname *r;
298
299     for (r = a->esetnames; r; r = r->next)
300         if (!data1_matchstr(r->name, name))
301             return r;
302     return 0;
303 }
304
305 /* we have multiple versions of data1_getelementbyname */
306 #define DATA1_GETELEMENTBYTAGNAME_VERSION 1
307
308 data1_element *data1_getelementbytagname (data1_handle dh, data1_absyn *abs,
309                                           data1_element *parent,
310                                           const char *tagname)
311 {
312     data1_element *r;
313     struct data1_hash_table *ht;
314
315     /* It's now possible to have a data1 tree with no abstract syntax */
316     if ( !abs )
317         return 0;
318
319     if (!parent)
320         r = abs->main_elements;
321     else
322         r = parent->children;
323
324 #if DATA1_GETELEMENTBYTAGNAME_VERSION==1
325     /* using hash search */
326     if (!r)
327         return 0;
328
329     ht = r->hash;
330     if (!ht)
331     {
332         /* build hash table (the first time) */
333         ht = r->hash = data1_hash_open(29, data1_nmem_get(dh));
334         for (; r; r = r->next)
335         {
336             data1_name *n;
337             
338             for (n = r->tag->names; n; n = n->next)
339                 data1_hash_insert(ht, n->name, r, 0);
340         }
341     }
342     return data1_hash_lookup(ht, tagname);
343 #else
344     /* using linear search */
345     for (; r; r = r->next)
346     {
347         data1_name *n;
348
349         for (n = r->tag->names; n; n = n->next)
350             if (!data1_matchstr(tagname, n->name))
351                 return r;
352     }
353     return 0;
354 #endif
355 }
356
357 data1_element *data1_getelementbyname (data1_handle dh, data1_absyn *absyn,
358                                        const char *name)
359 {
360     data1_element *r;
361
362     /* It's now possible to have a data1 tree with no abstract syntax */
363     if ( !absyn )
364         return 0;
365     for (r = absyn->main_elements; r; r = r->next)
366         if (!data1_matchstr(r->name, name))
367             return r;
368     return 0;
369 }
370
371
372 void fix_element_ref (data1_handle dh, data1_absyn *absyn, data1_element *e)
373 {
374     /* It's now possible to have a data1 tree with no abstract syntax */
375     if ( !absyn )
376         return;
377
378     for (; e; e = e->next)
379     {
380         if (!e->sub_name)
381         {
382             if (e->children)
383                 fix_element_ref (dh, absyn, e->children);
384         }
385         else
386         {
387             data1_sub_elements *sub_e = absyn->sub_elements;
388             while (sub_e && strcmp (e->sub_name, sub_e->name))
389                 sub_e = sub_e->next;
390             if (sub_e)
391                 e->children = sub_e->elements;
392             else
393                 yaz_log (YLOG_WARN, "Unresolved reference to sub-elements %s",
394                       e->sub_name);
395         }
396     }
397 }
398 /* *ostrich*
399
400    New function, a bit dummy now... I've seen it in zrpn.c... We should build
401    more clever regexps...
402
403
404       //a    ->    ^a/.*$
405       //a/b  ->    ^b/a/.*$
406       /a     ->    ^a/$
407       /a/b   ->    ^b/a/$
408
409       /      ->    none
410
411    pop, 2002-12-13
412
413    Now [] predicates are supported
414
415    pop, 2003-01-17
416
417  */
418
419 static const char * mk_xpath_regexp (data1_handle dh, const char *expr) 
420 {
421     const char *p = expr;
422     int abs = 1;
423     int e = 0;
424     char *stack[32];
425     char *res_p, *res = 0;
426     size_t res_size = 1;
427     
428     if (*p != '/')
429         return ("");
430     p++;
431     if (*p == '/') 
432     { 
433         abs =0;
434         p++;
435     }
436     while (*p)
437     {
438         int is_predicate = 0;
439         char *s;
440         int i, j;
441         for (i = 0; *p && !strchr("/",*p); i++, p++)
442             ;
443         res_size += (i+3); /* we'll add / between later .. */
444         stack[e] = (char *) nmem_malloc(data1_nmem_get(dh), i+1);
445         s = stack[e];
446         for (j = 0; j < i; j++)
447         {
448             const char *pp = p-i+j;
449             if (*pp == '[')
450                 is_predicate=1;
451             else if (*pp == ']')
452                 is_predicate=0;
453             else 
454             {
455                 if (!is_predicate) {
456                     if (*pp == '*') 
457                         *s++ = '.';
458                     *s++ = *pp;
459                 }
460             }
461         }
462         *s = 0;
463         e++;
464         if (*p)
465             p++;
466     }
467     res_p = res = nmem_malloc(data1_nmem_get(dh), res_size + 10);
468
469     if (stack[e-1][0] == '@')  /* path/@attr spec (leaf is attribute) */
470         strcpy(res_p, "/");
471     else
472         strcpy(res_p, "[^@]*/");  /* path .. (index all cdata below it) */
473     res_p = res_p + strlen(res_p);
474     while (--e >= 0) {
475         sprintf(res_p, "%s/", stack[e]);
476         res_p += strlen(stack[e]) + 1;
477     }
478     if (!abs)
479     {
480         sprintf(res_p, ".*"); 
481         res_p += 2;
482     }
483     sprintf (res_p, "$");
484     res_p++;
485     yaz_log(YLOG_DEBUG, "Got regexp: %s", res);
486     return res;
487 }
488
489 static int parse_termlists(data1_handle dh, data1_termlist ***tpp,
490                            char *cp, const char *file, int lineno,
491                            const char *element_name, data1_absyn *res,
492                            int xpelement,
493                            data1_attset *attset)
494 {
495     data1_termlist **tp = *tpp;
496     while(1)
497     {
498         char attname[512], structure[512];
499         char *source;
500         int r, i;
501         int level = 0;
502         structure[0] = '\0';
503         for (i = 0; cp[i] && i<sizeof(attname)-1; i++)
504             if (strchr(":,", cp[i]))
505                 break;
506             else
507                 attname[i] = cp[i];
508         if (i == 0)
509         {
510             if (*cp)
511                 yaz_log(YLOG_WARN,
512                         "%s:%d: Syntax error in termlistspec '%s'",
513                         file, lineno, cp);
514             break;
515         }
516         attname[i] = '\0';
517         r = 1;
518         cp += i;
519         if (*cp == ':')
520             cp++;
521
522         for (i = 0; cp[i] && i<sizeof(structure)-1; i++)
523             if (level == 0 && strchr(",", cp[i]))
524                 break;
525             else
526             {
527                 structure[i] = cp[i];
528                 if (cp[i] == '(')
529                     level++;
530                 else if (cp[i] == ')')
531                     level--;
532             }
533         structure[i] = '\0';
534         if (i)
535             r = 2;
536         cp += i;
537         if (*cp)
538             cp++;  /* skip , */
539
540         *tp = (data1_termlist *)
541             nmem_malloc(data1_nmem_get(dh), sizeof(**tp));
542         (*tp)->next = 0;
543         
544         if (*attname == '!')
545         {
546             if (!xpelement && element_name)
547                 strcpy(attname, element_name);
548             else if (xpelement)
549                 strcpy(attname, ZEBRA_XPATH_CDATA);
550         }
551         if (attset)
552         {
553             if (!data1_getattbyname(dh, attset, attname))
554             {
555                 yaz_log(YLOG_WARN, "Index '%s' not found in attset(s)",
556                         attname);
557             }
558         }
559
560         (*tp)->index_name = nmem_strdup(data1_nmem_get(dh), attname);
561         assert (*(*tp)->index_name != '!');
562         if (r == 2 && (source = strchr(structure, ':')))
563             *source++ = '\0';   /* cut off structure .. */
564         else
565             source = "data";    /* ok: default is leaf data */
566         (*tp)->source = (char *)
567             nmem_strdup (data1_nmem_get (dh), source);
568         
569         if (r < 2) /* is the structure qualified? */
570             (*tp)->structure = "w";
571         else 
572             (*tp)->structure = (char *)
573                 nmem_strdup (data1_nmem_get (dh), structure);
574         tp = &(*tp)->next;
575     }
576
577     *tpp = tp;
578     return 0;
579 }
580
581 /* quinn
582  * Converts a 'melm' field[$subfield] pattern to a simple xpath
583  */
584 static int melm2xpath(char *melm, char *buf)
585 {
586     char *dollar;
587     char *field = melm;
588     char *subfield;
589     char *fieldtype;
590     if ((dollar = strchr(melm, '$'))) {
591         *dollar = '\0';
592         subfield = ++dollar;
593     } else
594         subfield = "";
595     if (field[0] == '0' && field[1] == '0')
596         fieldtype = "controlfield";
597     else
598         fieldtype = "datafield";
599     sprintf(buf, "/*/%s[@tag=\"%s\"]", fieldtype, field);
600     if (*subfield) 
601         sprintf(buf + strlen(buf), "/subfield[@code=\"%s\"]", subfield);
602     else if (field[0] != '0' || field[1] != '0')
603         strcat(buf, "/subfield");
604     yaz_log(YLOG_DEBUG, "Created xpath: '%s'", buf);
605     return 0;
606 }
607
608 const char *data1_systag_lookup(data1_absyn *absyn, const char *tag,
609                                 const char *default_value)
610 {
611     struct data1_systag *p = absyn->systags;
612     for (; p; p = p->next)
613         if (!strcmp(p->name, tag))
614             return p->value;
615     return default_value;
616 }
617
618 #define l_isspace(c) ((c) == '\t' || (c) == ' ' || (c) == '\n' || (c) == '\r')
619
620 int read_absyn_line(FILE *f, int *lineno, char *line, int len,
621                     char *argv[], int num)
622 {
623     char *p;
624     int argc;
625     int quoted = 0;
626     
627     while ((p = fgets(line, len, f)))
628     {
629         (*lineno)++;
630         while (*p && l_isspace(*p))
631             p++;
632         if (*p && *p != '#')
633             break;
634     }
635     if (!p)
636         return 0;
637     
638     for (argc = 0; *p ; argc++)
639     {
640         if (*p == '#')  /* trailing comment */
641             break;
642         argv[argc] = p;
643         while (*p && !(l_isspace(*p) && !quoted)) {
644           if (*p =='"') quoted = 1 - quoted;
645           if (*p =='[') quoted = 1;
646           if (*p ==']') quoted = 0;
647           p++;
648         }
649         if (*p)
650         {
651             *(p++) = '\0';
652             while (*p && l_isspace(*p))
653                 p++;
654         }
655     }
656     return argc;
657 }
658
659 data1_marctab *data1_absyn_getmarctab(data1_handle dh, data1_node *root)
660 {
661     if (root->u.root.absyn)
662         return root->u.root.absyn->marc;
663     return 0;
664 }
665
666 data1_element *data1_absyn_getelements(data1_handle dh,
667                                        data1_node *root)
668 {
669     if (root->u.root.absyn)
670         return root->u.root.absyn->main_elements;
671     return 0;
672 }
673
674 static data1_absyn *data1_read_absyn(data1_handle dh, const char *file,
675                                      enum DATA1_XPATH_INDEXING default_xpath)
676 {
677     data1_sub_elements *cur_elements = NULL;
678     data1_xpelement **cur_xpelement = NULL;
679     data1_attset *attset_list = data1_empty_attset(dh);
680     data1_attset_child **attset_childp = &attset_list->children;
681
682     data1_absyn *res = 0;
683     FILE *f;
684     data1_element **ppl[D1_MAX_NESTING];
685     data1_esetname **esetpp;
686     data1_maptab **maptabp;
687     data1_marctab **marcp;
688     data1_termlist *all = 0;
689     data1_tagset **tagset_childp;
690     struct data1_systag **systagsp;
691     int level = 0;
692     int lineno = 0;
693     int argc;
694     char *argv[50], line[512];
695
696     f = data1_path_fopen(dh, file, "r");
697     
698     res = (data1_absyn *) nmem_malloc(data1_nmem_get(dh), sizeof(*res));
699     res->name = 0;
700     res->oid = 0;
701     res->tagset = 0;
702     res->encoding = 0;
703     res->xpath_indexing = 
704         (f ? DATA1_XPATH_INDEXING_DISABLE : default_xpath);
705     res->systags = 0;
706     systagsp = &res->systags;
707     tagset_childp = &res->tagset;
708
709     res->varset = 0;
710     res->esetnames = 0;
711     esetpp = &res->esetnames;
712     res->maptabs = 0;
713     maptabp = &res->maptabs;
714     res->marc = 0;
715     marcp = &res->marc;
716     res->sub_elements = NULL;
717     res->main_elements = NULL;
718     res->xp_elements = NULL;
719     cur_xpelement = &res->xp_elements;
720
721     while (f && (argc = read_absyn_line(f, &lineno, line, 512, argv, 50)))
722     {
723         char *cmd = *argv;
724         if (!strcmp(cmd, "elm") || !strcmp(cmd, "element"))
725         {
726             data1_element *new_element;
727             int i;
728             char *p, *sub_p, *path, *name, *termlists;
729             int type, value;
730             data1_termlist **tp;
731
732             if (argc < 4)
733             {
734                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args to elm", file, lineno);
735                 continue;
736             }
737             path = argv[1];
738             name = argv[2];
739             termlists = argv[3];
740
741             if (!cur_elements)
742             {
743                 cur_elements = (data1_sub_elements *)
744                     nmem_malloc(data1_nmem_get(dh), sizeof(*cur_elements));
745                 cur_elements->next = res->sub_elements;
746                 cur_elements->elements = NULL;
747                 cur_elements->name = "main";
748                 res->sub_elements = cur_elements;
749                 
750                 level = 0;
751                 ppl[level] = &cur_elements->elements;
752             }
753             p = path;
754             for (i = 1;; i++)
755             {
756                 char *e;
757
758                 if ((e = strchr(p, '/')))
759                     p = e+1;
760                 else
761                     break;
762             }
763             if (i > level+1)
764             {
765                 yaz_log(YLOG_WARN, "%s:%d: Bad level increase", file, lineno);
766                 fclose(f);
767                 return 0;
768             }
769             level = i;
770             new_element = *ppl[level-1] = data1_mk_element(dh);
771             
772             tp = &new_element->termlists;
773             ppl[level-1] = &new_element->next;
774             ppl[level] = &new_element->children;
775             
776             /* consider subtree (if any) ... */
777             if ((sub_p = strchr (p, ':')) && sub_p[1])
778             {
779                 *sub_p++ = '\0';
780                 new_element->sub_name =
781                     nmem_strdup (data1_nmem_get(dh), sub_p);            
782             }
783             /* well-defined tag */
784             if (sscanf(p, "(%d,%d)", &type, &value) == 2)
785             {
786                 if (!res->tagset)
787                 {
788                     yaz_log(YLOG_WARN, "%s:%d: No tagset loaded", file, lineno);
789                     fclose(f);
790                     return 0;
791                 }
792                 if (!(new_element->tag = data1_gettagbynum (dh, res->tagset,
793                                                             type, value)))
794                 {
795                     yaz_log(YLOG_WARN, "%s:%d: Couldn't find tag %s in tagset",
796                          file, lineno, p);
797                     fclose(f);
798                     return 0;
799                 }
800             }
801             /* private tag */
802             else if (*p)
803             {
804                 data1_tag *nt =
805                     new_element->tag = (data1_tag *)
806                     nmem_malloc(data1_nmem_get (dh),
807                                 sizeof(*new_element->tag));
808                 nt->which = DATA1T_string;
809                 nt->value.string = nmem_strdup(data1_nmem_get (dh), p);
810                 nt->names = (data1_name *)
811                     nmem_malloc(data1_nmem_get(dh), 
812                                 sizeof(*new_element->tag->names));
813                 nt->names->name = nt->value.string;
814                 nt->names->next = 0;
815                 nt->kind = DATA1K_string;
816                 nt->next = 0;
817                 nt->tagset = 0;
818             }
819             else
820             {
821                 yaz_log(YLOG_WARN, "%s:%d: Bad element", file, lineno);
822                 fclose(f);
823                 return 0;
824             }
825             /* parse termList definitions */
826             p = termlists;
827             if (*p != '-')
828             {
829                 if (parse_termlists (dh, &tp, p, file, lineno, name, res, 0,
830                                      attset_list))
831                 {
832                     fclose (f);
833                     return 0;
834                 }
835                 *tp = all; /* append any ALL entries to the list */
836             }
837             new_element->name = nmem_strdup(data1_nmem_get (dh), name);
838         }
839         /* *ostrich*
840            New code to support xelm directive
841            for each xelm a dfa is built. xelms are stored in res->xp_elements
842            
843            maybe we should use a simple sscanf instead of dfa?
844            
845            pop, 2002-12-13
846
847            Now [] predicates are supported. regexps and xpath structure is
848            a bit redundant, however it's comfortable later...
849
850            pop, 2003-01-17
851         */
852
853         else if (!strcmp(cmd, "xelm") || !strcmp(cmd, "melm")) {
854
855             int i;
856             char *p, *xpath_expr, *termlists;
857             const char *regexp;
858             struct DFA *dfa = 0;
859             data1_termlist **tp;
860             char melm_xpath[128];
861             data1_xpelement *xp_ele = 0;
862             data1_xpelement *last_match = 0;
863             
864             if (argc != 3)
865             {
866                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args to %s",
867                         file, lineno, cmd);
868                 continue;
869             }
870
871             if (!strcmp(cmd, "melm")) {
872                 if (melm2xpath(argv[1], melm_xpath) < 0)
873                     continue;
874                 xpath_expr = melm_xpath;
875             } else {
876                 xpath_expr = argv[1];
877             }
878             termlists = argv[2];
879             regexp = mk_xpath_regexp(dh, xpath_expr);
880
881 #if OPTIMIZE_MELM
882             /* get last of existing regulars with same regexp */
883             for (xp_ele = res->xp_elements; xp_ele; xp_ele = xp_ele->next)
884                 if (!strcmp(xp_ele->regexp, regexp))
885                     last_match = xp_ele;
886 #endif
887             if (!last_match)
888             {
889                 /* new regular expression . Parse + generate */
890                 const char *regexp_ptr = regexp;
891
892                 dfa = dfa_init();
893                 i = dfa_parse (dfa, &regexp_ptr);
894                 if (i || *regexp_ptr) {
895                     yaz_log(YLOG_WARN, "%s:%d: Bad xpath to xelm", file, lineno);
896                     dfa_delete (&dfa);
897                     continue;
898                 }
899             }
900             *cur_xpelement = (data1_xpelement *)
901                 nmem_malloc(data1_nmem_get(dh), sizeof(**cur_xpelement));
902             (*cur_xpelement)->next = 0;
903             (*cur_xpelement)->match_next = 0;
904             if (last_match)
905                 last_match->match_next = *cur_xpelement;
906 #if OPTIMIZE_MELM
907             (*cur_xpelement)->regexp = regexp;
908 #endif
909             (*cur_xpelement)->xpath_expr = nmem_strdup(data1_nmem_get (dh), 
910                                                        xpath_expr); 
911             
912             if (dfa)
913                 dfa_mkstate (dfa);
914             (*cur_xpelement)->dfa = dfa;
915             
916 #ifdef ENHANCED_XELM 
917             (*cur_xpelement)->xpath_len =
918                 zebra_parse_xpath_str(
919                     xpath_expr, 
920                     (*cur_xpelement)->xpath, XPATH_STEP_COUNT,
921                     data1_nmem_get(dh));
922 #endif
923             (*cur_xpelement)->termlists = 0;
924             tp = &(*cur_xpelement)->termlists;
925             
926             /* parse termList definitions */
927             p = termlists;
928             if (*p != '-')
929             {
930                 if (parse_termlists (dh, &tp, p, file, lineno,
931                                      xpath_expr, res, 1, attset_list))
932                 {
933                     fclose (f);
934                     return 0;
935                 }
936                 *tp = all; /* append any ALL entries to the list */
937             }
938             cur_xpelement = &(*cur_xpelement)->next;
939         }
940         else if (!strcmp(cmd, "section"))
941         {
942             char *name;
943             
944             if (argc < 2)
945             {
946                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args to section",
947                         file, lineno);
948                 continue;
949             }
950             name = argv[1];
951             
952             cur_elements = (data1_sub_elements *)
953                 nmem_malloc(data1_nmem_get(dh), sizeof(*cur_elements));
954             cur_elements->next = res->sub_elements;
955             cur_elements->elements = NULL;
956             cur_elements->name = nmem_strdup (data1_nmem_get(dh), name);
957             res->sub_elements = cur_elements;
958             
959             level = 0;
960             ppl[level] = &cur_elements->elements;
961         }
962         else if (!strcmp(cmd, "xpath"))
963         {
964             if (argc != 2)
965             {
966                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args to 'xpath' directive",
967                      file, lineno);
968                 continue;
969             }
970             if (!strcmp(argv[1], "enable"))
971                 res->xpath_indexing = DATA1_XPATH_INDEXING_ENABLE;
972             else if (!strcmp (argv[1], "disable"))
973                 res->xpath_indexing = DATA1_XPATH_INDEXING_DISABLE;
974             else
975             {
976                 yaz_log(YLOG_WARN, "%s:%d: Expecting disable/enable "
977                         "after 'xpath' directive", file, lineno);
978             }
979         }
980         else if (!strcmp(cmd, "all"))
981         {
982             data1_termlist **tp = &all;
983             if (all)
984             {
985                 yaz_log(YLOG_WARN, "%s:%d: Too many 'all' directives - ignored",
986                      file, lineno);
987                 continue;
988             }
989             if (argc != 2)
990             {
991                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args to 'all' directive",
992                      file, lineno);
993                 continue;
994             }
995             if (parse_termlists (dh, &tp, argv[1], file, lineno, 0, res, 0,
996                                  attset_list))
997             {
998                 fclose (f);
999                 return 0;
1000             }
1001         }
1002         else if (!strcmp(cmd, "name"))
1003         {
1004             if (argc != 2)
1005             {
1006                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args to name directive",
1007                      file, lineno);
1008                 continue;
1009             }
1010             res->name = nmem_strdup(data1_nmem_get(dh), argv[1]);
1011         }
1012         else if (!strcmp(cmd, "reference"))
1013         {
1014             char *name;
1015             
1016             if (argc != 2)
1017             {
1018                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args to reference",
1019                      file, lineno);
1020                 continue;
1021             }
1022             name = argv[1];
1023             res->oid = yaz_string_to_oid_nmem(yaz_oid_std(),
1024                                               CLASS_SCHEMA, name, 
1025                                               data1_nmem_get(dh));
1026             if (!res->oid)
1027             {
1028                 yaz_log(YLOG_WARN, "%s:%d: Unknown tagset ref '%s'", 
1029                      file, lineno, name);
1030                 continue;
1031             }
1032         }
1033         else if (!strcmp(cmd, "attset"))
1034         {
1035            char *name;
1036            data1_attset *attset;
1037            
1038            if (argc != 2)
1039            {
1040                yaz_log(YLOG_WARN, "%s:%d: Bad # of args to attset",
1041                     file, lineno);
1042                continue;
1043            }
1044            name = argv[1];
1045            if (!(attset = data1_get_attset (dh, name)))
1046            {
1047                yaz_log(YLOG_WARN, "%s:%d: Couldn't find attset  %s",
1048                        file, lineno, name);
1049                continue;
1050            }
1051            *attset_childp = (data1_attset_child *)
1052                nmem_malloc (data1_nmem_get(dh), sizeof(**attset_childp));
1053            (*attset_childp)->child = attset;
1054            (*attset_childp)->next = 0;
1055            attset_childp = &(*attset_childp)->next;
1056         }
1057         else if (!strcmp(cmd, "tagset"))
1058         {
1059             char *name;
1060             int type = 0;
1061             if (argc < 2)
1062             {
1063                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args to tagset",
1064                      file, lineno);
1065                 continue;
1066             }
1067             name = argv[1];
1068             if (argc == 3)
1069                 type = atoi(argv[2]);
1070             *tagset_childp = data1_read_tagset (dh, name, type);
1071             if (!(*tagset_childp))
1072             {
1073                 yaz_log(YLOG_WARN, "%s:%d: Couldn't load tagset %s",
1074                      file, lineno, name);
1075                 continue;
1076             }
1077             tagset_childp = &(*tagset_childp)->next;
1078         }
1079         else if (!strcmp(cmd, "varset"))
1080         {
1081             char *name;
1082
1083             if (argc != 2)
1084             {
1085                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args in varset",
1086                      file, lineno);
1087                 continue;
1088             }
1089             name = argv[1];
1090             if (!(res->varset = data1_read_varset (dh, name)))
1091             {
1092                 yaz_log(YLOG_WARN, "%s:%d: Couldn't load Varset %s",
1093                      file, lineno, name);
1094                 continue;
1095             }
1096         }
1097         else if (!strcmp(cmd, "esetname"))
1098         {
1099             char *name, *fname;
1100
1101             if (argc != 3)
1102             {
1103                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args in esetname",
1104                      file, lineno);
1105                 continue;
1106             }
1107             name = argv[1];
1108             fname = argv[2];
1109             
1110             *esetpp = (data1_esetname *)
1111                 nmem_malloc(data1_nmem_get(dh), sizeof(**esetpp));
1112             (*esetpp)->name = nmem_strdup(data1_nmem_get(dh), name);
1113             (*esetpp)->next = 0;
1114             if (*fname == '@')
1115                 (*esetpp)->spec = 0;
1116             else if (!((*esetpp)->spec = data1_read_espec1 (dh, fname)))
1117             {
1118                 yaz_log(YLOG_WARN, "%s:%d: Espec-1 read failed for %s",
1119                      file, lineno, fname);
1120                 continue;
1121             }
1122             esetpp = &(*esetpp)->next;
1123         }
1124         else if (!strcmp(cmd, "maptab"))
1125         {
1126             char *name;
1127             
1128             if (argc != 2)
1129             {
1130                 yaz_log(YLOG_WARN, "%s:%d: Bad # of args for maptab",
1131                      file, lineno);
1132                 continue;
1133             }
1134             name = argv[1];
1135             if (!(*maptabp = data1_read_maptab (dh, name)))
1136             {
1137                 yaz_log(YLOG_WARN, "%s:%d: Couldn't load maptab %s",
1138                      file, lineno, name);
1139                 continue;
1140             }
1141             maptabp = &(*maptabp)->next;
1142         }
1143         else if (!strcmp(cmd, "marc"))
1144         {
1145             char *name;
1146             
1147             if (argc != 2)
1148             {
1149                 yaz_log(YLOG_WARN, "%s:%d: Bad # or args for marc",
1150                      file, lineno);
1151                 continue;
1152             }
1153             name = argv[1];
1154             if (!(*marcp = data1_read_marctab (dh, name)))
1155             {
1156                 yaz_log(YLOG_WARN, "%s:%d: Couldn't read marctab %s",
1157                      file, lineno, name);
1158                 continue;
1159             }
1160             marcp = &(*marcp)->next;
1161         }
1162         else if (!strcmp(cmd, "encoding"))
1163         {
1164             if (argc != 2)
1165             {
1166                 yaz_log(YLOG_WARN, "%s:%d: Bad # or args for encoding",
1167                      file, lineno);
1168                 continue;
1169             }
1170             res->encoding = nmem_strdup (data1_nmem_get(dh), argv[1]);
1171         }
1172         else if (!strcmp(cmd, "systag"))
1173         {
1174             if (argc != 3)
1175             {
1176                 yaz_log(YLOG_WARN, "%s:%d: Bad # or args for systag",
1177                      file, lineno);
1178                 continue;
1179             }
1180             *systagsp = nmem_malloc (data1_nmem_get(dh), sizeof(**systagsp));
1181
1182             (*systagsp)->name = nmem_strdup(data1_nmem_get(dh), argv[1]);
1183             (*systagsp)->value = nmem_strdup(data1_nmem_get(dh), argv[2]);
1184             systagsp = &(*systagsp)->next;
1185         }
1186         else
1187         {
1188             yaz_log(YLOG_WARN, "%s:%d: Unknown directive '%s'", file, 
1189                     lineno, cmd);
1190             continue;
1191         }
1192     }
1193     if (f)
1194         fclose(f);
1195     
1196     for (cur_elements = res->sub_elements; cur_elements;
1197          cur_elements = cur_elements->next)
1198     {
1199         if (!strcmp (cur_elements->name, "main"))
1200             res->main_elements = cur_elements->elements;
1201         fix_element_ref (dh, res, cur_elements->elements);
1202     }
1203     *systagsp = 0;
1204     return res;
1205 }
1206
1207 /*
1208  * Local variables:
1209  * c-basic-offset: 4
1210  * c-file-style: "Stroustrup"
1211  * indent-tabs-mode: nil
1212  * End:
1213  * vim: shiftwidth=4 tabstop=8 expandtab
1214  */
1215