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