255874a3210b834c2259477d6a69cfdf09904f6c
[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 #include <epan/proto.h>
41
42 typedef struct {
43         const char* name;
44         gboolean usable;
45         char* repr;
46 } fvt_cache_entry_t;
47
48 static uat_t* dfilter_macro_uat = NULL;
49 static dfilter_macro_t* macros = NULL;
50 static guint num_macros;
51 static GHashTable* fvt_cache = NULL;
52
53 static gboolean free_value(gpointer k _U_, gpointer v, gpointer u _U_) {
54         fvt_cache_entry_t* e = v;
55         if (e->repr) g_free(e->repr);
56         g_free(e);
57         return TRUE;
58 }
59
60 static gboolean fvt_cache_cb(proto_node * node, gpointer data _U_) {
61         field_info* finfo = node->finfo;
62         fvt_cache_entry_t* e;
63         
64         if (!finfo) return FALSE;
65         
66         if ((e = g_hash_table_lookup(fvt_cache,finfo->hfinfo->abbrev))) {
67                 e->usable = FALSE;
68         } else if (finfo->value.ftype->val_to_string_repr) {
69                 switch (finfo->hfinfo->type) {
70                         case FT_NONE:
71                         case FT_PROTOCOL:
72                                 return FALSE;
73                         default:
74                                 break;
75                 }
76                 e = g_malloc(sizeof(fvt_cache_entry_t));
77                 e->name = finfo->hfinfo->abbrev,
78                 e->repr = fvalue_to_string_repr(&(finfo->value), FTREPR_DFILTER, NULL);
79                 e->usable = TRUE;
80                 g_hash_table_insert(fvt_cache,(void*)finfo->hfinfo->abbrev,e);
81         }
82         return FALSE;
83 }
84
85 void dfilter_macro_build_ftv_cache(void* tree_root) {
86         g_hash_table_foreach_remove(fvt_cache,free_value,NULL);
87         proto_tree_traverse_in_order(tree_root, fvt_cache_cb, NULL);
88 }
89
90 void dfilter_macro_foreach(dfilter_macro_cb_t cb, void* data) {
91         guint i;
92
93         for (i = 0; i < num_macros; i++) {
94                 cb(&(macros[i]),data);
95         }
96         return;
97 }
98
99 static void macro_fprint(dfilter_macro_t* m, void* ud) {
100         FILE* f = ud;
101
102         fprintf(f,"%s\t%s\n",m->name,m->text);
103 }
104
105 void dfilter_macro_save(const gchar* filename, gchar** error) {
106         FILE* f = fopen(filename,"w");
107
108         if (!f) {
109                 *error = ep_strdup_printf("Could not open file: '%s', error: %s\n", filename, strerror(errno) );
110                 return;
111         }
112
113         dfilter_macro_foreach(macro_fprint, f);
114
115         fclose(f);
116
117         return;
118 }
119
120 #ifdef DUMP_MACROS
121 static void macro_dump(dfilter_macro_t* m _U_, void* ud _U_) {
122         gchar** part = m->parts;
123         int* args_pos = m->args_pos;
124
125         printf("\n->%s\t%s\t%d [%d]\n\t'%s'\n",
126                    m->name, m->text, m->argc, m->usable, *(part++));
127
128         while (*part) {
129                 printf("\t$%d '%s'\n",*args_pos,*part);
130
131                 args_pos++;
132                 part++;
133         }
134 }
135 #else
136 #define macro_dump(a,b)
137 #endif
138
139 void dfilter_macro_dump(void) {
140 #ifdef DUMP_MACROS
141         dfilter_macro_foreach(macro_dump, NULL);
142 #endif
143 }
144
145 static gchar* dfilter_macro_resolve(gchar* name, gchar** args, const gchar** error) {
146         GString* text;
147         int argc = 0;
148         dfilter_macro_t* m = NULL;
149         fvt_cache_entry_t* e;
150         int* arg_pos_p;
151         gchar** parts;
152         gchar* ret;
153         guint i;
154
155         for (i = 0; i < num_macros; i++) {
156                 dfilter_macro_t* c = &(macros[i]);
157                 if ( c->usable && g_str_equal(c->name,name) ) {
158                         m = c;
159                         break;
160                 }
161         }
162
163         if (!m) {
164                 if (fvt_cache && (e = g_hash_table_lookup(fvt_cache,name) )) {
165                         if(e->usable) {
166                                 return e->repr;
167                         } else {
168                                 *error = ep_strdup_printf("macro '%s' is unusable", name);
169                                 return NULL;
170                         }
171                 } else {
172                         *error = ep_strdup_printf("macro '%s' does not exist", name);
173                         return NULL;
174                 }
175         }
176
177         if (args) {
178                 while(args[argc]) argc++;
179         }
180
181         if (argc != m->argc) {
182                 *error = ep_strdup_printf("wrong number of arguments for macro '%s', expecting %d instead of %d",
183                                                                   name, m->argc, argc);
184                 return NULL;
185         }
186
187         arg_pos_p = m->args_pos;
188         parts = m->parts;
189
190         text = g_string_new(*(parts++));
191
192         if (args) {
193                 while (*parts) {
194                         g_string_sprintfa(text,"%s%s",
195                                                   args[*(arg_pos_p++)],
196                                                   *(parts++));
197                 }
198         }
199
200         ret = ep_strdup(text->str);
201
202         g_string_free(text,TRUE);
203
204         return ret;
205 }
206
207
208 gchar* dfilter_macro_apply(const gchar* text, guint depth, const gchar** error) {
209         enum { OUTSIDE, STARTING, NAME, ARGS } state = OUTSIDE;
210         GString* out;
211         GString* name = NULL;
212         GString* arg = NULL;
213         GPtrArray* args = NULL;
214         gchar c;
215         const gchar* r = text;
216         gboolean changed = FALSE;
217
218         if ( depth > 31) {
219                 *error = "too much nesting in macros";
220                 return NULL;
221         }
222
223 #define FGS(n) if (n) g_string_free(n,TRUE); n = NULL
224
225 #define FREE_ALL() \
226         do { \
227                 FGS(name); \
228                 FGS(arg); \
229                 if (args) { \
230                         while(args->len) { void* p = g_ptr_array_remove_index_fast(args,0); if (p) g_free(p); } \
231                         g_ptr_array_free(args,TRUE); \
232                         args = NULL; } } while(0)
233
234                 *error = NULL;
235                 out = g_string_sized_new(64);
236
237                 while(1) {
238                         c = *r++;
239
240                         switch(state) {
241                                 case OUTSIDE: {
242                                         switch(c) {
243                                                 case '\0': {
244                                                         goto finish;
245                                                 } case '$': {
246                                                         state = STARTING;
247                                                         break;
248                                                 } default: {
249                                                         g_string_append_c(out,c);
250                                                         break;
251                                                 }
252                                         }
253                                         break;
254                                 } case STARTING: {
255                                         switch (c) {
256                                                 case '{': {
257                                                         args = g_ptr_array_new();
258                                                         arg = g_string_sized_new(32);
259                                                         name = g_string_sized_new(32);
260
261                                                         state = NAME;
262
263                                                         break;
264                                                 } case '\0': {
265                                                         g_string_append_c(out,'$');
266
267                                                         goto finish;
268                                                 } default: {
269                                                         g_string_append_c(out,'$');
270                                                         g_string_append_c(out,c);
271
272                                                         state = OUTSIDE;
273
274                                                         break;
275                                                 }
276                                         }
277                                         break;
278                                 } case NAME: {
279                                         if ( isalnum((int)c) || c == '_' || c == '-' || c == '.' ) {
280                                                 g_string_append_c(name,c);
281                                         } else if ( c == ':') {
282                                                 state = ARGS;
283                                         } else if ( c == '}') {
284                                                 gchar* resolved;
285
286                                                 g_ptr_array_add(args,NULL);
287
288                                                 resolved = dfilter_macro_resolve(name->str, (gchar**)args->pdata, error);
289                                                 if (*error) goto on_error;
290
291                                                 changed = TRUE;
292
293                                                 g_string_append(out,resolved);
294
295                                                 FREE_ALL();
296
297                                                 state = OUTSIDE;
298                                         } else if ( c == '\0') {
299                                                 *error = "end of filter in the middle of a macro expression";
300                                                 goto on_error;
301                                         } else {
302                                                 *error = "invalid char in macro name";
303                                                 goto on_error;
304                                         }
305                                         break;
306                                 } case ARGS: {
307                                         switch(c) {
308                                                 case '\0': {
309                                                         *error = "end of filter in the middle of a macro expression";
310                                                         goto on_error;
311                                                 } case ';': {
312                                                         g_ptr_array_add(args,arg->str);
313                                                         g_string_free(arg,FALSE);
314
315                                                         arg = g_string_sized_new(32);
316                                                         break;
317                                                 } case '\\': {
318                                                         c = *r++;
319                                                         if (c) {
320                                                                 g_string_append_c(arg,c);
321                                                                 break;
322                                                         } else {
323                                                                 *error = "end of filter in the middle of a macro expression";
324                                                                 goto on_error;
325                                                         }
326                                                 } default: {
327                                                         g_string_append_c(arg,c);
328                                                         break;
329                                                 } case '}': {
330                                                         gchar* resolved;
331                                                         g_ptr_array_add(args,arg->str);
332                                                         g_ptr_array_add(args,NULL);
333
334                                                         g_string_free(arg,FALSE);
335                                                         arg = NULL;
336
337                                                         resolved = dfilter_macro_resolve(name->str, (gchar**)args->pdata, error);
338                                                         if (*error) goto on_error;
339
340                                                         changed = TRUE;
341
342                                                         g_string_append(out,resolved);
343
344                                                         FREE_ALL();
345
346                                                         state = OUTSIDE;
347                                                         break;
348                                                 }
349                                         }
350                                         break;
351                                 }
352                         }
353                 }
354
355 finish:
356                 {
357                         FREE_ALL();
358
359                         if (changed) {
360                                 gchar* resolved = dfilter_macro_apply(out->str, depth++, error);
361                                 g_string_free(out,TRUE);
362                                 return (*error) ? NULL : resolved;
363                         } else {
364                                 gchar* out_str = ep_strdup(out->str);
365                                 g_string_free(out,TRUE);
366                                 return out_str;
367                         }
368                 }
369 on_error:
370                 {
371                         FREE_ALL();
372                         if (! *error) *error = "unknown error in macro expression";
373                         g_string_free(out,TRUE);
374                         return NULL;
375                 }
376 }
377
378 static void macro_update(void* mp, const gchar** error) {
379         dfilter_macro_t* m = mp;
380         GPtrArray* parts;
381         GArray* args_pos;
382         const gchar* r;
383         gchar* w;
384         gchar* part;
385         int argc = 0;
386         guint i;
387
388         *error = NULL;
389
390         for (i = 0; i < num_macros; i++) {
391                 if (m == &(macros[i])) continue;
392
393                 if ( g_str_equal(m->name,macros[i].name) ) {
394                         *error = ep_strdup_printf("macro '%s' exists already", m->name);
395                         m->usable = FALSE;
396                         return;
397                 }
398         }
399
400         parts = g_ptr_array_new();
401         args_pos = g_array_new(FALSE,FALSE,sizeof(int));
402
403         m->priv = part = w = g_strdup(m->text);
404         r = m->text;
405         g_ptr_array_add(parts,part);
406
407         while (r && *r) {
408
409                 switch (*r) {
410                         default:
411                                 *(w++) = *(r++);
412                                 break;
413                         case '\0':
414                                 *(w++) = *(r++);
415                                 goto done;
416                         case '\\':
417                                 *(w++) = *(++r);
418                                 r++;
419                                 break;
420                         case '$': {
421                                 int cnt = 0;
422                                 int arg_pos = 0;
423                                 do {
424                                         char c = *(r+1);
425                                         if (c >= '0' && c <= '9') {
426                                                 cnt++;
427                                                 r++;
428                                                 *(w++) = '\0';
429                                                 arg_pos *= 10;
430                                                 arg_pos += c - '0';
431                                         } else {
432                                                 break;
433                                         }
434                                 } while(*r);
435
436                                 if (cnt) {
437                                         *(w++) = '\0';
438                                         r++;
439                                         argc = argc < arg_pos ? arg_pos : argc;
440                                         arg_pos--;
441                                         g_array_append_val(args_pos,arg_pos);
442                                         g_ptr_array_add(parts,w);
443                                 } else {
444                                         *(w++) = *(r++);
445                                 }
446                                 break;
447                         }
448                 }
449
450         }
451
452 done:
453         g_ptr_array_add(parts,NULL);
454
455         if (m->parts) g_free(m->parts);
456
457         m->parts = (gchar**)parts->pdata;
458
459         if (m->args_pos) g_free(m->args_pos);
460
461         m->args_pos = (int*)(void *)args_pos->data;
462
463         g_ptr_array_free(parts,FALSE);
464         g_array_free(args_pos,FALSE);
465
466         m->argc = argc;
467
468         m->usable = TRUE;
469
470         macro_dump(m,NULL);
471
472         return;
473 }
474
475 static void macro_free(void* r) {
476         dfilter_macro_t* m = r;
477
478         g_free(m->name);
479         g_free(m->text);
480         g_free(m->priv);
481         g_free(m->parts);
482         g_free(m->args_pos);
483 }
484
485 static void* macro_copy(void* dest, const void* orig, unsigned len _U_) {
486         dfilter_macro_t* d = dest;
487         const dfilter_macro_t* m = orig;
488         guint nparts = 0;
489
490         d->name = g_strdup(m->name);
491         d->text = g_strdup(m->text);
492         d->usable = m->usable;
493
494         if (m->parts) {
495                 do nparts++; while (m->parts[nparts]);
496                 d->priv = g_strdup(m->priv);
497                 d->parts = g_memdup(m->parts,nparts*sizeof(void*));
498                 d->args_pos = g_memdup(m->args_pos,(--nparts)*sizeof(int));
499         }
500
501
502         return d;
503 }
504
505
506 static gboolean macro_name_chk(void* r _U_, const char* in_name, unsigned name_len, void* u1 _U_, void* u2 _U_, const char** error) {
507         guint i;
508
509         if (name_len == 0) {
510                 *error = "invalid name";
511                 return FALSE;
512         }
513
514         for (i=0; i < name_len; i++) {
515                 if (!(in_name[i] == '_' || isalnum((guchar)in_name[i]) ) ) {
516                         *error = "invalid char in name";
517                         return FALSE;
518                 }
519         }
520
521         return TRUE;
522 }
523
524 UAT_CSTRING_CB_DEF(macro,name,dfilter_macro_t)
525 UAT_CSTRING_CB_DEF(macro,text,dfilter_macro_t)
526
527 void dfilter_macro_init(void) {
528         static uat_field_t uat_fields[] =  {
529                 UAT_FLD_CSTRING_OTHER(macro,name,macro_name_chk,"The name of the macro."),
530                 UAT_FLD_CSTRING_ISPRINT(macro,text,"The text this macro resolves to."),
531                 UAT_END_FIELDS
532         };
533
534         dfilter_macro_uat = uat_new("Display Filter Macros",
535                                     sizeof(dfilter_macro_t),
536                                     DFILTER_MACRO_FILENAME,
537                                     (void*) &macros,
538                                     &num_macros,
539                                     NULL,
540                                     "ChDisplayFilterMacrosSection",
541                                     macro_copy,
542                                     macro_update,
543                                     macro_free,
544                                     uat_fields);
545
546         fvt_cache = g_hash_table_new(g_str_hash,g_str_equal);
547 }
548
549 void dfilter_macro_get_uat(void** p) {
550         *p = dfilter_macro_uat;
551 }
552
553
554
555