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