/[pcre]/code/trunk/pcretest.c
ViewVC logotype

Diff of /code/trunk/pcretest.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 91 by nigel, Sat Feb 24 21:41:34 2007 UTC revision 255 by ph10, Wed Sep 19 08:50:04 2007 UTC
# Line 36  POSSIBILITY OF SUCH DAMAGE. Line 36  POSSIBILITY OF SUCH DAMAGE.
36  */  */
37    
38    
39    #ifdef HAVE_CONFIG_H
40    #include "config.h"
41    #endif
42    
43  #include <ctype.h>  #include <ctype.h>
44  #include <stdio.h>  #include <stdio.h>
45  #include <string.h>  #include <string.h>
# Line 44  POSSIBILITY OF SUCH DAMAGE. Line 48  POSSIBILITY OF SUCH DAMAGE.
48  #include <locale.h>  #include <locale.h>
49  #include <errno.h>  #include <errno.h>
50    
51  #ifndef _WIN32  
52  #include <sys/resource.h>  /* A number of things vary for Windows builds. Originally, pcretest opened its
53    input and output without "b"; then I was told that "b" was needed in some
54    environments, so it was added for release 5.0 to both the input and output. (It
55    makes no difference on Unix-like systems.) Later I was told that it is wrong
56    for the input on Windows. I've now abstracted the modes into two macros that
57    are set here, to make it easier to fiddle with them, and removed "b" from the
58    input mode under Windows. */
59    
60    #if defined(_WIN32) || defined(WIN32)
61    #include <io.h>                /* For _setmode() */
62    #include <fcntl.h>             /* For _O_BINARY */
63    #define INPUT_MODE   "r"
64    #define OUTPUT_MODE  "wb"
65    
66    #else
67    #include <sys/time.h>          /* These two includes are needed */
68    #include <sys/resource.h>      /* for setrlimit(). */
69    #define INPUT_MODE   "rb"
70    #define OUTPUT_MODE  "wb"
71  #endif  #endif
72    
 #define PCRE_SPY        /* For Win32 build, import data, not export */  
