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