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