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