9863d6f4f2de2ee7d38db75d81dbb53cbf5877a6
[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     return GNUNET_strdup ("");
75
76   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "pre-partial: %s\n", regex);
77   if ('|' == regex[len - 1])
78     regex[len - 1] = ')';
79   if ('(' == regex[len - 1])
80     regex[len - 1] = '\0';
81
82   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "partial: %s\n", regex);
83   return regex;
84 }
85
86
87 /**
88  * Add a single regex to a context, combining with exisiting regex by-prefix.
89  *
90  * @param ctx Context with 0 or more regexes.
91  * @param regex Regex to add.
92  */
93 static void
94 regex_add (struct RegexCombineCtx *ctx, const char *regex)
95 {
96   struct RegexCombineCtx *p;
97   const char *rest;
98
99   rest = &regex[1];
100   for (p = ctx->head; NULL != p; p = p->next)
101   {
102     if (p->s[0] == regex[0])
103     {
104       if (1 == strlen(p->s))
105       {
106         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "common char %s\n", p->s);
107         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "adding %s\n", rest);
108         regex_add (p, rest);
109       }
110       else
111       {
112         struct RegexCombineCtx *new;
113         new = GNUNET_malloc (sizeof (struct RegexCombineCtx));
114         new->s = GNUNET_strdup (&p->s[1]);
115         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " p has now %s\n", p->s);
116         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " p will have %.1s\n", p->s);
117         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " regex is %s\n", regex);
118         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " new has now %s\n", new->s);
119         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " rest is now %s\n", rest);
120         p->s[1] = '\0'; /* dont realloc */
121         GNUNET_CONTAINER_DLL_insert (p->head, p->tail, new);
122         regex_add (p, rest);
123       }
124       return;
125     }
126   }
127   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " no  match\n");
128   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " new state %s\n", regex);
129   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " under %s\n", ctx->s);
130   p = GNUNET_malloc (sizeof (struct RegexCombineCtx));
131   p->s = GNUNET_strdup (regex);
132   GNUNET_CONTAINER_DLL_insert (ctx->head, ctx->tail, p);
133 }
134
135
136 /**
137  * Free all resources used by the context node and all its children.
138  *
139  * @param ctx Context to free.
140  */
141 static void
142 regex_ctx_destroy (struct RegexCombineCtx *ctx)
143 {
144   struct RegexCombineCtx *p;
145   struct RegexCombineCtx *next;
146
147   for (p = ctx->head; NULL != p; p = next)
148   {
149     next = p->next;
150     regex_ctx_destroy (p);
151   }
152   GNUNET_free (ctx->s);
153   GNUNET_free (ctx);
154 }
155
156
157 /**
158  * Return a prefix-combine regex that matches the same strings as
159  * any of the original regexes.
160  *
161  * WARNING: only useful for reading specific regexes for specific applications,
162  *          namely the gnunet-regex-profiler / gnunet-regex-daemon.
163  *          This function DOES NOT support arbitrary regex combining.
164  */
165 char *
166 GNUNET_REGEX_combine (char * const regexes[])
167 {
168   unsigned int i;
169   char *combined;
170   const char *current;
171   struct RegexCombineCtx *ctx;
172
173   ctx = GNUNET_malloc (sizeof (struct RegexCombineCtx));
174   for (i = 0; regexes[i]; i++)
175   {
176     current = regexes[i];
177     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Regex %u: %s\n", i, current);
178     regex_add (ctx, current);
179   }
180   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "\nCombining...\n");
181
182   combined = regex_combine (ctx);
183
184   regex_ctx_destroy (ctx);
185
186   return combined;
187 }
188
189
190 /**
191  * Read a set of regexes from a file, one per line and return them in an array
192  * suitable for GNUNET_REGEX_combine.
193  * The array must be free'd using GNUNET_REGEX_free_from_file.
194  *
195  * @param filename Name of the file containing the regexes.
196  *
197  * @return A newly allocated, NULL terminated array of regexes.
198  */
199 char **
200 GNUNET_REGEX_read_from_file (const char *filename)
201 {
202   struct GNUNET_DISK_FileHandle *f;
203   unsigned int nr;
204   unsigned int offset;
205   off_t size;
206   size_t len;
207   char *buffer;
208   char *regex;
209   char **regexes;
210
211   f = GNUNET_DISK_file_open (filename,
212                              GNUNET_DISK_OPEN_READ,
213                              GNUNET_DISK_PERM_NONE);
214   if (NULL == f)
215   {
216     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
217                 "Can't open file %s for reading\n", filename);
218     return NULL;
219   }
220   if (GNUNET_OK != GNUNET_DISK_file_handle_size (f, &size))
221   {
222     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
223                 "Can't get size of file %s\n", filename);
224     return NULL;
225   }
226   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
227               "using file %s, size %llu\n",
228               filename, (unsigned long long) size);
229
230   buffer = GNUNET_malloc (size + 1);
231   GNUNET_DISK_file_read (f, buffer, size);
232   GNUNET_DISK_file_close (f);
233   regexes = GNUNET_malloc (sizeof (char *));
234   nr = 1;
235   offset = 0;
236   regex = NULL;
237   do
238   {
239     if (NULL == regex)
240       regex = GNUNET_malloc (size + 1);
241     len = (size_t) sscanf (&buffer[offset], "%s", regex);
242     if (0 == len)
243       break;
244     len = strlen (regex);
245     offset += len + 1;
246     if (len < 1)
247       continue;
248     if (len < 6 || strncmp (&regex[len - 6], "(0|1)*", 6) != 0)
249     {
250       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
251                   "%s (line %u) does not end in \"(0|1)*\"\n",
252                   buffer, nr);
253     }
254     else
255     {
256       len -= 6;
257       buffer[len] = '\0';
258     }
259     regex = GNUNET_realloc (regex, len + 1);
260     GNUNET_array_grow (regexes, nr, nr + 1);
261     regexes[nr - 2] = regex;
262     regexes[nr - 1] = NULL;
263   } while (offset < size);
264   GNUNET_free (buffer);
265
266   return regexes;
267 }
268
269
270 /**
271  * Free all memory reserved for a set of regexes created by read_from_file.
272  *
273  * @param regexes NULL-terminated array of regexes.
274  */
275 void
276 GNUNET_REGEX_free_from_file (char **regexes)
277 {
278   unsigned int i;
279
280   for (i = 0; regexes[i]; i++)
281     GNUNET_free (regexes[i]);
282   GNUNET_free (regexes);
283 }
284
285 /* end of regex_test_lib.c */