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