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