73    
74  /* We include pcre_internal.h because we need the internal info for displaying  /* We have to include pcre_internal.h because we need the internal info for
75  the results of pcre_study() and we also need to know about the internal  displaying the results of pcre_study() and we also need to know about the
76  macros, structures, and other internal data values; pcretest has "inside  internal macros, structures, and other internal data values; pcretest has
77  information" compared to a program that strictly follows the PCRE API. */  "inside information" compared to a program that strictly follows the PCRE API.
78    
79    Although pcre_internal.h does itself include pcre.h, we explicitly include it
80    here before pcre_internal.h so that the PCRE_EXP_xxx macros get set
81    appropriately for an application, not for building PCRE. */
82    
83    #include "pcre.h"
84  #include "pcre_internal.h"  #include "pcre_internal.h"
85    
86  /* We need access to the data tables that PCRE uses. So as not to have to keep  /* We need access to the data tables that PCRE uses. So as not to have to keep
# Line 68  symbols to prevent clashes. */ Line 94  symbols to prevent clashes. */
94  #define _pcre_utf8_table4      utf8_table4  #define _pcre_utf8_table4      utf8_table4
95  #define _pcre_utt              utt  #define _pcre_utt              utt
96  #define _pcre_utt_size         utt_size  #define _pcre_utt_size         utt_size
97    #define _pcre_utt_names        utt_names
98  #define _pcre_OP_lengths       OP_lengths  #define _pcre_OP_lengths       OP_lengths
99    
100  #include "pcre_tables.c"  #include "pcre_tables.c"
101    
102  /* We also need the pcre_printint() function for printing out compiled  /* We also need the pcre_printint() function for printing out compiled
103  patterns. This function is in a separate file so that it can be included in  patterns. This function is in a separate file so that it can be included in
104  pcre_compile.c when that module is compiled with debugging enabled. */  pcre_compile.c when that module is compiled with debugging enabled.
105    
106    The definition of the macro PRINTABLE, which determines whether to print an
107    output character as-is or as a hex value when showing compiled patterns, is
108    contained in this file. We uses it here also, in cases when the locale has not
109    been explicitly changed, so as to get consistent output from systems that
110    differ in their output from isprint() even in the "C" locale. */
111    
112  #include "pcre_printint.src"  #include "pcre_printint.src"
113    
114    #define PRINTHEX(c) (locale_set? isprint(c) : PRINTABLE(c))
115    
116    
117  /* It is possible to compile this test program without including support for  /* It is possible to compile this test program without including support for
118  testing the POSIX interface, though this is not available via the standard  testing the POSIX interface, though this is not available via the standard
# Line 87  Makefile. */ Line 122  Makefile. */
122  #include "pcreposix.h"  #include "pcreposix.h"
123  #endif  #endif
124    
125  /* It is also possible, for the benefit of the version imported into Exim, to  /* It is also possible, for the benefit of the version currently imported into
126  build pcretest without support for UTF8 (define NOUTF8), without the interface  Exim, to build pcretest without support for UTF8 (define NOUTF8), without the
127  to the DFA matcher (NODFA), and without the doublecheck of the old "info"  interface to the DFA matcher (NODFA), and without the doublecheck of the old
128  function (define NOINFOCHECK). */  "info" function (define NOINFOCHECK). In fact, we automatically cut out the
129    UTF8 support if PCRE is built without it. */
130    
131    #ifndef SUPPORT_UTF8
132    #ifndef NOUTF8
133    #define NOUTF8
134    #endif
135    #endif
136    
137    
138  /* Other parameters */  /* Other parameters */
# Line 103  function (define NOINFOCHECK). */ Line 145  function (define NOINFOCHECK). */
145  #endif  #endif
146  #endif  #endif
147    
148    /* This is the default loop count for timing. */
149    
150  #define LOOPREPEAT 500000  #define LOOPREPEAT 500000
151    
152  /* Static variables */  /* Static variables */
# Line 113  static int callout_count; Line 157  static int callout_count;
157  static int callout_extra;  static int callout_extra;
158  static int callout_fail_count;  static int callout_fail_count;
159  static int callout_fail_id;  static int callout_fail_id;
160    static int debug_lengths;
161  static int first_callout;  static int first_callout;
162    static int locale_set = 0;
163  static int show_malloc;  static int show_malloc;
164  static int use_utf8;  static int use_utf8;
165  static size_t gotten_store;  static size_t gotten_store;
# Line 157  uschar *here = start; Line 203  uschar *here = start;
203  for (;;)  for (;;)
204    {    {
205    int rlen = buffer_size - (here - buffer);    int rlen = buffer_size - (here - buffer);
206    
207    if (rlen > 1000)    if (rlen > 1000)
208      {      {
209      int dlen;      int dlen;
# Line 213  return NULL;  /* Control never gets here Line 260  return NULL;  /* Control never gets here
260    
261  /* We don't use strtoul() because SunOS4 doesn't have it. Rather than mess  /* We don't use strtoul() because SunOS4 doesn't have it. Rather than mess
262  around with conditional compilation, just do the job by hand. It is only used  around with conditional compilation, just do the job by hand. It is only used
263  for unpicking the -o argument, so just keep it simple.  for unpicking arguments, so just keep it simple.
264    
265  Arguments:  Arguments:
266    str           string to be converted    str           string to be converted
# Line 311  Arguments: Line 358  Arguments:
358  Returns:     number of characters placed in the buffer  Returns:     number of characters placed in the buffer
359  */  */
360    
361    #if !defined NOUTF8
362    
363  static int  static int
364  ord2utf8(int cvalue, uschar *utf8bytes)  ord2utf8(int cvalue, uschar *utf8bytes)
365  {  {
# Line 327  for (j = i; j > 0; j--) Line 376  for (j = i; j > 0; j--)
376  return i + 1;  return i + 1;
377  }  }
378    
379    #endif
380    
381    
382    
383  /*************************************************  /*************************************************
# Line 353  while (length-- > 0) Line 404  while (length-- > 0)
404        {        {
405        length -= rc - 1;        length -= rc - 1;
406        p += rc;        p += rc;
407        if (c < 256 && isprint(c))        if (PRINTHEX(c))
408          {          {
409          if (f != NULL) fprintf(f, "%c", c);          if (f != NULL) fprintf(f, "%c", c);
410          yield++;          yield++;
411          }          }
412        else        else
413          {          {
414          int n;          int n = 4;
415          if (f != NULL) fprintf(f, "\\x{%02x}%n", c, &n);          if (f != NULL) fprintf(f, "\\x{%02x}", c);
416          yield += n;          yield += (n <= 0x000000ff)? 2 :
417                     (n <= 0x00000fff)? 3 :
418                     (n <= 0x0000ffff)? 4 :
419                     (n <= 0x000fffff)? 5 : 6;
420          }          }
421        continue;        continue;
422        }        }
# Line 371  while (length-- > 0) Line 425  while (length-- > 0)
425    
426     /* Not UTF-8, or malformed UTF-8  */     /* Not UTF-8, or malformed UTF-8  */
427    
428    if (isprint(c = *(p++)))    c = *p++;
429      if (PRINTHEX(c))
430      {      {
431      if (f != NULL) fprintf(f, "%c", c);      if (f != NULL) fprintf(f, "%c", c);
432      yield++;      yield++;
# Line 610  return count; Line 665  return count;
665    
666    
667  /*************************************************  /*************************************************
668    *         Case-independent strncmp() function    *
669    *************************************************/
670    
671    /*
672    Arguments:
673      s         first string
674      t         second string
675      n         number of characters to compare
676    
677    Returns:    < 0, = 0, or > 0, according to the comparison
678    */
679    
680    static int
681    strncmpic(uschar *s, uschar *t, int n)
682    {
683    while (n--)
684      {
685      int c = tolower(*s++) - tolower(*t++);
686      if (c) return c;
687      }
688    return 0;
689    }
690    
691    
692    
693    /*************************************************
694  *         Check newline indicator                *  *         Check newline indicator                *
695  *************************************************/  *************************************************/
696    
697  /* This is used both at compile and run-time to check for <xxx> escapes, where  /* This is used both at compile and run-time to check for <xxx> escapes, where
698  xxx is LF, CR, or CRLF. Print a message and return 0 if there is no match.  xxx is LF, CR, CRLF, ANYCRLF, or ANY. Print a message and return 0 if there is
699    no match.
700    
701  Arguments:  Arguments:
702    p           points after the leading '<'    p           points after the leading '<'
# Line 626  Returns:      appropriate PCRE_NEWLINE_x Line 708  Returns:      appropriate PCRE_NEWLINE_x
708  static int  static int
709  check_newline(uschar *p, FILE *f)  check_newline(uschar *p, FILE *f)
710  {  {
711  if (strncmp((char *)p, "cr>", 3) == 0) return PCRE_NEWLINE_CR;  if (strncmpic(p, (uschar *)"cr>", 3) == 0) return PCRE_NEWLINE_CR;
712  if (strncmp((char *)p, "lf>", 3) == 0) return PCRE_NEWLINE_LF;  if (strncmpic(p, (uschar *)"lf>", 3) == 0) return PCRE_NEWLINE_LF;
713  if (strncmp((char *)p, "crlf>", 5) == 0) return PCRE_NEWLINE_CRLF;  if (strncmpic(p, (uschar *)"crlf>", 5) == 0) return PCRE_NEWLINE_CRLF;
714    if (strncmpic(p, (uschar *)"anycrlf>", 8) == 0) return PCRE_NEWLINE_ANYCRLF;
715    if (strncmpic(p, (uschar *)"any>", 4) == 0) return PCRE_NEWLINE_ANY;
716    if (strncmpic(p, (uschar *)"bsr_anycrlf>", 12) == 0) return PCRE_BSR_ANYCRLF;
717    if (strncmpic(p, (uschar *)"bsr_unicode>", 12) == 0) return PCRE_BSR_UNICODE;
718  fprintf(f, "Unknown newline type at: <%s\n", p);  fprintf(f, "Unknown newline type at: <%s\n", p);
719  return 0;  return 0;
720  }  }
# Line 636  return 0; Line 722  return 0;
722    
723    
724  /*************************************************  /*************************************************
725    *             Usage function                     *
726    *************************************************/
727    
728    static void
729    usage(void)
730    {
731    printf("Usage:     pcretest [options] [<input> [<output>]]\n");
732    printf("  -b       show compiled code (bytecode)\n");
733    printf("  -C       show PCRE compile-time options and exit\n");
734    printf("  -d       debug: show compiled code and information (-b and -i)\n");
735    #if !defined NODFA
736    printf("  -dfa     force DFA matching for all subjects\n");
737    #endif
738    printf("  -help    show usage information\n");
739    printf("  -i       show information about compiled patterns\n"
740           "  -m       output memory used information\n"
741           "  -o <n>   set size of offsets vector to <n>\n");
742    #if !defined NOPOSIX
743    printf("  -p       use POSIX interface\n");
744    #endif
745    printf("  -q       quiet: do not output PCRE version number at start\n");
746    printf("  -S <n>   set stack size to <n> megabytes\n");
747    printf("  -s       output store (memory) used information\n"
748           "  -t       time compilation and execution\n");
749    printf("  -t <n>   time compilation and execution, repeating <n> times\n");
750    printf("  -tm      time execution (matching) only\n");
751    printf("  -tm <n>  time execution (matching) only, repeating <n> times\n");
752    }
753    
754    
755    
756    /*************************************************
757  *                Main Program                    *  *                Main Program                    *
758  *************************************************/  *************************************************/
759    
# Line 650  int options = 0; Line 768  int options = 0;
768  int study_options = 0;  int study_options = 0;
769  int op = 1;  int op = 1;
770  int timeit = 0;  int timeit = 0;
771    int timeitm = 0;
772  int showinfo = 0;  int showinfo = 0;
773  int showstore = 0;  int showstore = 0;
774  int quiet = 0;  int quiet = 0;
# Line 681  buffer = (unsigned char *)malloc(buffer_ Line 800  buffer = (unsigned char *)malloc(buffer_
800  dbuffer = (unsigned char *)malloc(buffer_size);  dbuffer = (unsigned char *)malloc(buffer_size);
801  pbuffer = (unsigned char *)malloc(buffer_size);  pbuffer = (unsigned char *)malloc(buffer_size);
802    
803  /* The outfile variable is static so that new_malloc can use it. The _setmode()  /* The outfile variable is static so that new_malloc can use it. */
 stuff is some magic that I don't understand, but which apparently does good  
 things in Windows. It's related to line terminations.  */  
   
 #if defined(_WIN32) || defined(WIN32)  
 _setmode( _fileno( stdout ), 0x8000 );  
 #endif  /* defined(_WIN32) || defined(WIN32) */  
804    
805  outfile = stdout;  outfile = stdout;
806    
807    /* The following  _setmode() stuff is some Windows magic that tells its runtime
808    library to translate CRLF into a single LF character. At least, that's what
809    I've been told: never having used Windows I take this all on trust. Originally
810    it set 0x8000, but then I was advised that _O_BINARY was better. */
811    
812    #if defined(_WIN32) || defined(WIN32)
813    _setmode( _fileno( stdout ), _O_BINARY );
814    #endif
815    
816  /* Scan options */  /* Scan options */
817    
818  while (argc > 1 && argv[op][0] == '-')  while (argc > 1 && argv[op][0] == '-')
# Line 699  while (argc > 1 && argv[op][0] == '-') Line 821  while (argc > 1 && argv[op][0] == '-')
821    
822    if (strcmp(argv[op], "-s") == 0 || strcmp(argv[op], "-m") == 0)    if (strcmp(argv[op], "-s") == 0 || strcmp(argv[op], "-m") == 0)
823      showstore = 1;      showstore = 1;
   else if (strcmp(argv[op], "-t") == 0) timeit = 1;  
824    else if (strcmp(argv[op], "-q") == 0) quiet = 1;    else if (strcmp(argv[op], "-q") == 0) quiet = 1;
825      else if (strcmp(argv[op], "-b") == 0) debug = 1;
826    else if (strcmp(argv[op], "-i") == 0) showinfo = 1;    else if (strcmp(argv[op], "-i") == 0) showinfo = 1;
827    else if (strcmp(argv[op], "-d") == 0) showinfo = debug = 1;    else if (strcmp(argv[op], "-d") == 0) showinfo = debug = 1;
828  #if !defined NODFA  #if !defined NODFA
# Line 713  while (argc > 1 && argv[op][0] == '-') Line 835  while (argc > 1 && argv[op][0] == '-')
835      op++;      op++;
836      argc--;      argc--;
837      }      }
838      else if (strcmp(argv[op], "-t") == 0 || strcmp(argv[op], "-tm") == 0)
839        {
840        int both = argv[op][2] == 0;
841        int temp;
842        if (argc > 2 && (temp = get_value((unsigned char *)argv[op+1], &endptr),
843                         *endptr == 0))
844          {
845          timeitm = temp;
846          op++;
847          argc--;
848          }
849        else timeitm = LOOPREPEAT;
850        if (both) timeit = timeitm;
851        }
852    else if (strcmp(argv[op], "-S") == 0 && argc > 2 &&    else if (strcmp(argv[op], "-S") == 0 && argc > 2 &&
853        ((stack_size = get_value((unsigned char *)argv[op+1], &endptr)),        ((stack_size = get_value((unsigned char *)argv[op+1], &endptr)),
854          *endptr == 0))          *endptr == 0))
855      {      {
856  #ifdef _WIN32  #if defined(_WIN32) || defined(WIN32)
857      printf("PCRE: -S not supported on this OS\n");      printf("PCRE: -S not supported on this OS\n");
858      exit(1);      exit(1);
859  #else  #else
# Line 749  while (argc > 1 && argv[op][0] == '-') Line 885  while (argc > 1 && argv[op][0] == '-')
885      printf("  %sUnicode properties support\n", rc? "" : "No ");      printf("  %sUnicode properties support\n", rc? "" : "No ");
886      (void)pcre_config(PCRE_CONFIG_NEWLINE, &rc);      (void)pcre_config(PCRE_CONFIG_NEWLINE, &rc);
887      printf("  Newline sequence is %s\n", (rc == '\r')? "CR" :      printf("  Newline sequence is %s\n", (rc == '\r')? "CR" :
888        (rc == '\n')? "LF" : "CRLF");        (rc == '\n')? "LF" : (rc == ('\r'<<8 | '\n'))? "CRLF" :
889          (rc == -2)? "ANYCRLF" :
890          (rc == -1)? "ANY" : "???");
891        (void)pcre_config(PCRE_CONFIG_BSR, &rc);
892        printf("  \\R matches %s\n", rc? "CR, LF, or CRLF only" :
893                                         "all Unicode newlines");
894      (void)pcre_config(PCRE_CONFIG_LINK_SIZE, &rc);      (void)pcre_config(PCRE_CONFIG_LINK_SIZE, &rc);
895      printf("  Internal link size = %d\n", rc);      printf("  Internal link size = %d\n", rc);
896      (void)pcre_config(PCRE_CONFIG_POSIX_MALLOC_THRESHOLD, &rc);      (void)pcre_config(PCRE_CONFIG_POSIX_MALLOC_THRESHOLD, &rc);
# Line 760  while (argc > 1 && argv[op][0] == '-') Line 901  while (argc > 1 && argv[op][0] == '-')
901      printf("  Default recursion depth limit = %d\n", rc);      printf("  Default recursion depth limit = %d\n", rc);
902      (void)pcre_config(PCRE_CONFIG_STACKRECURSE, &rc);      (void)pcre_config(PCRE_CONFIG_STACKRECURSE, &rc);
903      printf("  Match recursion uses %s\n", rc? "stack" : "heap");      printf("  Match recursion uses %s\n", rc? "stack" : "heap");
904      exit(0);      goto EXIT;
905        }
906      else if (strcmp(argv[op], "-help") == 0 ||
907               strcmp(argv[op], "--help") == 0)
908        {
909        usage();
910        goto EXIT;
911      }      }
912    else    else
913      {      {
914      printf("** Unknown or malformed option %s\n", argv[op]);      printf("** Unknown or malformed option %s\n", argv[op]);
915      printf("Usage:   pcretest [options] [<input> [<output>]]\n");      usage();
     printf("  -C     show PCRE compile-time options and exit\n");  
     printf("  -d     debug: show compiled code; implies -i\n");  
 #if !defined NODFA  
     printf("  -dfa   force DFA matching for all subjects\n");  
 #endif  
     printf("  -i     show information about compiled pattern\n"  
            "  -m     output memory used information\n"  
            "  -o <n> set size of offsets vector to <n>\n");  
 #if !defined NOPOSIX  
     printf("  -p     use POSIX interface\n");  
 #endif  
     printf("  -S <n> set stack size to <n> megabytes\n");  
     printf("  -s     output store (memory) used information\n"  
            "  -t     time compilation and execution\n");  
916      yield = 1;      yield = 1;
917      goto EXIT;      goto EXIT;
918      }      }
# Line 794  offsets = (int *)malloc(size_offsets_max Line 927  offsets = (int *)malloc(size_offsets_max
927  if (offsets == NULL)  if (offsets == NULL)
928    {    {
929    printf("** Failed to get %d bytes of memory for offsets vector\n",    printf("** Failed to get %d bytes of memory for offsets vector\n",
930      size_offsets_max * sizeof(int));      (int)(size_offsets_max * sizeof(int)));
931    yield = 1;    yield = 1;
932    goto EXIT;    goto EXIT;
933    }    }
# Line 803  if (offsets == NULL) Line 936  if (offsets == NULL)
936    
937  if (argc > 1)  if (argc > 1)
938    {    {
939    infile = fopen(argv[op], "rb");    infile = fopen(argv[op], INPUT_MODE);
940    if (infile == NULL)    if (infile == NULL)
941      {      {
942      printf("** Failed to open %s\n", argv[op]);      printf("** Failed to open %s\n", argv[op]);
# Line 814  if (argc > 1) Line 947  if (argc > 1)
947    
948  if (argc > 2)  if (argc > 2)
949    {    {
950    outfile = fopen(argv[op+1], "wb");    outfile = fopen(argv[op+1], OUTPUT_MODE);
951    if (outfile == NULL)    if (outfile == NULL)
952      {      {
953      printf("** Failed to open %s\n", argv[op+1]);      printf("** Failed to open %s\n", argv[op+1]);
# Line 859  while (!done) Line 992  while (!done)
992    int do_showinfo = showinfo;    int do_showinfo = showinfo;
993    int do_showrest = 0;    int do_showrest = 0;
994    int do_flip = 0;    int do_flip = 0;
995    int erroroffset, len, delimiter;    int erroroffset, len, delimiter, poffset;
996    
997    use_utf8 = 0;    use_utf8 = 0;
998      debug_lengths = 1;
999    
1000    if (infile == stdin) printf("  re> ");    if (infile == stdin) printf("  re> ");
1001    if (extend_inputline(infile, buffer) == NULL) break;    if (extend_inputline(infile, buffer) == NULL) break;
# Line 969  while (!done) Line 1103  while (!done)
1103      }      }
1104    
1105    pp = p;    pp = p;
1106      poffset = p - buffer;
1107    
1108    for(;;)    for(;;)
1109      {      {
# Line 989  while (!done) Line 1124  while (!done)
1124      if (infile != stdin) fprintf(outfile, "%s", (char *)pp);      if (infile != stdin) fprintf(outfile, "%s", (char *)pp);
1125      }      }
1126    
1127      /* The buffer may have moved while being extended; reset the start of data
1128      pointer to the correct relative point in the buffer. */
1129    
1130      p = buffer + poffset;
1131    
1132    /* If the first character after the delimiter is backslash, make    /* If the first character after the delimiter is backslash, make
1133    the pattern end with backslash. This is purely to provide a way    the pattern end with backslash. This is purely to provide a way
1134    of testing for the error message when a pattern ends with backslash. */    of testing for the error message when a pattern ends with backslash. */
# Line 1020  while (!done) Line 1160  while (!done)
1160    
1161        case '+': do_showrest = 1; break;        case '+': do_showrest = 1; break;
1162        case 'A': options |= PCRE_ANCHORED; break;        case 'A': options |= PCRE_ANCHORED; break;
1163          case 'B': do_debug = 1; break;
1164        case 'C': options |= PCRE_AUTO_CALLOUT; break;        case 'C': options |= PCRE_AUTO_CALLOUT; break;
1165        case 'D': do_debug = do_showinfo = 1; break;        case 'D': do_debug = do_showinfo = 1; break;
1166        case 'E': options |= PCRE_DOLLAR_ENDONLY; break;        case 'E': options |= PCRE_DOLLAR_ENDONLY; break;
# Line 1037  while (!done) Line 1178  while (!done)
1178        case 'S': do_study = 1; break;        case 'S': do_study = 1; break;
1179        case 'U': options |= PCRE_UNGREEDY; break;        case 'U': options |= PCRE_UNGREEDY; break;
1180        case 'X': options |= PCRE_EXTRA; break;        case 'X': options |= PCRE_EXTRA; break;
1181          case 'Z': debug_lengths = 0; break;
1182        case '8': options |= PCRE_UTF8; use_utf8 = 1; break;        case '8': options |= PCRE_UTF8; use_utf8 = 1; break;
1183        case '?': options |= PCRE_NO_UTF8_CHECK; break;        case '?': options |= PCRE_NO_UTF8_CHECK; break;
1184    
1185        case 'L':        case 'L':
1186        ppp = pp;        ppp = pp;
1187        /* The '\r' test here is so that it works on Windows */        /* The '\r' test here is so that it works on Windows. */
1188        while (*ppp != '\n' && *ppp != '\r' && *ppp != ' ') ppp++;        /* The '0' test is just in case this is an unterminated line. */
1189          while (*ppp != 0 && *ppp != '\n' && *ppp != '\r' && *ppp != ' ') ppp++;
1190        *ppp = 0;        *ppp = 0;
1191        if (setlocale(LC_CTYPE, (const char *)pp) == NULL)        if (setlocale(LC_CTYPE, (const char *)pp) == NULL)
1192          {          {
1193          fprintf(outfile, "** Failed to set locale \"%s\"\n", pp);          fprintf(outfile, "** Failed to set locale \"%s\"\n", pp);
1194          goto SKIP_DATA;          goto SKIP_DATA;
1195          }          }
1196          locale_set = 1;
1197        tables = pcre_maketables();        tables = pcre_maketables();
1198        pp = ppp;        pp = ppp;
1199        break;        break;
# Line 1116  while (!done) Line 1260  while (!done)
1260  #endif  /* !defined NOPOSIX */  #endif  /* !defined NOPOSIX */
1261    
1262      {      {
1263      if (timeit)      if (timeit > 0)
1264        {        {
1265        register int i;        register int i;
1266        clock_t time_taken;        clock_t time_taken;
1267        clock_t start_time = clock();        clock_t start_time = clock();
1268        for (i = 0; i < LOOPREPEAT; i++)        for (i = 0; i < timeit; i++)
1269          {          {
1270          re = pcre_compile((char *)p, options, &error, &erroroffset, tables);          re = pcre_compile((char *)p, options, &error, &erroroffset, tables);
1271          if (re != NULL) free(re);          if (re != NULL) free(re);
1272          }          }
1273        time_taken = clock() - start_time;        time_taken = clock() - start_time;
1274        fprintf(outfile, "Compile time %.3f milliseconds\n",        fprintf(outfile, "Compile time %.4f milliseconds\n",
1275          (((double)time_taken * 1000.0) / (double)LOOPREPEAT) /          (((double)time_taken * 1000.0) / (double)timeit) /
1276            (double)CLOCKS_PER_SEC);            (double)CLOCKS_PER_SEC);
1277        }        }
1278    
# Line 1180  while (!done) Line 1324  while (!done)
1324    
1325      if (do_study)      if (do_study)
1326        {        {
1327        if (timeit)        if (timeit > 0)
1328          {          {
1329          register int i;          register int i;
1330          clock_t time_taken;          clock_t time_taken;
1331          clock_t start_time = clock();          clock_t start_time = clock();
1332          for (i = 0; i < LOOPREPEAT; i++)          for (i = 0; i < timeit; i++)
1333            extra = pcre_study(re, study_options, &error);            extra = pcre_study(re, study_options, &error);
1334          time_taken = clock() - start_time;          time_taken = clock() - start_time;
1335          if (extra != NULL) free(extra);          if (extra != NULL) free(extra);
1336          fprintf(outfile, "  Study time %.3f milliseconds\n",          fprintf(outfile, "  Study time %.4f milliseconds\n",
1337            (((double)time_taken * 1000.0) / (double)LOOPREPEAT) /            (((double)time_taken * 1000.0) / (double)timeit) /
1338              (double)CLOCKS_PER_SEC);              (double)CLOCKS_PER_SEC);
1339          }          }
1340        extra = pcre_study(re, study_options, &error);        extra = pcre_study(re, study_options, &error);
# Line 1208  while (!done) Line 1352  while (!done)
1352      if (do_flip)      if (do_flip)
1353        {        {
1354        real_pcre *rre = (real_pcre *)re;        real_pcre *rre = (real_pcre *)re;
1355        rre->magic_number = byteflip(rre->magic_number, sizeof(rre->magic_number));        rre->magic_number =
1356            byteflip(rre->magic_number, sizeof(rre->magic_number));
1357        rre->size = byteflip(rre->size, sizeof(rre->size));        rre->size = byteflip(rre->size, sizeof(rre->size));
1358        rre->options = byteflip(rre->options, sizeof(rre->options));        rre->options = byteflip(rre->options, sizeof(rre->options));
1359        rre->top_bracket = byteflip(rre->top_bracket, sizeof(rre->top_bracket));        rre->flags = (pcre_uint16)byteflip(rre->flags, sizeof(rre->flags));
1360        rre->top_backref = byteflip(rre->top_backref, sizeof(rre->top_backref));        rre->top_bracket =
1361        rre->first_byte = byteflip(rre->first_byte, sizeof(rre->first_byte));          (pcre_uint16)byteflip(rre->top_bracket, sizeof(rre->top_bracket));
1362        rre->req_byte = byteflip(rre->req_byte, sizeof(rre->req_byte));        rre->top_backref =
1363        rre->name_table_offset = byteflip(rre->name_table_offset,          (pcre_uint16)byteflip(rre->top_backref, sizeof(rre->top_backref));
1364          rre->first_byte =
1365            (pcre_uint16)byteflip(rre->first_byte, sizeof(rre->first_byte));
1366          rre->req_byte =
1367            (pcre_uint16)byteflip(rre->req_byte, sizeof(rre->req_byte));
1368          rre->name_table_offset = (pcre_uint16)byteflip(rre->name_table_offset,
1369          sizeof(rre->name_table_offset));          sizeof(rre->name_table_offset));
1370        rre->name_entry_size = byteflip(rre->name_entry_size,        rre->name_entry_size = (pcre_uint16)byteflip(rre->name_entry_size,
1371          sizeof(rre->name_entry_size));          sizeof(rre->name_entry_size));
1372        rre->name_count = byteflip(rre->name_count, sizeof(rre->name_count));        rre->name_count = (pcre_uint16)byteflip(rre->name_count,
1373            sizeof(rre->name_count));
1374    
1375        if (extra != NULL)        if (extra != NULL)
1376          {          {
# Line 1233  while (!done) Line 1384  while (!done)
1384    
1385      SHOW_INFO:      SHOW_INFO:
1386    
1387        if (do_debug)
1388          {
1389          fprintf(outfile, "------------------------------------------------------------------\n");
1390          pcre_printint(re, outfile, debug_lengths);
1391          }
1392    
1393      if (do_showinfo)      if (do_showinfo)
1394        {        {
1395        unsigned long int get_options, all_options;        unsigned long int get_options, all_options;
1396  #if !defined NOINFOCHECK  #if !defined NOINFOCHECK
1397        int old_first_char, old_options, old_count;        int old_first_char, old_options, old_count;
1398  #endif  #endif
1399        int count, backrefmax, first_char, need_char;        int count, backrefmax, first_char, need_char, okpartial, jchanged,
1400            hascrorlf;
1401        int nameentrysize, namecount;        int nameentrysize, namecount;
1402        const uschar *nametable;        const uschar *nametable;
1403    
       if (do_debug)  
         {  
         fprintf(outfile, "------------------------------------------------------------------\n");  
         pcre_printint(re, outfile);  
         }  
   
1404        new_info(re, NULL, PCRE_INFO_OPTIONS, &get_options);        new_info(re, NULL, PCRE_INFO_OPTIONS, &get_options);
1405        new_info(re, NULL, PCRE_INFO_SIZE, &size);        new_info(re, NULL, PCRE_INFO_SIZE, &size);
1406        new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count);        new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count);
# Line 1258  while (!done) Line 1410  while (!done)
1410        new_info(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);        new_info(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);
1411        new_info(re, NULL, PCRE_INFO_NAMECOUNT, &namecount);        new_info(re, NULL, PCRE_INFO_NAMECOUNT, &namecount);
1412        new_info(re, NULL, PCRE_INFO_NAMETABLE, (void *)&nametable);        new_info(re, NULL, PCRE_INFO_NAMETABLE, (void *)&nametable);
1413          new_info(re, NULL, PCRE_INFO_OKPARTIAL, &okpartial);
1414          new_info(re, NULL, PCRE_INFO_JCHANGED, &jchanged);
1415          new_info(re, NULL, PCRE_INFO_HASCRORLF, &hascrorlf);
1416    
1417  #if !defined NOINFOCHECK  #if !defined NOINFOCHECK
1418        old_count = pcre_info(re, &old_options, &old_first_char);        old_count = pcre_info(re, &old_options, &old_first_char);
# Line 1299  while (!done) Line 1454  while (!done)
1454            }            }
1455          }          }
1456    
1457        /* The NOPARTIAL bit is a private bit in the options, so we have        if (!okpartial) fprintf(outfile, "Partial matching not supported\n");
1458        to fish it out via out back door */        if (hascrorlf) fprintf(outfile, "Contains explicit CR or LF match\n");
1459    
1460        all_options = ((real_pcre *)re)->options;        all_options = ((real_pcre *)re)->options;
1461        if (do_flip)        if (do_flip) all_options = byteflip(all_options, sizeof(all_options));
         {  
         all_options = byteflip(all_options, sizeof(all_options));  
          }  
   
       if ((all_options & PCRE_NOPARTIAL) != 0)  
         fprintf(outfile, "Partial matching not supported\n");  
1462    
1463        if (get_options == 0) fprintf(outfile, "No options\n");        if (get_options == 0) fprintf(outfile, "No options\n");
1464          else fprintf(outfile, "Options:%s%s%s%s%s%s%s%s%s%s%s%s%s\n",          else fprintf(outfile, "Options:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
1465            ((get_options & PCRE_ANCHORED) != 0)? " anchored" : "",            ((get_options & PCRE_ANCHORED) != 0)? " anchored" : "",
1466            ((get_options & PCRE_CASELESS) != 0)? " caseless" : "",            ((get_options & PCRE_CASELESS) != 0)? " caseless" : "",
1467            ((get_options & PCRE_EXTENDED) != 0)? " extended" : "",            ((get_options & PCRE_EXTENDED) != 0)? " extended" : "",
1468            ((get_options & PCRE_MULTILINE) != 0)? " multiline" : "",            ((get_options & PCRE_MULTILINE) != 0)? " multiline" : "",
1469            ((get_options & PCRE_FIRSTLINE) != 0)? " firstline" : "",            ((get_options & PCRE_FIRSTLINE) != 0)? " firstline" : "",
1470            ((get_options & PCRE_DOTALL) != 0)? " dotall" : "",            ((get_options & PCRE_DOTALL) != 0)? " dotall" : "",
1471              ((get_options & PCRE_BSR_ANYCRLF) != 0)? " bsr_anycrlf" : "",
1472              ((get_options & PCRE_BSR_UNICODE) != 0)? " bsr_unicode" : "",
1473            ((get_options & PCRE_DOLLAR_ENDONLY) != 0)? " dollar_endonly" : "",            ((get_options & PCRE_DOLLAR_ENDONLY) != 0)? " dollar_endonly" : "",
1474            ((get_options & PCRE_EXTRA) != 0)? " extra" : "",            ((get_options & PCRE_EXTRA) != 0)? " extra" : "",
1475            ((get_options & PCRE_UNGREEDY) != 0)? " ungreedy" : "",            ((get_options & PCRE_UNGREEDY) != 0)? " ungreedy" : "",
# Line 1327  while (!done) Line 1478  while (!done)
1478            ((get_options & PCRE_NO_UTF8_CHECK) != 0)? " no_utf8_check" : "",            ((get_options & PCRE_NO_UTF8_CHECK) != 0)? " no_utf8_check" : "",
1479            ((get_options & PCRE_DUPNAMES) != 0)? " dupnames" : "");            ((get_options & PCRE_DUPNAMES) != 0)? " dupnames" : "");
1480    
1481        switch (get_options & PCRE_NEWLINE_CRLF)        if (jchanged) fprintf(outfile, "Duplicate name status changes\n");
1482    
1483          switch (get_options & PCRE_NEWLINE_BITS)
1484          {          {
1485          case PCRE_NEWLINE_CR:          case PCRE_NEWLINE_CR:
1486          fprintf(outfile, "Forced newline sequence: CR\n");          fprintf(outfile, "Forced newline sequence: CR\n");
# Line 1341  while (!done) Line 1494  while (!done)
1494          fprintf(outfile, "Forced newline sequence: CRLF\n");          fprintf(outfile, "Forced newline sequence: CRLF\n");
1495          break;          break;
1496    
1497            case PCRE_NEWLINE_ANYCRLF:
1498            fprintf(outfile, "Forced newline sequence: ANYCRLF\n");
1499            break;
1500    
1501            case PCRE_NEWLINE_ANY:
1502            fprintf(outfile, "Forced newline sequence: ANY\n");
1503            break;
1504    
1505          default:          default:
1506          break;          break;
1507          }          }
# Line 1358  while (!done) Line 1519  while (!done)
1519          int ch = first_char & 255;          int ch = first_char & 255;
1520          const char *caseless = ((first_char & REQ_CASELESS) == 0)?          const char *caseless = ((first_char & REQ_CASELESS) == 0)?
1521            "" : " (caseless)";            "" : " (caseless)";
1522          if (isprint(ch))          if (PRINTHEX(ch))
1523            fprintf(outfile, "First char = \'%c\'%s\n", ch, caseless);            fprintf(outfile, "First char = \'%c\'%s\n", ch, caseless);
1524          else          else
1525            fprintf(outfile, "First char = %d%s\n", ch, caseless);            fprintf(outfile, "First char = %d%s\n", ch, caseless);
# Line 1373  while (!done) Line 1534  while (!done)
1534          int ch = need_char & 255;          int ch = need_char & 255;
1535          const char *caseless = ((need_char & REQ_CASELESS) == 0)?          const char *caseless = ((need_char & REQ_CASELESS) == 0)?
1536            "" : " (caseless)";            "" : " (caseless)";
1537          if (isprint(ch))          if (PRINTHEX(ch))
1538            fprintf(outfile, "Need char = \'%c\'%s\n", ch, caseless);            fprintf(outfile, "Need char = \'%c\'%s\n", ch, caseless);
1539          else          else
1540            fprintf(outfile, "Need char = %d%s\n", ch, caseless);            fprintf(outfile, "Need char = %d%s\n", ch, caseless);
# Line 1409  while (!done) Line 1570  while (!done)
1570                    fprintf(outfile, "\n  ");                    fprintf(outfile, "\n  ");
1571                    c = 2;                    c = 2;
1572                    }                    }
1573                  if (isprint(i) && i != ' ')                  if (PRINTHEX(i) && i != ' ')
1574                    {                    {
1575                    fprintf(outfile, "%c ", i);                    fprintf(outfile, "%c ", i);
1576                    c += 2;                    c += 2;
# Line 1441  while (!done) Line 1602  while (!done)
1602        else        else
1603          {          {
1604          uschar sbuf[8];          uschar sbuf[8];
1605          sbuf[0] = (true_size >> 24)  & 255;          sbuf[0] = (uschar)((true_size >> 24) & 255);
1606          sbuf[1] = (true_size >> 16)  & 255;          sbuf[1] = (uschar)((true_size >> 16) & 255);
1607          sbuf[2] = (true_size >>  8)  & 255;          sbuf[2] = (uschar)((true_size >>  8) & 255);
1608          sbuf[3] = (true_size)  & 255;          sbuf[3] = (uschar)((true_size) & 255);
1609    
1610          sbuf[4] = (true_study_size >> 24)  & 255;          sbuf[4] = (uschar)((true_study_size >> 24) & 255);
1611          sbuf[5] = (true_study_size >> 16)  & 255;          sbuf[5] = (uschar)((true_study_size >> 16) & 255);
1612          sbuf[6] = (true_study_size >>  8)  & 255;          sbuf[6] = (uschar)((true_study_size >>  8) & 255);
1613          sbuf[7] = (true_study_size)  & 255;          sbuf[7] = (uschar)((true_study_size) & 255);
1614    
1615          if (fwrite(sbuf, 1, 8, f) < 8 ||          if (fwrite(sbuf, 1, 8, f) < 8 ||
1616              fwrite(re, 1, true_size, f) < true_size)              fwrite(re, 1, true_size, f) < true_size)
# Line 1468  while (!done) Line 1629  while (!done)
1629                  strerror(errno));                  strerror(errno));
1630                }                }
1631              else fprintf(outfile, "Study data written to %s\n", to_file);              else fprintf(outfile, "Study data written to %s\n", to_file);
1632    
1633              }              }
1634            }            }
1635          fclose(f);          fclose(f);
# Line 1485  while (!done) Line 1647  while (!done)
1647    for (;;)    for (;;)
1648      {      {
1649      uschar *q;      uschar *q;
1650      uschar *bptr = dbuffer;      uschar *bptr;
1651      int *use_offsets = offsets;      int *use_offsets = offsets;
1652      int use_size_offsets = size_offsets;      int use_size_offsets = size_offsets;
1653      int callout_data = 0;      int callout_data = 0;
# Line 1541  while (!done) Line 1703  while (!done)
1703      p = buffer;      p = buffer;
1704      while (isspace(*p)) p++;      while (isspace(*p)) p++;
1705    
1706      q = dbuffer;      bptr = q = dbuffer;
1707      while ((c = *p++) != 0)      while ((c = *p++) != 0)
1708        {        {
1709        int i = 0;        int i = 0;
# Line 1736  while (!done) Line 1898  while (!done)
1898            if (offsets == NULL)            if (offsets == NULL)
1899              {              {
1900              printf("** Failed to get %d bytes of memory for offsets vector\n",              printf("** Failed to get %d bytes of memory for offsets vector\n",
1901                size_offsets_max * sizeof(int));                (int)(size_offsets_max * sizeof(int)));
1902              yield = 1;              yield = 1;
1903              goto EXIT;              goto EXIT;
1904              }              }
# Line 1866  while (!done) Line 2028  while (!done)
2028    
2029      for (;; gmatched++)    /* Loop for /g or /G */      for (;; gmatched++)    /* Loop for /g or /G */
2030        {        {
2031        if (timeit)        if (timeitm > 0)
2032          {          {
2033          register int i;          register int i;
2034          clock_t time_taken;          clock_t time_taken;
# Line 1876  while (!done) Line 2038  while (!done)
2038          if (all_use_dfa || use_dfa)          if (all_use_dfa || use_dfa)
2039            {            {
2040            int workspace[1000];            int workspace[1000];
2041            for (i = 0; i < LOOPREPEAT; i++)            for (i = 0; i < timeitm; i++)
2042              count = pcre_dfa_exec(re, NULL, (char *)bptr, len, start_offset,              count = pcre_dfa_exec(re, NULL, (char *)bptr, len, start_offset,
2043                options | g_notempty, use_offsets, use_size_offsets, workspace,                options | g_notempty, use_offsets, use_size_offsets, workspace,
2044                sizeof(workspace)/sizeof(int));                sizeof(workspace)/sizeof(int));
# Line 1884  while (!done) Line 2046  while (!done)
2046          else          else
2047  #endif  #endif
2048    
2049          for (i = 0; i < LOOPREPEAT; i++)          for (i = 0; i < timeitm; i++)
2050            count = pcre_exec(re, extra, (char *)bptr, len,            count = pcre_exec(re, extra, (char *)bptr, len,
2051              start_offset, options | g_notempty, use_offsets, use_size_offsets);              start_offset, options | g_notempty, use_offsets, use_size_offsets);
2052    
2053          time_taken = clock() - start_time;          time_taken = clock() - start_time;
2054          fprintf(outfile, "Execute time %.3f milliseconds\n",          fprintf(outfile, "Execute time %.4f milliseconds\n",
2055            (((double)time_taken * 1000.0) / (double)LOOPREPEAT) /            (((double)time_taken * 1000.0) / (double)timeitm) /
2056              (double)CLOCKS_PER_SEC);              (double)CLOCKS_PER_SEC);
2057          }          }
2058    
# Line 1966  while (!done) Line 2128  while (!done)
2128    
2129        if (count >= 0)        if (count >= 0)
2130          {          {
2131          int i;          int i, maxcount;
2132    
2133    #if !defined NODFA
2134            if (all_use_dfa || use_dfa) maxcount = use_size_offsets/2; else
2135    #endif
2136              maxcount = use_size_offsets/3;
2137    
2138            /* This is a check against a lunatic return value. */
2139    
2140            if (count > maxcount)
2141              {
2142              fprintf(outfile,
2143                "** PCRE error: returned count %d is too big for offset size %d\n",
2144                count, use_size_offsets);
2145              count = use_size_offsets/3;
2146              if (do_g || do_G)
2147                {
2148                fprintf(outfile, "** /%c loop abandoned\n", do_g? 'g' : 'G');
2149                do_g = do_G = FALSE;        /* Break g/G loop */
2150                }
2151              }
2152    
2153          for (i = 0; i < count * 2; i += 2)          for (i = 0; i < count * 2; i += 2)
2154            {            {
2155            if (use_offsets[i] < 0)            if (use_offsets[i] < 0)
# Line 2084  while (!done) Line 2267  while (!done)
2267          }          }
2268    
2269        /* Failed to match. If this is a /g or /G loop and we previously set        /* Failed to match. If this is a /g or /G loop and we previously set
2270        g_notempty after a null match, this is not necessarily the end.        g_notempty after a null match, this is not necessarily the end. We want
2271        We want to advance the start offset, and continue. In the case of UTF-8        to advance the start offset, and continue. We won't be at the end of the
2272        matching, the advance must be one character, not one byte. Fudge the        string - that was checked before setting g_notempty.
2273        offset values to achieve this. We won't be at the end of the string -  
2274        that was checked before setting g_notempty. */        Complication arises in the case when the newline option is "any" or
2275          "anycrlf". If the previous match was at the end of a line terminated by
2276          CRLF, an advance of one character just passes the \r, whereas we should
2277          prefer the longer newline sequence, as does the code in pcre_exec().
2278          Fudge the offset value to achieve this.
2279    
2280          Otherwise, in the case of UTF-8 matching, the advance must be one
2281          character, not one byte. */
2282    
2283        else        else
2284          {          {
2285          if (g_notempty != 0)          if (g_notempty != 0)
2286            {            {
2287            int onechar = 1;            int onechar = 1;
2288              unsigned int obits = ((real_pcre *)re)->options;
2289            use_offsets[0] = start_offset;            use_offsets[0] = start_offset;
2290            if (use_utf8)            if ((obits & PCRE_NEWLINE_BITS) == 0)
2291                {
2292                int d;
2293                (void)pcre_config(PCRE_CONFIG_NEWLINE, &d);
2294                obits = (d == '\r')? PCRE_NEWLINE_CR :
2295                        (d == '\n')? PCRE_NEWLINE_LF :
2296                        (d == ('\r'<<8 | '\n'))? PCRE_NEWLINE_CRLF :
2297                        (d == -2)? PCRE_NEWLINE_ANYCRLF :
2298                        (d == -1)? PCRE_NEWLINE_ANY : 0;
2299                }
2300              if (((obits & PCRE_NEWLINE_BITS) == PCRE_NEWLINE_ANY ||
2301                   (obits & PCRE_NEWLINE_BITS) == PCRE_NEWLINE_ANYCRLF)
2302                  &&
2303                  start_offset < len - 1 &&
2304                  bptr[start_offset] == '\r' &&
2305                  bptr[start_offset+1] == '\n')
2306                onechar++;
2307              else if (use_utf8)
2308              {              {
2309              while (start_offset + onechar < len)              while (start_offset + onechar < len)
2310                {                {
# Line 2131  while (!done) Line 2339  while (!done)
2339        character. */        character. */
2340    
2341        g_notempty = 0;        g_notempty = 0;
2342    
2343        if (use_offsets[0] == use_offsets[1])        if (use_offsets[0] == use_offsets[1])
2344          {          {
2345          if (use_offsets[0] == len) break;          if (use_offsets[0] == len) break;
# Line 2165  while (!done) Line 2374  while (!done)
2374      {      {
2375      new_free((void *)tables);      new_free((void *)tables);
2376      setlocale(LC_CTYPE, "C");      setlocale(LC_CTYPE, "C");
2377        locale_set = 0;
2378      }      }
2379    }    }
2380    

Legend:
Removed from v.91  
changed lines
  Added in v.255

  ViewVC Help
Powered by ViewVC 1.1.5