Fixed bug #234: mbox filter hangs when seeing a non-mbox filter.
[idzebra-moved-to-github.git] / recctrl / regxread.c
1 /* $Id: regxread.c,v 1.50.2.4 2006-10-30 14:14:20 adam 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 this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
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 "grsread.h"
33
34 #if HAVE_TCL_H
35 #define USE_NON_CONST
36 #include <tcl.h>
37
38 #if MAJOR_VERSION >= 8
39 #define HAVE_TCL_OBJECTS
40 #endif
41 #endif
42
43 #define REGX_DEBUG 0
44
45 #define F_WIN_EOF 2000000000
46 #define F_WIN_READ 1
47
48 #define REGX_EOF     0
49 #define REGX_PATTERN 1
50 #define REGX_BODY    2
51 #define REGX_BEGIN   3
52 #define REGX_END     4
53 #define REGX_CODE    5
54 #define REGX_CONTEXT 6
55 #define REGX_INIT    7
56
57 struct regxCode {
58     char *str;
59 #if HAVE_TCL_OBJECTS
60     Tcl_Obj *tcl_obj;
61 #endif
62 };
63
64 struct lexRuleAction {
65     int which; 
66     union {
67         struct {
68             struct DFA *dfa;    /* REGX_PATTERN */
69             int body;
70         } pattern;
71         struct regxCode *code;  /* REGX_CODE */
72     } u;
73     struct lexRuleAction *next;
74 };
75
76 struct lexRuleInfo {
77     int no;
78     struct lexRuleAction *actionList;
79 };
80
81 struct lexRule {
82     struct lexRuleInfo info;
83     struct lexRule *next;
84 };
85
86 struct lexContext {
87     char *name;
88     struct DFA *dfa;
89     struct lexRule *rules;
90     struct lexRuleInfo **fastRule;
91     int ruleNo;
92     int initFlag;
93
94     struct lexRuleAction *beginActionList;
95     struct lexRuleAction *endActionList;
96     struct lexRuleAction *initActionList;
97     struct lexContext *next;
98 };
99
100 struct lexConcatBuf {
101     int max;
102     char *buf;
103 };
104
105 struct lexSpec {
106     char *name;
107     struct lexContext *context;
108
109     struct lexContext **context_stack;
110     int context_stack_size;
111     int context_stack_top;
112
113     int lineNo;
114     NMEM m;
115     data1_handle dh;
116 #if HAVE_TCL_H
117     Tcl_Interp *tcl_interp;
118 #endif
119     void *f_win_fh;
120     void (*f_win_ef)(void *, off_t);
121
122     int f_win_start;      /* first byte of buffer is this file offset */
123     int f_win_end;        /* last byte of buffer is this offset - 1 */
124     int f_win_size;       /* size of buffer */
125     char *f_win_buf;      /* buffer itself */
126     int (*f_win_rf)(void *, char *, size_t);
127     off_t (*f_win_sf)(void *, off_t);
128
129     struct lexConcatBuf *concatBuf;
130     int maxLevel;
131     data1_node **d1_stack;
132     int d1_level;
133     int stop_flag;
134     
135     int *arg_start;
136     int *arg_end;
137     int arg_no;
138     int ptr;
139 };
140
141 struct lexSpecs {
142     struct lexSpec *spec;
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             logf (LOG_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             logf (LOG_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                 int sz = s - s0;
473                 xfree (*ap);
474                 *ap = NULL;
475                 logf (LOG_WARN, "regular expression error '%.*s'", sz, s0);
476                 return -1;
477             }
478             if (debug_dfa_tran)
479             {
480                 int sz = s - s0;
481                 printf ("pattern: %.*s\n", sz, s0);
482             }
483             dfa_mkstate ((*ap)->u.pattern.dfa);
484             s++;
485             break;
486         case REGX_BEGIN:
487             logf (LOG_WARN, "cannot use BEGIN here");
488             continue;
489         case REGX_INIT:
490             logf (LOG_WARN, "cannot use INIT here");
491             continue;
492         case REGX_END:
493             *ap = (struct lexRuleAction *) xmalloc (sizeof(**ap));
494             (*ap)->which = tok;
495             break;
496         }
497         ap = &(*ap)->next;
498     }
499     *ap = NULL;
500     return 0;
501 }
502
503 int readOneSpec (struct lexSpec *spec, const char *s)
504 {
505     int len, r, tok;
506     struct lexRule *rp;
507     struct lexContext *lc;
508
509     tok = readParseToken (&s, &len);
510     if (tok == REGX_CONTEXT)
511     {
512         char context_name[32];
513         tok = readParseToken (&s, &len);
514         if (tok != REGX_CODE)
515         {
516             logf (LOG_WARN, "missing name after CONTEXT keyword");
517             return 0;
518         }
519         if (len > 31)
520             len = 31;
521         memcpy (context_name, s, len);
522         context_name[len] = '\0';
523         lc = lexContextCreate (context_name);
524         lc->next = spec->context;
525         spec->context = lc;
526         return 0;
527     }
528     if (!spec->context)
529         spec->context = lexContextCreate ("main");
530        
531     switch (tok)
532     {
533     case REGX_BEGIN:
534         actionListDel (&spec->context->beginActionList);
535         actionListMk (spec, s, &spec->context->beginActionList);
536         break;
537     case REGX_END:
538         actionListDel (&spec->context->endActionList);
539         actionListMk (spec, s, &spec->context->endActionList);
540         break;
541     case REGX_INIT:
542         actionListDel (&spec->context->initActionList);
543         actionListMk (spec, s, &spec->context->initActionList);
544         break;
545     case REGX_PATTERN:
546 #if REGX_DEBUG
547         logf (LOG_LOG, "rule %d %s", spec->context->ruleNo, s);
548 #endif
549         r = dfa_parse (spec->context->dfa, &s);
550         if (r)
551         {
552             logf (LOG_WARN, "regular expression error. r=%d", r);
553             return -1;
554         }
555         if (*s != '/')
556         {
557             logf (LOG_WARN, "expects / at end of pattern. got %c", *s);
558             return -1;
559         }
560         s++;
561         rp = (struct lexRule *) xmalloc (sizeof(*rp));
562         rp->info.no = spec->context->ruleNo++;
563         rp->next = spec->context->rules;
564         spec->context->rules = rp;
565         actionListMk (spec, s, &rp->info.actionList);
566     }
567     return 0;
568 }
569
570 int readFileSpec (struct lexSpec *spec)
571 {
572     struct lexContext *lc;
573     int c, i, errors = 0;
574     FILE *spec_inf = 0;
575     WRBUF lineBuf;
576     char fname[256];
577
578 #if HAVE_TCL_H
579     if (spec->tcl_interp)
580     {
581         sprintf (fname, "%s.tflt", spec->name);
582         spec_inf = data1_path_fopen (spec->dh, fname, "r");
583     }
584 #endif
585     if (!spec_inf)
586     {
587         sprintf (fname, "%s.flt", spec->name);
588         spec_inf = data1_path_fopen (spec->dh, fname, "r");
589     }
590     if (!spec_inf)
591     {
592         logf (LOG_ERRNO|LOG_WARN, "cannot read spec file %s", spec->name);
593         return -1;
594     }
595     logf (LOG_LOG, "reading regx filter %s", fname);
596 #if HAVE_TCL_H
597     if (spec->tcl_interp)
598         logf (LOG_LOG, "Tcl enabled");
599 #endif
600
601 #if 0
602     debug_dfa_trav = 0;
603     debug_dfa_tran = 1;
604     debug_dfa_followpos = 0;
605     dfa_verbose = 1;
606 #endif
607
608     lineBuf = wrbuf_alloc();
609     spec->lineNo = 0;
610     c = getc (spec_inf);
611     while (c != EOF)
612     {
613         wrbuf_rewind (lineBuf);
614         if (c == '#' || c == '\n' || c == ' ' || c == '\t' || c == '\r')
615         {
616             while (c != '\n' && c != EOF)
617                 c = getc (spec_inf);
618             spec->lineNo++;
619             if (c == '\n')
620                 c = getc (spec_inf);
621         }
622         else
623         {
624             int addLine = 0;
625             
626             while (1)
627             {
628                 int c1 = c;
629                 wrbuf_putc(lineBuf, c);
630                 c = getc (spec_inf);
631                 while (c == '\r')
632                     c = getc (spec_inf);
633                 if (c == EOF)
634                     break;
635                 if (c1 == '\n')
636                 {
637                     if (c != ' ' && c != '\t')
638                         break;
639                     addLine++;
640                 }
641             }
642             wrbuf_putc(lineBuf, '\0');
643             readOneSpec (spec, wrbuf_buf(lineBuf));
644             spec->lineNo += addLine;
645         }
646     }
647     fclose (spec_inf);
648     wrbuf_free(lineBuf, 1);
649
650     for (lc = spec->context; lc; lc = lc->next)
651     {
652         struct lexRule *rp;
653         lc->fastRule = (struct lexRuleInfo **)
654             xmalloc (sizeof(*lc->fastRule) * lc->ruleNo);
655         for (i = 0; i < lc->ruleNo; i++)
656             lc->fastRule[i] = NULL;
657         for (rp = lc->rules; rp; rp = rp->next)
658             lc->fastRule[rp->info.no] = &rp->info;
659         dfa_mkstate (lc->dfa);
660     }
661     if (errors)
662         return -1;
663     
664     return 0;
665 }
666
667 #if 0
668 static struct lexSpec *curLexSpec = NULL;
669 #endif
670
671 static void execData (struct lexSpec *spec,
672                       const char *ebuf, int elen, int formatted_text,
673                       const char *attribute_str, int attribute_len)
674 {
675     struct data1_node *res, *parent;
676     int org_len;
677
678     if (elen == 0) /* shouldn't happen, but it does! */
679         return ;
680 #if REGX_DEBUG
681     if (elen > 80)
682         logf (LOG_LOG, "data(%d bytes) %.40s ... %.*s", elen,
683               ebuf, 40, ebuf + elen-40);
684     else if (elen == 1 && ebuf[0] == '\n')
685     {
686         logf (LOG_LOG, "data(new line)");
687     }
688     else if (elen > 0)
689         logf (LOG_LOG, "data(%d bytes) %.*s", elen, elen, ebuf);
690     else 
691         logf (LOG_LOG, "data(%d bytes)", elen);
692 #endif
693         
694     if (spec->d1_level <= 1)
695         return;
696
697     parent = spec->d1_stack[spec->d1_level -1];
698     assert (parent);
699
700     if (attribute_str)
701     {
702         data1_xattr **ap;
703         res = parent;
704         if (res->which != DATA1N_tag)
705             return;
706         /* sweep through exising attributes.. */
707         for (ap = &res->u.tag.attributes; *ap; ap = &(*ap)->next)
708             if (strlen((*ap)->name) == attribute_len &&
709                 !memcmp((*ap)->name, attribute_str, attribute_len))
710                 break;
711         if (!*ap)
712         {
713             /* new attribute. Create it with name + value */
714             *ap = nmem_malloc(spec->m, sizeof(**ap));
715
716             (*ap)->name = nmem_malloc(spec->m, attribute_len+1);
717             memcpy((*ap)->name, attribute_str, attribute_len);
718             (*ap)->name[attribute_len] = '\0';
719
720             (*ap)->value = nmem_malloc(spec->m, elen+1);
721             memcpy((*ap)->value, ebuf, elen);
722             (*ap)->value[elen] = '\0';
723             (*ap)->next = 0;
724         }
725         else
726         {
727             /* append to value if attribute already exists */
728             char *nv = nmem_malloc(spec->m, elen + 1 + strlen((*ap)->value));
729             strcpy(nv, (*ap)->value);
730             memcpy (nv + strlen(nv), ebuf, elen);
731             nv[strlen(nv)+elen] = '\0';
732             (*ap)->value = nv;
733         }
734     } 
735     else 
736     {
737         if ((res = spec->d1_stack[spec->d1_level]) && 
738             res->which == DATA1N_data)
739             org_len = res->u.data.len;
740         else
741         {
742             org_len = 0;
743             
744             res = data1_mk_node2 (spec->dh, spec->m, DATA1N_data, parent);
745             res->u.data.what = DATA1I_text;
746             res->u.data.len = 0;
747             res->u.data.formatted_text = formatted_text;
748             res->u.data.data = 0;
749             
750             if (spec->d1_stack[spec->d1_level])
751                 spec->d1_stack[spec->d1_level]->next = res;
752             spec->d1_stack[spec->d1_level] = res;
753         }
754         if (org_len + elen >= spec->concatBuf[spec->d1_level].max)
755         {
756             char *old_buf, *new_buf;
757             
758             spec->concatBuf[spec->d1_level].max = org_len + elen + 256;
759             new_buf = (char *) xmalloc (spec->concatBuf[spec->d1_level].max);
760             if ((old_buf = spec->concatBuf[spec->d1_level].buf))
761             {
762                 memcpy (new_buf, old_buf, org_len);
763                 xfree (old_buf);
764             }
765             spec->concatBuf[spec->d1_level].buf = new_buf;
766         }
767         memcpy (spec->concatBuf[spec->d1_level].buf + org_len, ebuf, elen);
768         res->u.data.len += elen;
769     }
770 }
771
772 static void execDataP (struct lexSpec *spec,
773                        const char *ebuf, int elen, int formatted_text)
774 {
775     execData (spec, ebuf, elen, formatted_text, 0, 0);
776 }
777
778 static void tagDataRelease (struct lexSpec *spec)
779 {
780     data1_node *res;
781     
782     if ((res = spec->d1_stack[spec->d1_level]) &&
783         res->which == DATA1N_data && 
784         res->u.data.what == DATA1I_text)
785     {
786         assert (!res->u.data.data);
787         assert (res->u.data.len > 0);
788         if (res->u.data.len > DATA1_LOCALDATA)
789             res->u.data.data = (char *) nmem_malloc (spec->m, res->u.data.len);
790         else
791             res->u.data.data = res->lbuf;
792         memcpy (res->u.data.data, spec->concatBuf[spec->d1_level].buf,
793                 res->u.data.len);
794     }
795 }
796
797 static void variantBegin (struct lexSpec *spec, 
798                           const char *class_str, int class_len,
799                           const char *type_str, int type_len,
800                           const char *value_str, int value_len)
801 {
802     struct data1_node *parent = spec->d1_stack[spec->d1_level -1];
803     char tclass[DATA1_MAX_SYMBOL], ttype[DATA1_MAX_SYMBOL];
804     data1_vartype *tp;
805     int i;
806     data1_node *res;
807
808     if (spec->d1_level == 0)
809     {
810         logf (LOG_WARN, "in variant begin. No record type defined");
811         return ;
812     }
813     if (class_len >= DATA1_MAX_SYMBOL)
814         class_len = DATA1_MAX_SYMBOL-1;
815     memcpy (tclass, class_str, class_len);
816     tclass[class_len] = '\0';
817
818     if (type_len >= DATA1_MAX_SYMBOL)
819         type_len = DATA1_MAX_SYMBOL-1;
820     memcpy (ttype, type_str, type_len);
821     ttype[type_len] = '\0';
822
823 #if REGX_DEBUG 
824     logf (LOG_LOG, "variant begin(%s,%s,%d)", tclass, ttype,
825           spec->d1_level);
826 #endif
827
828     if (!(tp =
829           data1_getvartypebyct(spec->dh, parent->root->u.root.absyn->varset,
830                                tclass, ttype)))
831         return;
832     
833     if (parent->which != DATA1N_variant)
834     {
835         res = data1_mk_node2 (spec->dh, spec->m, DATA1N_variant, parent);
836         if (spec->d1_stack[spec->d1_level])
837             tagDataRelease (spec);
838         spec->d1_stack[spec->d1_level] = res;
839         spec->d1_stack[++(spec->d1_level)] = NULL;
840     }
841     for (i = spec->d1_level-1; spec->d1_stack[i]->which == DATA1N_variant; i--)
842         if (spec->d1_stack[i]->u.variant.type == tp)
843         {
844             spec->d1_level = i;
845             break;
846         }
847
848 #if REGX_DEBUG 
849     logf (LOG_LOG, "variant node(%d)", spec->d1_level);
850 #endif
851     parent = spec->d1_stack[spec->d1_level-1];
852     res = data1_mk_node2 (spec->dh, spec->m, DATA1N_variant, parent);
853     res->u.variant.type = tp;
854
855     if (value_len >= DATA1_LOCALDATA)
856         value_len =DATA1_LOCALDATA-1;
857     memcpy (res->lbuf, value_str, value_len);
858     res->lbuf[value_len] = '\0';
859
860     res->u.variant.value = res->lbuf;
861     
862     if (spec->d1_stack[spec->d1_level])
863         tagDataRelease (spec);
864     spec->d1_stack[spec->d1_level] = res;
865     spec->d1_stack[++(spec->d1_level)] = NULL;
866 }
867
868 static void tagStrip (const char **tag, int *len)
869 {
870     int i;
871
872     for (i = *len; i > 0 && isspace((*tag)[i-1]); --i)
873         ;
874     *len = i;
875     for (i = 0; i < *len && isspace((*tag)[i]); i++)
876         ;
877     *tag += i;
878     *len -= i;
879 }
880
881 static void tagBegin (struct lexSpec *spec, 
882                       const char *tag, int len)
883 {
884     if (spec->d1_level == 0)
885     {
886         logf (LOG_WARN, "in element begin. No record type defined");
887         return ;
888     }
889     tagStrip (&tag, &len);
890     if (spec->d1_stack[spec->d1_level])
891         tagDataRelease (spec);
892
893 #if REGX_DEBUG 
894     logf (LOG_LOG, "begin tag(%.*s, %d)", len, tag, spec->d1_level);
895 #endif
896
897     spec->d1_stack[spec->d1_level] = data1_mk_tag_n (
898         spec->dh, spec->m, tag, len, 0, spec->d1_stack[spec->d1_level -1]);
899     spec->d1_stack[++(spec->d1_level)] = NULL;
900 }
901
902 static void tagEnd (struct lexSpec *spec, int min_level,
903                     const char *tag, int len)
904 {
905     tagStrip (&tag, &len);
906     while (spec->d1_level > min_level)
907     {
908         tagDataRelease (spec);
909         (spec->d1_level)--;
910         if (spec->d1_level == 0)
911             break;
912         if ((spec->d1_stack[spec->d1_level]->which == DATA1N_tag) &&
913             (!tag ||
914              (strlen(spec->d1_stack[spec->d1_level]->u.tag.tag) ==
915               (size_t) len &&
916               !memcmp (spec->d1_stack[spec->d1_level]->u.tag.tag, tag, len))))
917             break;
918     }
919 #if REGX_DEBUG
920     logf (LOG_LOG, "end tag(%d)", spec->d1_level);
921 #endif
922 }
923
924
925 static int tryMatch (struct lexSpec *spec, int *pptr, int *mptr,
926                      struct DFA *dfa, int greedy)
927 {
928     struct DFA_state *state = dfa->states[0];
929     struct DFA_tran *t;
930     unsigned char c = 0;
931     unsigned char c_prev = 0;
932     int ptr = *pptr;          /* current pointer */
933     int start_ptr = *pptr;    /* first char of match */
934     int last_ptr = 0;         /* last char of match */
935     int last_rule = 0;        /* rule number of current match */
936     int restore_ptr = 0;
937     int i;
938
939     if (ptr)
940     {
941         --ptr;
942         c = f_win_advance (spec, &ptr);
943     }
944     while (1)
945     {
946         if (dfa->states[0] == state)
947         {
948             c_prev = c;
949             restore_ptr = ptr;
950         }
951         c = f_win_advance (spec, &ptr);
952
953         if (ptr == F_WIN_EOF)
954         {
955             if (last_rule)
956             {
957                 *mptr = start_ptr;
958                 *pptr = last_ptr;
959                 return 1;
960             }
961             break;
962         }
963
964         t = state->trans;
965         i = state->tran_no;
966         while (1)
967             if (--i < 0)    /* no transition for character c */
968             {
969                 if (last_rule)
970                 {
971                     *mptr = start_ptr;     /* match starts here */
972                     *pptr = last_ptr;      /* match end here (+1) */
973                     return 1;
974                 }
975                 state = dfa->states[0];
976
977                 ptr = restore_ptr;
978                 c = f_win_advance (spec, &ptr);
979
980                 start_ptr = ptr;
981
982                 break;
983             }
984             else if (c >= t->ch[0] && c <= t->ch[1])
985             {
986                 state = dfa->states[t->to];
987                 if (state->rule_no && c_prev == '\n')
988                 {
989                     last_rule = state->rule_no;
990                     last_ptr = ptr;
991                 }
992                 else if (state->rule_nno)
993                 {
994                     last_rule = state->rule_nno;
995                     last_ptr = ptr;
996                 }
997                 break;
998             }
999             else
1000                 t++;
1001     }
1002     return 0;
1003 }
1004
1005 static int execTok (struct lexSpec *spec, const char **src,
1006                     const char **tokBuf, int *tokLen)
1007 {
1008     const char *s = *src;
1009
1010     while (*s == ' ' || *s == '\t')
1011         s++;
1012     if (!*s)
1013         return 0;
1014     if (*s == '$' && s[1] >= '0' && s[1] <= '9')
1015     {
1016         int n = 0;
1017         s++;
1018         while (*s >= '0' && *s <= '9')
1019             n = n*10 + (*s++ -'0');
1020         if (spec->arg_no == 0)
1021         {
1022             *tokBuf = "";
1023             *tokLen = 0;
1024         }
1025         else
1026         {
1027             if (n >= spec->arg_no)
1028                 n = spec->arg_no-1;
1029             *tokBuf = f_win_get (spec, spec->arg_start[n], spec->arg_end[n],
1030                                  tokLen);
1031         }
1032     }
1033     else if (*s == '\"')
1034     {
1035         *tokBuf = ++s;
1036         while (*s && *s != '\"')
1037             s++;
1038         *tokLen = s - *tokBuf;
1039         if (*s)
1040             s++;
1041         *src = s;
1042     }
1043     else if (*s == '\n' || *s == ';')
1044     {
1045         *src = s+1;
1046         return 1;
1047     }
1048     else if (*s == '-')
1049     {
1050         *tokBuf = s++;
1051         while (*s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' &&
1052                *s != ';')
1053             s++;
1054         *tokLen = s - *tokBuf;
1055         *src = s;
1056         return 3;
1057     }
1058     else
1059     {
1060         *tokBuf = s++;
1061         while (*s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' &&
1062                *s != ';')
1063             s++;
1064         *tokLen = s - *tokBuf;
1065     }
1066     *src = s;
1067     return 2;
1068 }
1069
1070 static char *regxStrz (const char *src, int len, char *str)
1071 {
1072     if (len > 63)
1073         len = 63;
1074     memcpy (str, src, len);
1075     str[len] = '\0';
1076     return str;
1077 }
1078
1079 #if HAVE_TCL_H
1080 static int cmd_tcl_begin (ClientData clientData, Tcl_Interp *interp,
1081                           int argc, char **argv)
1082 {
1083     struct lexSpec *spec = (struct lexSpec *) clientData;
1084     if (argc < 2)
1085         return TCL_ERROR;
1086     if (!strcmp(argv[1], "record") && argc == 3)
1087     {
1088         char *absynName = argv[2];
1089         data1_node *res;
1090
1091 #if REGX_DEBUG
1092         logf (LOG_LOG, "begin record %s", absynName);
1093 #endif
1094         res = data1_mk_root (spec->dh, spec->m, absynName);
1095         
1096         spec->d1_level = 0;
1097
1098         spec->d1_stack[spec->d1_level++] = res;
1099
1100         res = data1_mk_tag (spec->dh, spec->m, absynName, 0, res);
1101
1102         spec->d1_stack[spec->d1_level++] = res;
1103
1104         spec->d1_stack[spec->d1_level] = NULL;
1105     }
1106     else if (!strcmp(argv[1], "element") && argc == 3)
1107     {
1108         tagBegin (spec, argv[2], strlen(argv[2]));
1109     }
1110     else if (!strcmp (argv[1], "variant") && argc == 5)
1111     {
1112         variantBegin (spec, argv[2], strlen(argv[2]),
1113                       argv[3], strlen(argv[3]),
1114                       argv[4], strlen(argv[4]));
1115     }
1116     else if (!strcmp (argv[1], "context") && argc == 3)
1117     {
1118         struct lexContext *lc = spec->context;
1119 #if REGX_DEBUG
1120         logf (LOG_LOG, "begin context %s",argv[2]);
1121 #endif
1122         while (lc && strcmp (argv[2], lc->name))
1123             lc = lc->next;
1124         if (lc)
1125         {
1126             spec->context_stack[++(spec->context_stack_top)] = lc;
1127         }
1128         else
1129             logf (LOG_WARN, "unknown context %s", argv[2]);
1130     }
1131     else
1132         return TCL_ERROR;
1133     return TCL_OK;
1134 }
1135
1136 static int cmd_tcl_end (ClientData clientData, Tcl_Interp *interp,
1137                         int argc, char **argv)
1138 {
1139     struct lexSpec *spec = (struct lexSpec *) clientData;
1140     if (argc < 2)
1141         return TCL_ERROR;
1142     
1143     if (!strcmp (argv[1], "record"))
1144     {
1145         while (spec->d1_level)
1146         {
1147             tagDataRelease (spec);
1148             (spec->d1_level)--;
1149         }
1150 #if REGX_DEBUG
1151         logf (LOG_LOG, "end record");
1152 #endif
1153         spec->stop_flag = 1;
1154     }
1155     else if (!strcmp (argv[1], "element"))
1156     {
1157         int min_level = 2;
1158         char *element = 0;
1159         if (argc >= 3 && !strcmp(argv[2], "-record"))
1160         {
1161             min_level = 0;
1162             if (argc == 4)
1163                 element = argv[3];
1164         }
1165         else
1166             if (argc == 3)
1167                 element = argv[2];
1168         tagEnd (spec, min_level, element, (element ? strlen(element) : 0));
1169         if (spec->d1_level <= 1)
1170         {
1171 #if REGX_DEBUG
1172             logf (LOG_LOG, "end element end records");
1173 #endif
1174             spec->stop_flag = 1;
1175         }
1176     }
1177     else if (!strcmp (argv[1], "context"))
1178     {
1179 #if REGX_DEBUG
1180         logf (LOG_LOG, "end context");
1181 #endif
1182         if (spec->context_stack_top)
1183             (spec->context_stack_top)--;
1184     }
1185     else
1186         return TCL_ERROR;
1187     return TCL_OK;
1188 }
1189
1190 static int cmd_tcl_data (ClientData clientData, Tcl_Interp *interp,
1191                          int argc, char **argv)
1192 {
1193     int argi = 1;
1194     int textFlag = 0;
1195     const char *element = 0;
1196     const char *attribute = 0;
1197     struct lexSpec *spec = (struct lexSpec *) clientData;
1198     
1199     while (argi < argc)
1200     {
1201         if (!strcmp("-text", argv[argi]))
1202         {
1203             textFlag = 1;
1204             argi++;
1205         }
1206         else if (!strcmp("-element", argv[argi]))
1207         {
1208             argi++;
1209             if (argi < argc)
1210                 element = argv[argi++];
1211         }
1212         else if (!strcmp("-attribute", argv[argi]))
1213         {
1214             argi++;
1215             if (argi < argc)
1216                 attribute = argv[argi++];
1217         }
1218         else
1219             break;
1220     }
1221     if (element)
1222         tagBegin (spec, element, strlen(element));
1223
1224     while (argi < argc)
1225     {
1226 #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
1227         Tcl_DString ds;
1228         char *native = Tcl_UtfToExternalDString(0, argv[argi], -1, &ds);
1229         execData (spec, native, strlen(native), textFlag, attribute, 
1230                   attribute ? strlen(attribute) : 0);
1231         Tcl_DStringFree (&ds);
1232 #else
1233         execData (spec, argv[argi], strlen(argv[argi]), textFlag, attribute,
1234                   attribute ? strlen(attribute) : 0);
1235 #endif
1236         argi++;
1237     }
1238     if (element)
1239         tagEnd (spec, 2, NULL, 0);
1240     return TCL_OK;
1241 }
1242
1243 static int cmd_tcl_unread (ClientData clientData, Tcl_Interp *interp,
1244                            int argc, char **argv)
1245 {
1246     struct lexSpec *spec = (struct lexSpec *) clientData;
1247     int argi = 1;
1248     int offset = 0;
1249     int no;
1250     
1251     while (argi < argc)
1252     {
1253         if (!strcmp("-offset", argv[argi]))
1254         {
1255             argi++;
1256             if (argi < argc)
1257             {
1258                 offset = atoi(argv[argi]);
1259                 argi++;
1260             }
1261         }
1262         else
1263             break;
1264     }
1265     if (argi != argc-1)
1266         return TCL_ERROR;
1267     no = atoi(argv[argi]);
1268     if (no >= spec->arg_no)
1269         no = spec->arg_no - 1;
1270     spec->ptr = spec->arg_start[no] + offset;
1271     return TCL_OK;
1272 }
1273
1274 static void execTcl (struct lexSpec *spec, struct regxCode *code)
1275 {   
1276     int i;
1277     int ret;
1278     for (i = 0; i < spec->arg_no; i++)
1279     {
1280         char var_name[10], *var_buf;
1281         int var_len, ch;
1282         
1283         sprintf (var_name, "%d", i);
1284         var_buf = f_win_get (spec, spec->arg_start[i], spec->arg_end[i],
1285                              &var_len); 
1286         if (var_buf)
1287         {
1288             ch = var_buf[var_len];
1289             var_buf[var_len] = '\0';
1290             Tcl_SetVar (spec->tcl_interp, var_name, var_buf, 0);
1291             var_buf[var_len] = ch;
1292         }
1293     }
1294 #if HAVE_TCL_OBJECTS
1295     ret = Tcl_GlobalEvalObj(spec->tcl_interp, code->tcl_obj);
1296 #else
1297     ret = Tcl_GlobalEval (spec->tcl_interp, code->str);
1298 #endif
1299     if (ret != TCL_OK)
1300     {
1301         const char *err = Tcl_GetVar(spec->tcl_interp, "errorInfo", 0);
1302         logf(LOG_FATAL, "Tcl error, line=%d, \"%s\"\n%s", 
1303             spec->tcl_interp->errorLine,
1304             spec->tcl_interp->result,
1305             err ? err : "[NO ERRORINFO]");
1306     }
1307 }
1308 /* HAVE_TCL_H */
1309 #endif
1310
1311 static void execCode (struct lexSpec *spec, struct regxCode *code)
1312 {
1313     const char *s = code->str;
1314     int cmd_len, r;
1315     const char *cmd_str;
1316     
1317     r = execTok (spec, &s, &cmd_str, &cmd_len);
1318     while (r)
1319     {
1320         char *p, ptmp[64];
1321         
1322         if (r == 1)
1323         {
1324             r = execTok (spec, &s, &cmd_str, &cmd_len);
1325             continue;
1326         }
1327         p = regxStrz (cmd_str, cmd_len, ptmp);
1328         if (!strcmp (p, "begin"))
1329         {
1330             r = execTok (spec, &s, &cmd_str, &cmd_len);
1331             if (r < 2)
1332             {
1333                 logf (LOG_WARN, "missing keyword after 'begin'");
1334                 continue;
1335             }
1336             p = regxStrz (cmd_str, cmd_len, ptmp);
1337             if (!strcmp (p, "record"))
1338             {
1339                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1340                 if (r < 2)
1341                     continue;
1342                 if (spec->d1_level <= 1)
1343                 {
1344                     static char absynName[64];
1345                     data1_node *res;
1346
1347                     if (cmd_len > 63)
1348                         cmd_len = 63;
1349                     memcpy (absynName, cmd_str, cmd_len);
1350                     absynName[cmd_len] = '\0';
1351 #if REGX_DEBUG
1352                     logf (LOG_LOG, "begin record %s", absynName);
1353 #endif
1354                     res = data1_mk_root (spec->dh, spec->m, absynName);
1355                     
1356                     spec->d1_level = 0;
1357
1358                     spec->d1_stack[spec->d1_level++] = res;
1359
1360                     res = data1_mk_tag (spec->dh, spec->m, absynName, 0, res);
1361
1362                     spec->d1_stack[spec->d1_level++] = res;
1363
1364                     spec->d1_stack[spec->d1_level] = NULL;
1365                 }
1366                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1367             }
1368             else if (!strcmp (p, "element"))
1369             {
1370                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1371                 if (r < 2)
1372                     continue;
1373                 tagBegin (spec, cmd_str, cmd_len);
1374                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1375             } 
1376             else if (!strcmp (p, "variant"))
1377             {
1378                 int class_len;
1379                 const char *class_str = NULL;
1380                 int type_len;
1381                 const char *type_str = NULL;
1382                 int value_len;
1383                 const char *value_str = NULL;
1384                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1385                 if (r < 2)
1386                     continue;
1387                 class_str = cmd_str;
1388                 class_len = cmd_len;
1389                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1390                 if (r < 2)
1391                     continue;
1392                 type_str = cmd_str;
1393                 type_len = cmd_len;
1394
1395                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1396                 if (r < 2)
1397                     continue;
1398                 value_str = cmd_str;
1399                 value_len = cmd_len;
1400
1401                 variantBegin (spec, class_str, class_len,
1402                               type_str, type_len, value_str, value_len);
1403                 
1404                 
1405                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1406             }
1407             else if (!strcmp (p, "context"))
1408             {
1409                 if (r > 1)
1410                 {
1411                     struct lexContext *lc = spec->context;
1412                     r = execTok (spec, &s, &cmd_str, &cmd_len);
1413                     p = regxStrz (cmd_str, cmd_len, ptmp);
1414 #if REGX_DEBUG
1415                     logf (LOG_LOG, "begin context %s", p);
1416 #endif
1417                     while (lc && strcmp (p, lc->name))
1418                         lc = lc->next;
1419                     if (lc)
1420                         spec->context_stack[++(spec->context_stack_top)] = lc;
1421                     else
1422                         logf (LOG_WARN, "unknown context %s", p);
1423                     
1424                 }
1425                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1426             }
1427             else
1428             {
1429                 logf (LOG_WARN, "bad keyword '%s' after begin", p);
1430             }
1431         }
1432         else if (!strcmp (p, "end"))
1433         {
1434             r = execTok (spec, &s, &cmd_str, &cmd_len);
1435             if (r < 2)
1436             {
1437                 logf (LOG_WARN, "missing keyword after 'end'");
1438                 continue;
1439             }
1440             p = regxStrz (cmd_str, cmd_len, ptmp);
1441             if (!strcmp (p, "record"))
1442             {
1443                 while (spec->d1_level)
1444                 {
1445                     tagDataRelease (spec);
1446                     (spec->d1_level)--;
1447                 }
1448                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1449 #if REGX_DEBUG
1450                 logf (LOG_LOG, "end record");
1451 #endif
1452                 spec->stop_flag = 1;
1453             }
1454             else if (!strcmp (p, "element"))
1455             {
1456                 int min_level = 2;
1457                 while ((r = execTok (spec, &s, &cmd_str, &cmd_len)) == 3)
1458                 {
1459                     if (cmd_len==7 && !memcmp ("-record", cmd_str, cmd_len))
1460                         min_level = 0;
1461                 }
1462                 if (r > 2)
1463                 {
1464                     tagEnd (spec, min_level, cmd_str, cmd_len);
1465                     r = execTok (spec, &s, &cmd_str, &cmd_len);
1466                 }
1467                 else
1468                     tagEnd (spec, min_level, NULL, 0);
1469                 if (spec->d1_level <= 1)
1470                 {
1471 #if REGX_DEBUG
1472                     logf (LOG_LOG, "end element end records");
1473 #endif
1474                     spec->stop_flag = 1;
1475                 }
1476
1477             }
1478             else if (!strcmp (p, "context"))
1479             {
1480 #if REGX_DEBUG
1481                 logf (LOG_LOG, "end context");
1482 #endif
1483                 if (spec->context_stack_top)
1484                     (spec->context_stack_top)--;
1485                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1486             }       
1487             else
1488                 logf (LOG_WARN, "bad keyword '%s' after end", p);
1489         }
1490         else if (!strcmp (p, "data"))
1491         {
1492             int textFlag = 0;
1493             int element_len;
1494             const char *element_str = NULL;
1495             int attribute_len;
1496             const char *attribute_str = NULL;
1497             
1498             while ((r = execTok (spec, &s, &cmd_str, &cmd_len)) == 3)
1499             {
1500                 if (cmd_len==5 && !memcmp ("-text", cmd_str, cmd_len))
1501                     textFlag = 1;
1502                 else if (cmd_len==8 && !memcmp ("-element", cmd_str, cmd_len))
1503                 {
1504                     r = execTok (spec, &s, &element_str, &element_len);
1505                     if (r < 2)
1506                         break;
1507                 }
1508                 else if (cmd_len==10 && !memcmp ("-attribute", cmd_str, 
1509                                                  cmd_len))
1510                 {
1511                     r = execTok (spec, &s, &attribute_str, &attribute_len);
1512                     if (r < 2)
1513                         break;
1514                 }
1515                 else 
1516                     logf (LOG_WARN, "bad data option: %.*s",
1517                           cmd_len, cmd_str);
1518             }
1519             if (r != 2)
1520             {
1521                 logf (LOG_WARN, "missing data item after data");
1522                 continue;
1523             }
1524             if (element_str)
1525                 tagBegin (spec, element_str, element_len);
1526             do
1527             {
1528                 execData (spec, cmd_str, cmd_len, textFlag,
1529                           attribute_str, attribute_len);
1530                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1531             } while (r > 1);
1532             if (element_str)
1533                 tagEnd (spec, 2, NULL, 0);
1534         }
1535         else if (!strcmp (p, "unread"))
1536         {
1537             int no, offset;
1538             r = execTok (spec, &s, &cmd_str, &cmd_len);
1539             if (r==3 && cmd_len == 7 && !memcmp ("-offset", cmd_str, cmd_len))
1540             {
1541                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1542                 if (r < 2)
1543                 {
1544                     logf (LOG_WARN, "missing number after -offset");
1545                     continue;
1546                 }
1547                 p = regxStrz (cmd_str, cmd_len, ptmp);
1548                 offset = atoi (p);
1549                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1550             }
1551             else
1552                 offset = 0;
1553             if (r < 2)
1554             {
1555                 logf (LOG_WARN, "missing index after unread command");
1556                 continue;
1557             }
1558             if (cmd_len != 1 || *cmd_str < '0' || *cmd_str > '9')
1559             {
1560                 logf (LOG_WARN, "bad index after unread command");
1561                 continue;
1562             }
1563             else
1564             {
1565                 no = *cmd_str - '0';
1566                 if (no >= spec->arg_no)
1567                     no = spec->arg_no - 1;
1568                 spec->ptr = spec->arg_start[no] + offset;
1569             }
1570             r = execTok (spec, &s, &cmd_str, &cmd_len);
1571         }
1572         else if (!strcmp (p, "context"))
1573         {
1574             if (r > 1)
1575             {
1576                 struct lexContext *lc = spec->context;
1577                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1578                 p = regxStrz (cmd_str, cmd_len, ptmp);
1579                 
1580                 while (lc && strcmp (p, lc->name))
1581                     lc = lc->next;
1582                 if (lc)
1583                     spec->context_stack[spec->context_stack_top] = lc;
1584                 else
1585                     logf (LOG_WARN, "unknown context %s", p);
1586
1587             }
1588             r = execTok (spec, &s, &cmd_str, &cmd_len);
1589         }
1590         else
1591         {
1592             logf (LOG_WARN, "unknown code command '%.*s'", cmd_len, cmd_str);
1593             r = execTok (spec, &s, &cmd_str, &cmd_len);
1594             continue;
1595         }
1596         if (r > 1)
1597         {
1598             logf (LOG_WARN, "ignoring token %.*s", cmd_len, cmd_str);
1599             do {
1600                 r = execTok (spec, &s, &cmd_str, &cmd_len);
1601             } while (r > 1);
1602         }
1603     }
1604 }
1605
1606
1607 static int execAction (struct lexSpec *spec, struct lexRuleAction *ap,
1608                        int start_ptr, int *pptr)
1609 {
1610     int sptr;
1611     int arg_start[20];
1612     int arg_end[20];
1613     int arg_no = 1;
1614
1615     if (!ap)
1616         return 1;
1617     arg_start[0] = start_ptr;
1618     arg_end[0] = *pptr;
1619     spec->arg_start = arg_start;
1620     spec->arg_end = arg_end;
1621
1622     while (ap)
1623     {
1624         switch (ap->which)
1625         {
1626         case REGX_PATTERN:
1627             if (ap->u.pattern.body)
1628             {
1629                 arg_start[arg_no] = *pptr;
1630                 if (!tryMatch (spec, pptr, &sptr, ap->u.pattern.dfa, 0))
1631                 {
1632                     arg_end[arg_no] = F_WIN_EOF;
1633                     arg_no++;
1634                     arg_start[arg_no] = F_WIN_EOF;
1635                     arg_end[arg_no] = F_WIN_EOF;
1636                     yaz_log(LOG_DEBUG, "Pattern match rest of record");
1637                     *pptr = F_WIN_EOF;
1638                 }
1639                 else
1640                 {
1641                     arg_end[arg_no] = sptr;
1642                     arg_no++;
1643                     arg_start[arg_no] = sptr;
1644                     arg_end[arg_no] = *pptr;
1645                 }
1646             }
1647             else
1648             {
1649                 arg_start[arg_no] = *pptr;
1650                 if (!tryMatch (spec, pptr, &sptr, ap->u.pattern.dfa, 1))
1651                     return 1;
1652                 if (sptr != arg_start[arg_no])
1653                     return 1;
1654                 arg_end[arg_no] = *pptr;
1655             }
1656             arg_no++;
1657             break;
1658         case REGX_CODE:
1659             spec->arg_no = arg_no;
1660             spec->ptr = *pptr;
1661 #if HAVE_TCL_H
1662             if (spec->tcl_interp)
1663                 execTcl(spec, ap->u.code);
1664             else
1665                 execCode (spec, ap->u.code);
1666 #else
1667             execCode (spec, ap->u.code);
1668 #endif
1669             *pptr = spec->ptr;
1670             if (spec->stop_flag)
1671                 return 0;
1672             break;
1673         case REGX_END:
1674             arg_start[arg_no] = *pptr;
1675             arg_end[arg_no] = F_WIN_EOF;
1676             arg_no++;
1677             *pptr = F_WIN_EOF;
1678         }
1679         ap = ap->next;
1680     }
1681     return 1;
1682 }
1683
1684 static int execRule (struct lexSpec *spec, struct lexContext *context,
1685                      int ruleNo, int start_ptr, int *pptr)
1686 {
1687 #if REGX_DEBUG
1688     logf (LOG_LOG, "exec rule %d", ruleNo);
1689 #endif
1690     return execAction (spec, context->fastRule[ruleNo]->actionList,
1691                        start_ptr, pptr);
1692 }
1693
1694 data1_node *lexNode (struct lexSpec *spec, int *ptr)
1695 {
1696     struct lexContext *context = spec->context_stack[spec->context_stack_top];
1697     struct DFA_state *state = context->dfa->states[0];
1698     struct DFA_tran *t;
1699     unsigned char c;
1700     unsigned char c_prev = '\n';
1701     int i;
1702     int last_rule = 0;        /* rule number of current match */
1703     int last_ptr = *ptr;      /* last char of match */
1704     int start_ptr = *ptr;     /* first char of match */
1705     int skip_ptr = *ptr;      /* first char of run */
1706
1707     while (1)
1708     {
1709         c = f_win_advance (spec, ptr);
1710         if (*ptr == F_WIN_EOF)
1711         {
1712             /* end of file met */
1713             if (last_rule)
1714             {
1715                 /* there was a match */
1716                 if (skip_ptr < start_ptr)
1717                 {
1718                     /* deal with chars that didn't match */
1719                     int size;
1720                     char *buf;
1721                     buf = f_win_get (spec, skip_ptr, start_ptr, &size);
1722                     execDataP (spec, buf, size, 0);
1723                 }
1724                 /* restore pointer */
1725                 *ptr = last_ptr;
1726                 /* execute rule */
1727                 if (!execRule (spec, context, last_rule, start_ptr, ptr))
1728                     break;
1729                 /* restore skip pointer */
1730                 skip_ptr = *ptr;
1731                 last_rule = 0;
1732             }
1733             else if (skip_ptr < *ptr)
1734             {
1735                 /* deal with chars that didn't match */
1736                 int size;
1737                 char *buf;
1738                 buf = f_win_get (spec, skip_ptr, *ptr, &size);
1739                 execDataP (spec, buf, size, 0);
1740             }
1741             state = context->dfa->states[0];
1742             if (*ptr == F_WIN_EOF)
1743                 break;
1744         }
1745         t = state->trans;
1746         i = state->tran_no;
1747         while (1)
1748             if (--i < 0)
1749             {   /* no transition for character c ... */
1750                 if (last_rule)
1751                 {
1752                     if (skip_ptr < start_ptr)
1753                     {
1754                         /* deal with chars that didn't match */
1755                         int size;
1756                         char *buf;
1757                         buf = f_win_get (spec, skip_ptr, start_ptr, &size);
1758                         execDataP (spec, buf, size, 0);
1759                     }
1760                     /* restore pointer */
1761                     *ptr = last_ptr;
1762                     if (!execRule (spec, context, last_rule, start_ptr, ptr))
1763                     {
1764                         if (spec->f_win_ef && *ptr != F_WIN_EOF)
1765                         {
1766 #if REGX_DEBUG
1767                             logf (LOG_LOG, "regx: endf ptr=%d", *ptr);
1768 #endif
1769                             (*spec->f_win_ef)(spec->f_win_fh, *ptr);
1770                         }
1771                         return NULL;
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                 }
1807                 break;
1808             }
1809             else
1810                 t++;
1811     }
1812     return NULL;
1813 }
1814
1815 static data1_node *lexRoot (struct lexSpec *spec, off_t offset,
1816                             const char *context_name)
1817 {
1818     struct lexContext *lt = spec->context;
1819     int ptr = offset;
1820
1821     spec->stop_flag = 0;
1822     spec->d1_level = 0;
1823     spec->context_stack_top = 0;    
1824     while (lt)
1825     {
1826         if (!strcmp (lt->name, context_name))
1827             break;
1828         lt = lt->next;
1829     }
1830     if (!lt)
1831     {
1832         logf (LOG_WARN, "cannot find context %s", context_name);
1833         return NULL;
1834     }
1835     spec->context_stack[spec->context_stack_top] = lt;
1836     spec->d1_stack[spec->d1_level] = NULL;
1837 #if 1
1838     if (!lt->initFlag)
1839     {
1840         lt->initFlag = 1;
1841         execAction (spec, lt->initActionList, ptr, &ptr);
1842     }
1843 #endif
1844     execAction (spec, lt->beginActionList, ptr, &ptr);
1845     lexNode (spec, &ptr);
1846     while (spec->d1_level)
1847     {
1848         tagDataRelease (spec);
1849         (spec->d1_level)--;
1850     }
1851     execAction (spec, lt->endActionList, ptr, &ptr);
1852     return spec->d1_stack[0];
1853 }
1854
1855 void grs_destroy(void *clientData)
1856 {
1857     struct lexSpecs *specs = (struct lexSpecs *) clientData;
1858     if (specs->spec)
1859     {
1860         lexSpecDestroy(&specs->spec);
1861     }
1862     xfree (specs);
1863 }
1864
1865 void *grs_init(void)
1866 {
1867     struct lexSpecs *specs = (struct lexSpecs *) xmalloc (sizeof(*specs));
1868     specs->spec = 0;
1869     return specs;
1870 }
1871
1872 data1_node *grs_read_regx (struct grs_read_info *p)
1873 {
1874     int res;
1875     struct lexSpecs *specs = (struct lexSpecs *) p->clientData;
1876     struct lexSpec **curLexSpec = &specs->spec;
1877
1878 #if REGX_DEBUG
1879     logf (LOG_LOG, "grs_read_regx");
1880 #endif
1881     if (!*curLexSpec || strcmp ((*curLexSpec)->name, p->type))
1882     {
1883         if (*curLexSpec)
1884             lexSpecDestroy (curLexSpec);
1885         *curLexSpec = lexSpecCreate (p->type, p->dh);
1886         res = readFileSpec (*curLexSpec);
1887         if (res)
1888         {
1889             lexSpecDestroy (curLexSpec);
1890             return NULL;
1891         }
1892     }
1893     (*curLexSpec)->dh = p->dh;
1894     if (!p->offset)
1895     {
1896         (*curLexSpec)->f_win_start = 0;
1897         (*curLexSpec)->f_win_end = 0;
1898         (*curLexSpec)->f_win_rf = p->readf;
1899         (*curLexSpec)->f_win_sf = p->seekf;
1900         (*curLexSpec)->f_win_fh = p->fh;
1901         (*curLexSpec)->f_win_ef = p->endf;
1902         (*curLexSpec)->f_win_size = 500000;
1903     }
1904     (*curLexSpec)->m = p->mem;
1905     return lexRoot (*curLexSpec, p->offset, "main");
1906 }
1907
1908 static struct recTypeGrs regx_type = {
1909     "regx",
1910     grs_init,
1911     grs_destroy,
1912     grs_read_regx
1913 };
1914
1915 RecTypeGrs recTypeGrs_regx = &regx_type;
1916
1917 #if HAVE_TCL_H
1918 data1_node *grs_read_tcl (struct grs_read_info *p)
1919 {
1920     int res;
1921     struct lexSpecs *specs = (struct lexSpecs *) p->clientData;
1922     struct lexSpec **curLexSpec = &specs->spec;
1923
1924 #if REGX_DEBUG
1925     logf (LOG_LOG, "grs_read_tcl");
1926 #endif
1927     if (!*curLexSpec || strcmp ((*curLexSpec)->name, p->type))
1928     {
1929         Tcl_Interp *tcl_interp;
1930         if (*curLexSpec)
1931             lexSpecDestroy (curLexSpec);
1932         *curLexSpec = lexSpecCreate (p->type, p->dh);
1933         Tcl_FindExecutable("");
1934         tcl_interp = (*curLexSpec)->tcl_interp = Tcl_CreateInterp();
1935         Tcl_Init(tcl_interp);
1936         Tcl_CreateCommand (tcl_interp, "begin", cmd_tcl_begin, *curLexSpec, 0);
1937         Tcl_CreateCommand (tcl_interp, "end", cmd_tcl_end, *curLexSpec, 0);
1938         Tcl_CreateCommand (tcl_interp, "data", cmd_tcl_data, *curLexSpec, 0);
1939         Tcl_CreateCommand (tcl_interp, "unread", cmd_tcl_unread,
1940                            *curLexSpec, 0);
1941         res = readFileSpec (*curLexSpec);
1942         if (res)
1943         {
1944             lexSpecDestroy (curLexSpec);
1945             return NULL;
1946         }
1947     }
1948     (*curLexSpec)->dh = p->dh;
1949     if (!p->offset)
1950     {
1951         (*curLexSpec)->f_win_start = 0;
1952         (*curLexSpec)->f_win_end = 0;
1953         (*curLexSpec)->f_win_rf = p->readf;
1954         (*curLexSpec)->f_win_sf = p->seekf;
1955         (*curLexSpec)->f_win_fh = p->fh;
1956         (*curLexSpec)->f_win_ef = p->endf;
1957         (*curLexSpec)->f_win_size = 500000;
1958     }
1959     (*curLexSpec)->m = p->mem;
1960     return lexRoot (*curLexSpec, p->offset, "main");
1961 }
1962
1963 static struct recTypeGrs tcl_type = {
1964     "tcl",
1965     grs_init,
1966     grs_destroy,
1967     grs_read_tcl
1968 };
1969
1970 RecTypeGrs recTypeGrs_tcl = &tcl_type;
1971 #endif