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