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