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