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