updated libxml2 to 2.5.10
[TestXSLT.git] / libxml2 / xmlregexp.c
1 /*
2  * regexp.c: generic and extensible Regular Expression engine
3  *
4  * Basically designed with the purpose of compiling regexps for 
5  * the variety of validation/shemas mechanisms now available in
6  * XML related specifications thise includes:
7  *    - XML-1.0 DTD validation
8  *    - XML Schemas structure part 1
9  *    - XML Schemas Datatypes part 2 especially Appendix F
10  *    - RELAX-NG/TREX i.e. the counter proposal
11  *
12  * See Copyright for the status of this software.
13  *
14  * Daniel Veillard <veillard@redhat.com>
15  */
16
17 #define IN_LIBXML
18 #include "libxml.h"
19
20 #ifdef LIBXML_REGEXP_ENABLED
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <libxml/tree.h>
25 #include <libxml/parserInternals.h>
26 #include <libxml/xmlregexp.h>
27 #include <libxml/xmlautomata.h>
28 #include <libxml/xmlunicode.h>
29
30 /* #define DEBUG_REGEXP_GRAPH  */
31 /* #define DEBUG_REGEXP_EXEC */
32 /* #define DEBUG_PUSH */
33 /* #define DEBUG_COMPACTION */
34
35 #define ERROR(str) ctxt->error = 1;                                     \
36     xmlGenericError(xmlGenericErrorContext, "Regexp: %s: %s\n", str, ctxt->cur)
37 #define NEXT ctxt->cur++
38 #define CUR (*(ctxt->cur))
39 #define NXT(index) (ctxt->cur[index])
40
41 #define CUR_SCHAR(s, l) xmlStringCurrentChar(NULL, s, &l)
42 #define NEXTL(l) ctxt->cur += l;
43
44 /**
45  * TODO:
46  *
47  * macro to flag unimplemented blocks
48  */
49 #define TODO                                                            \
50     xmlGenericError(xmlGenericErrorContext,                             \
51             "Unimplemented block at %s:%d\n",                           \
52             __FILE__, __LINE__);
53
54
55 /************************************************************************
56  *                                                                      *
57  *                      Datatypes and structures                        *
58  *                                                                      *
59  ************************************************************************/
60
61 typedef enum {
62     XML_REGEXP_EPSILON = 1,
63     XML_REGEXP_CHARVAL,
64     XML_REGEXP_RANGES,
65     XML_REGEXP_SUBREG,
66     XML_REGEXP_STRING,
67     XML_REGEXP_ANYCHAR, /* . */
68     XML_REGEXP_ANYSPACE, /* \s */
69     XML_REGEXP_NOTSPACE, /* \S */
70     XML_REGEXP_INITNAME, /* \l */
71     XML_REGEXP_NOTINITNAME, /* \l */
72     XML_REGEXP_NAMECHAR, /* \c */
73     XML_REGEXP_NOTNAMECHAR, /* \C */
74     XML_REGEXP_DECIMAL, /* \d */
75     XML_REGEXP_NOTDECIMAL, /* \d */
76     XML_REGEXP_REALCHAR, /* \w */
77     XML_REGEXP_NOTREALCHAR, /* \w */
78     XML_REGEXP_LETTER,
79     XML_REGEXP_LETTER_UPPERCASE,
80     XML_REGEXP_LETTER_LOWERCASE,
81     XML_REGEXP_LETTER_TITLECASE,
82     XML_REGEXP_LETTER_MODIFIER,
83     XML_REGEXP_LETTER_OTHERS,
84     XML_REGEXP_MARK,
85     XML_REGEXP_MARK_NONSPACING,
86     XML_REGEXP_MARK_SPACECOMBINING,
87     XML_REGEXP_MARK_ENCLOSING,
88     XML_REGEXP_NUMBER,
89     XML_REGEXP_NUMBER_DECIMAL,
90     XML_REGEXP_NUMBER_LETTER,
91     XML_REGEXP_NUMBER_OTHERS,
92     XML_REGEXP_PUNCT,
93     XML_REGEXP_PUNCT_CONNECTOR,
94     XML_REGEXP_PUNCT_DASH,
95     XML_REGEXP_PUNCT_OPEN,
96     XML_REGEXP_PUNCT_CLOSE,
97     XML_REGEXP_PUNCT_INITQUOTE,
98     XML_REGEXP_PUNCT_FINQUOTE,
99     XML_REGEXP_PUNCT_OTHERS,
100     XML_REGEXP_SEPAR,
101     XML_REGEXP_SEPAR_SPACE,
102     XML_REGEXP_SEPAR_LINE,
103     XML_REGEXP_SEPAR_PARA,
104     XML_REGEXP_SYMBOL,
105     XML_REGEXP_SYMBOL_MATH,
106     XML_REGEXP_SYMBOL_CURRENCY,
107     XML_REGEXP_SYMBOL_MODIFIER,
108     XML_REGEXP_SYMBOL_OTHERS,
109     XML_REGEXP_OTHER,
110     XML_REGEXP_OTHER_CONTROL,
111     XML_REGEXP_OTHER_FORMAT,
112     XML_REGEXP_OTHER_PRIVATE,
113     XML_REGEXP_OTHER_NA,
114     XML_REGEXP_BLOCK_NAME
115 } xmlRegAtomType;
116
117 typedef enum {
118     XML_REGEXP_QUANT_EPSILON = 1,
119     XML_REGEXP_QUANT_ONCE,
120     XML_REGEXP_QUANT_OPT,
121     XML_REGEXP_QUANT_MULT,
122     XML_REGEXP_QUANT_PLUS,
123     XML_REGEXP_QUANT_ONCEONLY,
124     XML_REGEXP_QUANT_ALL,
125     XML_REGEXP_QUANT_RANGE
126 } xmlRegQuantType;
127
128 typedef enum {
129     XML_REGEXP_START_STATE = 1,
130     XML_REGEXP_FINAL_STATE,
131     XML_REGEXP_TRANS_STATE
132 } xmlRegStateType;
133
134 typedef enum {
135     XML_REGEXP_MARK_NORMAL = 0,
136     XML_REGEXP_MARK_START,
137     XML_REGEXP_MARK_VISITED
138 } xmlRegMarkedType;
139
140 typedef struct _xmlRegRange xmlRegRange;
141 typedef xmlRegRange *xmlRegRangePtr;
142
143 struct _xmlRegRange {
144     int neg;
145     xmlRegAtomType type;
146     int start;
147     int end;
148     xmlChar *blockName;
149 };
150
151 typedef struct _xmlRegAtom xmlRegAtom;
152 typedef xmlRegAtom *xmlRegAtomPtr;
153
154 typedef struct _xmlAutomataState xmlRegState;
155 typedef xmlRegState *xmlRegStatePtr;
156
157 struct _xmlRegAtom {
158     int no;
159     xmlRegAtomType type;
160     xmlRegQuantType quant;
161     int min;
162     int max;
163
164     void *valuep;
165     void *valuep2;
166     int neg;
167     int codepoint;
168     xmlRegStatePtr start;
169     xmlRegStatePtr stop;
170     int maxRanges;
171     int nbRanges;
172     xmlRegRangePtr *ranges;
173     void *data;
174 };
175
176 typedef struct _xmlRegCounter xmlRegCounter;
177 typedef xmlRegCounter *xmlRegCounterPtr;
178
179 struct _xmlRegCounter {
180     int min;
181     int max;
182 };
183
184 typedef struct _xmlRegTrans xmlRegTrans;
185 typedef xmlRegTrans *xmlRegTransPtr;
186
187 struct _xmlRegTrans {
188     xmlRegAtomPtr atom;
189     int to;
190     int counter;
191     int count;
192 };
193
194 struct _xmlAutomataState {
195     xmlRegStateType type;
196     xmlRegMarkedType mark;
197     xmlRegMarkedType reached;
198     int no;
199
200     int maxTrans;
201     int nbTrans;
202     xmlRegTrans *trans;
203 };
204
205 typedef struct _xmlAutomata xmlRegParserCtxt;
206 typedef xmlRegParserCtxt *xmlRegParserCtxtPtr;
207
208 struct _xmlAutomata {
209     xmlChar *string;
210     xmlChar *cur;
211
212     int error;
213     int neg;
214
215     xmlRegStatePtr start;
216     xmlRegStatePtr end;
217     xmlRegStatePtr state;
218
219     xmlRegAtomPtr atom;
220
221     int maxAtoms;
222     int nbAtoms;
223     xmlRegAtomPtr *atoms;
224
225     int maxStates;
226     int nbStates;
227     xmlRegStatePtr *states;
228
229     int maxCounters;
230     int nbCounters;
231     xmlRegCounter *counters;
232
233     int determinist;
234 };
235
236 struct _xmlRegexp {
237     xmlChar *string;
238     int nbStates;
239     xmlRegStatePtr *states;
240     int nbAtoms;
241     xmlRegAtomPtr *atoms;
242     int nbCounters;
243     xmlRegCounter *counters;
244     int determinist;
245     /*
246      * That's the compact form for determinists automatas
247      */
248     int nbstates;
249     int *compact;
250     void **transdata;
251     int nbstrings;
252     xmlChar **stringMap;
253 };
254
255 typedef struct _xmlRegExecRollback xmlRegExecRollback;
256 typedef xmlRegExecRollback *xmlRegExecRollbackPtr;
257
258 struct _xmlRegExecRollback {
259     xmlRegStatePtr state;/* the current state */
260     int index;          /* the index in the input stack */
261     int nextbranch;     /* the next transition to explore in that state */
262     int *counts;        /* save the automate state if it has some */
263 };
264
265 typedef struct _xmlRegInputToken xmlRegInputToken;
266 typedef xmlRegInputToken *xmlRegInputTokenPtr;
267
268 struct _xmlRegInputToken {
269     xmlChar *value;
270     void *data;
271 };
272
273 struct _xmlRegExecCtxt {
274     int status;         /* execution status != 0 indicate an error */
275     int determinist;    /* did we found an inderterministic behaviour */
276     xmlRegexpPtr comp;  /* the compiled regexp */
277     xmlRegExecCallbacks callback;
278     void *data;
279
280     xmlRegStatePtr state;/* the current state */
281     int transno;        /* the current transition on that state */
282     int transcount;     /* the number of char in char counted transitions */
283
284     /*
285      * A stack of rollback states
286      */
287     int maxRollbacks;
288     int nbRollbacks;
289     xmlRegExecRollback *rollbacks;
290
291     /*
292      * The state of the automata if any
293      */
294     int *counts;
295
296     /*
297      * The input stack
298      */
299     int inputStackMax;
300     int inputStackNr;
301     int index;
302     int *charStack;
303     const xmlChar *inputString; /* when operating on characters */
304     xmlRegInputTokenPtr inputStack;/* when operating on strings */
305
306 };
307
308 #define REGEXP_ALL_COUNTER      0x123456
309 #define REGEXP_ALL_LAX_COUNTER  0x123457
310
311 static void xmlFAParseRegExp(xmlRegParserCtxtPtr ctxt, int top);
312 static void xmlRegFreeState(xmlRegStatePtr state);
313 static void xmlRegFreeAtom(xmlRegAtomPtr atom);
314
315 /************************************************************************
316  *                                                                      *
317  *                      Allocation/Deallocation                         *
318  *                                                                      *
319  ************************************************************************/
320
321 static int xmlFAComputesDeterminism(xmlRegParserCtxtPtr ctxt);
322 /**
323  * xmlRegEpxFromParse:
324  * @ctxt:  the parser context used to build it
325  *
326  * Allocate a new regexp and fill it with the reult from the parser
327  *
328  * Returns the new regexp or NULL in case of error
329  */
330 static xmlRegexpPtr
331 xmlRegEpxFromParse(xmlRegParserCtxtPtr ctxt) {
332     xmlRegexpPtr ret;
333
334     ret = (xmlRegexpPtr) xmlMalloc(sizeof(xmlRegexp));
335     if (ret == NULL) {
336         xmlGenericError(xmlGenericErrorContext,
337              "out of memory compiling regexp\n");
338         return(NULL);
339     }
340     memset(ret, 0, sizeof(xmlRegexp));
341     ret->string = ctxt->string;
342     ret->nbStates = ctxt->nbStates;
343     ret->states = ctxt->states;
344     ret->nbAtoms = ctxt->nbAtoms;
345     ret->atoms = ctxt->atoms;
346     ret->nbCounters = ctxt->nbCounters;
347     ret->counters = ctxt->counters;
348     ret->determinist = ctxt->determinist;
349
350     if ((ret->determinist != 0) &&
351         (ret->nbCounters == 0) &&
352         (ret->atoms != NULL) &&
353         (ret->atoms[0] != NULL) &&
354         (ret->atoms[0]->type == XML_REGEXP_STRING)) {
355         int i, j, nbstates = 0, nbatoms = 0;
356         int *stateRemap;
357         int *stringRemap;
358         int *transitions;
359         void **transdata;
360         xmlChar **stringMap;
361         xmlChar *value;
362
363         /*
364          * Switch to a compact representation
365          * 1/ counting the effective number of states left
366          * 2/ conting the unique number of atoms, and check that
367          *    they are all of the string type
368          * 3/ build a table state x atom for the transitions
369          */
370
371         stateRemap = xmlMalloc(ret->nbStates * sizeof(int));
372         if (stateRemap == NULL) {
373             xmlGenericError(xmlGenericErrorContext,
374                  "out of memory compiling regexp\n");
375             xmlFree(ret);
376             return(NULL);
377         }
378         for (i = 0;i < ret->nbStates;i++) {
379             if (ret->states[i] != NULL) {
380                 stateRemap[i] = nbstates;
381                 nbstates++;
382             } else {
383                 stateRemap[i] = -1;
384             }
385         }
386 #ifdef DEBUG_COMPACTION
387         printf("Final: %d states\n", nbstates);
388 #endif
389         stringMap = xmlMalloc(ret->nbAtoms * sizeof(char *));
390         if (stringMap == NULL) {
391             xmlGenericError(xmlGenericErrorContext,
392                  "out of memory compiling regexp\n");
393             xmlFree(stateRemap);
394             xmlFree(ret);
395             return(NULL);
396         }
397         stringRemap = xmlMalloc(ret->nbAtoms * sizeof(int));
398         if (stringRemap == NULL) {
399             xmlGenericError(xmlGenericErrorContext,
400                  "out of memory compiling regexp\n");
401             xmlFree(stringMap);
402             xmlFree(stateRemap);
403             xmlFree(ret);
404             return(NULL);
405         }
406         for (i = 0;i < ret->nbAtoms;i++) {
407             if ((ret->atoms[i]->type == XML_REGEXP_STRING) &&
408                 (ret->atoms[i]->quant == XML_REGEXP_QUANT_ONCE)) {
409                 value = ret->atoms[i]->valuep;
410                 for (j = 0;j < nbatoms;j++) {
411                     if (xmlStrEqual(stringMap[j], value)) {
412                         stringRemap[i] = j;
413                         break;
414                     }
415                 }
416                 if (j >= nbatoms) {
417                     stringRemap[i] = nbatoms;
418                     stringMap[nbatoms] = xmlStrdup(value);
419                     if (stringMap[nbatoms] == NULL) {
420                         for (i = 0;i < nbatoms;i++)
421                             xmlFree(stringMap[i]);
422                         xmlFree(stringRemap);
423                         xmlFree(stringMap);
424                         xmlFree(stateRemap);
425                         xmlFree(ret);
426                         return(NULL);
427                     }
428                     nbatoms++;
429                 }
430             } else {
431                 xmlFree(stateRemap);
432                 xmlFree(stringRemap);
433                 for (i = 0;i < nbatoms;i++)
434                     xmlFree(stringMap[i]);
435                 xmlFree(stringMap);
436                 xmlFree(ret);
437                 return(NULL);
438             }
439         }
440 #ifdef DEBUG_COMPACTION
441         printf("Final: %d atoms\n", nbatoms);
442 #endif
443         transitions = (int *) xmlMalloc((nbstates + 1) *
444                                         (nbatoms + 1) * sizeof(int));
445         if (transitions == NULL) {
446             xmlFree(stateRemap);
447             xmlFree(stringRemap);
448             xmlFree(stringMap);
449             xmlFree(ret);
450             return(NULL);
451         }
452         memset(transitions, 0, (nbstates + 1) * (nbatoms + 1) * sizeof(int));
453
454         /*
455          * Allocate the transition table. The first entry for each
456          * state correspond to the state type.
457          */
458         transdata = NULL;
459
460         for (i = 0;i < ret->nbStates;i++) {
461             int stateno, atomno, targetno, prev;
462             xmlRegStatePtr state;
463             xmlRegTransPtr trans;
464
465             stateno = stateRemap[i];
466             if (stateno == -1)
467                 continue;
468             state = ret->states[i];
469
470             transitions[stateno * (nbatoms + 1)] = state->type;
471
472             for (j = 0;j < state->nbTrans;j++) {
473                 trans = &(state->trans[j]);
474                 if ((trans->to == -1) || (trans->atom == NULL))
475                     continue;
476                 atomno = stringRemap[trans->atom->no];
477                 if ((trans->atom->data != NULL) && (transdata == NULL)) {
478                     transdata = (void **) xmlMalloc(nbstates * nbatoms *
479                                                     sizeof(void *));
480                     if (transdata != NULL)
481                         memset(transdata, 0,
482                                nbstates * nbatoms * sizeof(void *));
483                     else {
484                         xmlGenericError(xmlGenericErrorContext,
485                              "out of memory compiling regexp\n");
486                         break;
487                     }
488                 }
489                 targetno = stateRemap[trans->to];
490                 /*
491                  * if the same atome can generate transition to 2 different
492                  * states then it means the automata is not determinist and
493                  * the compact form can't be used !
494                  */
495                 prev = transitions[stateno * (nbatoms + 1) + atomno + 1];
496                 if (prev != 0) {
497                     if (prev != targetno + 1) {
498                         ret->determinist = 0;
499 #ifdef DEBUG_COMPACTION
500                         printf("Indet: state %d trans %d, atom %d to %d : %d to %d\n",
501                                i, j, trans->atom->no, trans->to, atomno, targetno);
502                         printf("       previous to is %d\n", prev);
503 #endif
504                         ret->determinist = 0;
505                         if (transdata != NULL)
506                             xmlFree(transdata);
507                         xmlFree(transitions);
508                         xmlFree(stateRemap);
509                         xmlFree(stringRemap);
510                         for (i = 0;i < nbatoms;i++)
511                             xmlFree(stringMap[i]);
512                         xmlFree(stringMap);
513                         goto not_determ;
514                     }
515                 } else {
516 #if 0
517                     printf("State %d trans %d: atom %d to %d : %d to %d\n",
518                            i, j, trans->atom->no, trans->to, atomno, targetno);
519 #endif
520                     transitions[stateno * (nbatoms + 1) + atomno + 1] =
521                         targetno + 1; /* to avoid 0 */
522                     if (transdata != NULL)
523                         transdata[stateno * nbatoms + atomno] =
524                             trans->atom->data;
525                 }
526             }
527         }
528         ret->determinist = 1;
529 #ifdef DEBUG_COMPACTION
530         /*
531          * Debug
532          */
533         for (i = 0;i < nbstates;i++) {
534             for (j = 0;j < nbatoms + 1;j++) {
535                 printf("%02d ", transitions[i * (nbatoms + 1) + j]);
536             }
537             printf("\n");
538         }
539         printf("\n");
540 #endif
541         /*
542          * Cleanup of the old data
543          */
544         if (ret->states != NULL) {
545             for (i = 0;i < ret->nbStates;i++)
546                 xmlRegFreeState(ret->states[i]);
547             xmlFree(ret->states);
548         }
549         ret->states = NULL;
550         ret->nbStates = 0;
551         if (ret->atoms != NULL) {
552             for (i = 0;i < ret->nbAtoms;i++)
553                 xmlRegFreeAtom(ret->atoms[i]);
554             xmlFree(ret->atoms);
555         }
556         ret->atoms = NULL;
557         ret->nbAtoms = 0;
558
559         ret->compact = transitions;
560         ret->transdata = transdata;
561         ret->stringMap = stringMap;
562         ret->nbstrings = nbatoms;
563         ret->nbstates = nbstates;
564         xmlFree(stateRemap);
565         xmlFree(stringRemap);
566     }
567 not_determ:
568     ctxt->string = NULL;
569     ctxt->nbStates = 0;
570     ctxt->states = NULL;
571     ctxt->nbAtoms = 0;
572     ctxt->atoms = NULL;
573     ctxt->nbCounters = 0;
574     ctxt->counters = NULL;
575     return(ret);
576 }
577
578 /**
579  * xmlRegNewParserCtxt:
580  * @string:  the string to parse
581  *
582  * Allocate a new regexp parser context
583  *
584  * Returns the new context or NULL in case of error
585  */
586 static xmlRegParserCtxtPtr
587 xmlRegNewParserCtxt(const xmlChar *string) {
588     xmlRegParserCtxtPtr ret;
589
590     ret = (xmlRegParserCtxtPtr) xmlMalloc(sizeof(xmlRegParserCtxt));
591     if (ret == NULL)
592         return(NULL);
593     memset(ret, 0, sizeof(xmlRegParserCtxt));
594     if (string != NULL)
595         ret->string = xmlStrdup(string);
596     ret->cur = ret->string;
597     ret->neg = 0;
598     ret->error = 0;
599     ret->determinist = -1;
600     return(ret);
601 }
602
603 /**
604  * xmlRegNewRange:
605  * @ctxt:  the regexp parser context
606  * @neg:  is that negative
607  * @type:  the type of range
608  * @start:  the start codepoint
609  * @end:  the end codepoint
610  *
611  * Allocate a new regexp range
612  *
613  * Returns the new range or NULL in case of error
614  */
615 static xmlRegRangePtr
616 xmlRegNewRange(xmlRegParserCtxtPtr ctxt,
617                int neg, xmlRegAtomType type, int start, int end) {
618     xmlRegRangePtr ret;
619
620     ret = (xmlRegRangePtr) xmlMalloc(sizeof(xmlRegRange));
621     if (ret == NULL) {
622         ERROR("failed to allocate regexp range");
623         return(NULL);
624     }
625     ret->neg = neg;
626     ret->type = type;
627     ret->start = start;
628     ret->end = end;
629     return(ret);
630 }
631
632 /**
633  * xmlRegFreeRange:
634  * @range:  the regexp range
635  *
636  * Free a regexp range
637  */
638 static void
639 xmlRegFreeRange(xmlRegRangePtr range) {
640     if (range == NULL)
641         return;
642
643     if (range->blockName != NULL)
644         xmlFree(range->blockName);
645     xmlFree(range);
646 }
647
648 /**
649  * xmlRegNewAtom:
650  * @ctxt:  the regexp parser context
651  * @type:  the type of atom
652  *
653  * Allocate a new regexp range
654  *
655  * Returns the new atom or NULL in case of error
656  */
657 static xmlRegAtomPtr
658 xmlRegNewAtom(xmlRegParserCtxtPtr ctxt, xmlRegAtomType type) {
659     xmlRegAtomPtr ret;
660
661     ret = (xmlRegAtomPtr) xmlMalloc(sizeof(xmlRegAtom));
662     if (ret == NULL) {
663         ERROR("failed to allocate regexp atom");
664         return(NULL);
665     }
666     memset(ret, 0, sizeof(xmlRegAtom));
667     ret->type = type;
668     ret->quant = XML_REGEXP_QUANT_ONCE;
669     ret->min = 0;
670     ret->max = 0;
671     return(ret);
672 }
673
674 /**
675  * xmlRegFreeAtom:
676  * @atom:  the regexp atom
677  *
678  * Free a regexp atom
679  */
680 static void
681 xmlRegFreeAtom(xmlRegAtomPtr atom) {
682     int i;
683
684     if (atom == NULL)
685         return;
686
687     for (i = 0;i < atom->nbRanges;i++)
688         xmlRegFreeRange(atom->ranges[i]);
689     if (atom->ranges != NULL)
690         xmlFree(atom->ranges);
691     if (atom->type == XML_REGEXP_STRING)
692         xmlFree(atom->valuep);
693     xmlFree(atom);
694 }
695
696 static xmlRegStatePtr
697 xmlRegNewState(xmlRegParserCtxtPtr ctxt) {
698     xmlRegStatePtr ret;
699
700     ret = (xmlRegStatePtr) xmlMalloc(sizeof(xmlRegState));
701     if (ret == NULL) {
702         ERROR("failed to allocate regexp state");
703         return(NULL);
704     }
705     memset(ret, 0, sizeof(xmlRegState));
706     ret->type = XML_REGEXP_TRANS_STATE;
707     ret->mark = XML_REGEXP_MARK_NORMAL;
708     return(ret);
709 }
710
711 /**
712  * xmlRegFreeState:
713  * @state:  the regexp state
714  *
715  * Free a regexp state
716  */
717 static void
718 xmlRegFreeState(xmlRegStatePtr state) {
719     if (state == NULL)
720         return;
721
722     if (state->trans != NULL)
723         xmlFree(state->trans);
724     xmlFree(state);
725 }
726
727 /**
728  * xmlRegFreeParserCtxt:
729  * @ctxt:  the regexp parser context
730  *
731  * Free a regexp parser context
732  */
733 static void
734 xmlRegFreeParserCtxt(xmlRegParserCtxtPtr ctxt) {
735     int i;
736     if (ctxt == NULL)
737         return;
738
739     if (ctxt->string != NULL)
740         xmlFree(ctxt->string);
741     if (ctxt->states != NULL) {
742         for (i = 0;i < ctxt->nbStates;i++)
743             xmlRegFreeState(ctxt->states[i]);
744         xmlFree(ctxt->states);
745     }
746     if (ctxt->atoms != NULL) {
747         for (i = 0;i < ctxt->nbAtoms;i++)
748             xmlRegFreeAtom(ctxt->atoms[i]);
749         xmlFree(ctxt->atoms);
750     }
751     if (ctxt->counters != NULL)
752         xmlFree(ctxt->counters);
753     xmlFree(ctxt);
754 }
755
756 /************************************************************************
757  *                                                                      *
758  *                      Display of Data structures                      *
759  *                                                                      *
760  ************************************************************************/
761
762 static void
763 xmlRegPrintAtomType(FILE *output, xmlRegAtomType type) {
764     switch (type) {
765         case XML_REGEXP_EPSILON:
766             fprintf(output, "epsilon "); break;
767         case XML_REGEXP_CHARVAL:
768             fprintf(output, "charval "); break;
769         case XML_REGEXP_RANGES:
770             fprintf(output, "ranges "); break;
771         case XML_REGEXP_SUBREG:
772             fprintf(output, "subexpr "); break;
773         case XML_REGEXP_STRING:
774             fprintf(output, "string "); break;
775         case XML_REGEXP_ANYCHAR:
776             fprintf(output, "anychar "); break;
777         case XML_REGEXP_ANYSPACE:
778             fprintf(output, "anyspace "); break;
779         case XML_REGEXP_NOTSPACE:
780             fprintf(output, "notspace "); break;
781         case XML_REGEXP_INITNAME:
782             fprintf(output, "initname "); break;
783         case XML_REGEXP_NOTINITNAME:
784             fprintf(output, "notinitname "); break;
785         case XML_REGEXP_NAMECHAR:
786             fprintf(output, "namechar "); break;
787         case XML_REGEXP_NOTNAMECHAR:
788             fprintf(output, "notnamechar "); break;
789         case XML_REGEXP_DECIMAL:
790             fprintf(output, "decimal "); break;
791         case XML_REGEXP_NOTDECIMAL:
792             fprintf(output, "notdecimal "); break;
793         case XML_REGEXP_REALCHAR:
794             fprintf(output, "realchar "); break;
795         case XML_REGEXP_NOTREALCHAR:
796             fprintf(output, "notrealchar "); break;
797         case XML_REGEXP_LETTER:
798             fprintf(output, "LETTER "); break;
799         case XML_REGEXP_LETTER_UPPERCASE:
800             fprintf(output, "LETTER_UPPERCASE "); break;
801         case XML_REGEXP_LETTER_LOWERCASE:
802             fprintf(output, "LETTER_LOWERCASE "); break;
803         case XML_REGEXP_LETTER_TITLECASE:
804             fprintf(output, "LETTER_TITLECASE "); break;
805         case XML_REGEXP_LETTER_MODIFIER:
806             fprintf(output, "LETTER_MODIFIER "); break;
807         case XML_REGEXP_LETTER_OTHERS:
808             fprintf(output, "LETTER_OTHERS "); break;
809         case XML_REGEXP_MARK:
810             fprintf(output, "MARK "); break;
811         case XML_REGEXP_MARK_NONSPACING:
812             fprintf(output, "MARK_NONSPACING "); break;
813         case XML_REGEXP_MARK_SPACECOMBINING:
814             fprintf(output, "MARK_SPACECOMBINING "); break;
815         case XML_REGEXP_MARK_ENCLOSING:
816             fprintf(output, "MARK_ENCLOSING "); break;
817         case XML_REGEXP_NUMBER:
818             fprintf(output, "NUMBER "); break;
819         case XML_REGEXP_NUMBER_DECIMAL:
820             fprintf(output, "NUMBER_DECIMAL "); break;
821         case XML_REGEXP_NUMBER_LETTER:
822             fprintf(output, "NUMBER_LETTER "); break;
823         case XML_REGEXP_NUMBER_OTHERS:
824             fprintf(output, "NUMBER_OTHERS "); break;
825         case XML_REGEXP_PUNCT:
826             fprintf(output, "PUNCT "); break;
827         case XML_REGEXP_PUNCT_CONNECTOR:
828             fprintf(output, "PUNCT_CONNECTOR "); break;
829         case XML_REGEXP_PUNCT_DASH:
830             fprintf(output, "PUNCT_DASH "); break;
831         case XML_REGEXP_PUNCT_OPEN:
832             fprintf(output, "PUNCT_OPEN "); break;
833         case XML_REGEXP_PUNCT_CLOSE:
834             fprintf(output, "PUNCT_CLOSE "); break;
835         case XML_REGEXP_PUNCT_INITQUOTE:
836             fprintf(output, "PUNCT_INITQUOTE "); break;
837         case XML_REGEXP_PUNCT_FINQUOTE:
838             fprintf(output, "PUNCT_FINQUOTE "); break;
839         case XML_REGEXP_PUNCT_OTHERS:
840             fprintf(output, "PUNCT_OTHERS "); break;
841         case XML_REGEXP_SEPAR:
842             fprintf(output, "SEPAR "); break;
843         case XML_REGEXP_SEPAR_SPACE:
844             fprintf(output, "SEPAR_SPACE "); break;
845         case XML_REGEXP_SEPAR_LINE:
846             fprintf(output, "SEPAR_LINE "); break;
847         case XML_REGEXP_SEPAR_PARA:
848             fprintf(output, "SEPAR_PARA "); break;
849         case XML_REGEXP_SYMBOL:
850             fprintf(output, "SYMBOL "); break;
851         case XML_REGEXP_SYMBOL_MATH:
852             fprintf(output, "SYMBOL_MATH "); break;
853         case XML_REGEXP_SYMBOL_CURRENCY:
854             fprintf(output, "SYMBOL_CURRENCY "); break;
855         case XML_REGEXP_SYMBOL_MODIFIER:
856             fprintf(output, "SYMBOL_MODIFIER "); break;
857         case XML_REGEXP_SYMBOL_OTHERS:
858             fprintf(output, "SYMBOL_OTHERS "); break;
859         case XML_REGEXP_OTHER:
860             fprintf(output, "OTHER "); break;
861         case XML_REGEXP_OTHER_CONTROL:
862             fprintf(output, "OTHER_CONTROL "); break;
863         case XML_REGEXP_OTHER_FORMAT:
864             fprintf(output, "OTHER_FORMAT "); break;
865         case XML_REGEXP_OTHER_PRIVATE:
866             fprintf(output, "OTHER_PRIVATE "); break;
867         case XML_REGEXP_OTHER_NA:
868             fprintf(output, "OTHER_NA "); break;
869         case XML_REGEXP_BLOCK_NAME:
870             fprintf(output, "BLOCK "); break;
871     }
872 }
873
874 static void
875 xmlRegPrintQuantType(FILE *output, xmlRegQuantType type) {
876     switch (type) {
877         case XML_REGEXP_QUANT_EPSILON:
878             fprintf(output, "epsilon "); break;
879         case XML_REGEXP_QUANT_ONCE:
880             fprintf(output, "once "); break;
881         case XML_REGEXP_QUANT_OPT:
882             fprintf(output, "? "); break;
883         case XML_REGEXP_QUANT_MULT:
884             fprintf(output, "* "); break;
885         case XML_REGEXP_QUANT_PLUS:
886             fprintf(output, "+ "); break;
887         case XML_REGEXP_QUANT_RANGE:
888             fprintf(output, "range "); break;
889         case XML_REGEXP_QUANT_ONCEONLY:
890             fprintf(output, "onceonly "); break;
891         case XML_REGEXP_QUANT_ALL:
892             fprintf(output, "all "); break;
893     }
894 }
895 static void
896 xmlRegPrintRange(FILE *output, xmlRegRangePtr range) {
897     fprintf(output, "  range: ");
898     if (range->neg)
899         fprintf(output, "negative ");
900     xmlRegPrintAtomType(output, range->type);
901     fprintf(output, "%c - %c\n", range->start, range->end);
902 }
903
904 static void
905 xmlRegPrintAtom(FILE *output, xmlRegAtomPtr atom) {
906     fprintf(output, " atom: ");
907     if (atom == NULL) {
908         fprintf(output, "NULL\n");
909         return;
910     }
911     xmlRegPrintAtomType(output, atom->type);
912     xmlRegPrintQuantType(output, atom->quant);
913     if (atom->quant == XML_REGEXP_QUANT_RANGE)
914         fprintf(output, "%d-%d ", atom->min, atom->max);
915     if (atom->type == XML_REGEXP_STRING)
916         fprintf(output, "'%s' ", (char *) atom->valuep);
917     if (atom->type == XML_REGEXP_CHARVAL)
918         fprintf(output, "char %c\n", atom->codepoint);
919     else if (atom->type == XML_REGEXP_RANGES) {
920         int i;
921         fprintf(output, "%d entries\n", atom->nbRanges);
922         for (i = 0; i < atom->nbRanges;i++)
923             xmlRegPrintRange(output, atom->ranges[i]);
924     } else if (atom->type == XML_REGEXP_SUBREG) {
925         fprintf(output, "start %d end %d\n", atom->start->no, atom->stop->no);
926     } else {
927         fprintf(output, "\n");
928     }
929 }
930
931 static void
932 xmlRegPrintTrans(FILE *output, xmlRegTransPtr trans) {
933     fprintf(output, "  trans: ");
934     if (trans == NULL) {
935         fprintf(output, "NULL\n");
936         return;
937     }
938     if (trans->to < 0) {
939         fprintf(output, "removed\n");
940         return;
941     }
942     if (trans->counter >= 0) {
943         fprintf(output, "counted %d, ", trans->counter);
944     }
945     if (trans->count == REGEXP_ALL_COUNTER) {
946         fprintf(output, "all transition, ");
947     } else if (trans->count >= 0) {
948         fprintf(output, "count based %d, ", trans->count);
949     }
950     if (trans->atom == NULL) {
951         fprintf(output, "epsilon to %d\n", trans->to);
952         return;
953     }
954     if (trans->atom->type == XML_REGEXP_CHARVAL)
955         fprintf(output, "char %c ", trans->atom->codepoint);
956     fprintf(output, "atom %d, to %d\n", trans->atom->no, trans->to);
957 }
958     
959 static void
960 xmlRegPrintState(FILE *output, xmlRegStatePtr state) {
961     int i;
962
963     fprintf(output, " state: ");
964     if (state == NULL) {
965         fprintf(output, "NULL\n");
966         return;
967     }
968     if (state->type == XML_REGEXP_START_STATE)
969         fprintf(output, "START ");
970     if (state->type == XML_REGEXP_FINAL_STATE)
971         fprintf(output, "FINAL ");
972     
973     fprintf(output, "%d, %d transitions:\n", state->no, state->nbTrans);
974     for (i = 0;i < state->nbTrans; i++) {
975         xmlRegPrintTrans(output, &(state->trans[i]));
976     }
977 }
978
979 #ifdef DEBUG_REGEXP_GRAPH
980 static void
981 xmlRegPrintCtxt(FILE *output, xmlRegParserCtxtPtr ctxt) {
982     int i;
983
984     fprintf(output, " ctxt: ");
985     if (ctxt == NULL) {
986         fprintf(output, "NULL\n");
987         return;
988     }
989     fprintf(output, "'%s' ", ctxt->string);
990     if (ctxt->error)
991         fprintf(output, "error ");
992     if (ctxt->neg)
993         fprintf(output, "neg ");
994     fprintf(output, "\n");
995     fprintf(output, "%d atoms:\n", ctxt->nbAtoms);
996     for (i = 0;i < ctxt->nbAtoms; i++) {
997         fprintf(output, " %02d ", i);
998         xmlRegPrintAtom(output, ctxt->atoms[i]);
999     }
1000     if (ctxt->atom != NULL) {
1001         fprintf(output, "current atom:\n");
1002         xmlRegPrintAtom(output, ctxt->atom);
1003     }
1004     fprintf(output, "%d states:", ctxt->nbStates);
1005     if (ctxt->start != NULL)
1006         fprintf(output, " start: %d", ctxt->start->no);
1007     if (ctxt->end != NULL)
1008         fprintf(output, " end: %d", ctxt->end->no);
1009     fprintf(output, "\n");
1010     for (i = 0;i < ctxt->nbStates; i++) {
1011         xmlRegPrintState(output, ctxt->states[i]);
1012     }
1013     fprintf(output, "%d counters:\n", ctxt->nbCounters);
1014     for (i = 0;i < ctxt->nbCounters; i++) {
1015         fprintf(output, " %d: min %d max %d\n", i, ctxt->counters[i].min,
1016                                                 ctxt->counters[i].max);
1017     }
1018 }
1019 #endif
1020
1021 /************************************************************************
1022  *                                                                      *
1023  *               Finite Automata structures manipulations               *
1024  *                                                                      *
1025  ************************************************************************/
1026
1027 static void 
1028 xmlRegAtomAddRange(xmlRegParserCtxtPtr ctxt, xmlRegAtomPtr atom,
1029                    int neg, xmlRegAtomType type, int start, int end,
1030                    xmlChar *blockName) {
1031     xmlRegRangePtr range;
1032
1033     if (atom == NULL) {
1034         ERROR("add range: atom is NULL");
1035         return;
1036     }
1037     if (atom->type != XML_REGEXP_RANGES) {
1038         ERROR("add range: atom is not ranges");
1039         return;
1040     }
1041     if (atom->maxRanges == 0) {
1042         atom->maxRanges = 4;
1043         atom->ranges = (xmlRegRangePtr *) xmlMalloc(atom->maxRanges *
1044                                              sizeof(xmlRegRangePtr));
1045         if (atom->ranges == NULL) {
1046             ERROR("add range: allocation failed");
1047             atom->maxRanges = 0;
1048             return;
1049         }
1050     } else if (atom->nbRanges >= atom->maxRanges) {
1051         xmlRegRangePtr *tmp;
1052         atom->maxRanges *= 2;
1053         tmp = (xmlRegRangePtr *) xmlRealloc(atom->ranges, atom->maxRanges *
1054                                              sizeof(xmlRegRangePtr));
1055         if (tmp == NULL) {
1056             ERROR("add range: allocation failed");
1057             atom->maxRanges /= 2;
1058             return;
1059         }
1060         atom->ranges = tmp;
1061     }
1062     range = xmlRegNewRange(ctxt, neg, type, start, end);
1063     if (range == NULL)
1064         return;
1065     range->blockName = blockName;
1066     atom->ranges[atom->nbRanges++] = range;
1067     
1068 }
1069
1070 static int
1071 xmlRegGetCounter(xmlRegParserCtxtPtr ctxt) {
1072     if (ctxt->maxCounters == 0) {
1073         ctxt->maxCounters = 4;
1074         ctxt->counters = (xmlRegCounter *) xmlMalloc(ctxt->maxCounters *
1075                                              sizeof(xmlRegCounter));
1076         if (ctxt->counters == NULL) {
1077             ERROR("reg counter: allocation failed");
1078             ctxt->maxCounters = 0;
1079             return(-1);
1080         }
1081     } else if (ctxt->nbCounters >= ctxt->maxCounters) {
1082         xmlRegCounter *tmp;
1083         ctxt->maxCounters *= 2;
1084         tmp = (xmlRegCounter *) xmlRealloc(ctxt->counters, ctxt->maxCounters *
1085                                            sizeof(xmlRegCounter));
1086         if (tmp == NULL) {
1087             ERROR("reg counter: allocation failed");
1088             ctxt->maxCounters /= 2;
1089             return(-1);
1090         }
1091         ctxt->counters = tmp;
1092     }
1093     ctxt->counters[ctxt->nbCounters].min = -1;
1094     ctxt->counters[ctxt->nbCounters].max = -1;
1095     return(ctxt->nbCounters++);
1096 }
1097
1098 static int 
1099 xmlRegAtomPush(xmlRegParserCtxtPtr ctxt, xmlRegAtomPtr atom) {
1100     if (atom == NULL) {
1101         ERROR("atom push: atom is NULL");
1102         return(-1);
1103     }
1104     if (ctxt->maxAtoms == 0) {
1105         ctxt->maxAtoms = 4;
1106         ctxt->atoms = (xmlRegAtomPtr *) xmlMalloc(ctxt->maxAtoms *
1107                                              sizeof(xmlRegAtomPtr));
1108         if (ctxt->atoms == NULL) {
1109             ERROR("atom push: allocation failed");
1110             ctxt->maxAtoms = 0;
1111             return(-1);
1112         }
1113     } else if (ctxt->nbAtoms >= ctxt->maxAtoms) {
1114         xmlRegAtomPtr *tmp;
1115         ctxt->maxAtoms *= 2;
1116         tmp = (xmlRegAtomPtr *) xmlRealloc(ctxt->atoms, ctxt->maxAtoms *
1117                                              sizeof(xmlRegAtomPtr));
1118         if (tmp == NULL) {
1119             ERROR("atom push: allocation failed");
1120             ctxt->maxAtoms /= 2;
1121             return(-1);
1122         }
1123         ctxt->atoms = tmp;
1124     }
1125     atom->no = ctxt->nbAtoms;
1126     ctxt->atoms[ctxt->nbAtoms++] = atom;
1127     return(0);
1128 }
1129
1130 static void 
1131 xmlRegStateAddTrans(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr state,
1132                     xmlRegAtomPtr atom, xmlRegStatePtr target,
1133                     int counter, int count) {
1134     if (state == NULL) {
1135         ERROR("add state: state is NULL");
1136         return;
1137     }
1138     if (target == NULL) {
1139         ERROR("add state: target is NULL");
1140         return;
1141     }
1142     if (state->maxTrans == 0) {
1143         state->maxTrans = 4;
1144         state->trans = (xmlRegTrans *) xmlMalloc(state->maxTrans *
1145                                              sizeof(xmlRegTrans));
1146         if (state->trans == NULL) {
1147             ERROR("add range: allocation failed");
1148             state->maxTrans = 0;
1149             return;
1150         }
1151     } else if (state->nbTrans >= state->maxTrans) {
1152         xmlRegTrans *tmp;
1153         state->maxTrans *= 2;
1154         tmp = (xmlRegTrans *) xmlRealloc(state->trans, state->maxTrans *
1155                                              sizeof(xmlRegTrans));
1156         if (tmp == NULL) {
1157             ERROR("add range: allocation failed");
1158             state->maxTrans /= 2;
1159             return;
1160         }
1161         state->trans = tmp;
1162     }
1163 #ifdef DEBUG_REGEXP_GRAPH
1164     printf("Add trans from %d to %d ", state->no, target->no);
1165     if (count == REGEXP_ALL_COUNTER)
1166         printf("all transition");
1167     else if (count >= 0)
1168         printf("count based %d", count);
1169     else if (counter >= 0)
1170         printf("counted %d", counter);
1171     else if (atom == NULL)
1172         printf("epsilon transition");
1173     printf("\n");
1174 #endif
1175
1176     state->trans[state->nbTrans].atom = atom;
1177     state->trans[state->nbTrans].to = target->no;
1178     state->trans[state->nbTrans].counter = counter;
1179     state->trans[state->nbTrans].count = count;
1180     state->nbTrans++;
1181 }
1182
1183 static int
1184 xmlRegStatePush(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr state) {
1185     if (state == NULL) return(-1);
1186     if (ctxt->maxStates == 0) {
1187         ctxt->maxStates = 4;
1188         ctxt->states = (xmlRegStatePtr *) xmlMalloc(ctxt->maxStates *
1189                                              sizeof(xmlRegStatePtr));
1190         if (ctxt->states == NULL) {
1191             ERROR("add range: allocation failed");
1192             ctxt->maxStates = 0;
1193             return(-1);
1194         }
1195     } else if (ctxt->nbStates >= ctxt->maxStates) {
1196         xmlRegStatePtr *tmp;
1197         ctxt->maxStates *= 2;
1198         tmp = (xmlRegStatePtr *) xmlRealloc(ctxt->states, ctxt->maxStates *
1199                                              sizeof(xmlRegStatePtr));
1200         if (tmp == NULL) {
1201             ERROR("add range: allocation failed");
1202             ctxt->maxStates /= 2;
1203             return(-1);
1204         }
1205         ctxt->states = tmp;
1206     }
1207     state->no = ctxt->nbStates;
1208     ctxt->states[ctxt->nbStates++] = state;
1209     return(0);
1210 }
1211
1212 /**
1213  * xmlFAGenerateAllTransition:
1214  * @ctxt:  a regexp parser context
1215  * @from:  the from state
1216  * @to:  the target state or NULL for building a new one
1217  * @lax:
1218  *
1219  */
1220 static void
1221 xmlFAGenerateAllTransition(xmlRegParserCtxtPtr ctxt,
1222                            xmlRegStatePtr from, xmlRegStatePtr to,
1223                            int lax) {
1224     if (to == NULL) {
1225         to = xmlRegNewState(ctxt);
1226         xmlRegStatePush(ctxt, to);
1227         ctxt->state = to;
1228     }
1229     if (lax)
1230         xmlRegStateAddTrans(ctxt, from, NULL, to, -1, REGEXP_ALL_LAX_COUNTER);
1231     else
1232         xmlRegStateAddTrans(ctxt, from, NULL, to, -1, REGEXP_ALL_COUNTER);
1233 }
1234
1235 /**
1236  * xmlFAGenerateEpsilonTransition:
1237  * @ctxt:  a regexp parser context
1238  * @from:  the from state
1239  * @to:  the target state or NULL for building a new one
1240  *
1241  */
1242 static void
1243 xmlFAGenerateEpsilonTransition(xmlRegParserCtxtPtr ctxt,
1244                                xmlRegStatePtr from, xmlRegStatePtr to) {
1245     if (to == NULL) {
1246         to = xmlRegNewState(ctxt);
1247         xmlRegStatePush(ctxt, to);
1248         ctxt->state = to;
1249     }
1250     xmlRegStateAddTrans(ctxt, from, NULL, to, -1, -1);
1251 }
1252
1253 /**
1254  * xmlFAGenerateCountedEpsilonTransition:
1255  * @ctxt:  a regexp parser context
1256  * @from:  the from state
1257  * @to:  the target state or NULL for building a new one
1258  * counter:  the counter for that transition
1259  *
1260  */
1261 static void
1262 xmlFAGenerateCountedEpsilonTransition(xmlRegParserCtxtPtr ctxt,
1263             xmlRegStatePtr from, xmlRegStatePtr to, int counter) {
1264     if (to == NULL) {
1265         to = xmlRegNewState(ctxt);
1266         xmlRegStatePush(ctxt, to);
1267         ctxt->state = to;
1268     }
1269     xmlRegStateAddTrans(ctxt, from, NULL, to, counter, -1);
1270 }
1271
1272 /**
1273  * xmlFAGenerateCountedTransition:
1274  * @ctxt:  a regexp parser context
1275  * @from:  the from state
1276  * @to:  the target state or NULL for building a new one
1277  * counter:  the counter for that transition
1278  *
1279  */
1280 static void
1281 xmlFAGenerateCountedTransition(xmlRegParserCtxtPtr ctxt,
1282             xmlRegStatePtr from, xmlRegStatePtr to, int counter) {
1283     if (to == NULL) {
1284         to = xmlRegNewState(ctxt);
1285         xmlRegStatePush(ctxt, to);
1286         ctxt->state = to;
1287     }
1288     xmlRegStateAddTrans(ctxt, from, NULL, to, -1, counter);
1289 }
1290
1291 /**
1292  * xmlFAGenerateTransitions:
1293  * @ctxt:  a regexp parser context
1294  * @from:  the from state
1295  * @to:  the target state or NULL for building a new one
1296  * @atom:  the atom generating the transition
1297  *
1298  * Returns 0 if succes and -1 in case of error.
1299  */
1300 static int
1301 xmlFAGenerateTransitions(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr from,
1302                          xmlRegStatePtr to, xmlRegAtomPtr atom) {
1303     if (atom == NULL) {
1304         ERROR("genrate transition: atom == NULL");
1305         return(-1);
1306     }
1307     if (atom->type == XML_REGEXP_SUBREG) {
1308         /*
1309          * this is a subexpression handling one should not need to
1310          * create a new node excep for XML_REGEXP_QUANT_RANGE.
1311          */
1312         if (xmlRegAtomPush(ctxt, atom) < 0) {
1313             return(-1);
1314         }
1315         if ((to != NULL) && (atom->stop != to) &&
1316             (atom->quant != XML_REGEXP_QUANT_RANGE)) {
1317             /*
1318              * Generate an epsilon transition to link to the target
1319              */
1320             xmlFAGenerateEpsilonTransition(ctxt, atom->stop, to);
1321         }
1322         switch (atom->quant) {
1323             case XML_REGEXP_QUANT_OPT:
1324                 atom->quant = XML_REGEXP_QUANT_ONCE;
1325                 xmlFAGenerateEpsilonTransition(ctxt, atom->start, atom->stop);
1326                 break;
1327             case XML_REGEXP_QUANT_MULT:
1328                 atom->quant = XML_REGEXP_QUANT_ONCE;
1329                 xmlFAGenerateEpsilonTransition(ctxt, atom->start, atom->stop);
1330                 xmlFAGenerateEpsilonTransition(ctxt, atom->stop, atom->start);
1331                 break;
1332             case XML_REGEXP_QUANT_PLUS:
1333                 atom->quant = XML_REGEXP_QUANT_ONCE;
1334                 xmlFAGenerateEpsilonTransition(ctxt, atom->stop, atom->start);
1335                 break;
1336             case XML_REGEXP_QUANT_RANGE: {
1337                 int counter;
1338                 xmlRegStatePtr newstate;
1339
1340                 /*
1341                  * This one is nasty:
1342                  *   1/ register a new counter
1343                  *   2/ register an epsilon transition associated to
1344                  *      this counter going from atom->stop to atom->start
1345                  *   3/ create a new state
1346                  *   4/ generate a counted transition from atom->stop to
1347                  *      that state
1348                  */
1349                 counter = xmlRegGetCounter(ctxt);
1350                 ctxt->counters[counter].min = atom->min - 1;
1351                 ctxt->counters[counter].max = atom->max - 1;
1352                 atom->min = 0;
1353                 atom->max = 0;
1354                 atom->quant = XML_REGEXP_QUANT_ONCE;
1355                 xmlFAGenerateCountedEpsilonTransition(ctxt, atom->stop,
1356                                                       atom->start, counter);
1357                 if (to != NULL) {
1358                     newstate = to;
1359                 } else {
1360                     newstate = xmlRegNewState(ctxt);
1361                     xmlRegStatePush(ctxt, newstate);
1362                     ctxt->state = newstate;
1363                 }
1364                 xmlFAGenerateCountedTransition(ctxt, atom->stop,
1365                                                newstate, counter);
1366             }
1367             default:
1368                 break;
1369         }
1370         return(0);
1371     } else {
1372         if (to == NULL) {
1373             to = xmlRegNewState(ctxt);
1374             if (to != NULL)
1375                 xmlRegStatePush(ctxt, to);
1376             else {
1377                 return(-1);
1378             }
1379         }
1380         if (xmlRegAtomPush(ctxt, atom) < 0) {
1381             return(-1);
1382         }
1383         xmlRegStateAddTrans(ctxt, from, atom, to, -1, -1);
1384         ctxt->state = to;
1385     }
1386     switch (atom->quant) {
1387         case XML_REGEXP_QUANT_OPT:
1388             atom->quant = XML_REGEXP_QUANT_ONCE;
1389             xmlFAGenerateEpsilonTransition(ctxt, from, to);
1390             break;
1391         case XML_REGEXP_QUANT_MULT:
1392             atom->quant = XML_REGEXP_QUANT_ONCE;
1393             xmlFAGenerateEpsilonTransition(ctxt, from, to);
1394             xmlRegStateAddTrans(ctxt, to, atom, to, -1, -1);
1395             break;
1396         case XML_REGEXP_QUANT_PLUS:
1397             atom->quant = XML_REGEXP_QUANT_ONCE;
1398             xmlRegStateAddTrans(ctxt, to, atom, to, -1, -1);
1399             break;
1400         default:
1401             break;
1402     }
1403     return(0);
1404 }
1405
1406 /**
1407  * xmlFAReduceEpsilonTransitions:
1408  * @ctxt:  a regexp parser context
1409  * @fromnr:  the from state
1410  * @tonr:  the to state 
1411  * @cpunter:  should that transition be associted to a counted
1412  *
1413  */
1414 static void
1415 xmlFAReduceEpsilonTransitions(xmlRegParserCtxtPtr ctxt, int fromnr,
1416                               int tonr, int counter) {
1417     int transnr;
1418     xmlRegStatePtr from;
1419     xmlRegStatePtr to;
1420
1421 #ifdef DEBUG_REGEXP_GRAPH
1422     printf("xmlFAReduceEpsilonTransitions(%d, %d)\n", fromnr, tonr);
1423 #endif
1424     from = ctxt->states[fromnr];
1425     if (from == NULL)
1426         return;
1427     to = ctxt->states[tonr];
1428     if (to == NULL)
1429         return;
1430     if ((to->mark == XML_REGEXP_MARK_START) ||
1431         (to->mark == XML_REGEXP_MARK_VISITED))
1432         return;
1433
1434     to->mark = XML_REGEXP_MARK_VISITED;
1435     if (to->type == XML_REGEXP_FINAL_STATE) {
1436 #ifdef DEBUG_REGEXP_GRAPH
1437         printf("State %d is final, so %d becomes final\n", tonr, fromnr);
1438 #endif
1439         from->type = XML_REGEXP_FINAL_STATE;
1440     }
1441     for (transnr = 0;transnr < to->nbTrans;transnr++) {
1442         if (to->trans[transnr].atom == NULL) {
1443             /*
1444              * Don't remove counted transitions
1445              * Don't loop either
1446              */
1447             if (to->trans[transnr].to != fromnr) {
1448                 if (to->trans[transnr].count >= 0) {
1449                     int newto = to->trans[transnr].to;
1450
1451                     xmlRegStateAddTrans(ctxt, from, NULL,
1452                                         ctxt->states[newto], 
1453                                         -1, to->trans[transnr].count);
1454                 } else {
1455 #ifdef DEBUG_REGEXP_GRAPH
1456                     printf("Found epsilon trans %d from %d to %d\n",
1457                            transnr, tonr, to->trans[transnr].to);
1458 #endif
1459                     if (to->trans[transnr].counter >= 0) {
1460                         xmlFAReduceEpsilonTransitions(ctxt, fromnr,
1461                                               to->trans[transnr].to,
1462                                               to->trans[transnr].counter);
1463                     } else {
1464                         xmlFAReduceEpsilonTransitions(ctxt, fromnr,
1465                                               to->trans[transnr].to,
1466                                               counter);
1467                     }
1468                 }
1469             }
1470         } else {
1471             int newto = to->trans[transnr].to;
1472
1473             if (to->trans[transnr].counter >= 0) {
1474                 xmlRegStateAddTrans(ctxt, from, to->trans[transnr].atom, 
1475                                     ctxt->states[newto], 
1476                                     to->trans[transnr].counter, -1);
1477             } else {
1478                 xmlRegStateAddTrans(ctxt, from, to->trans[transnr].atom, 
1479                                     ctxt->states[newto], counter, -1);
1480             }
1481         }
1482     }
1483     to->mark = XML_REGEXP_MARK_NORMAL;
1484 }
1485
1486 /**
1487  * xmlFAEliminateEpsilonTransitions:
1488  * @ctxt:  a regexp parser context
1489  *
1490  */
1491 static void
1492 xmlFAEliminateEpsilonTransitions(xmlRegParserCtxtPtr ctxt) {
1493     int statenr, transnr;
1494     xmlRegStatePtr state;
1495
1496     if (ctxt->states == NULL) return;
1497
1498
1499     /*
1500      * build the completed transitions bypassing the epsilons
1501      * Use a marking algorithm to avoid loops
1502      */
1503     for (statenr = 0;statenr < ctxt->nbStates;statenr++) {
1504         state = ctxt->states[statenr];
1505         if (state == NULL)
1506             continue;
1507         for (transnr = 0;transnr < state->nbTrans;transnr++) {
1508             if ((state->trans[transnr].atom == NULL) &&
1509                 (state->trans[transnr].to >= 0)) {
1510                 if (state->trans[transnr].to == statenr) {
1511                     state->trans[transnr].to = -1;
1512 #ifdef DEBUG_REGEXP_GRAPH
1513                     printf("Removed loopback epsilon trans %d on %d\n",
1514                            transnr, statenr);
1515 #endif
1516                 } else if (state->trans[transnr].count < 0) {
1517                     int newto = state->trans[transnr].to;
1518
1519 #ifdef DEBUG_REGEXP_GRAPH
1520                     printf("Found epsilon trans %d from %d to %d\n",
1521                            transnr, statenr, newto);
1522 #endif
1523                     state->mark = XML_REGEXP_MARK_START;
1524                     xmlFAReduceEpsilonTransitions(ctxt, statenr,
1525                                       newto, state->trans[transnr].counter);
1526                     state->mark = XML_REGEXP_MARK_NORMAL;
1527 #ifdef DEBUG_REGEXP_GRAPH
1528                 } else {
1529                     printf("Found counted transition %d on %d\n",
1530                            transnr, statenr);
1531 #endif
1532                 }
1533             }
1534         }
1535     }
1536     /*
1537      * Eliminate the epsilon transitions
1538      */
1539     for (statenr = 0;statenr < ctxt->nbStates;statenr++) {
1540         state = ctxt->states[statenr];
1541         if (state == NULL)
1542             continue;
1543         for (transnr = 0;transnr < state->nbTrans;transnr++) {
1544             if ((state->trans[transnr].atom == NULL) &&
1545                 (state->trans[transnr].count < 0) &&
1546                 (state->trans[transnr].to >= 0)) {
1547                 state->trans[transnr].to = -1;
1548             }
1549         }
1550     }
1551
1552     /*
1553      * Use this pass to detect unreachable states too
1554      */
1555     for (statenr = 0;statenr < ctxt->nbStates;statenr++) {
1556         state = ctxt->states[statenr];
1557         if (state != NULL)
1558             state->reached = XML_REGEXP_MARK_NORMAL;
1559     }
1560     state = ctxt->states[0];
1561     if (state != NULL)
1562         state->reached = XML_REGEXP_MARK_START;
1563     while (state != NULL) {
1564         xmlRegStatePtr target = NULL;
1565         state->reached = XML_REGEXP_MARK_VISITED;
1566         /*
1567          * Mark all state reachable from the current reachable state
1568          */
1569         for (transnr = 0;transnr < state->nbTrans;transnr++) {
1570             if ((state->trans[transnr].to >= 0) &&
1571                 ((state->trans[transnr].atom != NULL) ||
1572                  (state->trans[transnr].count >= 0))) {
1573                 int newto = state->trans[transnr].to;
1574
1575                 if (ctxt->states[newto] == NULL)
1576                     continue;
1577                 if (ctxt->states[newto]->reached == XML_REGEXP_MARK_NORMAL) {
1578                     ctxt->states[newto]->reached = XML_REGEXP_MARK_START;
1579                     target = ctxt->states[newto];
1580                 }
1581             }
1582         }
1583         /*
1584          * find the next accessible state not explored
1585          */
1586         if (target == NULL) {
1587             for (statenr = 1;statenr < ctxt->nbStates;statenr++) {
1588                 state = ctxt->states[statenr];
1589                 if ((state != NULL) && (state->reached ==
1590                         XML_REGEXP_MARK_START)) {
1591                     target = state;
1592                     break;
1593                 }
1594             }
1595         }
1596         state = target;
1597     }
1598     for (statenr = 0;statenr < ctxt->nbStates;statenr++) {
1599         state = ctxt->states[statenr];
1600         if ((state != NULL) && (state->reached == XML_REGEXP_MARK_NORMAL)) {
1601 #ifdef DEBUG_REGEXP_GRAPH
1602             printf("Removed unreachable state %d\n", statenr);
1603 #endif
1604             xmlRegFreeState(state);
1605             ctxt->states[statenr] = NULL;
1606         }
1607     }
1608
1609 }
1610
1611 /**
1612  * xmlFACompareAtoms:
1613  * @atom1:  an atom
1614  * @atom2:  an atom
1615  *
1616  * Compares two atoms to check whether they are equivatents
1617  *
1618  * Returns 1 if yes and 0 otherwise
1619  */
1620 static int
1621 xmlFACompareAtoms(xmlRegAtomPtr atom1, xmlRegAtomPtr atom2) {
1622     if (atom1 == atom2)
1623         return(1);
1624     if ((atom1 == NULL) || (atom2 == NULL))
1625         return(0);
1626
1627     if (atom1->type != atom2->type)
1628         return(0);
1629     switch (atom1->type) {
1630         case XML_REGEXP_STRING:
1631             return(xmlStrEqual((xmlChar *)atom1->valuep,
1632                                (xmlChar *)atom2->valuep));
1633         case XML_REGEXP_EPSILON:
1634             return(1);
1635         case XML_REGEXP_CHARVAL:
1636             return(atom1->codepoint == atom2->codepoint);
1637         case XML_REGEXP_RANGES:
1638             TODO;
1639             return(0);
1640         default:
1641             break;
1642     }
1643     return(1);
1644 }
1645
1646 /**
1647  * xmlFARecurseDeterminism:
1648  * @ctxt:  a regexp parser context
1649  *
1650  * Check whether the associated regexp is determinist,
1651  * should be called after xmlFAEliminateEpsilonTransitions()
1652  *
1653  */
1654 static int
1655 xmlFARecurseDeterminism(xmlRegParserCtxtPtr ctxt, xmlRegStatePtr state,
1656                          int to, xmlRegAtomPtr atom) {
1657     int ret = 1;
1658     int transnr;
1659     xmlRegTransPtr t1;
1660
1661     if (state == NULL)
1662         return(ret);
1663     for (transnr = 0;transnr < state->nbTrans;transnr++) {
1664         t1 = &(state->trans[transnr]);
1665         /*
1666          * check transitions conflicting with the one looked at
1667          */
1668         if (t1->atom == NULL) {
1669             if (t1->to == -1)
1670                 continue;
1671             ret = xmlFARecurseDeterminism(ctxt, ctxt->states[t1->to],
1672                                            to, atom);
1673             if (ret == 0)
1674                 return(0);
1675             continue;
1676         }
1677         if (t1->to != to)
1678             continue;
1679         if (xmlFACompareAtoms(t1->atom, atom))
1680             return(0);
1681     }
1682     return(ret);
1683 }
1684
1685 /**
1686  * xmlFAComputesDeterminism:
1687  * @ctxt:  a regexp parser context
1688  *
1689  * Check whether the associated regexp is determinist,
1690  * should be called after xmlFAEliminateEpsilonTransitions()
1691  *
1692  */
1693 static int
1694 xmlFAComputesDeterminism(xmlRegParserCtxtPtr ctxt) {
1695     int statenr, transnr;
1696     xmlRegStatePtr state;
1697     xmlRegTransPtr t1, t2;
1698     int i;
1699     int ret = 1;
1700
1701 #ifdef DEBUG_REGEXP_GRAPH
1702     printf("xmlFAComputesDeterminism\n");
1703     xmlRegPrintCtxt(stdout, ctxt);
1704 #endif
1705     if (ctxt->determinist != -1)
1706         return(ctxt->determinist);
1707
1708     /*
1709      * Check for all states that there isn't 2 transitions
1710      * with the same atom and a different target.
1711      */
1712     for (statenr = 0;statenr < ctxt->nbStates;statenr++) {
1713         state = ctxt->states[statenr];
1714         if (state == NULL)
1715             continue;
1716         for (transnr = 0;transnr < state->nbTrans;transnr++) {
1717             t1 = &(state->trans[transnr]);
1718             /*
1719              * Determinism checks in case of counted or all transitions
1720              * will have to be handled separately
1721              */
1722             if (t1->atom == NULL)
1723                 continue;
1724             if (t1->to == -1) /* eliminated */
1725                 continue;
1726             for (i = 0;i < transnr;i++) {
1727                 t2 = &(state->trans[i]);
1728                 if (t2->to == -1) /* eliminated */
1729                     continue;
1730                 if (t2->atom != NULL) {
1731                     if (t1->to == t2->to) {
1732                         if (xmlFACompareAtoms(t1->atom, t2->atom))
1733                             t2->to = -1; /* eliminate */
1734                     } else {
1735                         /* not determinist ! */
1736                         if (xmlFACompareAtoms(t1->atom, t2->atom))
1737                             ret = 0;
1738                     }
1739                 } else if (t1->to != -1) {
1740                     /*
1741                      * do the closure in case of remaining specific
1742                      * epsilon transitions like choices or all
1743                      */
1744                     ret = xmlFARecurseDeterminism(ctxt, ctxt->states[t1->to],
1745                                                    t2->to, t2->atom);
1746                     if (ret == 0)
1747                         return(0);
1748                 }
1749             }
1750             if (ret == 0)
1751                 break;
1752         }
1753         if (ret == 0)
1754             break;
1755     }
1756     ctxt->determinist = ret;
1757     return(ret);
1758 }
1759
1760 /************************************************************************
1761  *                                                                      *
1762  *      Routines to check input against transition atoms                *
1763  *                                                                      *
1764  ************************************************************************/
1765
1766 static int
1767 xmlRegCheckCharacterRange(xmlRegAtomType type, int codepoint, int neg,
1768                           int start, int end, const xmlChar *blockName) {
1769     int ret = 0;
1770
1771     switch (type) {
1772         case XML_REGEXP_STRING:
1773         case XML_REGEXP_SUBREG:
1774         case XML_REGEXP_RANGES:
1775         case XML_REGEXP_EPSILON:
1776             return(-1);
1777         case XML_REGEXP_ANYCHAR:
1778             ret = ((codepoint != '\n') && (codepoint != '\r'));
1779             break;
1780         case XML_REGEXP_CHARVAL:
1781             ret = ((codepoint >= start) && (codepoint <= end));
1782             break;
1783         case XML_REGEXP_NOTSPACE:
1784             neg = !neg;
1785         case XML_REGEXP_ANYSPACE:
1786             ret = ((codepoint == '\n') || (codepoint == '\r') ||
1787                    (codepoint == '\t') || (codepoint == ' '));
1788             break;
1789         case XML_REGEXP_NOTINITNAME:
1790             neg = !neg;
1791         case XML_REGEXP_INITNAME:
1792             ret = (xmlIsLetter(codepoint) || 
1793                    (codepoint == '_') || (codepoint == ':'));
1794             break;
1795         case XML_REGEXP_NOTNAMECHAR:
1796             neg = !neg;
1797         case XML_REGEXP_NAMECHAR:
1798             ret = (xmlIsLetter(codepoint) || xmlIsDigit(codepoint) ||
1799                    (codepoint == '.') || (codepoint == '-') ||
1800                    (codepoint == '_') || (codepoint == ':') ||
1801                    xmlIsCombining(codepoint) || xmlIsExtender(codepoint));
1802             break;
1803         case XML_REGEXP_NOTDECIMAL:
1804             neg = !neg;
1805         case XML_REGEXP_DECIMAL:
1806             ret = xmlUCSIsCatNd(codepoint);
1807             break;
1808         case XML_REGEXP_REALCHAR:
1809             neg = !neg;
1810         case XML_REGEXP_NOTREALCHAR:
1811             ret = xmlUCSIsCatP(codepoint);
1812             if (ret == 0)
1813                 ret = xmlUCSIsCatZ(codepoint);
1814             if (ret == 0)
1815                 ret = xmlUCSIsCatC(codepoint);
1816             break;
1817         case XML_REGEXP_LETTER:
1818             ret = xmlUCSIsCatL(codepoint);
1819             break;
1820         case XML_REGEXP_LETTER_UPPERCASE:
1821             ret = xmlUCSIsCatLu(codepoint);
1822             break;
1823         case XML_REGEXP_LETTER_LOWERCASE:
1824             ret = xmlUCSIsCatLl(codepoint);
1825             break;
1826         case XML_REGEXP_LETTER_TITLECASE:
1827             ret = xmlUCSIsCatLt(codepoint);
1828             break;
1829         case XML_REGEXP_LETTER_MODIFIER:
1830             ret = xmlUCSIsCatLm(codepoint);
1831             break;
1832         case XML_REGEXP_LETTER_OTHERS:
1833             ret = xmlUCSIsCatLo(codepoint);
1834             break;
1835         case XML_REGEXP_MARK:
1836             ret = xmlUCSIsCatM(codepoint);
1837             break;
1838         case XML_REGEXP_MARK_NONSPACING:
1839             ret = xmlUCSIsCatMn(codepoint);
1840             break;
1841         case XML_REGEXP_MARK_SPACECOMBINING:
1842             ret = xmlUCSIsCatMc(codepoint);
1843             break;
1844         case XML_REGEXP_MARK_ENCLOSING:
1845             ret = xmlUCSIsCatMe(codepoint);
1846             break;
1847         case XML_REGEXP_NUMBER:
1848             ret = xmlUCSIsCatN(codepoint);
1849             break;
1850         case XML_REGEXP_NUMBER_DECIMAL:
1851             ret = xmlUCSIsCatNd(codepoint);
1852             break;
1853         case XML_REGEXP_NUMBER_LETTER:
1854             ret = xmlUCSIsCatNl(codepoint);
1855             break;
1856         case XML_REGEXP_NUMBER_OTHERS:
1857             ret = xmlUCSIsCatNo(codepoint);
1858             break;
1859         case XML_REGEXP_PUNCT:
1860             ret = xmlUCSIsCatP(codepoint);
1861             break;
1862         case XML_REGEXP_PUNCT_CONNECTOR:
1863             ret = xmlUCSIsCatPc(codepoint);
1864             break;
1865         case XML_REGEXP_PUNCT_DASH:
1866             ret = xmlUCSIsCatPd(codepoint);
1867             break;
1868         case XML_REGEXP_PUNCT_OPEN:
1869             ret = xmlUCSIsCatPs(codepoint);
1870             break;
1871         case XML_REGEXP_PUNCT_CLOSE:
1872             ret = xmlUCSIsCatPe(codepoint);
1873             break;
1874         case XML_REGEXP_PUNCT_INITQUOTE:
1875             ret = xmlUCSIsCatPi(codepoint);
1876             break;
1877         case XML_REGEXP_PUNCT_FINQUOTE:
1878             ret = xmlUCSIsCatPf(codepoint);
1879             break;
1880         case XML_REGEXP_PUNCT_OTHERS:
1881             ret = xmlUCSIsCatPo(codepoint);
1882             break;
1883         case XML_REGEXP_SEPAR:
1884             ret = xmlUCSIsCatZ(codepoint);
1885             break;
1886         case XML_REGEXP_SEPAR_SPACE:
1887             ret = xmlUCSIsCatZs(codepoint);
1888             break;
1889         case XML_REGEXP_SEPAR_LINE:
1890             ret = xmlUCSIsCatZl(codepoint);
1891             break;
1892         case XML_REGEXP_SEPAR_PARA:
1893             ret = xmlUCSIsCatZp(codepoint);
1894             break;
1895         case XML_REGEXP_SYMBOL:
1896             ret = xmlUCSIsCatS(codepoint);
1897             break;
1898         case XML_REGEXP_SYMBOL_MATH:
1899             ret = xmlUCSIsCatSm(codepoint);
1900             break;
1901         case XML_REGEXP_SYMBOL_CURRENCY:
1902             ret = xmlUCSIsCatSc(codepoint);
1903             break;
1904         case XML_REGEXP_SYMBOL_MODIFIER:
1905             ret = xmlUCSIsCatSk(codepoint);
1906             break;
1907         case XML_REGEXP_SYMBOL_OTHERS:
1908             ret = xmlUCSIsCatSo(codepoint);
1909             break;
1910         case XML_REGEXP_OTHER:
1911             ret = xmlUCSIsCatC(codepoint);
1912             break;
1913         case XML_REGEXP_OTHER_CONTROL:
1914             ret = xmlUCSIsCatCc(codepoint);
1915             break;
1916         case XML_REGEXP_OTHER_FORMAT:
1917             ret = xmlUCSIsCatCf(codepoint);
1918             break;
1919         case XML_REGEXP_OTHER_PRIVATE:
1920             ret = xmlUCSIsCatCo(codepoint);
1921             break;
1922         case XML_REGEXP_OTHER_NA:
1923             /* ret = xmlUCSIsCatCn(codepoint); */
1924             /* Seems it doesn't exist anymore in recent Unicode releases */
1925             ret = 0;
1926             break;
1927         case XML_REGEXP_BLOCK_NAME:
1928             ret = xmlUCSIsBlock(codepoint, (const char *) blockName);
1929             break;
1930     }
1931     if (neg)
1932         return(!ret);
1933     return(ret);
1934 }
1935
1936 static int
1937 xmlRegCheckCharacter(xmlRegAtomPtr atom, int codepoint) {
1938     int i, ret = 0;
1939     xmlRegRangePtr range;
1940
1941     if ((atom == NULL) || (!xmlIsChar(codepoint)))
1942         return(-1);
1943
1944     switch (atom->type) {
1945         case XML_REGEXP_SUBREG:
1946         case XML_REGEXP_EPSILON:
1947             return(-1);
1948         case XML_REGEXP_CHARVAL:
1949             return(codepoint == atom->codepoint);
1950         case XML_REGEXP_RANGES: {
1951             int accept = 0;
1952             for (i = 0;i < atom->nbRanges;i++) {
1953                 range = atom->ranges[i];
1954                 if (range->neg) {
1955                     ret = xmlRegCheckCharacterRange(range->type, codepoint,
1956                                                 0, range->start, range->end,
1957                                                 range->blockName);
1958                     if (ret != 0)
1959                         return(0); /* excluded char */
1960                 } else {
1961                     ret = xmlRegCheckCharacterRange(range->type, codepoint,
1962                                                 0, range->start, range->end,
1963                                                 range->blockName);
1964                     if (ret != 0)
1965                         accept = 1; /* might still be excluded */
1966                 }
1967             }
1968             return(accept);
1969         }
1970         case XML_REGEXP_STRING:
1971             printf("TODO: XML_REGEXP_STRING\n");
1972             return(-1);
1973         case XML_REGEXP_ANYCHAR:
1974         case XML_REGEXP_ANYSPACE:
1975         case XML_REGEXP_NOTSPACE:
1976         case XML_REGEXP_INITNAME:
1977         case XML_REGEXP_NOTINITNAME:
1978         case XML_REGEXP_NAMECHAR:
1979         case XML_REGEXP_NOTNAMECHAR:
1980         case XML_REGEXP_DECIMAL:
1981         case XML_REGEXP_NOTDECIMAL:
1982         case XML_REGEXP_REALCHAR:
1983         case XML_REGEXP_NOTREALCHAR:
1984         case XML_REGEXP_LETTER:
1985         case XML_REGEXP_LETTER_UPPERCASE:
1986         case XML_REGEXP_LETTER_LOWERCASE:
1987         case XML_REGEXP_LETTER_TITLECASE:
1988         case XML_REGEXP_LETTER_MODIFIER:
1989         case XML_REGEXP_LETTER_OTHERS:
1990         case XML_REGEXP_MARK:
1991         case XML_REGEXP_MARK_NONSPACING:
1992         case XML_REGEXP_MARK_SPACECOMBINING:
1993         case XML_REGEXP_MARK_ENCLOSING:
1994         case XML_REGEXP_NUMBER:
1995         case XML_REGEXP_NUMBER_DECIMAL:
1996         case XML_REGEXP_NUMBER_LETTER:
1997         case XML_REGEXP_NUMBER_OTHERS:
1998         case XML_REGEXP_PUNCT:
1999         case XML_REGEXP_PUNCT_CONNECTOR:
2000         case XML_REGEXP_PUNCT_DASH:
2001         case XML_REGEXP_PUNCT_OPEN:
2002         case XML_REGEXP_PUNCT_CLOSE:
2003         case XML_REGEXP_PUNCT_INITQUOTE:
2004         case XML_REGEXP_PUNCT_FINQUOTE:
2005         case XML_REGEXP_PUNCT_OTHERS:
2006         case XML_REGEXP_SEPAR:
2007         case XML_REGEXP_SEPAR_SPACE:
2008         case XML_REGEXP_SEPAR_LINE:
2009         case XML_REGEXP_SEPAR_PARA:
2010         case XML_REGEXP_SYMBOL:
2011         case XML_REGEXP_SYMBOL_MATH:
2012         case XML_REGEXP_SYMBOL_CURRENCY:
2013         case XML_REGEXP_SYMBOL_MODIFIER:
2014         case XML_REGEXP_SYMBOL_OTHERS:
2015         case XML_REGEXP_OTHER:
2016         case XML_REGEXP_OTHER_CONTROL:
2017         case XML_REGEXP_OTHER_FORMAT:
2018         case XML_REGEXP_OTHER_PRIVATE:
2019         case XML_REGEXP_OTHER_NA:
2020         case XML_REGEXP_BLOCK_NAME:
2021             ret = xmlRegCheckCharacterRange(atom->type, codepoint, 0, 0, 0,
2022                                             (const xmlChar *)atom->valuep);
2023             if (atom->neg)
2024                 ret = !ret;
2025             break;
2026     }
2027     return(ret);
2028 }
2029
2030 /************************************************************************
2031  *                                                                      *
2032  *      Saving an restoring state of an execution context               *
2033  *                                                                      *
2034  ************************************************************************/
2035
2036 #ifdef DEBUG_REGEXP_EXEC
2037 static void
2038 xmlFARegDebugExec(xmlRegExecCtxtPtr exec) {
2039     printf("state: %d:%d:idx %d", exec->state->no, exec->transno, exec->index);
2040     if (exec->inputStack != NULL) {
2041         int i;
2042         printf(": ");
2043         for (i = 0;(i < 3) && (i < exec->inputStackNr);i++)
2044             printf("%s ", exec->inputStack[exec->inputStackNr - (i + 1)]);
2045     } else {
2046         printf(": %s", &(exec->inputString[exec->index]));
2047     }
2048     printf("\n");
2049 }
2050 #endif
2051
2052 static void
2053 xmlFARegExecSave(xmlRegExecCtxtPtr exec) {
2054 #ifdef DEBUG_REGEXP_EXEC
2055     printf("saving ");
2056     exec->transno++;
2057     xmlFARegDebugExec(exec);
2058     exec->transno--;
2059 #endif
2060
2061     if (exec->maxRollbacks == 0) {
2062         exec->maxRollbacks = 4;
2063         exec->rollbacks = (xmlRegExecRollback *) xmlMalloc(exec->maxRollbacks *
2064                                              sizeof(xmlRegExecRollback));
2065         if (exec->rollbacks == NULL) {
2066             fprintf(stderr, "exec save: allocation failed");
2067             exec->maxRollbacks = 0;
2068             return;
2069         }
2070         memset(exec->rollbacks, 0,
2071                exec->maxRollbacks * sizeof(xmlRegExecRollback));
2072     } else if (exec->nbRollbacks >= exec->maxRollbacks) {
2073         xmlRegExecRollback *tmp;
2074         int len = exec->maxRollbacks;
2075
2076         exec->maxRollbacks *= 2;
2077         tmp = (xmlRegExecRollback *) xmlRealloc(exec->rollbacks,
2078                         exec->maxRollbacks * sizeof(xmlRegExecRollback));
2079         if (tmp == NULL) {
2080             fprintf(stderr, "exec save: allocation failed");
2081             exec->maxRollbacks /= 2;
2082             return;
2083         }
2084         exec->rollbacks = tmp;
2085         tmp = &exec->rollbacks[len];
2086         memset(tmp, 0, (exec->maxRollbacks - len) * sizeof(xmlRegExecRollback));
2087     }
2088     exec->rollbacks[exec->nbRollbacks].state = exec->state;
2089     exec->rollbacks[exec->nbRollbacks].index = exec->index;
2090     exec->rollbacks[exec->nbRollbacks].nextbranch = exec->transno + 1;
2091     if (exec->comp->nbCounters > 0) {
2092         if (exec->rollbacks[exec->nbRollbacks].counts == NULL) {
2093             exec->rollbacks[exec->nbRollbacks].counts = (int *)
2094                 xmlMalloc(exec->comp->nbCounters * sizeof(int));
2095             if (exec->rollbacks[exec->nbRollbacks].counts == NULL) {
2096                 fprintf(stderr, "exec save: allocation failed");
2097                 exec->status = -5;
2098                 return;
2099             }
2100         }
2101         memcpy(exec->rollbacks[exec->nbRollbacks].counts, exec->counts,
2102                exec->comp->nbCounters * sizeof(int));
2103     }
2104     exec->nbRollbacks++;
2105 }
2106
2107 static void
2108 xmlFARegExecRollBack(xmlRegExecCtxtPtr exec) {
2109     if (exec->nbRollbacks <= 0) {
2110         exec->status = -1;
2111 #ifdef DEBUG_REGEXP_EXEC
2112         printf("rollback failed on empty stack\n");
2113 #endif
2114         return;
2115     }
2116     exec->nbRollbacks--;
2117     exec->state = exec->rollbacks[exec->nbRollbacks].state;
2118     exec->index = exec->rollbacks[exec->nbRollbacks].index;
2119     exec->transno = exec->rollbacks[exec->nbRollbacks].nextbranch;
2120     if (exec->comp->nbCounters > 0) {
2121         if (exec->rollbacks[exec->nbRollbacks].counts == NULL) {
2122             fprintf(stderr, "exec save: allocation failed");
2123             exec->status = -6;
2124             return;
2125         }
2126         memcpy(exec->counts, exec->rollbacks[exec->nbRollbacks].counts,
2127                exec->comp->nbCounters * sizeof(int));
2128     }
2129
2130 #ifdef DEBUG_REGEXP_EXEC
2131     printf("restored ");
2132     xmlFARegDebugExec(exec);
2133 #endif
2134 }
2135
2136 /************************************************************************
2137  *                                                                      *
2138  *      Verifyer, running an input against a compiled regexp            *
2139  *                                                                      *
2140  ************************************************************************/
2141
2142 static int
2143 xmlFARegExec(xmlRegexpPtr comp, const xmlChar *content) {
2144     xmlRegExecCtxt execval;
2145     xmlRegExecCtxtPtr exec = &execval;
2146     int ret, codepoint, len;
2147
2148     exec->inputString = content;
2149     exec->index = 0;
2150     exec->determinist = 1;
2151     exec->maxRollbacks = 0;
2152     exec->nbRollbacks = 0;
2153     exec->rollbacks = NULL;
2154     exec->status = 0;
2155     exec->comp = comp;
2156     exec->state = comp->states[0];
2157     exec->transno = 0;
2158     exec->transcount = 0;
2159     if (comp->nbCounters > 0) {
2160         exec->counts = (int *) xmlMalloc(comp->nbCounters * sizeof(int));
2161         if (exec->counts == NULL)
2162             return(-1);
2163         memset(exec->counts, 0, comp->nbCounters * sizeof(int));
2164     } else
2165         exec->counts = NULL;
2166     while ((exec->status == 0) &&
2167            ((exec->inputString[exec->index] != 0) ||
2168             (exec->state->type != XML_REGEXP_FINAL_STATE))) {
2169         xmlRegTransPtr trans;
2170         xmlRegAtomPtr atom;
2171
2172         /*
2173          * End of input on non-terminal state, rollback, however we may
2174          * still have epsilon like transition for counted transitions
2175          * on counters, in that case don't break too early.
2176          */
2177         if ((exec->inputString[exec->index] == 0) && (exec->counts == NULL))
2178             goto rollback;
2179
2180         exec->transcount = 0;
2181         for (;exec->transno < exec->state->nbTrans;exec->transno++) {
2182             trans = &exec->state->trans[exec->transno];
2183             if (trans->to < 0)
2184                 continue;
2185             atom = trans->atom;
2186             ret = 0;
2187             if (trans->count >= 0) {
2188                 int count;
2189                 xmlRegCounterPtr counter;
2190
2191                 /*
2192                  * A counted transition.
2193                  */
2194
2195                 count = exec->counts[trans->count];
2196                 counter = &exec->comp->counters[trans->count];
2197 #ifdef DEBUG_REGEXP_EXEC
2198                 printf("testing count %d: val %d, min %d, max %d\n",
2199                        trans->count, count, counter->min,  counter->max);
2200 #endif
2201                 ret = ((count >= counter->min) && (count <= counter->max));
2202             } else if (atom == NULL) {
2203                 fprintf(stderr, "epsilon transition left at runtime\n");
2204                 exec->status = -2;
2205                 break;
2206             } else if (exec->inputString[exec->index] != 0) {
2207                 codepoint = CUR_SCHAR(&(exec->inputString[exec->index]), len);
2208                 ret = xmlRegCheckCharacter(atom, codepoint);
2209                 if ((ret == 1) && (atom->min > 0) && (atom->max > 0)) {
2210                     xmlRegStatePtr to = comp->states[trans->to];
2211
2212                     /*
2213                      * this is a multiple input sequence
2214                      */
2215                     if (exec->state->nbTrans > exec->transno + 1) {
2216                         xmlFARegExecSave(exec);
2217                     }
2218                     exec->transcount = 1;
2219                     do {
2220                         /*
2221                          * Try to progress as much as possible on the input
2222                          */
2223                         if (exec->transcount == atom->max) {
2224                             break;
2225                         }
2226                         exec->index += len;
2227                         /*
2228                          * End of input: stop here
2229                          */
2230                         if (exec->inputString[exec->index] == 0) {
2231                             exec->index -= len;
2232                             break;
2233                         }
2234                         if (exec->transcount >= atom->min) {
2235                             int transno = exec->transno;
2236                             xmlRegStatePtr state = exec->state;
2237
2238                             /*
2239                              * The transition is acceptable save it
2240                              */
2241                             exec->transno = -1; /* trick */
2242                             exec->state = to;
2243                             xmlFARegExecSave(exec);
2244                             exec->transno = transno;
2245                             exec->state = state;
2246                         }
2247                         codepoint = CUR_SCHAR(&(exec->inputString[exec->index]),
2248                                               len);
2249                         ret = xmlRegCheckCharacter(atom, codepoint);
2250                         exec->transcount++;
2251                     } while (ret == 1);
2252                     if (exec->transcount < atom->min)
2253                         ret = 0;
2254
2255                     /*
2256                      * If the last check failed but one transition was found
2257                      * possible, rollback
2258                      */
2259                     if (ret < 0)
2260                         ret = 0;
2261                     if (ret == 0) {
2262                         goto rollback;
2263                     }
2264                 }
2265             }
2266             if (ret == 1) {
2267                 if (exec->state->nbTrans > exec->transno + 1) {
2268                     xmlFARegExecSave(exec);
2269                 }
2270                 if (trans->counter >= 0) {
2271 #ifdef DEBUG_REGEXP_EXEC
2272                     printf("Increasing count %d\n", trans->counter);
2273 #endif
2274                     exec->counts[trans->counter]++;
2275                 }
2276 #ifdef DEBUG_REGEXP_EXEC
2277                 printf("entering state %d\n", trans->to);
2278 #endif
2279                 exec->state = comp->states[trans->to];
2280                 exec->transno = 0;
2281                 if (trans->atom != NULL) {
2282                     exec->index += len;
2283                 }
2284                 goto progress;
2285             } else if (ret < 0) {
2286                 exec->status = -4;
2287                 break;
2288             }
2289         }
2290         if ((exec->transno != 0) || (exec->state->nbTrans == 0)) {
2291 rollback:
2292             /*
2293              * Failed to find a way out
2294              */
2295             exec->determinist = 0;
2296             xmlFARegExecRollBack(exec);
2297         }
2298 progress:
2299         continue;
2300     }
2301     if (exec->rollbacks != NULL) {
2302         if (exec->counts != NULL) {
2303             int i;
2304
2305             for (i = 0;i < exec->maxRollbacks;i++)
2306                 if (exec->rollbacks[i].counts != NULL)
2307                     xmlFree(exec->rollbacks[i].counts);
2308         }
2309         xmlFree(exec->rollbacks);
2310     }
2311     if (exec->counts != NULL)
2312         xmlFree(exec->counts);
2313     if (exec->status == 0)
2314         return(1);
2315     if (exec->status == -1)
2316         return(0);
2317     return(exec->status);
2318 }
2319
2320 /************************************************************************
2321  *                                                                      *
2322  *      Progressive interface to the verifyer one atom at a time        *
2323  *                                                                      *
2324  ************************************************************************/
2325
2326 /**
2327  * xmlRegNewExecCtxt:
2328  * @comp: a precompiled regular expression
2329  * @callback: a callback function used for handling progresses in the
2330  *            automata matching phase
2331  * @data: the context data associated to the callback in this context
2332  *
2333  * Build a context used for progressive evaluation of a regexp.
2334  *
2335  * Returns the new context
2336  */
2337 xmlRegExecCtxtPtr
2338 xmlRegNewExecCtxt(xmlRegexpPtr comp, xmlRegExecCallbacks callback, void *data) {
2339     xmlRegExecCtxtPtr exec;
2340
2341     if (comp == NULL)
2342         return(NULL);
2343     if ((comp->compact == NULL) && (comp->states == NULL))
2344         return(NULL);
2345     exec = (xmlRegExecCtxtPtr) xmlMalloc(sizeof(xmlRegExecCtxt));
2346     if (exec == NULL) {
2347         return(NULL);
2348     }
2349     memset(exec, 0, sizeof(xmlRegExecCtxt));
2350     exec->inputString = NULL;
2351     exec->index = 0;
2352     exec->determinist = 1;
2353     exec->maxRollbacks = 0;
2354     exec->nbRollbacks = 0;
2355     exec->rollbacks = NULL;
2356     exec->status = 0;
2357     exec->comp = comp;
2358     if (comp->compact == NULL)
2359         exec->state = comp->states[0];
2360     exec->transno = 0;
2361     exec->transcount = 0;
2362     exec->callback = callback;
2363     exec->data = data;
2364     if (comp->nbCounters > 0) {
2365         exec->counts = (int *) xmlMalloc(comp->nbCounters * sizeof(int));
2366         if (exec->counts == NULL) {
2367             xmlFree(exec);
2368             return(NULL);
2369         }
2370         memset(exec->counts, 0, comp->nbCounters * sizeof(int));
2371     } else
2372         exec->counts = NULL;
2373     exec->inputStackMax = 0;
2374     exec->inputStackNr = 0;
2375     exec->inputStack = NULL;
2376     return(exec);
2377 }
2378
2379 /**
2380  * xmlRegFreeExecCtxt:
2381  * @exec: a regular expression evaulation context
2382  *
2383  * Free the structures associated to a regular expression evaulation context.
2384  */
2385 void
2386 xmlRegFreeExecCtxt(xmlRegExecCtxtPtr exec) {
2387     if (exec == NULL)
2388         return;
2389
2390     if (exec->rollbacks != NULL) {
2391         if (exec->counts != NULL) {
2392             int i;
2393
2394             for (i = 0;i < exec->maxRollbacks;i++)
2395                 if (exec->rollbacks[i].counts != NULL)
2396                     xmlFree(exec->rollbacks[i].counts);
2397         }
2398         xmlFree(exec->rollbacks);
2399     }
2400     if (exec->counts != NULL)
2401         xmlFree(exec->counts);
2402     if (exec->inputStack != NULL) {
2403         int i;
2404
2405         for (i = 0;i < exec->inputStackNr;i++) {
2406             if (exec->inputStack[i].value != NULL)
2407                 xmlFree(exec->inputStack[i].value);
2408         }
2409         xmlFree(exec->inputStack);
2410     }
2411     xmlFree(exec);
2412 }
2413
2414 static void
2415 xmlFARegExecSaveInputString(xmlRegExecCtxtPtr exec, const xmlChar *value,
2416                             void *data) {
2417 #ifdef DEBUG_PUSH
2418     printf("saving value: %d:%s\n", exec->inputStackNr, value);
2419 #endif
2420     if (exec->inputStackMax == 0) {
2421         exec->inputStackMax = 4;
2422         exec->inputStack = (xmlRegInputTokenPtr) 
2423             xmlMalloc(exec->inputStackMax * sizeof(xmlRegInputToken));
2424         if (exec->inputStack == NULL) {
2425             fprintf(stderr, "push input: allocation failed");
2426             exec->inputStackMax = 0;
2427             return;
2428         }
2429     } else if (exec->inputStackNr + 1 >= exec->inputStackMax) {
2430         xmlRegInputTokenPtr tmp;
2431
2432         exec->inputStackMax *= 2;
2433         tmp = (xmlRegInputTokenPtr) xmlRealloc(exec->inputStack,
2434                         exec->inputStackMax * sizeof(xmlRegInputToken));
2435         if (tmp == NULL) {
2436             fprintf(stderr, "push input: allocation failed");
2437             exec->inputStackMax /= 2;
2438             return;
2439         }
2440         exec->inputStack = tmp;
2441     }
2442     exec->inputStack[exec->inputStackNr].value = xmlStrdup(value);
2443     exec->inputStack[exec->inputStackNr].data = data;
2444     exec->inputStackNr++;
2445     exec->inputStack[exec->inputStackNr].value = NULL;
2446     exec->inputStack[exec->inputStackNr].data = NULL;
2447 }
2448
2449
2450 /**
2451  * xmlRegCompactPushString:
2452  * @exec: a regexp execution context
2453  * @comp:  the precompiled exec with a compact table
2454  * @value: a string token input
2455  * @data: data associated to the token to reuse in callbacks
2456  *
2457  * Push one input token in the execution context
2458  *
2459  * Returns: 1 if the regexp reached a final state, 0 if non-final, and
2460  *     a negative value in case of error.
2461  */
2462 static int
2463 xmlRegCompactPushString(xmlRegExecCtxtPtr exec,
2464                         xmlRegexpPtr comp,
2465                         const xmlChar *value,
2466                         void *data) {
2467     int state = exec->index;
2468     int i, target;
2469
2470     if ((comp == NULL) || (comp->compact == NULL) || (comp->stringMap == NULL))
2471         return(-1);
2472     
2473     if (value == NULL) {
2474         /*
2475          * are we at a final state ?
2476          */
2477         if (comp->compact[state * (comp->nbstrings + 1)] ==
2478             XML_REGEXP_FINAL_STATE)
2479             return(1);
2480         return(0);
2481     }
2482
2483 #ifdef DEBUG_PUSH
2484     printf("value pushed: %s\n", value);
2485 #endif
2486
2487     /*
2488      * Examine all outside transition from current state
2489      */
2490     for (i = 0;i < comp->nbstrings;i++) {
2491         target = comp->compact[state * (comp->nbstrings + 1) + i + 1];
2492         if ((target > 0) && (target <= comp->nbstates)) {
2493             target--; /* to avoid 0 */
2494             if (xmlStrEqual(comp->stringMap[i], value)) {
2495                 exec->index = target;
2496                 if ((exec->callback != NULL) && (comp->transdata != NULL)) {
2497                     exec->callback(exec->data, value,
2498                           comp->transdata[state * comp->nbstrings + i], data);
2499                 }
2500 #ifdef DEBUG_PUSH
2501                 printf("entering state %d\n", target);
2502 #endif
2503                 if (comp->compact[target * (comp->nbstrings + 1)] ==
2504                     XML_REGEXP_FINAL_STATE)
2505                     return(1);
2506                 return(0);
2507             }
2508         }
2509     }
2510     /*
2511      * Failed to find an exit transition out from current state for the
2512      * current token
2513      */
2514 #ifdef DEBUG_PUSH
2515     printf("failed to find a transition for %s on state %d\n", value, state);
2516 #endif
2517     exec->status = -1;
2518     return(-1);
2519 }
2520
2521 /**
2522  * xmlRegExecPushString:
2523  * @exec: a regexp execution context or NULL to indicate the end
2524  * @value: a string token input
2525  * @data: data associated to the token to reuse in callbacks
2526  *
2527  * Push one input token in the execution context
2528  *
2529  * Returns: 1 if the regexp reached a final state, 0 if non-final, and
2530  *     a negative value in case of error.
2531  */
2532 int
2533 xmlRegExecPushString(xmlRegExecCtxtPtr exec, const xmlChar *value,
2534                      void *data) {
2535     xmlRegTransPtr trans;
2536     xmlRegAtomPtr atom;
2537     int ret;
2538     int final = 0;
2539
2540     if (exec == NULL)
2541         return(-1);
2542     if (exec->comp == NULL)
2543         return(-1);
2544     if (exec->status != 0)
2545         return(exec->status);
2546
2547     if (exec->comp->compact != NULL)
2548         return(xmlRegCompactPushString(exec, exec->comp, value, data));
2549
2550     if (value == NULL) {
2551         if (exec->state->type == XML_REGEXP_FINAL_STATE)
2552             return(1);
2553         final = 1;
2554     }
2555
2556 #ifdef DEBUG_PUSH
2557     printf("value pushed: %s\n", value);
2558 #endif
2559     /*
2560      * If we have an active rollback stack push the new value there
2561      * and get back to where we were left
2562      */
2563     if ((value != NULL) && (exec->inputStackNr > 0)) {
2564         xmlFARegExecSaveInputString(exec, value, data);
2565         value = exec->inputStack[exec->index].value;
2566         data = exec->inputStack[exec->index].data;
2567 #ifdef DEBUG_PUSH
2568         printf("value loaded: %s\n", value);
2569 #endif
2570     }
2571
2572     while ((exec->status == 0) &&
2573            ((value != NULL) ||
2574             ((final == 1) &&
2575              (exec->state->type != XML_REGEXP_FINAL_STATE)))) {
2576
2577         /*
2578          * End of input on non-terminal state, rollback, however we may
2579          * still have epsilon like transition for counted transitions
2580          * on counters, in that case don't break too early.
2581          */
2582         if ((value == NULL) && (exec->counts == NULL))
2583             goto rollback;
2584
2585         exec->transcount = 0;
2586         for (;exec->transno < exec->state->nbTrans;exec->transno++) {
2587             trans = &exec->state->trans[exec->transno];
2588             if (trans->to < 0)
2589                 continue;
2590             atom = trans->atom;
2591             ret = 0;
2592             if (trans->count == REGEXP_ALL_LAX_COUNTER) {
2593                 int i;
2594                 int count;
2595                 xmlRegTransPtr t;
2596                 xmlRegCounterPtr counter;
2597
2598                 ret = 0;
2599
2600 #ifdef DEBUG_PUSH
2601                 printf("testing all lax %d\n", trans->count);
2602 #endif
2603                 /*
2604                  * Check all counted transitions from the current state
2605                  */
2606                 if ((value == NULL) && (final)) {
2607                     ret = 1;
2608                 } else if (value != NULL) {
2609                     for (i = 0;i < exec->state->nbTrans;i++) {
2610                         t = &exec->state->trans[i];
2611                         if ((t->counter < 0) || (t == trans))
2612                             continue;
2613                         counter = &exec->comp->counters[t->counter];
2614                         count = exec->counts[t->counter];
2615                         if ((count < counter->max) && 
2616                             (t->atom != NULL) &&
2617                             (xmlStrEqual(value, t->atom->valuep))) {
2618                             ret = 0;
2619                             break;
2620                         }
2621                         if ((count >= counter->min) &&
2622                             (count < counter->max) &&
2623                             (xmlStrEqual(value, t->atom->valuep))) {
2624                             ret = 1;
2625                             break;
2626                         }
2627                     }
2628                 }
2629             } else if (trans->count == REGEXP_ALL_COUNTER) {
2630                 int i;
2631                 int count;
2632                 xmlRegTransPtr t;
2633                 xmlRegCounterPtr counter;
2634
2635                 ret = 1;
2636
2637 #ifdef DEBUG_PUSH
2638                 printf("testing all %d\n", trans->count);
2639 #endif
2640                 /*
2641                  * Check all counted transitions from the current state
2642                  */
2643                 for (i = 0;i < exec->state->nbTrans;i++) {
2644                     t = &exec->state->trans[i];
2645                     if ((t->counter < 0) || (t == trans))
2646                         continue;
2647                     counter = &exec->comp->counters[t->counter];
2648                     count = exec->counts[t->counter];
2649                     if ((count < counter->min) || (count > counter->max)) {
2650                         ret = 0;
2651                         break;
2652                     }
2653                 }
2654             } else if (trans->count >= 0) {
2655                 int count;
2656                 xmlRegCounterPtr counter;
2657
2658                 /*
2659                  * A counted transition.
2660                  */
2661
2662                 count = exec->counts[trans->count];
2663                 counter = &exec->comp->counters[trans->count];
2664 #ifdef DEBUG_PUSH
2665                 printf("testing count %d: val %d, min %d, max %d\n",
2666                        trans->count, count, counter->min,  counter->max);
2667 #endif
2668                 ret = ((count >= counter->min) && (count <= counter->max));
2669             } else if (atom == NULL) {
2670                 fprintf(stderr, "epsilon transition left at runtime\n");
2671                 exec->status = -2;
2672                 break;
2673             } else if (value != NULL) {
2674                 ret = xmlStrEqual(value, atom->valuep);
2675                 if ((ret == 1) && (trans->counter >= 0)) {
2676                     xmlRegCounterPtr counter;
2677                     int count;
2678
2679                     count = exec->counts[trans->counter];
2680                     counter = &exec->comp->counters[trans->counter];
2681                     if (count >= counter->max)
2682                         ret = 0;
2683                 }
2684
2685                 if ((ret == 1) && (atom->min > 0) && (atom->max > 0)) {
2686                     xmlRegStatePtr to = exec->comp->states[trans->to];
2687
2688                     /*
2689                      * this is a multiple input sequence
2690                      */
2691                     if (exec->state->nbTrans > exec->transno + 1) {
2692                         if (exec->inputStackNr <= 0) {
2693                             xmlFARegExecSaveInputString(exec, value, data);
2694                         }
2695                         xmlFARegExecSave(exec);
2696                     }
2697                     exec->transcount = 1;
2698                     do {
2699                         /*
2700                          * Try to progress as much as possible on the input
2701                          */
2702                         if (exec->transcount == atom->max) {
2703                             break;
2704                         }
2705                         exec->index++;
2706                         value = exec->inputStack[exec->index].value;
2707                         data = exec->inputStack[exec->index].data;
2708 #ifdef DEBUG_PUSH
2709                         printf("value loaded: %s\n", value);
2710 #endif
2711
2712                         /*
2713                          * End of input: stop here
2714                          */
2715                         if (value == NULL) {
2716                             exec->index --;
2717                             break;
2718                         }
2719                         if (exec->transcount >= atom->min) {
2720                             int transno = exec->transno;
2721                             xmlRegStatePtr state = exec->state;
2722
2723                             /*
2724                              * The transition is acceptable save it
2725                              */
2726                             exec->transno = -1; /* trick */
2727                             exec->state = to;
2728                             if (exec->inputStackNr <= 0) {
2729                                 xmlFARegExecSaveInputString(exec, value, data);
2730                             }
2731                             xmlFARegExecSave(exec);
2732                             exec->transno = transno;
2733                             exec->state = state;
2734                         }
2735                         ret = xmlStrEqual(value, atom->valuep);
2736                         exec->transcount++;
2737                     } while (ret == 1);
2738                     if (exec->transcount < atom->min)
2739                         ret = 0;
2740
2741                     /*
2742                      * If the last check failed but one transition was found
2743                      * possible, rollback
2744                      */
2745                     if (ret < 0)
2746                         ret = 0;
2747                     if (ret == 0) {
2748                         goto rollback;
2749                     }
2750                 }
2751             }
2752             if (ret == 1) {
2753                 if ((exec->callback != NULL) && (atom != NULL)) {
2754                     exec->callback(exec->data, atom->valuep,
2755                                    atom->data, data);
2756                 }
2757                 if (exec->state->nbTrans > exec->transno + 1) {
2758                     if (exec->inputStackNr <= 0) {
2759                         xmlFARegExecSaveInputString(exec, value, data);
2760                     }
2761                     xmlFARegExecSave(exec);
2762                 }
2763                 if (trans->counter >= 0) {
2764 #ifdef DEBUG_PUSH
2765                     printf("Increasing count %d\n", trans->counter);
2766 #endif
2767                     exec->counts[trans->counter]++;
2768                 }
2769 #ifdef DEBUG_PUSH
2770                 printf("entering state %d\n", trans->to);
2771 #endif
2772                 exec->state = exec->comp->states[trans->to];
2773                 exec->transno = 0;
2774                 if (trans->atom != NULL) {
2775                     if (exec->inputStack != NULL) {
2776                         exec->index++;
2777                         if (exec->index < exec->inputStackNr) {
2778                             value = exec->inputStack[exec->index].value;
2779                             data = exec->inputStack[exec->index].data;
2780 #ifdef DEBUG_PUSH
2781                             printf("value loaded: %s\n", value);
2782 #endif
2783                         } else {
2784                             value = NULL;
2785                             data = NULL;
2786 #ifdef DEBUG_PUSH
2787                             printf("end of input\n");
2788 #endif
2789                         }
2790                     } else {
2791                         value = NULL;
2792                         data = NULL;
2793 #ifdef DEBUG_PUSH
2794                         printf("end of input\n");
2795 #endif
2796                     }
2797                 }
2798                 goto progress;
2799             } else if (ret < 0) {
2800                 exec->status = -4;
2801                 break;
2802             }
2803         }
2804         if ((exec->transno != 0) || (exec->state->nbTrans == 0)) {
2805 rollback:
2806             /*
2807              * Failed to find a way out
2808              */
2809             exec->determinist = 0;
2810             xmlFARegExecRollBack(exec);
2811             if (exec->status == 0) {
2812                 value = exec->inputStack[exec->index].value;
2813                 data = exec->inputStack[exec->index].data;
2814 #ifdef DEBUG_PUSH
2815                 printf("value loaded: %s\n", value);
2816 #endif
2817             }
2818         }
2819 progress:
2820         continue;
2821     }
2822     if (exec->status == 0) {
2823         return(exec->state->type == XML_REGEXP_FINAL_STATE);
2824     }
2825     return(exec->status);
2826 }
2827
2828 /**
2829  * xmlRegExecPushString2:
2830  * @exec: a regexp execution context or NULL to indicate the end
2831  * @value: the first string token input
2832  * @value2: the second string token input
2833  * @data: data associated to the token to reuse in callbacks
2834  *
2835  * Push one input token in the execution context
2836  *
2837  * Returns: 1 if the regexp reached a final state, 0 if non-final, and
2838  *     a negative value in case of error.
2839  */
2840 int
2841 xmlRegExecPushString2(xmlRegExecCtxtPtr exec, const xmlChar *value,
2842                       const xmlChar *value2, void *data) {
2843     xmlChar buf[150];
2844     int lenn, lenp, ret;
2845     xmlChar *str;
2846
2847     if (exec == NULL)
2848         return(-1);
2849     if (exec->comp == NULL)
2850         return(-1);
2851     if (exec->status != 0)
2852         return(exec->status);
2853
2854     if (value2 == NULL)
2855         return(xmlRegExecPushString(exec, value, data));
2856
2857     lenn = strlen((char *) value2);
2858     lenp = strlen((char *) value);
2859
2860     if (150 < lenn + lenp + 2) {
2861         str = (xmlChar *) xmlMallocAtomic(lenn + lenp + 2);
2862         if (str == NULL) {
2863             exec->status = -1;
2864             return(-1);
2865         }
2866     } else {
2867         str = buf;
2868     }
2869     memcpy(&str[0], value, lenp);
2870     str[lenp] = '|';
2871     memcpy(&str[lenp + 1], value2, lenn);
2872     str[lenn + lenp + 1] = 0;
2873
2874     if (exec->comp->compact != NULL)
2875         ret = xmlRegCompactPushString(exec, exec->comp, str, data);
2876     else
2877         ret = xmlRegExecPushString(exec, str, data);
2878
2879     if (str != buf)
2880         xmlFree(buf);
2881     return(ret);
2882 }
2883
2884 #if 0
2885 static int
2886 xmlRegExecPushChar(xmlRegExecCtxtPtr exec, int UCS) {
2887     xmlRegTransPtr trans;
2888     xmlRegAtomPtr atom;
2889     int ret;
2890     int codepoint, len;
2891
2892     if (exec == NULL)
2893         return(-1);
2894     if (exec->status != 0)
2895         return(exec->status);
2896
2897     while ((exec->status == 0) &&
2898            ((exec->inputString[exec->index] != 0) ||
2899             (exec->state->type != XML_REGEXP_FINAL_STATE))) {
2900
2901         /*
2902          * End of input on non-terminal state, rollback, however we may
2903          * still have epsilon like transition for counted transitions
2904          * on counters, in that case don't break too early.
2905          */
2906         if ((exec->inputString[exec->index] == 0) && (exec->counts == NULL))
2907             goto rollback;
2908
2909         exec->transcount = 0;
2910         for (;exec->transno < exec->state->nbTrans;exec->transno++) {
2911             trans = &exec->state->trans[exec->transno];
2912             if (trans->to < 0)
2913                 continue;
2914             atom = trans->atom;
2915             ret = 0;
2916             if (trans->count >= 0) {
2917                 int count;
2918                 xmlRegCounterPtr counter;
2919
2920                 /*
2921                  * A counted transition.
2922                  */
2923
2924                 count = exec->counts[trans->count];
2925                 counter = &exec->comp->counters[trans->count];
2926 #ifdef DEBUG_REGEXP_EXEC
2927                 printf("testing count %d: val %d, min %d, max %d\n",
2928                        trans->count, count, counter->min,  counter->max);
2929 #endif
2930                 ret = ((count >= counter->min) && (count <= counter->max));
2931             } else if (atom == NULL) {
2932                 fprintf(stderr, "epsilon transition left at runtime\n");
2933                 exec->status = -2;
2934                 break;
2935             } else if (exec->inputString[exec->index] != 0) {
2936                 codepoint = CUR_SCHAR(&(exec->inputString[exec->index]), len);
2937                 ret = xmlRegCheckCharacter(atom, codepoint);
2938                 if ((ret == 1) && (atom->min > 0) && (atom->max > 0)) {
2939                     xmlRegStatePtr to = exec->comp->states[trans->to];
2940
2941                     /*
2942                      * this is a multiple input sequence
2943                      */
2944                     if (exec->state->nbTrans > exec->transno + 1) {
2945                         xmlFARegExecSave(exec);
2946                     }
2947                     exec->transcount = 1;
2948                     do {
2949                         /*
2950                          * Try to progress as much as possible on the input
2951                          */
2952                         if (exec->transcount == atom->max) {
2953                             break;
2954                         }
2955                         exec->index += len;
2956                         /*
2957                          * End of input: stop here
2958                          */
2959                         if (exec->inputString[exec->index] == 0) {
2960                             exec->index -= len;
2961                             break;
2962                         }
2963                         if (exec->transcount >= atom->min) {
2964                             int transno = exec->transno;
2965                             xmlRegStatePtr state = exec->state;
2966
2967                             /*
2968                              * The transition is acceptable save it
2969                              */
2970                             exec->transno = -1; /* trick */
2971                             exec->state = to;
2972                             xmlFARegExecSave(exec);
2973                             exec->transno = transno;
2974                             exec->state = state;
2975                         }
2976                         codepoint = CUR_SCHAR(&(exec->inputString[exec->index]),
2977                                               len);
2978                         ret = xmlRegCheckCharacter(atom, codepoint);
2979                         exec->transcount++;
2980                     } while (ret == 1);
2981                     if (exec->transcount < atom->min)
2982                         ret = 0;
2983
2984                     /*
2985                      * If the last check failed but one transition was found
2986                      * possible, rollback
2987                      */
2988                     if (ret < 0)
2989                         ret = 0;
2990                     if (ret == 0) {
2991                         goto rollback;
2992                     }
2993                 }
2994             }
2995             if (ret == 1) {
2996                 if (exec->state->nbTrans > exec->transno + 1) {
2997                     xmlFARegExecSave(exec);
2998                 }
2999                 if (trans->counter >= 0) {
3000 #ifdef DEBUG_REGEXP_EXEC
3001                     printf("Increasing count %d\n", trans->counter);
3002 #endif
3003                     exec->counts[trans->counter]++;
3004                 }
3005 #ifdef DEBUG_REGEXP_EXEC
3006                 printf("entering state %d\n", trans->to);
3007 #endif
3008                 exec->state = exec->comp->states[trans->to];
3009                 exec->transno = 0;
3010                 if (trans->atom != NULL) {
3011                     exec->index += len;
3012                 }
3013                 goto progress;
3014             } else if (ret < 0) {
3015                 exec->status = -4;
3016                 break;
3017             }
3018         }
3019         if ((exec->transno != 0) || (exec->state->nbTrans == 0)) {
3020 rollback:
3021             /*
3022              * Failed to find a way out
3023              */
3024             exec->determinist = 0;
3025             xmlFARegExecRollBack(exec);
3026         }
3027 progress:
3028         continue;
3029     }
3030 }
3031 #endif
3032 /************************************************************************
3033  *                                                                      *
3034  *      Parser for the Shemas Datatype Regular Expressions              *
3035  *      http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#regexs      *
3036  *                                                                      *
3037  ************************************************************************/
3038
3039 /**
3040  * xmlFAIsChar:
3041  * @ctxt:  a regexp parser context
3042  *
3043  * [10]   Char   ::=   [^.\?*+()|#x5B#x5D]
3044  */
3045 static int
3046 xmlFAIsChar(xmlRegParserCtxtPtr ctxt) {
3047     int cur;
3048     int len;
3049
3050     cur = CUR_SCHAR(ctxt->cur, len);
3051     if ((cur == '.') || (cur == '\\') || (cur == '?') ||
3052         (cur == '*') || (cur == '+') || (cur == '(') ||
3053         (cur == ')') || (cur == '|') || (cur == 0x5B) ||
3054         (cur == 0x5D) || (cur == 0))
3055         return(-1);
3056     return(cur);
3057 }
3058
3059 /**
3060  * xmlFAParseCharProp:
3061  * @ctxt:  a regexp parser context
3062  *
3063  * [27]   charProp   ::=   IsCategory | IsBlock
3064  * [28]   IsCategory ::= Letters | Marks | Numbers | Punctuation |
3065  *                       Separators | Symbols | Others 
3066  * [29]   Letters   ::=   'L' [ultmo]?
3067  * [30]   Marks   ::=   'M' [nce]?
3068  * [31]   Numbers   ::=   'N' [dlo]?
3069  * [32]   Punctuation   ::=   'P' [cdseifo]?
3070  * [33]   Separators   ::=   'Z' [slp]?
3071  * [34]   Symbols   ::=   'S' [mcko]?
3072  * [35]   Others   ::=   'C' [cfon]?
3073  * [36]   IsBlock   ::=   'Is' [a-zA-Z0-9#x2D]+
3074  */
3075 static void
3076 xmlFAParseCharProp(xmlRegParserCtxtPtr ctxt) {
3077     int cur;
3078     xmlRegAtomType type = (xmlRegAtomType) 0;
3079     xmlChar *blockName = NULL;
3080     
3081     cur = CUR;
3082     if (cur == 'L') {
3083         NEXT;
3084         cur = CUR;
3085         if (cur == 'u') {
3086             NEXT;
3087             type = XML_REGEXP_LETTER_UPPERCASE;
3088         } else if (cur == 'l') {
3089             NEXT;
3090             type = XML_REGEXP_LETTER_LOWERCASE;
3091         } else if (cur == 't') {
3092             NEXT;
3093             type = XML_REGEXP_LETTER_TITLECASE;
3094         } else if (cur == 'm') {
3095             NEXT;
3096             type = XML_REGEXP_LETTER_MODIFIER;
3097         } else if (cur == 'o') {
3098             NEXT;
3099             type = XML_REGEXP_LETTER_OTHERS;
3100         } else {
3101             type = XML_REGEXP_LETTER;
3102         }
3103     } else if (cur == 'M') {
3104         NEXT;
3105         cur = CUR;
3106         if (cur == 'n') {
3107             NEXT;
3108             /* nonspacing */
3109             type = XML_REGEXP_MARK_NONSPACING;
3110         } else if (cur == 'c') {
3111             NEXT;
3112             /* spacing combining */
3113             type = XML_REGEXP_MARK_SPACECOMBINING;
3114         } else if (cur == 'e') {
3115             NEXT;
3116             /* enclosing */
3117             type = XML_REGEXP_MARK_ENCLOSING;
3118         } else {
3119             /* all marks */
3120             type = XML_REGEXP_MARK;
3121         }
3122     } else if (cur == 'N') {
3123         NEXT;
3124         cur = CUR;
3125         if (cur == 'd') {
3126             NEXT;
3127             /* digital */
3128             type = XML_REGEXP_NUMBER_DECIMAL;
3129         } else if (cur == 'l') {
3130             NEXT;
3131             /* letter */
3132             type = XML_REGEXP_NUMBER_LETTER;
3133         } else if (cur == 'o') {
3134             NEXT;
3135             /* other */
3136             type = XML_REGEXP_NUMBER_OTHERS;
3137         } else {
3138             /* all numbers */
3139             type = XML_REGEXP_NUMBER;
3140         }
3141     } else if (cur == 'P') {
3142         NEXT;
3143         cur = CUR;
3144         if (cur == 'c') {
3145             NEXT;
3146             /* connector */
3147             type = XML_REGEXP_PUNCT_CONNECTOR;
3148         } else if (cur == 'd') {
3149             NEXT;
3150             /* dash */
3151             type = XML_REGEXP_PUNCT_DASH;
3152         } else if (cur == 's') {
3153             NEXT;
3154             /* open */
3155             type = XML_REGEXP_PUNCT_OPEN;
3156         } else if (cur == 'e') {
3157             NEXT;
3158             /* close */
3159             type = XML_REGEXP_PUNCT_CLOSE;
3160         } else if (cur == 'i') {
3161             NEXT;
3162             /* initial quote */
3163             type = XML_REGEXP_PUNCT_INITQUOTE;
3164         } else if (cur == 'f') {
3165             NEXT;
3166             /* final quote */
3167             type = XML_REGEXP_PUNCT_FINQUOTE;
3168         } else if (cur == 'o') {
3169             NEXT;
3170             /* other */
3171             type = XML_REGEXP_PUNCT_OTHERS;
3172         } else {
3173             /* all punctuation */
3174             type = XML_REGEXP_PUNCT;
3175         }
3176     } else if (cur == 'Z') {
3177         NEXT;
3178         cur = CUR;
3179         if (cur == 's') {
3180             NEXT;
3181             /* space */
3182             type = XML_REGEXP_SEPAR_SPACE;
3183         } else if (cur == 'l') {
3184             NEXT;
3185             /* line */
3186             type = XML_REGEXP_SEPAR_LINE;
3187         } else if (cur == 'p') {
3188             NEXT;
3189             /* paragraph */
3190             type = XML_REGEXP_SEPAR_PARA;
3191         } else {
3192             /* all separators */
3193             type = XML_REGEXP_SEPAR;
3194         }
3195     } else if (cur == 'S') {
3196         NEXT;
3197         cur = CUR;
3198         if (cur == 'm') {
3199             NEXT;
3200             type = XML_REGEXP_SYMBOL_MATH;
3201             /* math */
3202         } else if (cur == 'c') {
3203             NEXT;
3204             type = XML_REGEXP_SYMBOL_CURRENCY;
3205             /* currency */
3206         } else if (cur == 'k') {
3207             NEXT;
3208             type = XML_REGEXP_SYMBOL_MODIFIER;
3209             /* modifiers */
3210         } else if (cur == 'o') {
3211             NEXT;
3212             type = XML_REGEXP_SYMBOL_OTHERS;
3213             /* other */
3214         } else {
3215             /* all symbols */
3216             type = XML_REGEXP_SYMBOL;
3217         }
3218     } else if (cur == 'C') {
3219         NEXT;
3220         cur = CUR;
3221         if (cur == 'c') {
3222             NEXT;
3223             /* control */
3224             type = XML_REGEXP_OTHER_CONTROL;
3225         } else if (cur == 'f') {
3226             NEXT;
3227             /* format */
3228             type = XML_REGEXP_OTHER_FORMAT;
3229         } else if (cur == 'o') {
3230             NEXT;
3231             /* private use */
3232             type = XML_REGEXP_OTHER_PRIVATE;
3233         } else if (cur == 'n') {
3234             NEXT;
3235             /* not assigned */
3236             type = XML_REGEXP_OTHER_NA;
3237         } else {
3238             /* all others */
3239             type = XML_REGEXP_OTHER;
3240         }
3241     } else if (cur == 'I') {
3242         const xmlChar *start;
3243         NEXT;
3244         cur = CUR;
3245         if (cur != 's') {
3246             ERROR("IsXXXX expected");
3247             return;
3248         }
3249         NEXT;
3250         start = ctxt->cur;
3251         cur = CUR;
3252         if (((cur >= 'a') && (cur <= 'z')) || 
3253             ((cur >= 'A') && (cur <= 'Z')) || 
3254             ((cur >= '0') && (cur <= '9')) || 
3255             (cur == 0x2D)) {
3256             NEXT;
3257             cur = CUR;
3258             while (((cur >= 'a') && (cur <= 'z')) || 
3259                 ((cur >= 'A') && (cur <= 'Z')) || 
3260                 ((cur >= '0') && (cur <= '9')) || 
3261                 (cur == 0x2D)) {
3262                 NEXT;
3263                 cur = CUR;
3264             }
3265         }
3266         type = XML_REGEXP_BLOCK_NAME;
3267         blockName = xmlStrndup(start, ctxt->cur - start);
3268     } else {
3269         ERROR("Unknown char property");
3270         return;
3271     }
3272     if (ctxt->atom == NULL) {
3273         ctxt->atom = xmlRegNewAtom(ctxt, type);
3274         if (ctxt->atom != NULL)
3275             ctxt->atom->valuep = blockName;
3276     } else if (ctxt->atom->type == XML_REGEXP_RANGES) {
3277         xmlRegAtomAddRange(ctxt, ctxt->atom, ctxt->neg,
3278                            type, 0, 0, blockName);
3279     }
3280 }
3281
3282 /**
3283  * xmlFAParseCharClassEsc:
3284  * @ctxt:  a regexp parser context
3285  *
3286  * [23] charClassEsc ::= ( SingleCharEsc | MultiCharEsc | catEsc | complEsc ) 
3287  * [24] SingleCharEsc ::= '\' [nrt\|.?*+(){}#x2D#x5B#x5D#x5E]
3288  * [25] catEsc   ::=   '\p{' charProp '}'
3289  * [26] complEsc ::=   '\P{' charProp '}'
3290  * [37] MultiCharEsc ::= '.' | ('\' [sSiIcCdDwW])
3291  */
3292 static void
3293 xmlFAParseCharClassEsc(xmlRegParserCtxtPtr ctxt) {
3294     int cur;
3295
3296     if (CUR == '.') {
3297         if (ctxt->atom == NULL) {
3298             ctxt->atom = xmlRegNewAtom(ctxt, XML_REGEXP_ANYCHAR);
3299         } else if (ctxt->atom->type == XML_REGEXP_RANGES) {
3300             xmlRegAtomAddRange(ctxt, ctxt->atom, ctxt->neg,
3301                                XML_REGEXP_ANYCHAR, 0, 0, NULL);
3302         }
3303         NEXT;
3304         return;
3305     }
3306     if (CUR != '\\') {
3307         ERROR("Escaped sequence: expecting \\");
3308         return;
3309     }
3310     NEXT;
3311     cur = CUR;
3312     if (cur == 'p') {
3313         NEXT;
3314         if (CUR != '{') {
3315             ERROR("Expecting '{'");
3316             return;
3317         }
3318         NEXT;
3319         xmlFAParseCharProp(ctxt);
3320         if (CUR != '}') {
3321             ERROR("Expecting '}'");
3322             return;
3323         }
3324         NEXT;
3325     } else if (cur == 'P') {
3326         NEXT;
3327         if (CUR != '{') {
3328             ERROR("Expecting '{'");
3329             return;
3330         }
3331         NEXT;
3332         xmlFAParseCharProp(ctxt);
3333         ctxt->atom->neg = 1;
3334         if (CUR != '}') {
3335             ERROR("Expecting '}'");
3336             return;
3337         }
3338         NEXT;
3339     } else if ((cur == 'n') || (cur == 'r') || (cur == 't') || (cur == '\\') ||
3340         (cur == '|') || (cur == '.') || (cur == '?') || (cur == '*') ||
3341         (cur == '+') || (cur == '(') || (cur == ')') || (cur == '{') ||
3342         (cur == '}') || (cur == 0x2D) || (cur == 0x5B) || (cur == 0x5D) ||
3343         (cur == 0x5E)) {
3344         if (ctxt->atom == NULL) {
3345             ctxt->atom = xmlRegNewAtom(ctxt, XML_REGEXP_CHARVAL);
3346             if (ctxt->atom != NULL)
3347                 ctxt->atom->codepoint = cur;
3348         } else if (ctxt->atom->type == XML_REGEXP_RANGES) {
3349             xmlRegAtomAddRange(ctxt, ctxt->atom, ctxt->neg,
3350                                XML_REGEXP_CHARVAL, cur, cur, NULL);
3351         }
3352         NEXT;
3353     } else if ((cur == 's') || (cur == 'S') || (cur == 'i') || (cur == 'I') ||
3354         (cur == 'c') || (cur == 'C') || (cur == 'd') || (cur == 'D') ||
3355         (cur == 'w') || (cur == 'W')) {
3356         xmlRegAtomType type = XML_REGEXP_ANYSPACE;
3357
3358         switch (cur) {
3359             case 's': 
3360                 type = XML_REGEXP_ANYSPACE;
3361                 break;
3362             case 'S': 
3363                 type = XML_REGEXP_NOTSPACE;
3364                 break;
3365             case 'i': 
3366                 type = XML_REGEXP_INITNAME;
3367                 break;
3368             case 'I': 
3369                 type = XML_REGEXP_NOTINITNAME;
3370                 break;
3371             case 'c': 
3372                 type = XML_REGEXP_NAMECHAR;
3373                 break;
3374             case 'C': 
3375                 type = XML_REGEXP_NOTNAMECHAR;
3376                 break;
3377             case 'd': 
3378                 type = XML_REGEXP_DECIMAL;
3379                 break;
3380             case 'D': 
3381                 type = XML_REGEXP_NOTDECIMAL;
3382                 break;
3383             case 'w': 
3384                 type = XML_REGEXP_REALCHAR;
3385                 break;
3386             case 'W': 
3387                 type = XML_REGEXP_NOTREALCHAR;
3388                 break;
3389         }
3390         NEXT;
3391         if (ctxt->atom == NULL) {
3392             ctxt->atom = xmlRegNewAtom(ctxt, type);
3393         } else if (ctxt->atom->type == XML_REGEXP_RANGES) {
3394             xmlRegAtomAddRange(ctxt, ctxt->atom, ctxt->neg,
3395                                type, 0, 0, NULL);
3396         }
3397     }
3398 }
3399
3400 /**
3401  * xmlFAParseCharRef:
3402  * @ctxt:  a regexp parser context
3403  *
3404  * [19]   XmlCharRef   ::=   ( '&#' [0-9]+ ';' ) | (' &#x' [0-9a-fA-F]+ ';' )
3405  */
3406 static int
3407 xmlFAParseCharRef(xmlRegParserCtxtPtr ctxt) {
3408     int ret = 0, cur;
3409
3410     if ((CUR != '&') || (NXT(1) != '#'))
3411         return(-1);
3412     NEXT;
3413     NEXT;
3414     cur = CUR;
3415     if (cur == 'x') {
3416         NEXT;
3417         cur = CUR;
3418         if (((cur >= '0') && (cur <= '9')) ||
3419             ((cur >= 'a') && (cur <= 'f')) ||
3420             ((cur >= 'A') && (cur <= 'F'))) {
3421             while (((cur >= '0') && (cur <= '9')) ||
3422                    ((cur >= 'A') && (cur <= 'F'))) {
3423                 if ((cur >= '0') && (cur <= '9'))
3424                     ret = ret * 16 + cur - '0';
3425                 else if ((cur >= 'a') && (cur <= 'f'))
3426                     ret = ret * 16 + 10 + (cur - 'a');
3427                 else
3428                     ret = ret * 16 + 10 + (cur - 'A');
3429                 NEXT;
3430                 cur = CUR;
3431             }
3432         } else {
3433             ERROR("Char ref: expecting [0-9A-F]");
3434             return(-1);
3435         }
3436     } else {
3437         if ((cur >= '0') && (cur <= '9')) {
3438             while ((cur >= '0') && (cur <= '9')) {
3439                 ret = ret * 10 + cur - '0';
3440                 NEXT;
3441                 cur = CUR;
3442             }
3443         } else {
3444             ERROR("Char ref: expecting [0-9]");
3445             return(-1);
3446         }
3447     }
3448     if (cur != ';') {
3449         ERROR("Char ref: expecting ';'");
3450         return(-1);
3451     } else {
3452         NEXT;
3453     }
3454     return(ret);
3455 }
3456
3457 /**
3458  * xmlFAParseCharRange:
3459  * @ctxt:  a regexp parser context
3460  *
3461  * [17]   charRange   ::=     seRange | XmlCharRef | XmlCharIncDash 
3462  * [18]   seRange   ::=   charOrEsc '-' charOrEsc
3463  * [20]   charOrEsc   ::=   XmlChar | SingleCharEsc
3464  * [21]   XmlChar   ::=   [^\#x2D#x5B#x5D]
3465  * [22]   XmlCharIncDash   ::=   [^\#x5B#x5D]
3466  */
3467 static void
3468 xmlFAParseCharRange(xmlRegParserCtxtPtr ctxt) {
3469     int cur;
3470     int start = -1;
3471     int end = -1;
3472
3473     if ((CUR == '&') && (NXT(1) == '#')) {
3474         end = start = xmlFAParseCharRef(ctxt);
3475         xmlRegAtomAddRange(ctxt, ctxt->atom, ctxt->neg,
3476                            XML_REGEXP_CHARVAL, start, end, NULL);
3477         return;
3478     }
3479     cur = CUR;
3480     if (cur == '\\') {
3481         NEXT;
3482         cur = CUR;
3483         switch (cur) {
3484             case 'n': start = 0xA; break;
3485             case 'r': start = 0xD; break;
3486             case 't': start = 0x9; break;
3487             case '\\': case '|': case '.': case '-': case '^': case '?':
3488             case '*': case '+': case '{': case '}': case '(': case ')':
3489             case '[': case ']':
3490                 start = cur; break;
3491             default:
3492                 ERROR("Invalid escape value");
3493                 return;
3494         }
3495         end = start;
3496     } else if ((cur != 0x5B) && (cur != 0x5D)) {
3497         end = start = cur;
3498     } else {
3499         ERROR("Expecting a char range");
3500         return;
3501     }
3502     NEXT;
3503     if (start == '-') {
3504         return;
3505     }
3506     cur = CUR;
3507     if (cur != '-') {
3508         xmlRegAtomAddRange(ctxt, ctxt->atom, ctxt->neg,
3509                               XML_REGEXP_CHARVAL, start, end, NULL);
3510         return;
3511     }
3512     NEXT;
3513     cur = CUR;
3514     if (cur == '\\') {
3515         NEXT;
3516         cur = CUR;
3517         switch (cur) {
3518             case 'n': end = 0xA; break;
3519             case 'r': end = 0xD; break;
3520             case 't': end = 0x9; break;
3521             case '\\': case '|': case '.': case '-': case '^': case '?':
3522             case '*': case '+': case '{': case '}': case '(': case ')':
3523             case '[': case ']':
3524                 end = cur; break;
3525             default:
3526                 ERROR("Invalid escape value");
3527                 return;
3528         }
3529     } else if ((cur != 0x5B) && (cur != 0x5D)) {
3530         end = cur;
3531     } else {
3532         ERROR("Expecting the end of a char range");
3533         return;
3534     }
3535     NEXT;
3536     /* TODO check that the values are acceptable character ranges for XML */
3537     if (end < start) {
3538         ERROR("End of range is before start of range");
3539     } else {
3540         xmlRegAtomAddRange(ctxt, ctxt->atom, ctxt->neg,
3541                            XML_REGEXP_CHARVAL, start, end, NULL);
3542     }
3543     return;
3544 }
3545
3546 /**
3547  * xmlFAParsePosCharGroup:
3548  * @ctxt:  a regexp parser context
3549  *
3550  * [14]   posCharGroup ::= ( charRange | charClassEsc  )+
3551  */
3552 static void
3553 xmlFAParsePosCharGroup(xmlRegParserCtxtPtr ctxt) {
3554     do {
3555         if ((CUR == '\\') || (CUR == '.')) {
3556             xmlFAParseCharClassEsc(ctxt);
3557         } else {
3558             xmlFAParseCharRange(ctxt);
3559         }
3560     } while ((CUR != ']') && (CUR != '^') && (CUR != '-') &&
3561              (ctxt->error == 0));
3562 }
3563
3564 /**
3565  * xmlFAParseCharGroup:
3566  * @ctxt:  a regexp parser context
3567  *
3568  * [13]   charGroup    ::= posCharGroup | negCharGroup | charClassSub
3569  * [15]   negCharGroup ::= '^' posCharGroup
3570  * [16]   charClassSub ::= ( posCharGroup | negCharGroup ) '-' charClassExpr  
3571  * [12]   charClassExpr ::= '[' charGroup ']'
3572  */
3573 static void
3574 xmlFAParseCharGroup(xmlRegParserCtxtPtr ctxt) {
3575     int n = ctxt->neg;
3576     while ((CUR != ']') && (ctxt->error == 0)) {
3577         if (CUR == '^') {
3578             int neg = ctxt->neg;
3579
3580             NEXT;
3581             ctxt->neg = !ctxt->neg;
3582             xmlFAParsePosCharGroup(ctxt);
3583             ctxt->neg = neg;
3584         } else if (CUR == '-') {
3585             NEXT;
3586             ctxt->neg = !ctxt->neg;
3587             if (CUR != '[') {
3588                 ERROR("charClassExpr: '[' expected");
3589                 break;
3590             }
3591             NEXT;
3592             xmlFAParseCharGroup(ctxt);
3593             if (CUR == ']') {
3594                 NEXT;
3595             } else {
3596                 ERROR("charClassExpr: ']' expected");
3597                 break;
3598             }
3599             break;
3600         } else if (CUR != ']') {
3601             xmlFAParsePosCharGroup(ctxt);
3602         }
3603     }
3604     ctxt->neg = n;
3605 }
3606
3607 /**
3608  * xmlFAParseCharClass:
3609  * @ctxt:  a regexp parser context
3610  *
3611  * [11]   charClass   ::=     charClassEsc | charClassExpr
3612  * [12]   charClassExpr   ::=   '[' charGroup ']'
3613  */
3614 static void
3615 xmlFAParseCharClass(xmlRegParserCtxtPtr ctxt) {
3616     if (CUR == '[') {
3617         NEXT;
3618         ctxt->atom = xmlRegNewAtom(ctxt, XML_REGEXP_RANGES);
3619         if (ctxt->atom == NULL)
3620             return;
3621         xmlFAParseCharGroup(ctxt);
3622         if (CUR == ']') {
3623             NEXT;
3624         } else {
3625             ERROR("xmlFAParseCharClass: ']' expected");
3626         }
3627     } else {
3628         xmlFAParseCharClassEsc(ctxt);
3629     }
3630 }
3631
3632 /**
3633  * xmlFAParseQuantExact:
3634  * @ctxt:  a regexp parser context
3635  *
3636  * [8]   QuantExact   ::=   [0-9]+
3637  *
3638  * Returns 0 if success or -1 in case of error
3639  */
3640 static int
3641 xmlFAParseQuantExact(xmlRegParserCtxtPtr ctxt) {
3642     int ret = 0;
3643     int ok = 0;
3644
3645     while ((CUR >= '0') && (CUR <= '9')) {
3646         ret = ret * 10 + (CUR - '0');
3647         ok = 1;
3648         NEXT;
3649     }
3650     if (ok != 1) {
3651         return(-1);
3652     }
3653     return(ret);
3654 }
3655
3656 /**
3657  * xmlFAParseQuantifier:
3658  * @ctxt:  a regexp parser context
3659  *
3660  * [4]   quantifier   ::=   [?*+] | ( '{' quantity '}' )
3661  * [5]   quantity   ::=   quantRange | quantMin | QuantExact
3662  * [6]   quantRange   ::=   QuantExact ',' QuantExact
3663  * [7]   quantMin   ::=   QuantExact ','
3664  * [8]   QuantExact   ::=   [0-9]+
3665  */
3666 static int
3667 xmlFAParseQuantifier(xmlRegParserCtxtPtr ctxt) {
3668     int cur;
3669
3670     cur = CUR;
3671     if ((cur == '?') || (cur == '*') || (cur == '+')) {
3672         if (ctxt->atom != NULL) {
3673             if (cur == '?')
3674                 ctxt->atom->quant = XML_REGEXP_QUANT_OPT;
3675             else if (cur == '*')
3676                 ctxt->atom->quant = XML_REGEXP_QUANT_MULT;
3677             else if (cur == '+')
3678                 ctxt->atom->quant = XML_REGEXP_QUANT_PLUS;
3679         }
3680         NEXT;
3681         return(1);
3682     }
3683     if (cur == '{') {
3684         int min = 0, max = 0;
3685
3686         NEXT;
3687         cur = xmlFAParseQuantExact(ctxt);
3688         if (cur >= 0)
3689             min = cur;
3690         if (CUR == ',') {
3691             NEXT;
3692             cur = xmlFAParseQuantExact(ctxt);
3693             if (cur >= 0)
3694                 max = cur;
3695         }
3696         if (CUR == '}') {
3697             NEXT;
3698         } else {
3699             ERROR("Unterminated quantifier");
3700         }
3701         if (max == 0)
3702             max = min;
3703         if (ctxt->atom != NULL) {
3704             ctxt->atom->quant = XML_REGEXP_QUANT_RANGE;
3705             ctxt->atom->min = min;
3706             ctxt->atom->max = max;
3707         }
3708         return(1);
3709     }
3710     return(0);
3711 }
3712
3713 /**
3714  * xmlFAParseAtom:
3715  * @ctxt:  a regexp parser context
3716  *
3717  * [9]   atom   ::=   Char | charClass | ( '(' regExp ')' )
3718  */
3719 static int
3720 xmlFAParseAtom(xmlRegParserCtxtPtr ctxt) {
3721     int codepoint, len;
3722
3723     codepoint = xmlFAIsChar(ctxt);
3724     if (codepoint > 0) {
3725         ctxt->atom = xmlRegNewAtom(ctxt, XML_REGEXP_CHARVAL);
3726         if (ctxt->atom == NULL)
3727             return(-1);
3728         codepoint = CUR_SCHAR(ctxt->cur, len);
3729         ctxt->atom->codepoint = codepoint;
3730         NEXTL(len);
3731         return(1);
3732     } else if (CUR == '|') {
3733         return(0);
3734     } else if (CUR == 0) {
3735         return(0);
3736     } else if (CUR == ')') {
3737         return(0);
3738     } else if (CUR == '(') {
3739         xmlRegStatePtr start, oldend;
3740
3741         NEXT;
3742         xmlFAGenerateEpsilonTransition(ctxt, ctxt->state, NULL);
3743         start = ctxt->state;
3744         oldend = ctxt->end;
3745         ctxt->end = NULL;
3746         ctxt->atom = NULL;
3747         xmlFAParseRegExp(ctxt, 0);
3748         if (CUR == ')') {
3749             NEXT;
3750         } else {
3751             ERROR("xmlFAParseAtom: expecting ')'");
3752         }
3753         ctxt->atom = xmlRegNewAtom(ctxt, XML_REGEXP_SUBREG);
3754         if (ctxt->atom == NULL)
3755             return(-1);
3756         ctxt->atom->start = start;
3757         ctxt->atom->stop = ctxt->state;
3758         ctxt->end = oldend;
3759         return(1);
3760     } else if ((CUR == '[') || (CUR == '\\') || (CUR == '.')) {
3761         xmlFAParseCharClass(ctxt);
3762         return(1);
3763     }
3764     return(0);
3765 }
3766
3767 /**
3768  * xmlFAParsePiece:
3769  * @ctxt:  a regexp parser context
3770  *
3771  * [3]   piece   ::=   atom quantifier?
3772  */
3773 static int
3774 xmlFAParsePiece(xmlRegParserCtxtPtr ctxt) {
3775     int ret;
3776
3777     ctxt->atom = NULL;
3778     ret = xmlFAParseAtom(ctxt);
3779     if (ret == 0)
3780         return(0);
3781     if (ctxt->atom == NULL) {
3782         ERROR("internal: no atom generated");
3783     }
3784     xmlFAParseQuantifier(ctxt);
3785     return(1);
3786 }
3787
3788 /**
3789  * xmlFAParseBranch:
3790  * @ctxt:  a regexp parser context
3791  * @first:  is taht the first
3792  *
3793  * [2]   branch   ::=   piece*
3794  8
3795  */
3796 static int
3797 xmlFAParseBranch(xmlRegParserCtxtPtr ctxt, int first) {
3798     xmlRegStatePtr previous;
3799     xmlRegAtomPtr prevatom = NULL;
3800     int ret;
3801
3802     previous = ctxt->state;
3803     ret = xmlFAParsePiece(ctxt);
3804     if (ret != 0) {
3805         if (first) {
3806             if (xmlFAGenerateTransitions(ctxt, previous, NULL, ctxt->atom) < 0)
3807                 return(-1);
3808             previous = ctxt->state;
3809         } else {
3810             prevatom = ctxt->atom;
3811         }
3812         ctxt->atom = NULL;
3813     }
3814     while ((ret != 0) && (ctxt->error == 0)) {
3815         ret = xmlFAParsePiece(ctxt);
3816         if (ret != 0) {
3817             if (first) {
3818                 if (xmlFAGenerateTransitions(ctxt, previous, NULL,
3819                                              ctxt->atom) < 0)
3820                     return(-1);
3821             } else {
3822                 if (xmlFAGenerateTransitions(ctxt, previous, NULL,
3823                                              prevatom) < 0)
3824                     return(-1);
3825                 prevatom = ctxt->atom;
3826             }
3827             previous = ctxt->state;
3828             ctxt->atom = NULL;
3829         }
3830     }
3831     if (!first) {
3832         if (xmlFAGenerateTransitions(ctxt, previous, ctxt->end, prevatom) < 0)
3833             return(-1);
3834     }
3835     return(0);
3836 }
3837
3838 /**
3839  * xmlFAParseRegExp:
3840  * @ctxt:  a regexp parser context
3841  * @top:  is that the top-level expressions ?
3842  *
3843  * [1]   regExp   ::=     branch  ( '|' branch )*
3844  */
3845 static void
3846 xmlFAParseRegExp(xmlRegParserCtxtPtr ctxt, int top) {
3847     xmlRegStatePtr start, end, oldend;
3848
3849     oldend = ctxt->end;
3850
3851     start = ctxt->state;
3852     xmlFAParseBranch(ctxt, (ctxt->end == NULL));
3853     if (CUR != '|') {
3854         ctxt->end = ctxt->state;
3855         return;
3856     }
3857     end = ctxt->state;
3858     while ((CUR == '|') && (ctxt->error == 0)) {
3859         NEXT;
3860         ctxt->state = start;
3861         ctxt->end = end;
3862         xmlFAParseBranch(ctxt, 0);
3863     }
3864     if (!top)
3865         ctxt->end = oldend;
3866 }
3867
3868 /************************************************************************
3869  *                                                                      *
3870  *                      The basic API                                   *
3871  *                                                                      *
3872  ************************************************************************/
3873
3874 /**
3875  * xmlRegexpPrint:
3876  * @output: the file for the output debug
3877  * @regexp: the compiled regexp
3878  *
3879  * Print the content of the compiled regular expression
3880  */
3881 void
3882 xmlRegexpPrint(FILE *output, xmlRegexpPtr regexp) {
3883     int i;
3884
3885     fprintf(output, " regexp: ");
3886     if (regexp == NULL) {
3887         fprintf(output, "NULL\n");
3888         return;
3889     }
3890     fprintf(output, "'%s' ", regexp->string);
3891     fprintf(output, "\n");
3892     fprintf(output, "%d atoms:\n", regexp->nbAtoms);
3893     for (i = 0;i < regexp->nbAtoms; i++) {
3894         fprintf(output, " %02d ", i);
3895         xmlRegPrintAtom(output, regexp->atoms[i]);
3896     }
3897     fprintf(output, "%d states:", regexp->nbStates);
3898     fprintf(output, "\n");
3899     for (i = 0;i < regexp->nbStates; i++) {
3900         xmlRegPrintState(output, regexp->states[i]);
3901     }
3902     fprintf(output, "%d counters:\n", regexp->nbCounters);
3903     for (i = 0;i < regexp->nbCounters; i++) {
3904         fprintf(output, " %d: min %d max %d\n", i, regexp->counters[i].min,
3905                                                 regexp->counters[i].max);
3906     }
3907 }
3908
3909 /**
3910  * xmlRegexpCompile:
3911  * @regexp:  a regular expression string
3912  *
3913  * Parses a regular expression conforming to XML Schemas Part 2 Datatype
3914  * Appendix F and build an automata suitable for testing strings against
3915  * that regular expression
3916  *
3917  * Returns the compiled expression or NULL in case of error
3918  */
3919 xmlRegexpPtr
3920 xmlRegexpCompile(const xmlChar *regexp) {
3921     xmlRegexpPtr ret;
3922     xmlRegParserCtxtPtr ctxt;
3923
3924     ctxt = xmlRegNewParserCtxt(regexp);
3925     if (ctxt == NULL)
3926         return(NULL);
3927
3928     /* initialize the parser */
3929     ctxt->end = NULL;
3930     ctxt->start = ctxt->state = xmlRegNewState(ctxt);
3931     xmlRegStatePush(ctxt, ctxt->start);
3932
3933     /* parse the expression building an automata */
3934     xmlFAParseRegExp(ctxt, 1);
3935     if (CUR != 0) {
3936         ERROR("xmlFAParseRegExp: extra characters");
3937     }
3938     ctxt->end = ctxt->state;
3939     ctxt->start->type = XML_REGEXP_START_STATE;
3940     ctxt->end->type = XML_REGEXP_FINAL_STATE;
3941
3942     /* remove the Epsilon except for counted transitions */
3943     xmlFAEliminateEpsilonTransitions(ctxt);
3944
3945
3946     if (ctxt->error != 0) {
3947         xmlRegFreeParserCtxt(ctxt);
3948         return(NULL);
3949     }
3950     ret = xmlRegEpxFromParse(ctxt);
3951     xmlRegFreeParserCtxt(ctxt);
3952     return(ret);
3953 }
3954
3955 /**
3956  * xmlRegexpExec:
3957  * @comp:  the compiled regular expression
3958  * @content:  the value to check against the regular expression
3959  *
3960  * Check if the regular expression generate the value
3961  *
3962  * Returns 1 if it matches, 0 if not and a negativa value in case of error
3963  */
3964 int
3965 xmlRegexpExec(xmlRegexpPtr comp, const xmlChar *content) {
3966     if ((comp == NULL) || (content == NULL))
3967         return(-1);
3968     return(xmlFARegExec(comp, content));
3969 }
3970
3971 /**
3972  * xmlRegexpIsDeterminist:
3973  * @comp:  the compiled regular expression
3974  *
3975  * Check if the regular expression is determinist
3976  *
3977  * Returns 1 if it yes, 0 if not and a negativa value in case of error
3978  */
3979 int
3980 xmlRegexpIsDeterminist(xmlRegexpPtr comp) {
3981     xmlAutomataPtr am;
3982     int ret;
3983
3984     if (comp == NULL)
3985         return(-1);
3986     if (comp->determinist != -1)
3987         return(comp->determinist);
3988
3989     am = xmlNewAutomata();
3990     if (am->states != NULL) {
3991         int i;
3992
3993         for (i = 0;i < am->nbStates;i++)
3994             xmlRegFreeState(am->states[i]);
3995         xmlFree(am->states);
3996     }
3997     am->nbAtoms = comp->nbAtoms;
3998     am->atoms = comp->atoms;
3999     am->nbStates = comp->nbStates;
4000     am->states = comp->states;
4001     am->determinist = -1;
4002     ret = xmlFAComputesDeterminism(am);
4003     am->atoms = NULL;
4004     am->states = NULL;
4005     xmlFreeAutomata(am);
4006     return(ret);
4007 }
4008
4009 /**
4010  * xmlRegFreeRegexp:
4011  * @regexp:  the regexp
4012  *
4013  * Free a regexp
4014  */
4015 void
4016 xmlRegFreeRegexp(xmlRegexpPtr regexp) {
4017     int i;
4018     if (regexp == NULL)
4019         return;
4020
4021     if (regexp->string != NULL)
4022         xmlFree(regexp->string);
4023     if (regexp->states != NULL) {
4024         for (i = 0;i < regexp->nbStates;i++)
4025             xmlRegFreeState(regexp->states[i]);
4026         xmlFree(regexp->states);
4027     }
4028     if (regexp->atoms != NULL) {
4029         for (i = 0;i < regexp->nbAtoms;i++)
4030             xmlRegFreeAtom(regexp->atoms[i]);
4031         xmlFree(regexp->atoms);
4032     }
4033     if (regexp->counters != NULL)
4034         xmlFree(regexp->counters);
4035     if (regexp->compact != NULL)
4036         xmlFree(regexp->compact);
4037     if (regexp->transdata != NULL)
4038         xmlFree(regexp->transdata);
4039     if (regexp->stringMap != NULL) {
4040         for (i = 0; i < regexp->nbstrings;i++)
4041             xmlFree(regexp->stringMap[i]);
4042         xmlFree(regexp->stringMap);
4043     }
4044
4045     xmlFree(regexp);
4046 }
4047
4048 #ifdef LIBXML_AUTOMATA_ENABLED
4049 /************************************************************************
4050  *                                                                      *
4051  *                      The Automata interface                          *
4052  *                                                                      *
4053  ************************************************************************/
4054
4055 /**
4056  * xmlNewAutomata:
4057  *
4058  * Create a new automata
4059  *
4060  * Returns the new object or NULL in case of failure
4061  */
4062 xmlAutomataPtr
4063 xmlNewAutomata(void) {
4064     xmlAutomataPtr ctxt;
4065
4066     ctxt = xmlRegNewParserCtxt(NULL);
4067     if (ctxt == NULL)
4068         return(NULL);
4069
4070     /* initialize the parser */
4071     ctxt->end = NULL;
4072     ctxt->start = ctxt->state = xmlRegNewState(ctxt);
4073     if (ctxt->start == NULL) {
4074         xmlFreeAutomata(ctxt);
4075         return(NULL);
4076     }
4077     if (xmlRegStatePush(ctxt, ctxt->start) < 0) {
4078         xmlRegFreeState(ctxt->start);
4079         xmlFreeAutomata(ctxt);
4080         return(NULL);
4081     }
4082
4083     return(ctxt);
4084 }
4085
4086 /**
4087  * xmlFreeAutomata:
4088  * @am: an automata
4089  *
4090  * Free an automata
4091  */
4092 void
4093 xmlFreeAutomata(xmlAutomataPtr am) {
4094     if (am == NULL)
4095         return;
4096     xmlRegFreeParserCtxt(am);
4097 }
4098
4099 /**
4100  * xmlAutomataGetInitState:
4101  * @am: an automata
4102  *
4103  * Initial state lookup
4104  *
4105  * Returns the initial state of the automata
4106  */
4107 xmlAutomataStatePtr
4108 xmlAutomataGetInitState(xmlAutomataPtr am) {
4109     if (am == NULL)
4110         return(NULL);
4111     return(am->start);
4112 }
4113
4114 /**
4115  * xmlAutomataSetFinalState:
4116  * @am: an automata
4117  * @state: a state in this automata
4118  *
4119  * Makes that state a final state
4120  *
4121  * Returns 0 or -1 in case of error
4122  */
4123 int
4124 xmlAutomataSetFinalState(xmlAutomataPtr am, xmlAutomataStatePtr state) {
4125     if ((am == NULL) || (state == NULL))
4126         return(-1);
4127     state->type = XML_REGEXP_FINAL_STATE;
4128     return(0);
4129 }
4130
4131 /**
4132  * xmlAutomataNewTransition:
4133  * @am: an automata
4134  * @from: the starting point of the transition
4135  * @to: the target point of the transition or NULL
4136  * @token: the input string associated to that transition
4137  * @data: data passed to the callback function if the transition is activated
4138  *
4139  * If @to is NULL, this create first a new target state in the automata
4140  * and then adds a transition from the @from state to the target state
4141  * activated by the value of @token
4142  *
4143  * Returns the target state or NULL in case of error
4144  */
4145 xmlAutomataStatePtr
4146 xmlAutomataNewTransition(xmlAutomataPtr am, xmlAutomataStatePtr from,
4147                          xmlAutomataStatePtr to, const xmlChar *token,
4148                          void *data) {
4149     xmlRegAtomPtr atom;
4150
4151     if ((am == NULL) || (from == NULL) || (token == NULL))
4152         return(NULL);
4153     atom = xmlRegNewAtom(am, XML_REGEXP_STRING);
4154     if (atom == NULL)
4155         return(NULL);
4156     atom->data = data;
4157     if (atom == NULL)
4158         return(NULL);
4159     atom->valuep = xmlStrdup(token);
4160
4161     if (xmlFAGenerateTransitions(am, from, to, atom) < 0) {
4162         xmlRegFreeAtom(atom);
4163         return(NULL);
4164     }
4165     if (to == NULL)
4166         return(am->state);
4167     return(to);
4168 }
4169
4170 /**
4171  * xmlAutomataNewTransition2:
4172  * @am: an automata
4173  * @from: the starting point of the transition
4174  * @to: the target point of the transition or NULL
4175  * @token: the first input string associated to that transition
4176  * @token2: the second input string associated to that transition
4177  * @data: data passed to the callback function if the transition is activated
4178  *
4179  * If @to is NULL, this create first a new target state in the automata
4180  * and then adds a transition from the @from state to the target state
4181  * activated by the value of @token
4182  *
4183  * Returns the target state or NULL in case of error
4184  */
4185 xmlAutomataStatePtr
4186 xmlAutomataNewTransition2(xmlAutomataPtr am, xmlAutomataStatePtr from,
4187                           xmlAutomataStatePtr to, const xmlChar *token,
4188                           const xmlChar *token2, void *data) {
4189     xmlRegAtomPtr atom;
4190
4191     if ((am == NULL) || (from == NULL) || (token == NULL))
4192         return(NULL);
4193     atom = xmlRegNewAtom(am, XML_REGEXP_STRING);
4194     atom->data = data;
4195     if (atom == NULL)
4196         return(NULL);
4197     if ((token2 == NULL) || (*token2 == 0)) {
4198         atom->valuep = xmlStrdup(token);
4199     } else {
4200         int lenn, lenp;
4201         xmlChar *str;
4202
4203         lenn = strlen((char *) token2);
4204         lenp = strlen((char *) token);
4205
4206         str = (xmlChar *) xmlMallocAtomic(lenn + lenp + 2);
4207         if (str == NULL) {
4208             xmlRegFreeAtom(atom);
4209             return(NULL);
4210         }
4211         memcpy(&str[0], token, lenp);
4212         str[lenp] = '|';
4213         memcpy(&str[lenp + 1], token2, lenn);
4214         str[lenn + lenp + 1] = 0;
4215
4216         atom->valuep = str;
4217     }
4218
4219     if (xmlFAGenerateTransitions(am, from, to, atom) < 0) {
4220         xmlRegFreeAtom(atom);
4221         return(NULL);
4222     }
4223     if (to == NULL)
4224         return(am->state);
4225     return(to);
4226 }
4227
4228 /**
4229  * xmlAutomataNewCountTrans:
4230  * @am: an automata
4231  * @from: the starting point of the transition
4232  * @to: the target point of the transition or NULL
4233  * @token: the input string associated to that transition
4234  * @min:  the minimum successive occurences of token
4235  * @max:  the maximum successive occurences of token
4236  * @data:  data associated to the transition
4237  *
4238  * If @to is NULL, this create first a new target state in the automata
4239  * and then adds a transition from the @from state to the target state
4240  * activated by a succession of input of value @token and whose number
4241  * is between @min and @max
4242  *
4243  * Returns the target state or NULL in case of error
4244  */
4245 xmlAutomataStatePtr
4246 xmlAutomataNewCountTrans(xmlAutomataPtr am, xmlAutomataStatePtr from,
4247                          xmlAutomataStatePtr to, const xmlChar *token,
4248                          int min, int max, void *data) {
4249     xmlRegAtomPtr atom;
4250
4251     if ((am == NULL) || (from == NULL) || (token == NULL))
4252         return(NULL);
4253     if (min < 0)
4254         return(NULL);
4255     if ((max < min) || (max < 1))
4256         return(NULL);
4257     atom = xmlRegNewAtom(am, XML_REGEXP_STRING);
4258     if (atom == NULL)
4259         return(NULL);
4260     atom->valuep = xmlStrdup(token);
4261     atom->data = data;
4262     if (min == 0)
4263         atom->min = 1;
4264     else
4265         atom->min = min;
4266     atom->max = max;
4267
4268     if (xmlFAGenerateTransitions(am, from, to, atom) < 0) {
4269         xmlRegFreeAtom(atom);
4270         return(NULL);
4271     }
4272     if (to == NULL)
4273         to = am->state;
4274     if (to == NULL)
4275         return(NULL);
4276     if (min == 0)
4277         xmlFAGenerateEpsilonTransition(am, from, to);
4278     return(to);
4279 }
4280
4281 /**
4282  * xmlAutomataNewOnceTrans:
4283  * @am: an automata
4284  * @from: the starting point of the transition
4285  * @to: the target point of the transition or NULL
4286  * @token: the input string associated to that transition
4287  * @min:  the minimum successive occurences of token
4288  * @max:  the maximum successive occurences of token
4289  * @data:  data associated to the transition
4290  *
4291  * If @to is NULL, this create first a new target state in the automata
4292  * and then adds a transition from the @from state to the target state
4293  * activated by a succession of input of value @token and whose number
4294  * is between @min and @max, moreover that transistion can only be crossed
4295  * once.
4296  *
4297  * Returns the target state or NULL in case of error
4298  */
4299 xmlAutomataStatePtr
4300 xmlAutomataNewOnceTrans(xmlAutomataPtr am, xmlAutomataStatePtr from,
4301                          xmlAutomataStatePtr to, const xmlChar *token,
4302                          int min, int max, void *data) {
4303     xmlRegAtomPtr atom;
4304     int counter;
4305
4306     if ((am == NULL) || (from == NULL) || (token == NULL))
4307         return(NULL);
4308     if (min < 1)
4309         return(NULL);
4310     if ((max < min) || (max < 1))
4311         return(NULL);
4312     atom = xmlRegNewAtom(am, XML_REGEXP_STRING);
4313     if (atom == NULL)
4314         return(NULL);
4315     atom->valuep = xmlStrdup(token);
4316     atom->data = data;
4317     atom->quant = XML_REGEXP_QUANT_ONCEONLY;
4318     if (min == 0)
4319         atom->min = 1;
4320     else
4321         atom->min = min;
4322     atom->max = max;
4323     /*
4324      * associate a counter to the transition.
4325      */
4326     counter = xmlRegGetCounter(am);
4327     am->counters[counter].min = 1;
4328     am->counters[counter].max = 1;
4329
4330     /* xmlFAGenerateTransitions(am, from, to, atom); */
4331     if (to == NULL) {
4332         to = xmlRegNewState(am);
4333         xmlRegStatePush(am, to);
4334     }
4335     xmlRegStateAddTrans(am, from, atom, to, counter, -1);
4336     xmlRegAtomPush(am, atom);
4337     am->state = to;
4338     if (to == NULL)
4339         to = am->state;
4340     if (to == NULL)
4341         return(NULL);
4342     return(to);
4343 }
4344
4345 /**
4346  * xmlAutomataNewState:
4347  * @am: an automata
4348  *
4349  * Create a new disconnected state in the automata
4350  *
4351  * Returns the new state or NULL in case of error
4352  */
4353 xmlAutomataStatePtr
4354 xmlAutomataNewState(xmlAutomataPtr am) {
4355     xmlAutomataStatePtr to; 
4356
4357     if (am == NULL)
4358         return(NULL);
4359     to = xmlRegNewState(am);
4360     xmlRegStatePush(am, to);
4361     return(to);
4362 }
4363
4364 /**
4365  * xmlAutomataNewEpsilon:
4366  * @am: an automata
4367  * @from: the starting point of the transition
4368  * @to: the target point of the transition or NULL
4369  *
4370  * If @to is NULL, this create first a new target state in the automata
4371  * and then adds a an epsilon transition from the @from state to the
4372  * target state
4373  *
4374  * Returns the target state or NULL in case of error
4375  */
4376 xmlAutomataStatePtr
4377 xmlAutomataNewEpsilon(xmlAutomataPtr am, xmlAutomataStatePtr from,
4378                       xmlAutomataStatePtr to) {
4379     if ((am == NULL) || (from == NULL))
4380         return(NULL);
4381     xmlFAGenerateEpsilonTransition(am, from, to);
4382     if (to == NULL)
4383         return(am->state);
4384     return(to);
4385 }
4386
4387 /**
4388  * xmlAutomataNewAllTrans:
4389  * @am: an automata
4390  * @from: the starting point of the transition
4391  * @to: the target point of the transition or NULL
4392  * @lax: allow to transition if not all all transitions have been activated
4393  *
4394  * If @to is NULL, this create first a new target state in the automata
4395  * and then adds a an ALL transition from the @from state to the
4396  * target state. That transition is an epsilon transition allowed only when
4397  * all transitions from the @from node have been activated.
4398  *
4399  * Returns the target state or NULL in case of error
4400  */
4401 xmlAutomataStatePtr
4402 xmlAutomataNewAllTrans(xmlAutomataPtr am, xmlAutomataStatePtr from,
4403                        xmlAutomataStatePtr to, int lax) {
4404     if ((am == NULL) || (from == NULL))
4405         return(NULL);
4406     xmlFAGenerateAllTransition(am, from, to, lax);
4407     if (to == NULL)
4408         return(am->state);
4409     return(to);
4410 }
4411
4412 /**
4413  * xmlAutomataNewCounter:
4414  * @am: an automata
4415  * @min:  the minimal value on the counter
4416  * @max:  the maximal value on the counter
4417  *
4418  * Create a new counter
4419  *
4420  * Returns the counter number or -1 in case of error
4421  */
4422 int             
4423 xmlAutomataNewCounter(xmlAutomataPtr am, int min, int max) {
4424     int ret;
4425
4426     if (am == NULL)
4427         return(-1);
4428
4429     ret = xmlRegGetCounter(am);
4430     if (ret < 0)
4431         return(-1);
4432     am->counters[ret].min = min;
4433     am->counters[ret].max = max;
4434     return(ret);
4435 }
4436
4437 /**
4438  * xmlAutomataNewCountedTrans:
4439  * @am: an automata
4440  * @from: the starting point of the transition
4441  * @to: the target point of the transition or NULL
4442  * @counter: the counter associated to that transition
4443  *
4444  * If @to is NULL, this create first a new target state in the automata
4445  * and then adds an epsilon transition from the @from state to the target state
4446  * which will increment the counter provided
4447  *
4448  * Returns the target state or NULL in case of error
4449  */
4450 xmlAutomataStatePtr
4451 xmlAutomataNewCountedTrans(xmlAutomataPtr am, xmlAutomataStatePtr from,
4452                 xmlAutomataStatePtr to, int counter) {
4453     if ((am == NULL) || (from == NULL) || (counter < 0))
4454         return(NULL);
4455     xmlFAGenerateCountedEpsilonTransition(am, from, to, counter);
4456     if (to == NULL)
4457         return(am->state);
4458     return(to);
4459 }
4460
4461 /**
4462  * xmlAutomataNewCounterTrans:
4463  * @am: an automata
4464  * @from: the starting point of the transition
4465  * @to: the target point of the transition or NULL
4466  * @counter: the counter associated to that transition
4467  *
4468  * If @to is NULL, this create first a new target state in the automata
4469  * and then adds an epsilon transition from the @from state to the target state
4470  * which will be allowed only if the counter is within the right range.
4471  *
4472  * Returns the target state or NULL in case of error
4473  */
4474 xmlAutomataStatePtr
4475 xmlAutomataNewCounterTrans(xmlAutomataPtr am, xmlAutomataStatePtr from,
4476                 xmlAutomataStatePtr to, int counter) {
4477     if ((am == NULL) || (from == NULL) || (counter < 0))
4478         return(NULL);
4479     xmlFAGenerateCountedTransition(am, from, to, counter);
4480     if (to == NULL)
4481         return(am->state);
4482     return(to);
4483 }
4484
4485 /**
4486  * xmlAutomataCompile:
4487  * @am: an automata
4488  *
4489  * Compile the automata into a Reg Exp ready for being executed.
4490  * The automata should be free after this point.
4491  *
4492  * Returns the compiled regexp or NULL in case of error
4493  */
4494 xmlRegexpPtr          
4495 xmlAutomataCompile(xmlAutomataPtr am) {
4496     xmlRegexpPtr ret;
4497
4498     if ((am == NULL) || (am->error != 0)) return(NULL);
4499     xmlFAEliminateEpsilonTransitions(am);
4500     /* xmlFAComputesDeterminism(am); */
4501     ret = xmlRegEpxFromParse(am);
4502
4503     return(ret);
4504 }
4505
4506 /**
4507  * xmlAutomataIsDeterminist:
4508  * @am: an automata
4509  *
4510  * Checks if an automata is determinist.
4511  *
4512  * Returns 1 if true, 0 if not, and -1 in case of error
4513  */
4514 int          
4515 xmlAutomataIsDeterminist(xmlAutomataPtr am) {
4516     int ret;
4517
4518     if (am == NULL)
4519         return(-1);
4520
4521     ret = xmlFAComputesDeterminism(am);
4522     return(ret);
4523 }
4524 #endif /* LIBXML_AUTOMATA_ENABLED */
4525 #endif /* LIBXML_REGEXP_ENABLED */