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