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