Using the new ylog.h everywhere, and fixing what that breaks!
[idzebra-moved-to-github.git] / recctrl / regxread.c
1 /* $Id: regxread.c,v 1.55 2004-11-19 10:27:13 heikki Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004
3    Index Data Aps
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23
24 #include <stdio.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <ctype.h>
28
29 #include <yaz/tpath.h>
30 #include <zebrautl.h>
31 #include <dfa.h>
32 #include <idzebra/recgrs.h>
33
34 #if HAVE_TCL_H
35 #include <tcl.h>
36
37 #if MAJOR_VERSION >= 8
38 #define HAVE_TCL_OBJECTS
39 #endif
40 #endif
41
42 #define REGX_DEBUG 0
43
44 #define F_WIN_EOF 2000000000
45 #define F_WIN_READ 1
46
47 #define REGX_EOF     0
48 #define REGX_PATTERN 1
49 #define REGX_BODY    2
50 #define REGX_BEGIN   3
51 #define REGX_END     4
52 #define REGX_CODE    5
53 #define REGX_CONTEXT 6
54 #define REGX_INIT    7
55
56 struct regxCode {
57     char *str;
58 #if HAVE_TCL_OBJECTS
59     Tcl_Obj *tcl_obj;
60 #endif
61 };
62
63 struct lexRuleAction {
64     int which; 
65     union {
66         struct {
67             struct DFA *dfa;    /* REGX_PATTERN */
68             int body;
69         } pattern;
70         struct regxCode *code;  /* REGX_CODE */
71     } u;
72     struct lexRuleAction *next;
73 };
74
75 struct lexRuleInfo {
76     int no;
77     struct lexRuleAction *actionList;
78 };
79
80 struct lexRule {
81     struct lexRuleInfo info;
82     struct lexRule *next;
83 };
84
85 struct lexContext {
86     char *name;
87     struct DFA *dfa;
88     struct lexRule *rules;
89     struct lexRuleInfo **fastRule;
90     int ruleNo;
91     int initFlag;
92
93     struct lexRuleAction *beginActionList;
94     struct lexRuleAction *endActionList;
95     struct lexRuleAction *initActionList;
96     struct lexContext *next;
97 };
98
99 struct lexConcatBuf {
100     int max;
101     char *buf;
102 };
103
104 struct lexSpec {
105     char *name;
106     struct lexContext *context;
107
108     struct lexContext **context_stack;
109     int context_stack_size;
110     int context_stack_top;
111
112     int lineNo;
113     NMEM m;
114     data1_handle dh;
115 #if HAVE_TCL_H
116     Tcl_Interp *tcl_interp;
117 #endif
118     void *f_win_fh;
119     void (*f_win_ef)(void *, off_t);
120
121     int f_win_start;      /* first byte of buffer is this file offset */
122     int f_win_end;        /* last byte of buffer is this offset - 1 */
123     int f_win_size;       /* size of buffer */
124     char *f_win_buf;      /* buffer itself */
125     int (*f_win_rf)(void *, char *, size_t);
126     off_t (*f_win_sf)(void *, off_t);
127
128     struct lexConcatBuf *concatBuf;
129     int maxLevel;
130     data1_node **d1_stack;
131     int d1_level;
132     int stop_flag;
133     
134     int *arg_start;
135     int *arg_end;
136     int arg_no;
137     int ptr;
138 };
139
140 struct lexSpecs {
141     struct lexSpec *spec;
142     char type[256];
143 };
144
145 static char *f_win_get (struct lexSpec *spec, off_t start_pos, off_t end_pos,
146                         int *size)
147 {
148     int i, r, off = start_pos - spec->f_win_start;
149
150     if (off >= 0 && end_pos <= spec->f_win_end)
151     {
152         *size = end_pos - start_pos;
153         return spec->f_win_buf + off;
154     }
155     if (off < 0 || start_pos >= spec->f_win_end)
156     {
157         (*spec->f_win_sf)(spec->f_win_fh, start_pos);
158         spec->f_win_start = start_pos;
159
160         if (!spec->f_win_buf)
161             spec->f_win_buf = (char *) xmalloc (spec->f_win_size);
162         *size = (*spec->f_win_rf)(spec->f_win_fh, spec->f_win_buf,
163                                   spec->f_win_size);
164         spec->f_win_end = spec->f_win_start + *size;
165
166         if (*size > end_pos - start_pos)
167             *size = end_pos - start_pos;
168         return spec->f_win_buf;
169     }
170     for (i = 0; i<spec->f_win_end - start_pos; i++)
171         spec->f_win_buf[i] = spec->f_win_buf[i + off];
172     r = (*spec->f_win_rf)(spec->f_win_fh,
173                           spec->f_win_buf + i,
174                           spec->f_win_size - i);
175     spec->f_win_start = start_pos;
176     spec->f_win_end += r;
177     *size = i + r;
178     if (*size > end_pos - start_pos)
179         *size = end_pos - start_pos;
180     return spec->f_win_buf;
181 }
182
183 static int f_win_advance (struct lexSpec *spec, int *pos)
184 {
185     int size;
186     char *buf;
187     
188     if (*pos >= spec->f_win_start && *pos < spec->f_win_end)
189         return spec->f_win_buf[(*pos)++ - spec->f_win_start];
190     if (*pos == F_WIN_EOF)
191         return 0;
192     buf = f_win_get (spec, *pos, *pos+1, &size);
193     if (size == 1)
194     {
195         (*pos)++;
196         return *buf;
197     }
198     *pos = F_WIN_EOF;
199     return 0;
200 }
201
202 static void regxCodeDel (struct regxCode **pp)
203 {
204     struct regxCode *p = *pp;
205     if (p)
206     {
207 #if HAVE_TCL_OBJECTS
208         if (p->tcl_obj)
209             Tcl_DecrRefCount (p->tcl_obj);
210 #endif
211         xfree (p->str); 
212         xfree (p);
213         *pp = NULL;
214     }
215 }
216
217 static void regxCodeMk (struct regxCode **pp, const char *buf, int len)
218 {
219     struct regxCode *p;
220
221     p = (struct regxCode *) xmalloc (sizeof(*p));
222     p->str = (char *) xmalloc (len+1);
223     memcpy (p->str, buf, len);
224     p->str[len] = '\0';
225 #if HAVE_TCL_OBJECTS
226     p->tcl_obj = Tcl_NewStringObj ((char *) buf, len);
227     if (p->tcl_obj)
228         Tcl_IncrRefCount (p->tcl_obj);
229 #endif
230     *pp = p;
231 }
232
233 static struct DFA *lexSpecDFA (void)
234 {
235     struct DFA *dfa;
236
237     dfa = dfa_init ();
238     dfa_parse_cmap_del (dfa, ' ');
239     dfa_parse_cmap_del (dfa, '\t');
240     dfa_parse_cmap_add (dfa, '/', 0);
241     return dfa;
242 }
243
244 static void actionListDel (struct lexRuleAction **rap)
245 {
246     struct lexRuleAction *ra1, *ra;
247
248     for (ra = *rap; ra; ra = ra1)
249     {
250         ra1 = ra->next;
251         switch (ra->which)
252         {
253         case REGX_PATTERN:
254             dfa_delete (&ra->u.pattern.dfa);
255             break;
256         case REGX_CODE:
257             regxCodeDel (&ra->u.code);
258             break;
259         }
260         xfree (ra);
261     }
262     *rap = NULL;
263 }
264
265 static struct lexContext *lexContextCreate (const char *name)
266 {
267     struct lexContext *p = (struct lexContext *) xmalloc (sizeof(*p));
268
269     p->name = xstrdup (name);
270     p->ruleNo = 1;
271     p->initFlag = 0;
272     p->dfa = lexSpecDFA ();
273     p->rules = NULL;
274     p->fastRule = NULL;
275     p->beginActionList = NULL;
276     p->endActionList = NULL;
277     p->initActionList = NULL;
278     p->next = NULL;
279     return p;
280 }
281
282 static void lexContextDestroy (struct lexContext *p)
283 {
284     struct lexRule *rp, *rp1;
285
286     dfa_delete (&p->dfa);
287     xfree (p->fastRule);
288     for (rp = p->rules; rp; rp = rp1)
289     {
290         rp1 = rp->next;
291         actionListDel (&rp->info.actionList);
292         xfree (rp);
293     }
294     actionListDel (&p->beginActionList);
295     actionListDel (&p->endActionList);
296     actionListDel (&p->initActionList);
297     xfree (p->name);
298     xfree (p);
299 }
300
301 static struct lexSpec *lexSpecCreate (const char *name, data1_handle dh)
302 {
303     struct lexSpec *p;
304     int i;
305     
306     p = (struct lexSpec *) xmalloc (sizeof(*p));
307     p->name = (char *) xmalloc (strlen(name)+1);
308     strcpy (p->name, name);
309
310 #if HAVE_TCL_H
311     p->tcl_interp = 0;
312 #endif
313     p->dh = dh;
314     p->context = NULL;
315     p->context_stack_size = 100;
316     p->context_stack = (struct lexContext **)
317         xmalloc (sizeof(*p->context_stack) * p->context_stack_size);
318     p->f_win_buf = NULL;
319
320     p->maxLevel = 128;
321     p->concatBuf = (struct lexConcatBuf *)
322         xmalloc (sizeof(*p->concatBuf) * p->maxLevel);
323     for (i = 0; i < p->maxLevel; i++)
324     {
325         p->concatBuf[i].max = 0;
326         p->concatBuf[i].buf = 0;
327     }
328     p->d1_stack = (data1_node **) xmalloc (sizeof(*p->d1_stack) * p->maxLevel);
329     p->d1_level = 0;
330     return p;
331 }
332
333 static void lexSpecDestroy (struct lexSpec **pp)
334 {
335     struct lexSpec *p;
336     struct lexContext *lt;
337     int i;
338
339     assert (pp);
340     p = *pp;
341     if (!p)
342         return ;
343
344     for (i = 0; i < p->maxLevel; i++)
345         xfree (p->concatBuf[i].buf);
346     xfree (p->concatBuf);
347
348     lt = p->context;
349     while (lt)
350     {
351         struct lexContext *lt_next = lt->next;
352         lexContextDestroy (lt);
353         lt = lt_next;
354     }
355 #if HAVE_TCL_OBJECTS
356     if (p->tcl_interp)
357         Tcl_DeleteInterp (p->tcl_interp);
358 #endif
359     xfree (p->name);
360     xfree (p->f_win_buf);
361     xfree (p->context_stack);
362     xfree (p->d1_stack);
363     xfree (p);
364     *pp = NULL;
365 }
366
367 static int readParseToken (const char **cpp, int *len)
368 {
369     const char *cp = *cpp;
370     char cmd[32];
371     int i, level;
372
373     while (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\r')
374         cp++;
375     switch (*cp)
376     {
377     case '\0':
378         return 0;
379     case '/':
380         *cpp = cp+1;
381         return REGX_PATTERN;
382     case '{':
383         *cpp = cp+1;
384         level = 1;
385         while (*++cp)
386         {
387             if (*cp == '{')
388                 level++;
389             else if (*cp == '}')
390             {
391                 level--;
392                 if (level == 0)
393                     break;
394             }
395         }
396         *len = cp - *cpp;
397         return REGX_CODE;
398     default:
399         i = 0;
400         while (1)
401         {
402             if (*cp >= 'a' && *cp <= 'z')
403                 cmd[i] = *cp;
404             else if (*cp >= 'A' && *cp <= 'Z')
405                 cmd[i] = *cp + 'a' - 'A';
406             else
407                 break;
408             if (i < (int) sizeof(cmd)-2)
409                 i++;
410             cp++;
411         }
412         cmd[i] = '\0';
413         if (i == 0)
414         {
415             yaz_log (YLOG_WARN, "bad character %d %c", *cp, *cp);
416             cp++;
417             while (*cp && *cp != ' ' && *cp != '\t' &&
418                    *cp != '\n' && *cp != '\r')
419                 cp++;
420             *cpp = cp;
421             return 0;
422         }
423         *cpp = cp;
424         if (!strcmp (cmd, "begin"))
425             return REGX_BEGIN;
426         else if (!strcmp (cmd, "end"))
427             return REGX_END;
428         else if (!strcmp (cmd, "body"))
429             return REGX_BODY;
430         else if (!strcmp (cmd, "context"))
431             return REGX_CONTEXT;
432         else if (!strcmp (cmd, "init"))
433             return REGX_INIT;
434         else
435         {
436             yaz_log (YLOG_WARN, "bad command %s", cmd);
437             return 0;
438         }
439     }
440 }
441
442 static int actionListMk (struct lexSpec *spec, const char *s,
443                          struct lexRuleAction **ap)
444 {
445     int r, tok, len;
446     int bodyMark = 0;
447     const char *s0;
448
449     while ((tok = readParseToken (&s, &len)))
450     {
451         switch (tok)
452         {
453         case REGX_BODY:
454             bodyMark = 1;
455             continue;
456         case REGX_CODE:
457             *ap = (struct lexRuleAction *) xmalloc (sizeof(**ap));
458             (*ap)->which = tok;
459             regxCodeMk (&(*ap)->u.code, s, len);
460             s += len+1;
461             break;
462         case REGX_PATTERN:
463             *ap = (struct lexRuleAction *) xmalloc (sizeof(**ap));
464             (*ap)->which = tok;
465             (*ap)->u.pattern.body = bodyMark;
466             bodyMark = 0;
467             (*ap)->u.pattern.dfa = lexSpecDFA ();
468             s0 = s;
469             r = dfa_parse ((*ap)->u.pattern.dfa, &s);
470             if (r || *s != '/')
471             {
472                 xfree (*ap);
473                 *ap = NULL;
474                 yaz_log (YLOG_WARN, "regular expression error '%.*s'", s-s0, s0);
475                 return -1;
476             }
477             if (debug_dfa_tran)
478                 printf ("pattern: %.*s\n", s-s0, s0);
479             dfa_mkstate ((*ap)->u.pattern.dfa);
480             s++;
481             break;
482         case REGX_BEGIN:
483             yaz_log (YLOG_WARN, "cannot use BEGIN here");
484             continue;
485         case REGX_INIT:
486             yaz_log (YLOG_WARN, "cannot use INIT here");
487             continue;
488         case REGX_END:
489             *ap = (struct lexRuleAction *) xmalloc (sizeof(**ap));
490             (*ap)->which = tok;
491             break;
492         }
493         ap = &(*ap)->next;
494     }
495     *ap = NULL;
496     return 0;
497 }
498
499 int readOneSpec (struct lexSpec *spec, const char *s)
500 {
501     int len, r, tok;
502     struct lexRule *rp;
503     struct lexContext *lc;
504
505     tok = readParseToken (&s, &len);
506     if (tok == REGX_CONTEXT)
507     {
508         char context_name[32];
509         tok = readParseToken (&s, &len);
510         if (tok != REGX_CODE)
511         {
512             yaz_log (YLOG_WARN, "missing name after CONTEXT keyword");
513             return 0;
514         }
515         if (len > 31)
516             len = 31;
517         memcpy (context_name, s, len);
518         context_name[len] = '\0';
519         lc = lexContextCreate (context_name);
520         lc->next = spec->context;
521         spec->context = lc;
522         return 0;
523     }
524     if (!spec->context)
525         spec->context = lexContextCreate ("main");
526        
527     switch (tok)
528     {
529     case REGX_BEGIN:
530         actionListDel (&spec->context->beginActionList);
531         actionListMk (spec, s, &spec->context->beginActionList);
532         break;
533     case REGX_END:
534         actionListDel (&spec->context->endActionList);
535         actionListMk (spec, s, &spec->context->endActionList);
536         break;
537     case REGX_INIT:
538         actionListDel (&spec->context->initActionList);
539         actionListMk (spec, s, &spec->context->initActionList);
540         break;
541     case REGX_PATTERN:
542 #if REGX_DEBUG
543         yaz_log (YLOG_LOG, "rule %d %s", spec->context->ruleNo, s);
544 #endif
545         r = dfa_parse (spec->context->dfa, &s);
546         if (r)
547         {
548             yaz_log (YLOG_WARN, "regular expression error. r=%d", r);
549             return -1;
550         }
551         if (*s != '/')
552         {
553             yaz_log (YLOG_WARN, "expects / at end of pattern. got %c", *s);
554             return -1;
555         }
556         s++;
557         rp = (struct lexRule *) xmalloc (sizeof(*rp));
558         rp->info.no = spec->context->ruleNo++;
559         rp->next = spec->context->rules;
560         spec->context->rules = rp;
561         actionListMk (spec, s, &rp->info.actionList);
562     }
563     return 0;
564 }
565
566 int readFileSpec (struct lexSpec *spec)
567 {
568     struct lexContext *lc;
569     int c, i, errors = 0;
570     FILE *spec_inf = 0;
571     WRBUF lineBuf;
572     char fname[256];
573
574 #if HAVE_TCL_H
575     if (spec->tcl_interp)
576     {
577         sprintf (fname, "%s.tflt", spec->name);
578         spec_inf = data1_path_fopen (spec->dh, fname, "r");
579     }
580 #endif
581     if (!spec_inf)
582     {
583         sprintf (fname, "%s.flt", spec->name);
584         spec_inf = data1_path_fopen (spec->dh, fname, "r");
585     }
586     if (!spec_inf)
587     {
588         yaz_log (YLOG_ERRNO|YLOG_WARN, "cannot read spec file %s", spec->name);
589         return -1;
590     }
591     yaz_log (YLOG_LOG, "reading regx filter %s", fname);
592 #if HAVE_TCL_H
593     if (spec->tcl_interp)
594         yaz_log (YLOG_LOG, "Tcl enabled");
595 #endif
596
597 #if 0
598     debug_dfa_trav = 0;
599     debug_dfa_tran = 1;
600     debug_dfa_followpos = 0;
601     dfa_verbose = 1;
602 #endif
603
604     lineBuf = wrbuf_alloc();
605     spec->lineNo = 0;
606     c = getc (spec_inf);
607     while (c != EOF)
608     {
609         wrbuf_rewind (lineBuf);
610         if (c == '#' || c == '\n' || c == ' ' || c == '\t' || c == '\r')
611         {
612             while (c != '\n' && c != EOF)
613                 c = getc (spec_inf);
614             spec->lineNo++;
615             if (c == '\n')
616                 c = getc (spec_inf);
617         }
618         else
619         {
620             int addLine = 0;
621             
622             while (1)
623             {
624                 int c1 = c;
625                 wrbuf_putc(lineBuf, c);
626                 c = getc (spec_inf);
627                 while (c == '\r')
628                     c = getc (spec_inf);
629                 if (c == EOF)
630                     break;
631                 if (c1 == '\n')
632                 {
633                     if (c != ' ' && c != '\t')
634                         break;
635                     addLine++;
636                 }
637             }
638             wrbuf_putc(lineBuf, '\0');
639             readOneSpec (spec, wrbuf_buf(lineBuf));
640             spec->lineNo += addLine;
641         }
642     }
643     fclose (spec_inf);
644     wrbuf_free(lineBuf, 1);
645
646     for (lc = spec->context; lc; lc = lc->next)
647     {
648         struct lexRule *rp;
649         lc->fastRule = (struct lexRuleInfo **)
650             xmalloc (sizeof(*lc->fastRule) * lc->ruleNo);
651         for (i = 0; i < lc->ruleNo; i++)
652             lc->fastRule[i] = NULL;
653         for (rp = lc->rules; rp; rp = rp->next)
654             lc->fastRule[rp->info.no] = &rp->info;
655         dfa_mkstate (lc->dfa);
656     }
657     if (errors)
658         return -1;
659     
660     return 0;
661 }
662
663 #if 0
664 static struct lexSpec *curLexSpec = NULL;
665 #endif
666
667 static void execData (struct lexSpec *spec,
668                       const char *ebuf, int elen, int formatted_text,
669                       const char *attribute_str, int attribute_len)
670 {
671     struct data1_node *res, *parent;
672     int org_len;
673
674     if (elen == 0) /* shouldn't happen, but it does! */
675         return ;
676 #if REGX_DEBUG
677     if (elen > 80)
678         yaz_log (YLOG_LOG, "data(%d bytes) %.40s ... %.*s", elen,
679               ebuf, 40, ebuf + elen-40);
680     else if (elen == 1 && ebuf[0] == '\n')
681     {
682         yaz_log (YLOG_LOG, "data(new line)");
683     }
684     else if (elen > 0)
685         yaz_log (YLOG_LOG, "data(%d bytes) %.*s", elen, elen, ebuf);
686     else 
687         yaz_log (YLOG_LOG, "data(%d bytes)", elen);
688 #endif
689         
690     if (spec->d1_level <= 1)
691         return;
692
693     parent = spec->d1_stack[spec->d1_level -1];
694     assert (parent);
695
696     if (attribute_str)
697     {
698         data1_xattr **ap;
699         res = parent;
700         if (res->which != DATA1N_tag)
701             return;
702         /* sweep through exising attributes.. */
703         for (ap = &res->u.tag.attributes; *ap; ap = &(*ap)->next)
704             if (strlen((*ap)->name) == attribute_len &&
705                 !memcmp((*ap)->name, attribute_str, attribute_len))
706                 break;
707         if (!*ap)
708         {
709             /* new attribute. Create it with name + value */
710             *ap = nmem_malloc(spec->m, sizeof(**ap));
711
712             (*ap)->name = nmem_malloc(spec->m, attribute_len+1);
713             memcpy((*ap)->name, attribute_str, attribute_len);
714             (*ap)->name[attribute_len] = '\0';
715
716             (*ap)->value = nmem_malloc(spec->m, elen+1);
717             memcpy((*ap)->value, ebuf, elen);
718             (*ap)->value[elen] = '\0';
719             (*ap)->next = 0;
720         }
721         else
722         {
723             /* append to value if attribute already exists */
724             char *nv = nmem_malloc(spec->m, elen + 1 + strlen((*ap)->value));
725             strcpy(nv, (*ap)->value);
726             memcpy (nv + strlen(nv), ebuf, elen);
727             nv[strlen(nv)+elen] = '\0';
728             (*ap)->value = nv;
729         }
730     } 
731     else 
732     {
733         if ((res = spec->d1_stack[spec->d1_level]) && 
734             res->which == DATA1N_data)
735             org_len = res->u.data.len;
736         else
737         {
738             org_len = 0;
739             
740             res = data1_mk_node2 (spec->dh, spec->m, DATA1N_data, parent);
741             res->u.data.what = DATA1I_text;
742             res->u.data.len = 0;
743             res->u.data.formatted_text = formatted_text;
744             res->u.data.data = 0;
745             
746             if (spec->d1_stack[spec->d1_level])
747                 spec->d1_stack[spec->d1_level]->next = res;
748             spec->d1_stack[spec->d1_level] = res;
749         }
750         if (org_len + elen >= spec->concatBuf[spec->d1_level].max)
751         {
752             char *old_buf, *new_buf;
753             
754             spec->concatBuf[spec->d1_level].max = org_len + elen + 256;
755             new_buf = (char *) xmalloc (spec->concatBuf[spec->d1_level].max);
756             if ((old_buf = spec->concatBuf[spec->d1_level].buf))
757             {
758                 memcpy (new_buf, old_buf, org_len);
759                 xfree (old_buf);
760             }
761             spec->concatBuf[spec->d1_level].buf = new_buf;
762         }
763         memcpy (spec->concatBuf[spec->d1_level].buf + org_len, ebuf, elen);
764         res->u.data.len += elen;
765     }
766 }
767
768 static void execDataP (struct lexSpec *spec,
769                        const char *ebuf, int elen, int formatted_text)
770 {
771     execData (spec, ebuf, elen, formatted_text, 0, 0);
772 }
773
774 static void tagDataRelease (struct lexSpec *spec)
775 {
776     data1_node *res;
777     
778     if ((res = spec->d1_stack[spec->d1_level]) &&
779         res->which == DATA1N_data && 
780         res->u.data.what == DATA1I_text)
781     {
782         assert (!res->u.data.data);
783         assert (res->u.data.len > 0);
784         if (res->u.data.len > DATA1_LOCALDATA)
785             res->u.data.data = (char *) nmem_malloc (spec->m, res->u.data.len);
786         else
787             res->u.data.data = res->lbuf;
788         memcpy (res->u.data.data, spec->concatBuf[spec->d1_level].buf,
789                 res->u.data.len);
790     }
791 }
792
793 static void variantBegin (struct lexSpec *spec, 
794                           const char *class_str, int class_len,
795                           const char *type_str, int type_len,
796                           const char *value_str, int value_len)
797 {
798     struct data1_node *parent = spec->d1_stack[spec->d1_level -1];
799     char tclass[DATA1_MAX_SYMBOL], ttype[DATA1_MAX_SYMBOL];
800     data1_vartype *tp;
801     int i;
802     data1_node *res;
803
804     if (spec->d1_level == 0)
805     {
806         yaz_log (YLOG_WARN, "in variant begin. No record type defined");
807         return ;
808     }
809     if (class_len >= DATA1_MAX_SYMBOL)
810         class_len = DATA1_MAX_SYMBOL-1;
811     memcpy (tclass, class_str, class_len);
812     tclass[class_len] = '\0';
813
814     if (type_len >= DATA1_MAX_SYMBOL)
815         type_len = DATA1_MAX_SYMBOL-1;
816     memcpy (ttype, type_str, type_len);
817     ttype[type_len] = '\0';
818
819 #if REGX_DEBUG 
820     yaz_log (YLOG_LOG, "variant begin(%s,%s,%d)", tclass, ttype,
821           spec->d1_level);
822 #endif
823
824     if (!(tp =
825           data1_getvartypeby_absyn(spec->dh, parent->root->u.root.absyn,
826                                    tclass, ttype)))
827         return;
828     
829     if (parent->which != DATA1N_variant)
830     {
831         res = data1_mk_node2 (spec->dh, spec->m, DATA1N_variant, parent);
832         if (spec->d1_stack[spec->d1_level])
833             tagDataRelease (spec);
834         spec->d1_stack[spec->d1_level] = res;
835         spec->d1_stack[++(spec->d1_level)] = NULL;
836     }
837     for (i = spec->d1_level-1; spec->d1_stack[i]->which == DATA1N_variant; i--)
838         if (spec->d1_stack[i]->u.variant.type == tp)
839         {
840             spec->d1_level = i;
841             break;
842         }
843
844 #if REGX_DEBUG 
845     yaz_log (YLOG_LOG, "variant node(%d)", spec->d1_level);
846 #endif
847     parent = spec->d1_stack[spec->d1_level-1];
848     res = data1_mk_node2 (spec->dh, spec->m, DATA1N_variant, parent);
849     res->u.variant.type = tp;
850
851     if (value_len >= DATA1_LOCALDATA)
852         value_len =DATA1_LOCALDATA-1;
853     memcpy (res->lbuf, value_str, value_len);
854     res->lbuf[value_len] = '\0';
855
856     res->u.variant.value = res->lbuf;
857     
858     if (spec->d1_stack[spec->d1_level])
859         tagDataRelease (spec);
860     spec->d1_stack[spec->d1_level] = res;
861     spec->d1_stack[++(spec->d1_level)] = NULL;
862 }
863
864 static void tagStrip (const char **tag, int *len)
865 {
866     int i;
867
868     for (i = *len; i > 0 && isspace((*tag)[i-1]); --i)
869         ;
870     *len = i;
871     for (i = 0; i < *len && isspace((*tag)[i]); i++)
872         ;
873     *tag += i;
874     *len -= i;
875 }
876
877 static void tagBegin (struct lexSpec *spec, 
878                       const char *tag, int len)
879 {
880     if (spec->d1_level == 0)
881     {
882         yaz_log (YLOG_WARN, "in element begin. No record type defined");
883         return ;
884     }
885     tagStrip (&tag, &len);
886     if (spec->d1_stack[spec->d1_level])
887         tagDataRelease (spec);
888
889 #if REGX_DEBUG 
890     yaz_log (YLOG_LOG, "begin tag(%.*s, %d)", len, tag, spec->d1_level);
891 #endif
892
893     spec->d1_stack[spec->d1_level] = data1_mk_tag_n (
894         spec->dh, spec->m, tag, len, 0, spec->d1_stack[spec->d1_level -1]);
895     spec->d1_stack[++(spec->d1_level)] = NULL;
896 }
897
898 static void tagEnd (struct lexSpec *spec, int min_level,
899                     const char *tag, int len)
900 {
901     tagStrip (&tag, &len);
902     while (spec->d1_level > min_level)
903     {
904         tagDataRelease (spec);
905         (spec->d1_level)--;
906         if (spec->d1_level == 0)
907             break;
908         if ((spec->d1_stack[spec->d1_level]->which == DATA1N_tag) &&
909             (!tag ||
910              (strlen(spec->d1_stack[spec->d1_level]->u.tag.tag) ==
911               (size_t) len &&
912               !memcmp (spec->d1_stack[spec->d1_level]->u.tag.tag, tag, len))))
913             break;
914     }
915 #if REGX_DEBUG
916     yaz_log (YLOG_LOG, "end tag(%d)", spec->d1_level);
917 #endif
918 }
919
920
921 static int tryMatch (struct lexSpec *spec, int *pptr, int *mptr,
922                      struct DFA *dfa, int greedy)
923 {
924     struct DFA_state *state = dfa->states[0];
925     struct DFA_tran *t;
926     unsigned char c = 0;
927     unsigned char c_prev = 0;
928     int ptr = *pptr;          /* current pointer */
929     int start_ptr = *pptr;    /* first char of match */
930     int last_ptr = 0;         /* last char of match */
931     int last_rule = 0;        /* rule number of current match */
932     int restore_ptr = 0;
933     int i;
934
935     if (ptr)
936     {
937         --ptr;
938         c = f_win_advance (spec, &ptr);
939     }
940     while (1)
941     {
942         if (dfa->states[0] == state)
943         {
944             c_prev = c;
945             restore_ptr = ptr;
946         }
947         c = f_win_advance (spec, &ptr);
948
949         if (ptr == F_WIN_EOF)
950         {
951             if (last_rule)
952             {
953                 *mptr = start_ptr;
954                 *pptr = last_ptr;
955                 return 1;
956             }
957             break;
958         }
959
960         t = state->trans;
961         i = state->tran_no;
962         while (1)
963             if (--i < 0)    /* no transition for character c */
964             {
965                 if (last_rule)
966                 {
967                     *mptr = start_ptr;     /* match starts here */
968                     *pptr = last_ptr;      /* match end here (+1) */
969                     return 1;
970                 }
971                 state = dfa->states[0];
972
973                 ptr = restore_ptr;
974                 c = f_win_advance (spec, &ptr);
975
976                 start_ptr = ptr;
977
978                 break;
979             }
980             else if (c >= t->ch[0] && c <= t->ch[1])
981             {
982                 state = dfa->states[t->to];
983                 if (state->rule_no && c_prev == '\n')
984                 {
985                     last_rule = state->rule_no;
986                     last_ptr = ptr;
987                 }
988                 else if (state->rule_nno)
989                 {
990                     last_rule = state->rule_nno;
991                     last_ptr = ptr;
992                 }
993                 break;
994             }
995             else
996                 t++;
997     }
998     return 0;
999 }
1000
1001 static int execTok (struct lexSpec *spec, const char **src,
1002                     const char **tokBuf, int *tokLen)
1003 {
1004     const char *s = *src;
1005
1006     while (*s == ' ' || *s == '\t')
1007         s++;
1008     if (!*s)
1009         return 0;
1010     if (*s == '$' && s[1] >= '0' && s[1] <= '9')
1011     {
1012         int n = 0;
1013         s++;
1014         while (*s >= '0' && *s <= '9')
1015             n = n*10 + (*s++ -'0');
1016         if (spec->arg_no == 0)
1017         {
1018             *tokBuf = "";
1019             *tokLen = 0;
1020         }
1021         else
1022         {
1023             if (n >= spec->arg_no)
1024                 n = spec->arg_no-1;
1025             *tokBuf = f_win_get (spec, spec->arg_start[n], spec->arg_end[n],
1026                                  tokLen);
1027         }
1028     }
1029     else if (*s == '\"')
1030     {
1031         *tokBuf = ++s;
1032         while (*s && *s != '\"')
1033             s++;
1034         *tokLen = s - *tokBuf;
1035         if (*s)
1036             s++;
1037         *src = s;
1038     }
1039     else if (*s == '\n' || *s == ';')
1040     {
1041         *src = s+1;
1042         return 1;
1043     }
1044     else if (*s == '-')
1045     {
1046         *tokBuf = s++;
1047         while (*s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' &&
1048                *s != ';')
1049             s++;
1050         *tokLen = s - *tokBuf;
1051         *src = s;
1052         return 3;
1053     }
1054     else
1055     {
1056         *tokBuf = s++;
1057         while (*s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' &&
1058                *s != ';')
1059             s++;
1060         *tokLen = s - *tokBuf;
1061     }
1062     *src = s;
1063     return 2;
1064 }
1065
1066 static char *regxStrz (const char *src, int len, char *str)
1067 {
1068     if (len > 63)
1069         len = 63;
1070     memcpy (str, src, len);
1071     str[len] = '\0';
1072     return str;
1073 }
1074
1075 #if HAVE_TCL_H
1076 static int cmd_tcl_begin (ClientData clientData, Tcl_Interp *interp,
1077                           int argc, const char **argv)
1078 {
1079     struct lexSpec *spec = (struct lexSpec *) clientData;
1080     if (argc < 2)
1081         return TCL_ERROR;
1082     if (!strcmp(argv[1], "record") && argc == 3)
1083     {
1084         const char *absynName = argv[2];
1085         data1_node *res;
1086
1087 #if REGX_DEBUG
1088         yaz_log (YLOG_LOG, "begin record %s", absynName);
1089 #endif
1090         res = data1_mk_root (spec->dh, spec->m, absynName);
1091         
1092         spec->d1_level = 0;
1093
1094         spec->d1_stack[spec->d1_level++] = res;
1095
1096         res = data1_mk_tag (spec->dh, spec->m, absynName, 0, res);
1097
1098         spec->d1_stack[spec->d1_level++] = res;
1099
1100         spec->d1_stack[spec->d1_level] = NULL;
1101     }
1102     else if (!strcmp(argv[1], "element") && argc == 3)
1103     {
1104         tagBegin (spec, argv[2], strlen(argv[2]));
1105     }
1106     else if (!strcmp (argv[1], "variant") && argc == 5)
1107     {
1108         variantBegin (spec, argv[2], strlen(argv[2]),
1109                       argv[3], strlen(argv[3]),
1110                       argv[4], strlen(argv[4]));
1111     }
1112     else if (!strcmp (argv[1], "context") && argc == 3)
1113     {
1114         struct lexContext *lc = spec->context;
1115 #if REGX_DEBUG
1116         yaz_log (YLOG_LOG, "begin context %s",argv[2]);
1117 #endif
1118         while (lc && strcmp (argv[2], lc->name))
1119             lc = lc->next;
1120         if (lc)
1121         {
1122             spec->context_stack[++(spec->context_stack_top)] = lc;
1123         }
1124         else
1125             yaz_log (YLOG_WARN, "unknown context %s", argv[2]);
1126     }
1127     else
1128         return TCL_ERROR;
1129     return TCL_OK;
1130 }
1131
1132 static int cmd_tcl_end (ClientData clientData, Tcl_Interp *interp,
1133                         int argc, const char **argv)
1134 {
1135     struct lexSpec *spec = (struct lexSpec *) clientData;
1136     if (argc < 2)
1137         return TCL_ERROR;
1138     
1139     if (!strcmp (argv[1], "record"))
1140     {
1141         while (spec->d1_level)
1142         {
1143             tagDataRelease (spec);
1144             (spec->d1_level)--;
1145         }
1146 #if REGX_DEBUG
1147         yaz_log (YLOG_LOG, "end record");
1148 #endif
1149         spec->stop_flag = 1;
1150     }
1151     else if (!strcmp (argv[1], "element"))
1152     {
1153         int min_level = 2;
1154         const char *element = 0;
1155         if (argc >= 3 && !strcmp(argv[2], "-record"))
1156         {
1157             min_level = 0;
1158             if (argc == 4)
1159                 element = argv[3];
1160         }
1161         else
1162             if (argc == 3)
1163                 element = argv[2];
1164         tagEnd (spec, min_level, element, (element ? strlen(element) : 0));
1165         if (spec->d1_level <= 1)
1166         {
1167 #if REGX_DEBUG
1168             yaz_log (YLOG_LOG, "end element end records");
1169 #endif
1170             spec->stop_flag = 1;
1171         }
1172     }
1173     else if (!strcmp (argv[1], "context"))
1174     {
1175 #if REGX_DEBUG
1176         yaz_log (YLOG_LOG, "end context");
1177 #endif
1178         if (spec->context_stack_top)
1179             (spec->context_stack_top)--;
1180     }
1181     else
1182         return TCL_ERROR;
1183     return TCL_OK;
1184 }
1185
1186 static int cmd_tcl_data (ClientData clientData, Tcl_Interp *interp,
1187                          int argc, const char **argv)
1188 {
1189     int argi = 1;
1190     int textFlag = 0;
1191     const char *element = 0;
1192     const char *attribute = 0;
1193     struct lexSpec *spec = (struct lexSpec *) clientData;
1194     
1195     while (argi < argc)
1196     {
1197         if (!strcmp("-text", argv[argi]))
1198         {
1199             textFlag = 1;
1200             argi++;
1201         }
1202         else if (!strcmp("-element", argv[argi]))
1203         {
1204             argi++;
1205             if (argi < argc)
1206                 element = argv[argi++];
1207         }
1208         else if (!strcmp("-attribute", argv[argi]))
1209         {
1210             argi++;
1211             if (argi < argc)
1212                 attribute = argv[argi++];
1213         }
1214         else
1215             break;
1216     }
1217     if (element)
1218         tagBegin (spec, element, strlen(element));
1219
1220     while (argi < argc)
1221     {
1222 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
1223         Tcl_DString ds;
1224         char *native = Tcl_UtfToExternalDString(0, argv[argi], -1, &ds);
1225         execData (spec, native, strlen(native), textFlag, attribute, 
1226                   attribute ? strlen(attribute) : 0);
1227         Tcl_DStringFree (&ds);
1228 #else
1229         execData (spec, argv[argi], strlen(argv[argi]), textFlag, attribute,
1230                   attribute ? strlen(attribute) : 0);
1231 #endif
1232         argi++;
1233     }
1234     if (element)
1235         tagEnd (spec, 2, NULL, 0);
1236     return TCL_OK;
1237 }
1238
1239 static int cmd_tcl_unread (ClientData clientData, Tcl_Interp *interp,
1240                            int argc, const char **argv)
1241 {
1242     struct lexSpec *spec = (struct lexSpec *) clientData;
1243     int argi = 1;
1244     int offset = 0;
1245     int no;
1246     
1247     while (argi < argc)
1248     {
1249         if (!strcmp("-offset", argv[argi]))
1250         {
1251             argi++;
1252             if (argi < argc)
1253             {
1254                 offset = atoi(argv[argi]);
1255                 argi++;
1256             }
1257         }
1258         else
1259             break;
1260     }
1261     if (argi != argc-1)
1262         return TCL_ERROR;
1263     no = atoi(argv[argi]);
1264     if (no >= spec->arg_no)
1265         no = spec->arg_no - 1;
1266     spec->ptr = spec->arg_start[no] + offset;
1267     return TCL_OK;
1268 }
1269
1270 static void execTcl (struct lexSpec *spec, struct regxCode *code)
1271 {   
1272     int i;
1273     int ret;
1274     for (i = 0; i < spec->arg_no; i++)
1275     {
1276         char var_name[10], *var_buf;
1277         int var_len, ch;
1278         
1279         sprintf (var_name, "%d", i);
1280         var_buf = f_win_get (spec, spec->arg_start[i], spec->arg_end[i],
1281                              &var_len); 
1282         if (var_buf)
1283         {
1284             ch = var_buf[var_len];
1285             var_buf[var_len] = '\0';
1286             Tcl_SetVar (spec->tcl_interp, var_name, var_buf, 0);
1287             var_buf[var_len] = ch;
1288         }
1289     }
1290 #if HAVE_TCL_OBJECTS
1291     ret = Tcl_GlobalEvalObj(spec->tcl_interp, code->tcl_obj);
1292 #else
1293     ret = Tcl_GlobalEval (spec->tcl_interp, code->str);
1294 #endif
1295     if (ret != TCL_OK)
1296     {
1297         const char *err = Tcl_GetVar(spec->tcl_interp, "errorInfo", 0);
1298         yaz_log(YLOG_FATAL, "Tcl error, line=%d, \"%s\"\n%s", 
1299             spec->tcl_interp->errorLine,
1300             spec->tcl_interp->result,
1301             err ? err : "[NO ERRORINFO]");
1302     }
1303 }
1304 /* HAVE_TCL_H */
1305 #endif
1306
1307 static void execCode (struct lexSpec *spec, struct regxCode *code)
1308 {
1309     const char *s = code->str;
1310     int cmd_len, r;
1311     const char *cmd_str;
1312     
1313     r = execTok (spec, &s, &cmd_str, &cmd_len);
1314     while (r)
1315     {
1316         char *p, ptmp[64];
1317         
1318         if (r == 1)
1319         {
1320             r = execTok (spec, &s, &cmd_str, &cmd_len);
1321             continue;
1322         }
1323         p = regxStrz (cmd_str, cmd_len, ptmp);
1324         if (!strcmp (p, "begin"))
1325         {
1326             r = execTok (spec, &s, &cmd_str, &cmd_len);
1327             if (r < 2)
1328             {
1329                 yaz_log (YLOG_WARN, "missing keyword after 'begin'");
1330                 continue;
1331             }
1332             p = regxStrz (cmd_str, cmd_len, ptmp);
1333             if (!strcmp (p, "record"))
1334             {
1335                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1336                 if (r < 2)
1337                     continue;
1338                 if (spec->d1_level <= 1)
1339                 {
1340                     static char absynName[64];
1341                     data1_node *res;
1342
1343                     if (cmd_len > 63)
1344                         cmd_len = 63;
1345                     memcpy (absynName, cmd_str, cmd_len);
1346                     absynName[cmd_len] = '\0';
1347 #if REGX_DEBUG
1348                     yaz_log (YLOG_LOG, "begin record %s", absynName);
1349 #endif
1350                     res = data1_mk_root (spec->dh, spec->m, absynName);
1351                     
1352                     spec->d1_level = 0;
1353
1354                     spec->d1_stack[spec->d1_level++] = res;
1355
1356                     res = data1_mk_tag (spec->dh, spec->m, absynName, 0, res);
1357
1358                     spec->d1_stack[spec->d1_level++] = res;
1359
1360                     spec->d1_stack[spec->d1_level] = NULL;
1361                 }
1362                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1363             }
1364             else if (!strcmp (p, "element"))
1365             {
1366                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1367                 if (r < 2)
1368                     continue;
1369                 tagBegin (spec, cmd_str, cmd_len);
1370                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1371             } 
1372             else if (!strcmp (p, "variant"))
1373             {
1374                 int class_len;
1375                 const char *class_str = NULL;
1376                 int type_len;
1377                 const char *type_str = NULL;
1378                 int value_len;
1379                 const char *value_str = NULL;
1380                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1381                 if (r < 2)
1382                     continue;
1383                 class_str = cmd_str;
1384                 class_len = cmd_len;
1385                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1386                 if (r < 2)
1387                     continue;
1388                 type_str = cmd_str;
1389                 type_len = cmd_len;
1390
1391                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1392                 if (r < 2)
1393                     continue;
1394                 value_str = cmd_str;
1395                 value_len = cmd_len;
1396
1397                 variantBegin (spec, class_str, class_len,
1398                               type_str, type_len, value_str, value_len);
1399                 
1400                 
1401                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1402             }
1403             else if (!strcmp (p, "context"))
1404             {
1405                 if (r > 1)
1406                 {
1407                     struct lexContext *lc = spec->context;
1408                     r = execTok (spec, &s, &cmd_str, &cmd_len);
1409                     p = regxStrz (cmd_str, cmd_len, ptmp);
1410 #if REGX_DEBUG
1411                     yaz_log (YLOG_LOG, "begin context %s", p);
1412 #endif
1413                     while (lc && strcmp (p, lc->name))
1414                         lc = lc->next;
1415                     if (lc)
1416                         spec->context_stack[++(spec->context_stack_top)] = lc;
1417                     else
1418                         yaz_log (YLOG_WARN, "unknown context %s", p);
1419                     
1420                 }
1421                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1422             }
1423             else
1424             {
1425                 yaz_log (YLOG_WARN, "bad keyword '%s' after begin", p);
1426             }
1427         }
1428         else if (!strcmp (p, "end"))
1429         {
1430             r = execTok (spec, &s, &cmd_str, &cmd_len);
1431             if (r < 2)
1432             {
1433                 yaz_log (YLOG_WARN, "missing keyword after 'end'");
1434                 continue;
1435             }
1436             p = regxStrz (cmd_str, cmd_len, ptmp);
1437             if (!strcmp (p, "record"))
1438             {
1439                 while (spec->d1_level)
1440                 {
1441                     tagDataRelease (spec);
1442                     (spec->d1_level)--;
1443                 }
1444                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1445 #if REGX_DEBUG
1446                 yaz_log (YLOG_LOG, "end record");
1447 #endif
1448                 spec->stop_flag = 1;
1449             }
1450             else if (!strcmp (p, "element"))
1451             {
1452                 int min_level = 2;
1453                 while ((r = execTok (spec, &s, &cmd_str, &cmd_len)) == 3)
1454                 {
1455                     if (cmd_len==7 && !memcmp ("-record", cmd_str, cmd_len))
1456                         min_level = 0;
1457                 }
1458                 if (r > 2)
1459                 {
1460                     tagEnd (spec, min_level, cmd_str, cmd_len);
1461                     r = execTok (spec, &s, &cmd_str, &cmd_len);
1462                 }
1463                 else
1464                     tagEnd (spec, min_level, NULL, 0);
1465                 if (spec->d1_level <= 1)
1466                 {
1467 #if REGX_DEBUG
1468                     yaz_log (YLOG_LOG, "end element end records");
1469 #endif
1470                     spec->stop_flag = 1;
1471                 }
1472
1473             }
1474             else if (!strcmp (p, "context"))
1475             {
1476 #if REGX_DEBUG
1477                 yaz_log (YLOG_LOG, "end context");
1478 #endif
1479                 if (spec->context_stack_top)
1480                     (spec->context_stack_top)--;
1481                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1482             }       
1483             else
1484                 yaz_log (YLOG_WARN, "bad keyword '%s' after end", p);
1485         }
1486         else if (!strcmp (p, "data"))
1487         {
1488             int textFlag = 0;
1489             int element_len;
1490             const char *element_str = NULL;
1491             int attribute_len;
1492             const char *attribute_str = NULL;
1493             
1494             while ((r = execTok (spec, &s, &cmd_str, &cmd_len)) == 3)
1495             {
1496                 if (cmd_len==5 && !memcmp ("-text", cmd_str, cmd_len))
1497                     textFlag = 1;
1498                 else if (cmd_len==8 && !memcmp ("-element", cmd_str, cmd_len))
1499                 {
1500                     r = execTok (spec, &s, &element_str, &element_len);
1501                     if (r < 2)
1502                         break;
1503                 }
1504                 else if (cmd_len==10 && !memcmp ("-attribute", cmd_str, 
1505                                                  cmd_len))
1506                 {
1507                     r = execTok (spec, &s, &attribute_str, &attribute_len);
1508                     if (r < 2)
1509                         break;
1510                 }
1511                 else 
1512                     yaz_log (YLOG_WARN, "bad data option: %.*s",
1513                           cmd_len, cmd_str);
1514             }
1515             if (r != 2)
1516             {
1517                 yaz_log (YLOG_WARN, "missing data item after data");
1518                 continue;
1519             }
1520             if (element_str)
1521                 tagBegin (spec, element_str, element_len);
1522             do
1523             {
1524                 execData (spec, cmd_str, cmd_len, textFlag,
1525                           attribute_str, attribute_len);
1526                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1527             } while (r > 1);
1528             if (element_str)
1529                 tagEnd (spec, 2, NULL, 0);
1530         }
1531         else if (!strcmp (p, "unread"))
1532         {
1533             int no, offset;
1534             r = execTok (spec, &s, &cmd_str, &cmd_len);
1535             if (r==3 && cmd_len == 7 && !memcmp ("-offset", cmd_str, cmd_len))
1536             {
1537                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1538                 if (r < 2)
1539                 {
1540                     yaz_log (YLOG_WARN, "missing number after -offset");
1541                     continue;
1542                 }
1543                 p = regxStrz (cmd_str, cmd_len, ptmp);
1544                 offset = atoi (p);
1545                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1546             }
1547             else
1548                 offset = 0;
1549             if (r < 2)
1550             {
1551                 yaz_log (YLOG_WARN, "missing index after unread command");
1552                 continue;
1553             }
1554             if (cmd_len != 1 || *cmd_str < '0' || *cmd_str > '9')
1555             {
1556                 yaz_log (YLOG_WARN, "bad index after unread command");
1557                 continue;
1558             }
1559             else
1560             {
1561                 no = *cmd_str - '0';
1562                 if (no >= spec->arg_no)
1563                     no = spec->arg_no - 1;
1564                 spec->ptr = spec->arg_start[no] + offset;
1565             }
1566             r = execTok (spec, &s, &cmd_str, &cmd_len);
1567         }
1568         else if (!strcmp (p, "context"))
1569         {
1570             if (r > 1)
1571             {
1572                 struct lexContext *lc = spec->context;
1573                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1574                 p = regxStrz (cmd_str, cmd_len, ptmp);
1575                 
1576                 while (lc && strcmp (p, lc->name))
1577                     lc = lc->next;
1578                 if (lc)
1579                     spec->context_stack[spec->context_stack_top] = lc;
1580                 else
1581                     yaz_log (YLOG_WARN, "unknown context %s", p);
1582
1583             }
1584             r = execTok (spec, &s, &cmd_str, &cmd_len);
1585         }
1586         else
1587         {
1588             yaz_log (YLOG_WARN, "unknown code command '%.*s'", cmd_len, cmd_str);
1589             r = execTok (spec, &s, &cmd_str, &cmd_len);
1590             continue;
1591         }
1592         if (r > 1)
1593         {
1594             yaz_log (YLOG_WARN, "ignoring token %.*s", cmd_len, cmd_str);
1595             do {
1596                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1597             } while (r > 1);
1598         }
1599     }
1600 }
1601
1602
1603 static int execAction (struct lexSpec *spec, struct lexRuleAction *ap,
1604                        int start_ptr, int *pptr)
1605 {
1606     int sptr;
1607     int arg_start[20];
1608     int arg_end[20];
1609     int arg_no = 1;
1610
1611     if (!ap)
1612         return 1;
1613     arg_start[0] = start_ptr;
1614     arg_end[0] = *pptr;
1615     spec->arg_start = arg_start;
1616     spec->arg_end = arg_end;
1617
1618     while (ap)
1619     {
1620         switch (ap->which)
1621         {
1622         case REGX_PATTERN:
1623             if (ap->u.pattern.body)
1624             {
1625                 arg_start[arg_no] = *pptr;
1626                 if (!tryMatch (spec, pptr, &sptr, ap->u.pattern.dfa, 0))
1627                 {
1628                     arg_end[arg_no] = F_WIN_EOF;
1629                     arg_no++;
1630                     arg_start[arg_no] = F_WIN_EOF;
1631                     arg_end[arg_no] = F_WIN_EOF;
1632                     yaz_log(YLOG_DEBUG, "Pattern match rest of record");
1633                     *pptr = F_WIN_EOF;
1634                 }
1635                 else
1636                 {
1637                     arg_end[arg_no] = sptr;
1638                     arg_no++;
1639                     arg_start[arg_no] = sptr;
1640                     arg_end[arg_no] = *pptr;
1641                 }
1642             }
1643             else
1644             {
1645                 arg_start[arg_no] = *pptr;
1646                 if (!tryMatch (spec, pptr, &sptr, ap->u.pattern.dfa, 1))
1647                     return 1;
1648                 if (sptr != arg_start[arg_no])
1649                     return 1;
1650                 arg_end[arg_no] = *pptr;
1651             }
1652             arg_no++;
1653             break;
1654         case REGX_CODE:
1655             spec->arg_no = arg_no;
1656             spec->ptr = *pptr;
1657 #if HAVE_TCL_H
1658             if (spec->tcl_interp)
1659                 execTcl(spec, ap->u.code);
1660             else
1661                 execCode (spec, ap->u.code);
1662 #else
1663             execCode (spec, ap->u.code);
1664 #endif
1665             *pptr = spec->ptr;
1666             if (spec->stop_flag)
1667                 return 0;
1668             break;
1669         case REGX_END:
1670             arg_start[arg_no] = *pptr;
1671             arg_end[arg_no] = F_WIN_EOF;
1672             arg_no++;
1673             *pptr = F_WIN_EOF;
1674         }
1675         ap = ap->next;
1676     }
1677     return 1;
1678 }
1679
1680 static int execRule (struct lexSpec *spec, struct lexContext *context,
1681                      int ruleNo, int start_ptr, int *pptr)
1682 {
1683 #if REGX_DEBUG
1684     yaz_log (YLOG_LOG, "exec rule %d", ruleNo);
1685 #endif
1686     return execAction (spec, context->fastRule[ruleNo]->actionList,
1687                        start_ptr, pptr);
1688 }
1689
1690 data1_node *lexNode (struct lexSpec *spec, int *ptr)
1691 {
1692     struct lexContext *context = spec->context_stack[spec->context_stack_top];
1693     struct DFA_state *state = context->dfa->states[0];
1694     struct DFA_tran *t;
1695     unsigned char c;
1696     unsigned char c_prev = '\n';
1697     int i;
1698     int last_rule = 0;        /* rule number of current match */
1699     int last_ptr = *ptr;      /* last char of match */
1700     int start_ptr = *ptr;     /* first char of match */
1701     int skip_ptr = *ptr;      /* first char of run */
1702
1703     while (1)
1704     {
1705         c = f_win_advance (spec, ptr);
1706         if (*ptr == F_WIN_EOF)
1707         {
1708             /* end of file met */
1709             if (last_rule)
1710             {
1711                 /* there was a match */
1712                 if (skip_ptr < start_ptr)
1713                 {
1714                     /* deal with chars that didn't match */
1715                     int size;
1716                     char *buf;
1717                     buf = f_win_get (spec, skip_ptr, start_ptr, &size);
1718                     execDataP (spec, buf, size, 0);
1719                 }
1720                 /* restore pointer */
1721                 *ptr = last_ptr;
1722                 /* execute rule */
1723                 if (!execRule (spec, context, last_rule, start_ptr, ptr))
1724                     break;
1725                 /* restore skip pointer */
1726                 skip_ptr = *ptr;
1727                 last_rule = 0;
1728             }
1729             else if (skip_ptr < *ptr)
1730             {
1731                 /* deal with chars that didn't match */
1732                 int size;
1733                 char *buf;
1734                 buf = f_win_get (spec, skip_ptr, *ptr, &size);
1735                 execDataP (spec, buf, size, 0);
1736             }
1737             if (*ptr == F_WIN_EOF)
1738                 break;
1739         }
1740         t = state->trans;
1741         i = state->tran_no;
1742         while (1)
1743             if (--i < 0)
1744             {   /* no transition for character c ... */
1745                 if (last_rule)
1746                 {
1747                     if (skip_ptr < start_ptr)
1748                     {
1749                         /* deal with chars that didn't match */
1750                         int size;
1751                         char *buf;
1752                         buf = f_win_get (spec, skip_ptr, start_ptr, &size);
1753                         execDataP (spec, buf, size, 0);
1754                     }
1755                     /* restore pointer */
1756                     *ptr = last_ptr;
1757                     if (!execRule (spec, context, last_rule, start_ptr, ptr))
1758                     {
1759                         if (spec->f_win_ef && *ptr != F_WIN_EOF)
1760                         {
1761 #if REGX_DEBUG
1762                             yaz_log (YLOG_LOG, "regx: endf ptr=%d", *ptr);
1763 #endif
1764                             (*spec->f_win_ef)(spec->f_win_fh, *ptr);
1765                         }
1766                         return NULL;
1767                     }
1768                     context = spec->context_stack[spec->context_stack_top];
1769                     skip_ptr = *ptr;
1770                     last_rule = 0;
1771                     last_ptr = start_ptr = *ptr;
1772                     if (start_ptr > 0)
1773                     {
1774                         --start_ptr;
1775                         c_prev = f_win_advance (spec, &start_ptr);
1776                     }
1777                 }
1778                 else
1779                 {
1780                     c_prev = f_win_advance (spec, &start_ptr);
1781                     *ptr = start_ptr;
1782                 }
1783                 state = context->dfa->states[0];
1784                 break;
1785             }
1786             else if (c >= t->ch[0] && c <= t->ch[1])
1787             {   /* transition ... */
1788                 state = context->dfa->states[t->to];
1789                 if (state->rule_no)
1790                 {
1791                     if (c_prev == '\n')
1792                     {
1793                         last_rule = state->rule_no;
1794                         last_ptr = *ptr;
1795                     } 
1796                     else if (state->rule_nno)
1797                     {
1798                         last_rule = state->rule_nno;
1799                         last_ptr = *ptr;
1800                     }
1801                 }
1802                 break;
1803             }
1804             else
1805                 t++;
1806     }
1807     return NULL;
1808 }
1809
1810 static data1_node *lexRoot (struct lexSpec *spec, off_t offset,
1811                             const char *context_name)
1812 {
1813     struct lexContext *lt = spec->context;
1814     int ptr = offset;
1815
1816     spec->stop_flag = 0;
1817     spec->d1_level = 0;
1818     spec->context_stack_top = 0;    
1819     while (lt)
1820     {
1821         if (!strcmp (lt->name, context_name))
1822             break;
1823         lt = lt->next;
1824     }
1825     if (!lt)
1826     {
1827         yaz_log (YLOG_WARN, "cannot find context %s", context_name);
1828         return NULL;
1829     }
1830     spec->context_stack[spec->context_stack_top] = lt;
1831     spec->d1_stack[spec->d1_level] = NULL;
1832 #if 1
1833     if (!lt->initFlag)
1834     {
1835         lt->initFlag = 1;
1836         execAction (spec, lt->initActionList, ptr, &ptr);
1837     }
1838 #endif
1839     execAction (spec, lt->beginActionList, ptr, &ptr);
1840     lexNode (spec, &ptr);
1841     while (spec->d1_level)
1842     {
1843         tagDataRelease (spec);
1844         (spec->d1_level)--;
1845     }
1846     execAction (spec, lt->endActionList, ptr, &ptr);
1847     return spec->d1_stack[0];
1848 }
1849
1850 void grs_destroy(void *clientData)
1851 {
1852     struct lexSpecs *specs = (struct lexSpecs *) clientData;
1853     if (specs->spec)
1854     {
1855         lexSpecDestroy(&specs->spec);
1856     }
1857     xfree (specs);
1858 }
1859
1860 void *grs_init(Res res, RecType recType)
1861 {
1862     struct lexSpecs *specs = (struct lexSpecs *) xmalloc (sizeof(*specs));
1863     specs->spec = 0;
1864     strcpy(specs->type, "");
1865     return specs;
1866 }
1867
1868
1869 void grs_config(void *clientData, Res res, const char *args)
1870 {
1871     struct lexSpecs *specs = (struct lexSpecs *) clientData;
1872     if (strlen(args) < sizeof(specs->type))
1873         strcpy(specs->type, args);
1874 }
1875
1876 data1_node *grs_read_regx (struct grs_read_info *p)
1877 {
1878     int res;
1879     struct lexSpecs *specs = (struct lexSpecs *) p->clientData;
1880     struct lexSpec **curLexSpec = &specs->spec;
1881
1882 #if REGX_DEBUG
1883     yaz_log (YLOG_LOG, "grs_read_regx");
1884 #endif
1885     if (!*curLexSpec || strcmp ((*curLexSpec)->name, specs->type))
1886     {
1887         if (*curLexSpec)
1888             lexSpecDestroy (curLexSpec);
1889         *curLexSpec = lexSpecCreate (specs->type, p->dh);
1890         res = readFileSpec (*curLexSpec);
1891         if (res)
1892         {
1893             lexSpecDestroy (curLexSpec);
1894             return NULL;
1895         }
1896     }
1897     (*curLexSpec)->dh = p->dh;
1898     if (!p->offset)
1899     {
1900         (*curLexSpec)->f_win_start = 0;
1901         (*curLexSpec)->f_win_end = 0;
1902         (*curLexSpec)->f_win_rf = p->readf;
1903         (*curLexSpec)->f_win_sf = p->seekf;
1904         (*curLexSpec)->f_win_fh = p->fh;
1905         (*curLexSpec)->f_win_ef = p->endf;
1906         (*curLexSpec)->f_win_size = 500000;
1907     }
1908     (*curLexSpec)->m = p->mem;
1909     return lexRoot (*curLexSpec, p->offset, "main");
1910 }
1911
1912 static int extract_regx(void *clientData, struct recExtractCtrl *ctrl)
1913 {
1914     return zebra_grs_extract(clientData, ctrl, grs_read_regx);
1915 }
1916
1917 static int retrieve_regx(void *clientData, struct recRetrieveCtrl *ctrl)
1918 {
1919     return zebra_grs_retrieve(clientData, ctrl, grs_read_regx);
1920 }
1921
1922 static struct recType regx_type = {
1923     "grs.regx",
1924     grs_init,
1925     grs_config,
1926     grs_destroy,
1927     extract_regx,
1928     retrieve_regx,
1929 };
1930
1931
1932 #if HAVE_TCL_H
1933 data1_node *grs_read_tcl (struct grs_read_info *p)
1934 {
1935     int res;
1936     struct lexSpecs *specs = (struct lexSpecs *) p->clientData;
1937     struct lexSpec **curLexSpec = &specs->spec;
1938
1939 #if REGX_DEBUG
1940     yaz_log (YLOG_LOG, "grs_read_tcl");
1941 #endif
1942     if (!*curLexSpec || strcmp ((*curLexSpec)->name, specs->type))
1943     {
1944         Tcl_Interp *tcl_interp;
1945         if (*curLexSpec)
1946             lexSpecDestroy (curLexSpec);
1947         *curLexSpec = lexSpecCreate (specs->type, p->dh);
1948         Tcl_FindExecutable("");
1949         tcl_interp = (*curLexSpec)->tcl_interp = Tcl_CreateInterp();
1950         Tcl_Init(tcl_interp);
1951         Tcl_CreateCommand (tcl_interp, "begin", cmd_tcl_begin, *curLexSpec, 0);
1952         Tcl_CreateCommand (tcl_interp, "end", cmd_tcl_end, *curLexSpec, 0);
1953         Tcl_CreateCommand (tcl_interp, "data", cmd_tcl_data, *curLexSpec, 0);
1954         Tcl_CreateCommand (tcl_interp, "unread", cmd_tcl_unread,
1955                            *curLexSpec, 0);
1956         res = readFileSpec (*curLexSpec);
1957         if (res)
1958         {
1959             lexSpecDestroy (curLexSpec);
1960             return NULL;
1961         }
1962     }
1963     (*curLexSpec)->dh = p->dh;
1964     if (!p->offset)
1965     {
1966         (*curLexSpec)->f_win_start = 0;
1967         (*curLexSpec)->f_win_end = 0;
1968         (*curLexSpec)->f_win_rf = p->readf;
1969         (*curLexSpec)->f_win_sf = p->seekf;
1970         (*curLexSpec)->f_win_fh = p->fh;
1971         (*curLexSpec)->f_win_ef = p->endf;
1972         (*curLexSpec)->f_win_size = 500000;
1973     }
1974     (*curLexSpec)->m = p->mem;
1975     return lexRoot (*curLexSpec, p->offset, "main");
1976 }
1977
1978 static int extract_tcl(void *clientData, struct recExtractCtrl *ctrl)
1979 {
1980     return zebra_grs_extract(clientData, ctrl, grs_read_tcl);
1981 }
1982
1983 static int retrieve_tcl(void *clientData, struct recRetrieveCtrl *ctrl)
1984 {
1985     return zebra_grs_retrieve(clientData, ctrl, grs_read_tcl);
1986 }
1987
1988 static struct recType tcl_type = {
1989     "grs.tcl",
1990     grs_init,
1991     grs_config,
1992     grs_destroy,
1993     extract_tcl,
1994     retrieve_tcl,
1995 };
1996
1997 #endif
1998
1999 RecType
2000 #ifdef IDZEBRA_STATIC_GRS_REGX
2001 idzebra_filter_grs_regx
2002 #else
2003 idzebra_filter
2004 #endif
2005
2006 [] = {
2007     &regx_type,
2008 #if HAVE_TCL_H
2009     &tcl_type,
2010 #endif
2011     0,
2012 };