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