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