(tests): Add tst-getaddrinfo3.
[kopensolaris-gnu/glibc.git] / posix / tst-regex.c
1 /* Copyright (C) 2001, 2003 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <spawn.h>
20 #include "spawn_int.h"
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <error.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <iconv.h>
28 #include <locale.h>
29 #include <mcheck.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34 #include <unistd.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <regex.h>
38
39
40 #if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0
41 static clockid_t cl;
42 static int use_clock;
43 #endif
44 static iconv_t cd;
45 static char *mem;
46 static char *umem;
47 static size_t memlen;
48 static size_t umemlen;
49 static int timing;
50
51 static int test_expr (const char *expr, int expected, int expectedicase);
52 static int run_test (const char *expr, const char *mem, size_t memlen,
53                      int icase, int expected);
54 static int run_test_backwards (const char *expr, const char *mem,
55                                size_t memlen, int icase, int expected);
56
57
58 int
59 main (int argc, char *argv[])
60 {
61   const char *file;
62   int fd;
63   struct stat st;
64   int result;
65   char *inmem;
66   char *outmem;
67   size_t inlen;
68   size_t outlen;
69   static const struct option options[] =
70     {
71       {"timing",no_argument,    &timing,        1 },
72       {NULL,    0,              NULL,           0 }
73     };
74
75   mtrace ();
76
77   while (getopt_long (argc, argv, "", options, NULL) >= 0);
78
79   /* Make the content of the file available in memory.  */
80   file = "../ChangeLog.8";
81   fd = open (file, O_RDONLY);
82   if (fd == -1)
83     error (EXIT_FAILURE, errno, "cannot open %s", basename (file));
84
85   if (fstat (fd, &st) != 0)
86     error (EXIT_FAILURE, errno, "cannot stat %s", basename (file));
87   memlen = st.st_size;
88
89   mem = (char *) malloc (memlen + 1);
90   if (mem == NULL)
91     error (EXIT_FAILURE, errno, "while allocating buffer");
92
93   if ((size_t) read (fd, mem, memlen) != memlen)
94     error (EXIT_FAILURE, 0, "cannot read entire file");
95   mem[memlen] = '\0';
96
97   close (fd);
98
99   /* We have to convert a few things from Latin-1 to UTF-8.  */
100   cd = iconv_open ("UTF-8", "ISO-8859-1");
101   if (cd == (iconv_t) -1)
102     error (EXIT_FAILURE, errno, "cannot get conversion descriptor");
103
104   /* For the second test we have to convert the file content to UTF-8.
105      Since the text is mostly ASCII it should be enough to allocate
106      twice as much memory for the UTF-8 text than for the Latin-1
107      text.  */
108   umem = (char *) calloc (2, memlen);
109   if (umem == NULL)
110     error (EXIT_FAILURE, errno, "while allocating buffer");
111
112   inmem = mem;
113   inlen = memlen;
114   outmem = umem;
115   outlen = 2 * memlen - 1;
116   iconv (cd, &inmem, &inlen, &outmem, &outlen);
117   umemlen = outmem - umem;
118   if (inlen != 0)
119     error (EXIT_FAILURE, errno, "cannot convert buffer");
120
121 #if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0
122 # if _POSIX_CPUTIME == 0
123   if (sysconf (_SC_CPUTIME) < 0)
124     use_clock = 0;
125   else
126 # endif
127     /* See whether we can use the CPU clock.  */
128     use_clock = clock_getcpuclockid (0, &cl) == 0;
129 #endif
130
131 #ifdef DEBUG
132   re_set_syntax (RE_DEBUG);
133 #endif
134
135   /* Run the actual tests.  All tests are run in a single-byte and a
136      multi-byte locale.  */
137   result = test_expr ("[äáàâéèêíìîñöóòôüúùû]", 2, 2);
138   result |= test_expr ("G.ran", 2, 3);
139   result |= test_expr ("G.\\{1\\}ran", 2, 3);
140   result |= test_expr ("G.*ran", 3, 44);
141   result |= test_expr ("[äáàâ]", 0, 0);
142   result |= test_expr ("Uddeborg", 2, 2);
143   result |= test_expr (".Uddeborg", 2, 2);
144
145   /* Free the resources.  */
146   free (umem);
147   iconv_close (cd);
148   free (mem);
149
150   return result;
151 }
152
153
154 static int
155 test_expr (const char *expr, int expected, int expectedicase)
156 {
157   int result;
158   char *inmem;
159   char *outmem;
160   size_t inlen;
161   size_t outlen;
162   char *uexpr;
163
164   /* First test: search with an ISO-8859-1 locale.  */
165   if (setlocale (LC_ALL, "de_DE.ISO-8859-1") == NULL)
166     error (EXIT_FAILURE, 0, "cannot set locale de_DE.ISO-8859-1");
167
168   printf ("\nTest \"%s\" with 8-bit locale\n", expr);
169   result = run_test (expr, mem, memlen, 0, expected);
170   printf ("\nTest \"%s\" with 8-bit locale, case insensitive\n", expr);
171   result |= run_test (expr, mem, memlen, 1, expectedicase);
172   printf ("\nTest \"%s\" backwards with 8-bit locale\n", expr);
173   result |= run_test_backwards (expr, mem, memlen, 0, expected);
174   printf ("\nTest \"%s\" backwards with 8-bit locale, case insensitive\n",
175           expr);
176   result |= run_test_backwards (expr, mem, memlen, 1, expectedicase);
177
178   /* Second test: search with an UTF-8 locale.  */
179   if (setlocale (LC_ALL, "de_DE.UTF-8") == NULL)
180     error (EXIT_FAILURE, 0, "cannot set locale de_DE.UTF-8");
181
182   inmem = (char *) expr;
183   inlen = strlen (expr);
184   outlen = inlen * MB_CUR_MAX;
185   outmem = uexpr = alloca (outlen + 1);
186   memset (outmem, '\0', outlen + 1);
187   iconv (cd, &inmem, &inlen, &outmem, &outlen);
188   if (inlen != 0)
189     error (EXIT_FAILURE, errno, "cannot convert expression");
190
191   /* Run the tests.  */
192   printf ("\nTest \"%s\" with multi-byte locale\n", expr);
193   result |= run_test (uexpr, umem, umemlen, 0, expected);
194   printf ("\nTest \"%s\" with multi-byte locale, case insensitive\n", expr);
195   result |= run_test (uexpr, umem, umemlen, 1, expectedicase);
196   printf ("\nTest \"%s\" backwards with multi-byte locale\n", expr);
197   result |= run_test_backwards (uexpr, umem, umemlen, 0, expected);
198   printf ("\nTest \"%s\" backwards with multi-byte locale, case insensitive\n",
199           expr);
200   result |= run_test_backwards (uexpr, umem, umemlen, 1, expectedicase);
201
202   return result;
203 }
204
205
206 static int
207 run_test (const char *expr, const char *mem, size_t memlen, int icase,
208           int expected)
209 {
210 #if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0
211   struct timespec start;
212   struct timespec finish;
213 #endif
214   regex_t re;
215   int err;
216   size_t offset;
217   int cnt;
218
219 #if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0
220   if (use_clock && !timing)
221     use_clock = clock_gettime (cl, &start) == 0;
222 #endif
223
224   err = regcomp (&re, expr, REG_NEWLINE | (icase ? REG_ICASE : 0));
225   if (err != REG_NOERROR)
226     {
227       char buf[200];
228       regerror (err, &re, buf, sizeof buf);
229       error (EXIT_FAILURE, 0, "cannot compile expression: %s", buf);
230     }
231
232   cnt = 0;
233   offset = 0;
234   assert (mem[memlen] == '\0');
235   while (offset < memlen)
236     {
237       regmatch_t ma[1];
238       const char *sp;
239       const char *ep;
240
241       err = regexec (&re, mem + offset, 1, ma, 0);
242       if (err == REG_NOMATCH)
243         break;
244
245       if (err != REG_NOERROR)
246         {
247           char buf[200];
248           regerror (err, &re, buf, sizeof buf);
249           error (EXIT_FAILURE, 0, "cannot use expression: %s", buf);
250         }
251
252       assert (ma[0].rm_so >= 0);
253       sp = mem + offset + ma[0].rm_so;
254       while (sp > mem && sp[-1] != '\n')
255         --sp;
256
257       ep = mem + offset + ma[0].rm_so;
258       while (*ep != '\0' && *ep != '\n')
259         ++ep;
260
261       printf ("match %d: \"%.*s\"\n", ++cnt, (int) (ep - sp), sp);
262
263       offset = ep + 1 - mem;
264     }
265
266   regfree (&re);
267
268 #if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0
269   if (use_clock && !timing)
270     {
271       use_clock = clock_gettime (cl, &finish) == 0;
272       if (use_clock)
273         {
274           if (finish.tv_nsec < start.tv_nsec)
275             {
276               finish.tv_nsec -= start.tv_nsec - 1000000000;
277               finish.tv_sec -= 1 + start.tv_sec;
278             }
279           else
280             {
281               finish.tv_nsec -= start.tv_nsec;
282               finish.tv_sec -= start.tv_sec;
283             }
284
285           printf ("elapsed time: %ld.%09ld sec\n",
286                   finish.tv_sec, finish.tv_nsec);
287         }
288     }
289
290   if (use_clock && timing)
291     {
292       struct timespec mintime = { .tv_sec = 24 * 60 * 60 };
293
294       for (int i = 0; i < 10; ++i)
295         {
296           offset = 0;
297           use_clock = clock_gettime (cl, &start) == 0;
298
299           if (!use_clock)
300             continue;
301
302           err = regcomp (&re, expr, REG_NEWLINE | (icase ? REG_ICASE : 0));
303           if (err != REG_NOERROR)
304             continue;
305
306           while (offset < memlen)
307             {
308               regmatch_t ma[1];
309
310               err = regexec (&re, mem + offset, 1, ma, 0);
311               if (err != REG_NOERROR)
312                 break;
313
314               offset += ma[0].rm_eo;
315             }
316
317           regfree (&re);
318
319           use_clock = clock_gettime (cl, &finish) == 0;
320           if (use_clock)
321             {
322               if (finish.tv_nsec < start.tv_nsec)
323                 {
324                   finish.tv_nsec -= start.tv_nsec - 1000000000;
325                   finish.tv_sec -= 1 + start.tv_sec;
326                 }
327               else
328                 {
329                   finish.tv_nsec -= start.tv_nsec;
330                   finish.tv_sec -= start.tv_sec;
331                 }
332               if (finish.tv_sec < mintime.tv_sec
333                   || (finish.tv_sec == mintime.tv_sec
334                       && finish.tv_nsec < mintime.tv_nsec))
335                 mintime = finish;
336             }
337         }
338       printf ("elapsed time: %ld.%09ld sec\n",
339               mintime.tv_sec, mintime.tv_nsec);
340     }
341 #endif
342
343   /* Return an error if the number of matches found is not match we
344      expect.  */
345   return cnt != expected;
346 }
347
348
349 static int
350 run_test_backwards (const char *expr, const char *mem, size_t memlen,
351                     int icase, int expected)
352 {
353 #if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0
354   struct timespec start;
355   struct timespec finish;
356 #endif
357   struct re_pattern_buffer re;
358   const char *err;
359   size_t offset;
360   int cnt;
361
362 #if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0
363   if (use_clock && !timing)
364     use_clock = clock_gettime (cl, &start) == 0;
365 #endif
366
367   re_set_syntax ((RE_SYNTAX_POSIX_BASIC & ~RE_DOT_NEWLINE)
368                  | RE_HAT_LISTS_NOT_NEWLINE
369                  | (icase ? RE_ICASE : 0));
370
371   memset (&re, 0, sizeof (re));
372   re.fastmap = malloc (256);
373   if (re.fastmap == NULL)
374     error (EXIT_FAILURE, errno, "cannot allocate fastmap");
375
376   err = re_compile_pattern (expr, strlen (expr), &re);
377   if (err != NULL)
378     error (EXIT_FAILURE, 0, "cannot compile expression: %s", err);
379
380   if (re_compile_fastmap (&re))
381     error (EXIT_FAILURE, 0, "couldn't compile fastmap");
382
383   cnt = 0;
384   offset = memlen;
385   assert (mem[memlen] == '\0');
386   while (offset <= memlen)
387     {
388       int start;
389       const char *sp;
390       const char *ep;
391
392       start = re_search (&re, mem, memlen, offset, -offset, NULL);
393       if (start == -1)
394         break;
395
396       if (start == -2)
397         error (EXIT_FAILURE, 0, "internal error in re_search");
398
399       sp = mem + start;
400       while (sp > mem && sp[-1] != '\n')
401         --sp;
402
403       ep = mem + start;
404       while (*ep != '\0' && *ep != '\n')
405         ++ep;
406
407       printf ("match %d: \"%.*s\"\n", ++cnt, (int) (ep - sp), sp);
408
409       offset = sp - 1 - mem;
410     }
411
412   regfree (&re);
413
414 #if defined _POSIX_CPUTIME && _POSIX_CPUTIME >= 0
415   if (use_clock && !timing)
416     {
417       use_clock = clock_gettime (cl, &finish) == 0;
418       if (use_clock)
419         {
420           if (finish.tv_nsec < start.tv_nsec)
421             {
422               finish.tv_nsec -= start.tv_nsec - 1000000000;
423               finish.tv_sec -= 1 + start.tv_sec;
424             }
425           else
426             {
427               finish.tv_nsec -= start.tv_nsec;
428               finish.tv_sec -= start.tv_sec;
429             }
430
431           printf ("elapsed time: %ld.%09ld sec\n",
432                   finish.tv_sec, finish.tv_nsec);
433         }
434     }
435
436   if (use_clock && timing)
437     {
438       struct timespec mintime = { .tv_sec = 24 * 60 * 60 };
439
440       for (int i = 0; i < 10; ++i)
441         {
442           offset = memlen;
443           use_clock = clock_gettime (cl, &start) == 0;
444
445           if (!use_clock)
446             continue;
447
448           memset (&re, 0, sizeof (re));
449           re.fastmap = malloc (256);
450           if (re.fastmap == NULL)
451             continue;
452
453           err = re_compile_pattern (expr, strlen (expr), &re);
454           if (err != NULL)
455             continue;
456
457           if (re_compile_fastmap (&re))
458             {
459               regfree (&re);
460               continue;
461             }
462
463           while (offset <= memlen)
464             {
465               int start;
466               const char *sp;
467
468               start = re_search (&re, mem, memlen, offset, -offset, NULL);
469               if (start < -1)
470                 break;
471
472               sp = mem + start;
473               while (sp > mem && sp[-1] != '\n')
474                 --sp;
475
476               offset = sp - 1 - mem;
477             }
478
479           regfree (&re);
480
481           use_clock = clock_gettime (cl, &finish) == 0;
482           if (use_clock)
483             {
484               if (finish.tv_nsec < start.tv_nsec)
485                 {
486                   finish.tv_nsec -= start.tv_nsec - 1000000000;
487                   finish.tv_sec -= 1 + start.tv_sec;
488                 }
489               else
490                 {
491                   finish.tv_nsec -= start.tv_nsec;
492                   finish.tv_sec -= start.tv_sec;
493                 }
494               if (finish.tv_sec < mintime.tv_sec
495                   || (finish.tv_sec == mintime.tv_sec
496                       && finish.tv_nsec < mintime.tv_nsec))
497                 mintime = finish;
498             }
499         }
500       printf ("elapsed time: %ld.%09ld sec\n",
501               mintime.tv_sec, mintime.tv_nsec);
502     }
503 #endif
504
505   /* Return an error if the number of matches found is not match we
506      expect.  */
507   return cnt != expected;
508 }