some cleanup and pset of svn:ignore
[obnox/wireshark/wip.git] / epan / dfilter / dfilter-macro.c
1 /* dfilter-macro.c
2  *
3  * $Id$
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 2001 Gerald Combs
8  * 
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <stdio.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <string.h>
33
34 #include "dfilter-int.h"
35 #include "dfilter.h"
36 #include "dfilter-macro.h"
37 #include <epan/emem.h>
38 #include <epan/uat.h>
39 #include <epan/report_err.h>
40
41 static uat_t* dfilter_macro_uat = NULL;
42 static dfilter_macro_t* macros = NULL;
43 static guint num_macros;
44
45 void dfilter_macro_foreach(dfilter_macro_cb_t cb, void* data) {
46         guint i;
47         
48         for (i = 0; i < num_macros; i++) {
49                 cb(&(macros[i]),data);
50         }
51         return;
52 }
53
54 static void macro_fprint(dfilter_macro_t* m, void* ud) {
55         FILE* f = ud;
56         
57         fprintf(f,"%s\t%s\n",m->name,m->text);
58 }
59
60 void dfilter_macro_save(const gchar* filename, gchar** error) {
61         FILE* f = fopen(filename,"w");
62         
63         if (!f) {
64                 *error = ep_strdup_printf("Could not open file: '%s', error: %s\n", filename, strerror(errno) );
65                 return;
66         }
67         
68         dfilter_macro_foreach(macro_fprint, f);
69                 
70         fclose(f);
71         
72         return;
73 }
74
75 #ifdef DUMP_MACROS
76 static void macro_dump(dfilter_macro_t* m _U_, void* ud _U_) {
77         gchar** part = m->parts;
78         int* args_pos = m->args_pos;
79
80         printf("\n->%s\t%s\t%d [%d]\n\t'%s'\n",
81                    m->name, m->text, m->argc, m->usable, *(part++));
82         
83         while (*part) {
84                 printf("\t$%d '%s'\n",*args_pos,*part);
85                 
86                 args_pos++;
87                 part++;
88         }
89 }
90 #else
91 #define macro_dump(a,b)
92 #endif
93
94 void dfilter_macro_dump(void) {
95 #ifdef DUMP_MACROS
96         dfilter_macro_foreach(macro_dump, NULL);
97 #endif
98 }
99
100 static gchar* dfilter_macro_resolve(gchar* name, gchar** args, gchar** error) {
101         GString* text;
102         int argc = 0;
103         dfilter_macro_t* m = NULL;
104         int* arg_pos_p;
105         gchar** parts;
106         gchar* ret;
107         guint i;
108         
109         for (i = 0; i < num_macros; i++) {
110                 dfilter_macro_t* c = &(macros[i]);
111                 if ( c->usable && g_str_equal(c->name,name) ) {
112                         m = c;
113                         break;
114                 }
115         }
116         
117         if (!m) {
118                 *error = ep_strdup_printf("macro '%s' does not exist", name);
119                 return NULL;
120         }
121         
122         if (args) {
123                 while(args[argc]) argc++;
124         }
125         
126         if (argc != m->argc) {
127                 *error = ep_strdup_printf("wrong number of arguments for macro '%s', expecting %d instead of %d",
128                                                                   name, m->argc, argc);
129                 return NULL;
130         }
131         
132         arg_pos_p = m->args_pos;
133         parts = m->parts;
134         
135         text = g_string_new(*(parts++));
136         
137         while (*parts) {
138                 g_string_sprintfa(text,"%s%s",
139                                                   args[*(arg_pos_p++)],
140                                                   *(parts++));
141         }
142         
143         ret = ep_strdup(text->str);
144         
145         g_string_free(text,TRUE);
146         
147         return ret;
148 }
149
150
151 gchar* dfilter_macro_apply(const gchar* text, guint depth, gchar** error) {
152         enum { OUTSIDE, STARTING, NAME, ARGS } state = OUTSIDE;
153         GString* out;
154         GString* name = NULL;
155         GString* arg = NULL;
156         GPtrArray* args = NULL;
157         gchar c;
158         const gchar* r = text;
159         gboolean changed = FALSE;
160         
161         if ( depth > 31) {
162                 *error = "too much nesting in macros";
163                 return NULL;
164         }
165                         
166 #define FGS(n) if (n) g_string_free(n,TRUE); n = NULL
167         
168 #define FREE_ALL() do { \
169         FGS(name); \
170         FGS(arg); \
171         if (args) { \
172                 while(args->len) { void* p = g_ptr_array_remove_index_fast(args,0); if (p) g_free(p); } \
173                 g_ptr_array_free(args,TRUE); \
174                 args = NULL; } } while(0)
175                 
176                 *error = NULL;
177                 out = g_string_sized_new(64);
178
179                 while(1) {
180                         c = *r++;
181                         
182                         switch(state) {
183                                 case OUTSIDE: {
184                                         switch(c) {
185                                                 case '\0': {
186                                                         goto finish;
187                                                 } case '$': {
188                                                         state = STARTING;
189                                                         break;
190                                                 } default: {
191                                                         g_string_append_c(out,c);
192                                                         break;
193                                                 }
194                                         }
195                                         break;
196                                 } case STARTING: {
197                                         switch (c) {
198                                                 case '{': {
199                                                         args = g_ptr_array_new();
200                                                         arg = g_string_sized_new(32);
201                                                         name = g_string_sized_new(32);
202                                                         
203                                                         state = NAME;
204                                                         
205                                                         break;
206                                                 } case '\0': {
207                                                         g_string_append_c(out,'$');
208                                                         
209                                                         goto finish;
210                                                 } default: {
211                                                         g_string_append_c(out,'$');
212                                                         g_string_append_c(out,c);
213                                                         
214                                                         state = OUTSIDE;
215                                                         
216                                                         break;
217                                                 }
218                                         }
219                                         break;
220                                 } case NAME: {
221                                         if ( isalnum(c) || c == '_' ) {
222                                                 g_string_append_c(name,c);
223                                         } else if ( c == ':') {
224                                                 state = ARGS;
225                                         } else if ( c == '}') {
226                                                 gchar* resolved;
227                                                 
228                                                 g_ptr_array_add(args,NULL);
229                                                 
230                                                 resolved = dfilter_macro_resolve(name->str, (gchar**)args->pdata, error);
231                                                 if (*error) goto on_error;
232                                                 
233                                                 changed = TRUE;
234                                                 
235                                                 g_string_append(out,resolved);
236                                                 
237                                                 FREE_ALL();
238                                                 
239                                                 state = OUTSIDE;
240                                         } else if ( c == '\0') {
241                                                 *error = "end of filter in the middle of a macro expression";
242                                                 goto on_error;                                  
243                                         } else {
244                                                 *error = "invalid char in macro name";
245                                                 goto on_error;
246                                         }
247                                         break;
248                                 } case ARGS: {
249                                         switch(c) {
250                                                 case '\0': {
251                                                         *error = "end of filter in the middle of a macro expression";
252                                                         goto on_error;                                  
253                                                 } case ';': {
254                                                         g_ptr_array_add(args,arg->str);
255                                                         g_string_free(arg,FALSE);
256                                                         
257                                                         arg = g_string_sized_new(32);
258                                                         break;
259                                                 } case '\\': {
260                                                         c = *r++;
261                                                         if (c) {
262                                                                 g_string_append_c(arg,c);
263                                                                 break;
264                                                         } else {
265                                                                 *error = "end of filter in the middle of a macro expression";
266                                                                 goto on_error;                                  
267                                                         }
268                                                 } default: {
269                                                         g_string_append_c(arg,c);
270                                                         break;
271                                                 } case '}': {
272                                                         gchar* resolved;
273                                                         g_ptr_array_add(args,arg->str);
274                                                         g_ptr_array_add(args,NULL);
275
276                                                         g_string_free(arg,FALSE);
277                                                         arg = NULL;
278                                                         
279                                                         resolved = dfilter_macro_resolve(name->str, (gchar**)args->pdata, error);
280                                                         if (*error) goto on_error;
281                                                         
282                                                         changed = TRUE;
283                                                         
284                                                         g_string_append(out,resolved);
285                                                         
286                                                         FREE_ALL();
287                                                         
288                                                         state = OUTSIDE;
289                                                         break;
290                                                 }
291                                         }
292                                         break;
293                                 }
294                         }
295                 }
296                 
297 finish:
298                 {
299                         FREE_ALL();
300                         
301                         if (changed) {
302                                 gchar* resolved = dfilter_macro_apply(out->str, depth++, error);
303                                 g_string_free(out,TRUE);
304                                 return (*error) ? NULL : resolved;
305                         } else {
306                                 gchar* out_str = ep_strdup(out->str);
307                                 g_string_free(out,TRUE);
308                                 return out_str;
309                         }
310                 }
311 on_error:
312                 {
313                         FREE_ALL();
314                         if (! *error) *error = "unknown error in macro expression";
315                         g_string_free(out,TRUE);
316                         return NULL;
317                 }
318 }
319
320 static void macro_update(void* mp, gchar** error) {
321         dfilter_macro_t* m = mp;
322         GPtrArray* parts;
323         GArray* args_pos;
324         const gchar* r;
325         gchar* w;
326         gchar* part;
327         int argc = 0;
328         guint i;
329         
330         *error = NULL;
331         
332         for (i = 0; i < num_macros; i++) {
333                 if (m == &(macros[i])) continue;
334                 
335                 if ( g_str_equal(m->name,macros[i].name) ) {
336                         *error = ep_strdup_printf("macro '%s' exists already", m->name);
337                         m->usable = FALSE;
338                         return;
339                 }
340         }
341         
342         parts = g_ptr_array_new();
343         args_pos = g_array_new(FALSE,FALSE,sizeof(int));
344
345         m->priv = part = w = g_strdup(m->text);
346         r = m->text;
347         g_ptr_array_add(parts,part);
348         
349         do {
350                 
351                 switch (*r) { 
352                         default:
353                                 *(w++) = *(r++);
354                                 break;
355                         case '\0':
356                                 *(w++) = *(r++);
357                                 goto done;
358                         case '\\':
359                                 *(w++) = *(++r);
360                                 r++;
361                                 break;
362                         case '$': {
363                                 int cnt = 0;
364                                 int arg_pos = 0;
365                                 do {
366                                         char c = *(r+1);
367                                         if (c >= '0' && c <= '9') {
368                                                 cnt++;
369                                                 r++;
370                                                 arg_pos *= 10;
371                                                 arg_pos += c - '0';
372                                         } else {
373                                                 break;
374                                         }
375                                 } while(1);
376                                 
377                                 if (cnt) {
378                                         *(w++) = '\0';
379                                         r++;
380                                         argc = argc < arg_pos ? arg_pos : argc;
381                                         arg_pos--;
382                                         g_array_append_val(args_pos,arg_pos);
383                                         g_ptr_array_add(parts,w);
384                                 } else {
385                                         *(w++) = *(r++);
386                                 }
387                                 break;
388                         }
389                 }
390                                 
391         } while(1);
392
393 done:
394         g_ptr_array_add(parts,NULL);
395
396         if (m->parts) g_free(m->parts);
397
398         m->parts = (gchar**)parts->pdata;
399         
400         if (m->args_pos) g_free(m->args_pos);
401         
402         m->args_pos = (int*)args_pos->data;
403
404         g_ptr_array_free(parts,FALSE);
405         g_array_free(args_pos,FALSE);
406         
407         m->argc = argc;
408         
409         m->usable = TRUE;
410         
411         macro_dump(m,NULL);
412         
413         return;
414 }
415
416 static void macro_free(void* r) {
417         dfilter_macro_t* m = r;
418                 
419         g_free(m->name);
420         g_free(m->text);
421         g_free(m->priv);
422         g_free(m->parts);
423         g_free(m->args_pos);
424 }
425
426 static void* macro_copy(void* dest, const void* orig, unsigned len _U_) {
427         dfilter_macro_t* d = dest;
428         const dfilter_macro_t* m = orig;
429         guint nparts = 0;
430
431         d->name = g_strdup(m->name);
432         d->text = g_strdup(m->text);
433         d->usable = m->usable;
434
435         if (m->parts) {
436                 do nparts++; while (m->parts[nparts]); 
437                 d->priv = g_strdup(m->priv);
438                 d->parts = g_memdup(m->parts,nparts*sizeof(void*));
439                 d->args_pos = g_memdup(m->args_pos,(--nparts)*sizeof(int));
440         }
441         
442         
443         return d;
444 }
445
446 gboolean macro_name_chk(void* r _U_, const char* in_name, unsigned name_len, char** error) {
447         guint i;
448         
449         for (i=0; i < name_len; i++) {
450                 if (!(in_name[i] == '_' || isalnum(in_name[i]) ) ) {
451                         *error = "invalid char in name";
452                         return FALSE;
453                 }
454         }
455         
456         return TRUE;
457 }
458
459 static void macro_name_set(void* r, const char* in_name, unsigned len) {
460         dfilter_macro_t* m = r;
461         char* name = g_malloc(len+1);
462         memcpy(name,in_name,len);
463         name[len] = '\0';
464         g_free(m->name);
465         m->name = name;
466 }
467
468 static void macro_name_tostr(void* r, char** out_name, unsigned* out_len) {
469         dfilter_macro_t* m = r;
470         *out_len = strlen(m->name);
471         *out_name = m->name;
472 }
473
474 gboolean macro_text_chk(void* r _U_, const char* in_name, unsigned name_len, char** error) {
475         guint i;
476         
477         for (i=0; i < name_len; i++) {
478                 if (! isprint(in_name[i]) ) {
479                         *error = "invalid char in text";
480                         return FALSE;
481                 }
482         }
483         
484         return TRUE;
485 }
486
487 static void macro_text_set(void* r, const char* in_name, unsigned len) {
488         dfilter_macro_t* m = r;
489         char* text = g_malloc(len+1);
490         memcpy(text,in_name,len);
491         text[len] = '\0';
492         g_free(m->text);
493         m->text = text;
494 }
495
496 static void macro_text_tostr(void* r, char** out_name, unsigned* out_len) {
497         dfilter_macro_t* m = r;
498         *out_len = strlen(m->text);
499         *out_name = m->text;
500 }
501
502 void dfilter_macro_init(void) {
503         char* error = NULL;
504         dfilter_macro_uat = uat_new("Display Filter Macros",
505                                                                 sizeof(dfilter_macro_t),
506                                                                 DFILTER_MACRO_FILENAME,
507                                                                 (void**) &macros,
508                                                                 &num_macros,
509                                                                 macro_copy,
510                                                                 macro_update,
511                                                                 macro_free,
512                                                                 &error,
513                                                                 "name", PT_TXTMOD_STRING, macro_name_chk, macro_name_set, macro_name_tostr,
514                                                                 "text", PT_TXTMOD_STRING, macro_text_chk, macro_text_set, macro_text_tostr,
515                                                                 NULL );
516         
517         if(error) {
518                 report_failure("error while loading '" DFILTER_MACRO_FILENAME "':\n%s",error);
519         }
520         
521 }
522
523 void dfilter_macro_get_uat(void** p) {
524         *p = dfilter_macro_uat;
525 }
526