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