- coverity 10398
[oweals/gnunet.git] / src / regex / regex_test_lib.c
1 /*
2  *  This file is part of GNUnet
3  *  (C) 2012 Christian Grothoff (and other contributing authors)
4  * 
5  *  GNUnet is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published
7  *  by the Free Software Foundation; either version 3, or (at your
8  *  option) any later version.
9  * 
10  *  GNUnet is distributed in the hope that it will be useful, but
11  *  WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  General Public License for more details.
14  * 
15  *  You should have received a copy of the GNU General Public License
16  *  along with GNUnet; see the file COPYING.  If not, write to the
17  *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  *  Boston, MA 02111-1307, USA.
19  */
20 /**
21  * @file src/regex/regex_test_lib.c
22  * @brief library to read regexes representing IP networks from a file.
23  *        and simplyfinying the into one big regex, in order to run
24  *        tests (regex performance, mesh profiler).
25  * @author Bartlomiej Polot
26  */
27
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30
31 struct RegexCombineCtx {
32   struct RegexCombineCtx *next;
33   struct RegexCombineCtx *prev;
34
35   struct RegexCombineCtx *head;
36   struct RegexCombineCtx *tail;
37
38   char *s;
39 };
40
41
42 /**
43  * Extract a string from all prefix-combined regexes.
44  *
45  * @param ctx Context with 0 or more regexes.
46  *
47  * @return Regex that matches any of the added regexes.
48  */
49 static char *
50 regex_combine (struct RegexCombineCtx *ctx)
51 {
52   struct RegexCombineCtx *p;
53   size_t len;
54   char *regex;
55   char *tmp;
56   char *s;
57
58   if (NULL != ctx->s)
59     GNUNET_asprintf (&regex, "%s(", ctx->s);
60   else
61     regex = GNUNET_strdup ("(");
62   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "prefix: %s\n", regex);
63
64   for (p = ctx->head; NULL != p; p = p->next)
65   {
66     s = regex_combine (p);
67     GNUNET_asprintf (&tmp, "%s%s|", regex, s);
68     GNUNET_free_non_null (s);
69     GNUNET_free_non_null (regex);
70     regex = tmp;
71   }
72   len = strlen (regex);
73   if (1 == len)
74   {
75     GNUNET_free (regex);
76     return GNUNET_strdup ("");
77   }
78
79   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pre-partial: %s\n", regex);
80   if ('|' == regex[len - 1])
81     regex[len - 1] = ')';
82   if ('(' == regex[len - 1])
83     regex[len - 1] = '\0';
84
85   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "partial: %s\n", regex);
86   return regex;
87 }
88
89
90 /**
91  * Add a single regex to a context, combining with exisiting regex by-prefix.
92  *
93  * @param ctx Context with 0 or more regexes.
94  * @param regex Regex to add.
95  */
96 static void
97 regex_add (struct RegexCombineCtx *ctx, const char *regex)
98 {
99   struct RegexCombineCtx *p;
100   const char *rest;
101
102   rest = &regex[1];
103   for (p = ctx->head; NULL != p; p = p->next)
104   {
105     if (p->s[0] == regex[0])
106     {
107       if (1 == strlen(p->s))
108       {
109         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "common char %s\n", p->s);
110         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "adding %s\n", rest);
111         regex_add (p, rest);
112       }
113       else
114       {
115         struct RegexCombineCtx *new;
116         new = GNUNET_malloc (sizeof (struct RegexCombineCtx));
117         new->s = GNUNET_strdup (&p->s[1]);
118         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " p has now %s\n", p->s);
119         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " p will have %.1s\n", p->s);
120         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " regex is %s\n", regex);
121         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " new has now %s\n", new->s);
122         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " rest is now %s\n", rest);
123         p->s[1] = '\0'; /* dont realloc */
124         GNUNET_CONTAINER_DLL_insert (p->head, p->tail, new);
125         regex_add (p, rest);
126       }
127       return;
128     }
129   }
130   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " no  match\n");
131   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " new state %s\n", regex);
132   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " under %s\n", ctx->s);
133   p = GNUNET_malloc (sizeof (struct RegexCombineCtx));
134   p->s = GNUNET_strdup (regex);
135   GNUNET_CONTAINER_DLL_insert (ctx->head, ctx->tail, p);
136 }
137
138
139 /**
140  * Free all resources used by the context node and all its children.
141  *
142  * @param ctx Context to free.
143  */
144 static void
145 regex_ctx_destroy (struct RegexCombineCtx *ctx)
146 {
147   struct RegexCombineCtx *p;
148   struct RegexCombineCtx *next;
149
150   for (p = ctx->head; NULL != p; p = next)
151   {
152     next = p->next;
153     regex_ctx_destroy (p);
154   }
155   GNUNET_free (ctx->s);
156   GNUNET_free (ctx);
157 }
158
159
160 /**
161  * Return a prefix-combine regex that matches the same strings as
162  * any of the original regexes.
163  *
164  * WARNING: only useful for reading specific regexes for specific applications,
165  *          namely the gnunet-regex-profiler / gnunet-regex-daemon.
166  *          This function DOES NOT support arbitrary regex combining.
167  */
168 char *
169 GNUNET_REGEX_combine (char * const regexes[])
170 {
171   unsigned int i;
172   char *combined;
173   const char *current;
174   struct RegexCombineCtx *ctx;
175
176   ctx = GNUNET_malloc (sizeof (struct RegexCombineCtx));
177   for (i = 0; regexes[i]; i++)
178   {
179     current = regexes[i];
180     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Regex %u: %s\n", i, current);
181     regex_add (ctx, current);
182   }
183   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\nCombining...\n");
184
185   combined = regex_combine (ctx);
186
187   regex_ctx_destroy (ctx);
188
189   return combined;
190 }
191
192
193 /**
194  * Read a set of regexes from a file, one per line and return them in an array
195  * suitable for GNUNET_REGEX_combine.
196  * The array must be free'd using GNUNET_REGEX_free_from_file.
197  *
198  * @param filename Name of the file containing the regexes.
199  *
200  * @return A newly allocated, NULL terminated array of regexes.
201  */
202 char **
203 GNUNET_REGEX_read_from_file (const char *filename)
204 {
205   struct GNUNET_DISK_FileHandle *f;
206   unsigned int nr;
207   unsigned int offset;
208   off_t size;
209   size_t len;
210   char *buffer;
211   char *regex;
212   char **regexes;
213
214   f = GNUNET_DISK_file_open (filename,
215                              GNUNET_DISK_OPEN_READ,
216                              GNUNET_DISK_PERM_NONE);
217   if (NULL == f)
218   {
219     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
220                 "Can't open file %s for reading\n", filename);
221     return NULL;
222   }
223   if (GNUNET_OK != GNUNET_DISK_file_handle_size (f, &size))
224   {
225     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
226                 "Can't get size of file %s\n", filename);
227     GNUNET_DISK_file_close (f);
228     return NULL;
229   }
230   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
231               "using file %s, size %llu\n",
232               filename, (unsigned long long) size);
233
234   buffer = GNUNET_malloc (size + 1);
235   GNUNET_DISK_file_read (f, buffer, size);
236   GNUNET_DISK_file_close (f);
237   regexes = GNUNET_malloc (sizeof (char *));
238   nr = 1;
239   offset = 0;
240   regex = NULL;
241   do
242   {
243     if (NULL == regex)
244       regex = GNUNET_malloc (size + 1);
245     len = (size_t) sscanf (&buffer[offset], "%s", regex);
246     if (0 == len)
247       break;
248     len = strlen (regex);
249     offset += len + 1;
250     if (len < 1)
251       continue;
252     if (len < 6 || strncmp (&regex[len - 6], "(0|1)*", 6) != 0)
253     {
254       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
255                   "%s (line %u) does not end in \"(0|1)*\"\n",
256                   buffer, nr);
257     }
258     else
259     {
260       len -= 6;
261       buffer[len] = '\0';
262     }
263     regex = GNUNET_realloc (regex, len + 1);
264     GNUNET_array_grow (regexes, nr, nr + 1);
265     regexes[nr - 2] = regex;
266     regexes[nr - 1] = NULL;
267   } while (offset < size);
268   GNUNET_free (buffer);
269
270   return regexes;
271 }
272
273
274 /**
275  * Free all memory reserved for a set of regexes created by read_from_file.
276  *
277  * @param regexes NULL-terminated array of regexes.
278  */
279 void
280 GNUNET_REGEX_free_from_file (char **regexes)
281 {
282   unsigned int i;
283
284   for (i = 0; regexes[i]; i++)
285     GNUNET_free (regexes[i]);
286   GNUNET_free (regexes);
287 }
288
289 /* end of regex_test_lib.c */