From Andrew Narver:
[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 #include <wsutil/file_util.h>
42
43 typedef struct {
44         const char* name;
45         gboolean usable;
46         char* repr;
47 } fvt_cache_entry_t;
48
49 static uat_t* dfilter_macro_uat = NULL;
50 static dfilter_macro_t* macros = NULL;
51 static guint num_macros;
52 static GHashTable* fvt_cache = NULL;
53
54 /* #define DUMP_DFILTER_MACRO */
55 #ifdef DUMP_DFILTER_MACRO
56 void dump_dfilter_macro_t(const dfilter_macro_t *m, const char *function, const char *file, int line);
57 #define DUMP_MACRO(m) dump_dfilter_macro_t(m, __func__, __FILE__, __LINE__)
58 #else
59 #define DUMP_MACRO(m)
60 #endif
61
62 static gboolean free_value(gpointer k _U_, gpointer v, gpointer u _U_) {
63         fvt_cache_entry_t* e = v;
64         if (e->repr) g_free(e->repr);
65         g_free(e);
66         return TRUE;
67 }
68
69 static gboolean fvt_cache_cb(proto_node * node, gpointer data _U_) {
70         field_info* finfo = node->finfo;
71         fvt_cache_entry_t* e;
72
73         if (!finfo) return FALSE;
74
75         if ((e = g_hash_table_lookup(fvt_cache,finfo->hfinfo->abbrev))) {
76                 e->usable = FALSE;
77         } else if (finfo->value.ftype->val_to_string_repr) {
78                 switch (finfo->hfinfo->type) {
79                         case FT_NONE:
80                         case FT_PROTOCOL:
81                                 return FALSE;
82                         default:
83                                 break;
84                 }
85                 e = g_malloc(sizeof(fvt_cache_entry_t));
86                 e->name = finfo->hfinfo->abbrev,
87                 e->repr = fvalue_to_string_repr(&(finfo->value), FTREPR_DFILTER, NULL);
88                 e->usable = TRUE;
89                 g_hash_table_insert(fvt_cache,(void*)finfo->hfinfo->abbrev,e);
90         }
91         return FALSE;
92 }
93
94 void dfilter_macro_build_ftv_cache(void* tree_root) {
95         g_hash_table_foreach_remove(fvt_cache,free_value,NULL);
96         proto_tree_traverse_post_order(tree_root, fvt_cache_cb, NULL);
97 }
98
99 void dfilter_macro_foreach(dfilter_macro_cb_t cb, void* data) {
100         guint i;
101
102         for (i = 0; i < num_macros; i++) {
103                 cb(&(macros[i]),data);
104         }
105         return;
106 }
107
108 static void macro_fprint(dfilter_macro_t* m, void* ud) {
109         FILE* f = ud;
110
111         fprintf(f,"%s\t%s\n",m->name,m->text);
112 }
113
114 void dfilter_macro_save(const gchar* filename, gchar** error) {
115         FILE* f = ws_fopen(filename,"w");
116
117         if (!f) {
118                 *error = ep_strdup_printf("Could not open file: '%s', error: %s\n", filename, strerror(errno) );
119                 return;
120         }
121
122         dfilter_macro_foreach(macro_fprint, f);
123
124         fclose(f);
125
126         return;
127 }
128
129 #ifdef DUMP_MACROS
130 static void macro_dump(dfilter_macro_t* m _U_, void* ud _U_) {
131         gchar** part = m->parts;
132         int* args_pos = m->args_pos;
133
134         printf("\n->%s\t%s\t%d [%d]\n\t'%s'\n",
135                    m->name, m->text, m->argc, m->usable, *(part++));
136
137         while (*part) {
138                 printf("\t$%d '%s'\n",*args_pos,*part);
139
140                 args_pos++;
141                 part++;
142         }
143 }
144 #else
145 #define macro_dump(a,b)
146 #endif
147
148 void dfilter_macro_dump(void) {
149 #ifdef DUMP_MACROS
150         dfilter_macro_foreach(macro_dump, NULL);
151 #endif
152 }
153
154 static gchar* dfilter_macro_resolve(gchar* name, gchar** args, const gchar** error) {
155         GString* text;
156         int argc = 0;
157         dfilter_macro_t* m = NULL;
158         fvt_cache_entry_t* e;
159         int* arg_pos_p;
160         gchar** parts;
161         gchar* ret;
162         guint i;
163
164         for (i = 0; i < num_macros; i++) {
165                 dfilter_macro_t* c = &(macros[i]);
166                 if ( c->usable && g_str_equal(c->name,name) ) {
167                         m = c;
168                         break;
169                 }
170         }
171
172         if (!m) {
173                 if (fvt_cache && (e = g_hash_table_lookup(fvt_cache,name) )) {
174                         if(e->usable) {
175                                 return e->repr;
176                         } else {
177                                 *error = ep_strdup_printf("macro '%s' is unusable", name);
178                                 return NULL;
179                         }
180                 } else {
181                         *error = ep_strdup_printf("macro '%s' does not exist", name);
182                         return NULL;
183                 }
184         }
185
186         DUMP_MACRO(m);
187
188         if (args) {
189                 while(args[argc]) argc++;
190         }
191
192         if (argc != m->argc) {
193                 *error = ep_strdup_printf("wrong number of arguments for macro '%s', expecting %d instead of %d",
194                                                                   name, m->argc, argc);
195                 return NULL;
196         }
197
198         arg_pos_p = m->args_pos;
199         parts = m->parts;
200
201         text = g_string_new(*(parts++));
202
203         if (args) {
204                 while (*parts) {
205                         g_string_append_printf(text,"%s%s",
206                                                args[*(arg_pos_p++)],
207                                                *(parts++));
208                 }
209         }
210
211         ret = ep_strdup(text->str);
212
213         g_string_free(text,TRUE);
214
215         return ret;
216 }
217
218
219 gchar* dfilter_macro_apply(const gchar* text, guint depth, const gchar** error) {
220         enum { OUTSIDE, STARTING, NAME, ARGS } state = OUTSIDE;
221         GString* out;
222         GString* name = NULL;
223         GString* arg = NULL;
224         GPtrArray* args = NULL;
225         gchar c;
226         const gchar* r = text;
227         gboolean changed = FALSE;
228
229         if ( depth > 31) {
230                 *error = "too much nesting in macros";
231                 return NULL;
232         }
233
234 #define FGS(n) if (n) g_string_free(n,TRUE); n = NULL
235
236 #define FREE_ALL() \
237         do { \
238                 FGS(name); \
239                 FGS(arg); \
240                 if (args) { \
241                         while(args->len) { void* p = g_ptr_array_remove_index_fast(args,0); if (p) g_free(p); } \
242                         g_ptr_array_free(args,TRUE); \
243                         args = NULL; } } while(0)
244
245                 *error = NULL;
246                 out = g_string_sized_new(64);
247
248                 while(1) {
249                         c = *r++;
250
251                         switch(state) {
252                                 case OUTSIDE: {
253                                         switch(c) {
254                                                 case '\0': {
255                                                         goto finish;
256                                                 } case '$': {
257                                                         state = STARTING;
258                                                         break;
259                                                 } default: {
260                                                         g_string_append_c(out,c);
261                                                         break;
262                                                 }
263                                         }
264                                         break;
265                                 } case STARTING: {
266                                         switch (c) {
267                                                 case '{': {
268                                                         args = g_ptr_array_new();
269                                                         arg = g_string_sized_new(32);
270                                                         name = g_string_sized_new(32);
271
272                                                         state = NAME;
273
274                                                         break;
275                                                 } case '\0': {
276                                                         g_string_append_c(out,'$');
277
278                                                         goto finish;
279                                                 } default: {
280                                                         g_string_append_c(out,'$');
281                                                         g_string_append_c(out,c);
282
283                                                         state = OUTSIDE;
284
285                                                         break;
286                                                 }
287                                         }
288                                         break;
289                                 } case NAME: {
290                                         if ( isalnum((int)c) || c == '_' || c == '-' || c == '.' ) {
291                                                 g_string_append_c(name,c);
292                                         } else if ( c == ':') {
293                                                 state = ARGS;
294                                         } else if ( c == '}') {
295                                                 gchar* resolved;
296
297                                                 g_ptr_array_add(args,NULL);
298
299                                                 resolved = dfilter_macro_resolve(name->str, (gchar**)args->pdata, error);
300                                                 if (*error) goto on_error;
301
302                                                 changed = TRUE;
303
304                                                 g_string_append(out,resolved);
305
306                                                 FREE_ALL();
307
308                                                 state = OUTSIDE;
309                                         } else if ( c == '\0') {
310                                                 *error = "end of filter in the middle of a macro expression";
311                                                 goto on_error;
312                                         } else {
313                                                 *error = "invalid char in macro name";
314                                                 goto on_error;
315                                         }
316                                         break;
317                                 } case ARGS: {
318                                         switch(c) {
319                                                 case '\0': {
320                                                         *error = "end of filter in the middle of a macro expression";
321                                                         goto on_error;
322                                                 } case ';': {
323                                                         g_ptr_array_add(args,arg->str);
324                                                         g_string_free(arg,FALSE);
325
326                                                         arg = g_string_sized_new(32);
327                                                         break;
328                                                 } case '\\': {
329                                                         c = *r++;
330                                                         if (c) {
331                                                                 g_string_append_c(arg,c);
332                                                                 break;
333                                                         } else {
334                                                                 *error = "end of filter in the middle of a macro expression";
335                                                                 goto on_error;
336                                                         }
337                                                 } default: {
338                                                         g_string_append_c(arg,c);
339                                                         break;
340                                                 } case '}': {
341                                                         gchar* resolved;
342                                                         g_ptr_array_add(args,arg->str);
343                                                         g_ptr_array_add(args,NULL);
344
345                                                         g_string_free(arg,FALSE);
346                                                         arg = NULL;
347
348                                                         resolved = dfilter_macro_resolve(name->str, (gchar**)args->pdata, error);
349                                                         if (*error) goto on_error;
350
351                                                         changed = TRUE;
352
353                                                         g_string_append(out,resolved);
354
355                                                         FREE_ALL();
356
357                                                         state = OUTSIDE;
358                                                         break;
359                                                 }
360                                         }
361                                         break;
362                                 }
363                         }
364                 }
365
366 finish:
367                 {
368                         FREE_ALL();
369
370                         if (changed) {
371                                 gchar* resolved = dfilter_macro_apply(out->str, depth++, error);
372                                 g_string_free(out,TRUE);
373                                 return (*error) ? NULL : resolved;
374                         } else {
375                                 gchar* out_str = ep_strdup(out->str);
376                                 g_string_free(out,TRUE);
377                                 return out_str;
378                         }
379                 }
380 on_error:
381                 {
382                         FREE_ALL();
383                         if (! *error) *error = "unknown error in macro expression";
384                         g_string_free(out,TRUE);
385                         return NULL;
386                 }
387 }
388
389 static void macro_update(void* mp, const gchar** error) {
390         dfilter_macro_t* m = mp;
391         GPtrArray* parts;
392         GArray* args_pos;
393         const gchar* r;
394         gchar* w;
395         gchar* part;
396         int argc = 0;
397         guint i;
398
399         DUMP_MACRO(m);
400
401         *error = NULL;
402
403         for (i = 0; i < num_macros; i++) {
404                 if (m == &(macros[i])) continue;
405
406                 if ( g_str_equal(m->name,macros[i].name) ) {
407                         *error = ep_strdup_printf("macro '%s' exists already", m->name);
408                         m->usable = FALSE;
409                         return;
410                 }
411         }
412
413         parts = g_ptr_array_new();
414         args_pos = g_array_new(FALSE,FALSE,sizeof(int));
415
416         m->priv = part = w = g_strdup(m->text);
417         r = m->text;
418         g_ptr_array_add(parts,part);
419
420         while (r && *r) {
421
422                 switch (*r) {
423                         default:
424                                 *(w++) = *(r++);
425                                 break;
426                         case '\0':
427                                 *(w++) = *(r++);
428                                 goto done;
429                         case '\\':
430                                 *(w++) = *(++r);
431                                 r++;
432                                 break;
433                         case '$': {
434                                 int cnt = 0;
435                                 int arg_pos = 0;
436                                 do {
437                                         char c = *(r+1);
438                                         if (c >= '0' && c <= '9') {
439                                                 cnt++;
440                                                 r++;
441                                                 *(w++) = '\0';
442                                                 arg_pos *= 10;
443                                                 arg_pos += c - '0';
444                                         } else {
445                                                 break;
446                                         }
447                                 } while(*r);
448
449                                 if (cnt) {
450                                         *(w++) = '\0';
451                                         r++;
452                                         argc = argc < arg_pos ? arg_pos : argc;
453                                         arg_pos--;
454                                         g_array_append_val(args_pos,arg_pos);
455                                         g_ptr_array_add(parts,w);
456                                 } else {
457                                         *(w++) = *(r++);
458                                 }
459                                 break;
460                         }
461                 }
462
463         }
464
465 done:
466         g_ptr_array_add(parts,NULL);
467
468         if (m->parts) g_free(m->parts);
469
470         m->parts = (gchar**)parts->pdata;
471
472         if (m->args_pos) g_free(m->args_pos);
473
474         m->args_pos = (int*)(void *)args_pos->data;
475
476         g_ptr_array_free(parts,FALSE);
477         g_array_free(args_pos,FALSE);
478
479         m->argc = argc;
480
481         m->usable = TRUE;
482
483         macro_dump(m,NULL);
484
485         DUMP_MACRO(m);
486
487         return;
488 }
489
490 static void macro_free(void* r) {
491         dfilter_macro_t* m = r;
492
493         DUMP_MACRO(r);
494
495         g_free(m->name);
496         g_free(m->text);
497         g_free(m->priv);
498         g_free(m->parts);
499         g_free(m->args_pos);
500 }
501
502 static void* macro_copy(void* dest, const void* orig, unsigned len _U_) {
503         dfilter_macro_t* d = dest;
504         const dfilter_macro_t* m = orig;
505
506         DUMP_MACRO(m);
507
508         d->name = g_strdup(m->name);
509         d->text = g_strdup(m->text);
510         d->usable = m->usable;
511
512         if (m->parts) {
513                 guint nparts = 0;
514
515                 /*
516                  * Copy the contents of m->priv (a "cooked" version
517                  * of m->text) into d->priv.
518                  *
519                  * First we clone m->text into d->priv, this gets
520                  * us a NUL terminated string of the proper length.
521                  *
522                  * Then we loop copying bytes from m->priv into
523                  * d-priv.  Since m->priv contains internal ACSII NULs
524                  * we use the length of m->text to stop the copy.
525                  */
526
527                 d->priv = g_strdup(m->text);
528                 {
529                         const gchar* oldText = m->text;
530                         const gchar* oldPriv = m->priv;
531                         gchar* newPriv = d->priv;
532                         while(oldText && *oldText) {
533                                 *(newPriv++) = *(oldPriv++);
534                                 oldText++;
535                         }
536                 }
537
538                 /*
539                  * The contents of the m->parts array contains pointers
540                  * into various sections of m->priv.  Since it's
541                  * an argv style array of ponters, this array is
542                  * actually one larger than the number of parts
543                  * to hold the final NULL terminator.
544                  *
545                  * The following copy clones the original m->parts
546                  * array into d->parts but then fixes-up the pointers
547                  * so that they point into the appropriate sections
548                  * of the d->priv.
549                  */
550
551                 do nparts++; while (m->parts[nparts]);
552                 d->parts = g_memdup(m->parts,(nparts+1)*sizeof(void*));
553                 nparts = 0;
554                 while(m->parts[nparts]) {
555                         if(nparts) {
556                                 d->parts[nparts] = d->parts[nparts - 1] + (m->parts[nparts] - m->parts[nparts - 1]);
557                         } else {
558                                 d->parts[nparts] = d->priv;
559                         }
560                         nparts++;
561                 }
562
563                 /*
564                  * Clone the contents of m->args_pos into d->args_pos.
565                  */
566
567                 d->args_pos = g_memdup(m->args_pos,(--nparts)*sizeof(int));
568         }
569
570         DUMP_MACRO(d);
571
572         return d;
573 }
574
575 static gboolean macro_name_chk(void* r _U_, const char* in_name, unsigned name_len, void* u1 _U_, void* u2 _U_, const char** error) {
576         guint i;
577
578         if (name_len == 0) {
579                 *error = "invalid name";
580                 return FALSE;
581         }
582
583         for (i=0; i < name_len; i++) {
584                 if (!(in_name[i] == '_' || isalnum((guchar)in_name[i]) ) ) {
585                         *error = "invalid char in name";
586                         return FALSE;
587                 }
588         }
589
590         return TRUE;
591 }
592
593 UAT_CSTRING_CB_DEF(macro,name,dfilter_macro_t)
594 UAT_CSTRING_CB_DEF(macro,text,dfilter_macro_t)
595
596 void dfilter_macro_init(void) {
597         static uat_field_t uat_fields[] =  {
598                 UAT_FLD_CSTRING_OTHER(macro,name,macro_name_chk,"The name of the macro."),
599                 UAT_FLD_CSTRING_ISPRINT(macro,text,"The text this macro resolves to."),
600                 UAT_END_FIELDS
601         };
602
603         dfilter_macro_uat = uat_new("Display Filter Macros",
604                                     sizeof(dfilter_macro_t),
605                                     DFILTER_MACRO_FILENAME,
606                                     TRUE,
607                                     (void*) &macros,
608                                     &num_macros,
609                                     NULL,
610                                     "ChDisplayFilterMacrosSection",
611                                     macro_copy,
612                                     macro_update,
613                                     macro_free,
614                                     uat_fields);
615
616         fvt_cache = g_hash_table_new(g_str_hash,g_str_equal);
617 }
618
619 void dfilter_macro_get_uat(void** p) {
620         *p = dfilter_macro_uat;
621 }
622
623 #ifdef DUMP_DFILTER_MACRO
624 /*
625  * The dfilter_macro_t has several characteristics that are
626  * not immediattly obvious. The dump_dfilter_filter_macro_t()
627  * function can be used to help "visualize" the contents of
628  * a dfilter_macro_t.
629  *
630  * Some non-obvious components of this struct include:
631  *
632  *    m->parts is an argv style array of pointers into the
633  *    m->priv string.
634  *
635  *    The last pointer of an m->parts array should contain
636  *    NULL to indicate the end of the parts pointer array.
637  *
638  *    m->priv is a "cooked" copy of the m->text string.
639  *    Any variable substitution indicators within m->text
640  *    ("$1", "$2", ...) will have been replaced with ASCII
641  *    NUL characters within m->priv.
642  *
643  *    The first element of m->parts array (m-parts[0]) will
644  *    usually have the same pointer value as m->priv (unless
645  *    the dfilter-macro starts off with a variable
646  *    substitution indicator (e.g. "$1").
647  */
648
649 void dump_dfilter_macro_t(const dfilter_macro_t *m, const char *function, const char *file, int line)
650 {
651         printf("\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
652
653         if(m == NULL) {
654                 printf("  dfilter_macro_t * == NULL! (via: %s(): %s:%d)\n", function, file, line);
655                 printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
656         }
657
658         printf("DUMP of dfilter_macro_t: %p (via: %s(): %s:%d)\n", m, function, file, line);
659
660         printf("  &dfilter_macro->name     == %p\n", &m->name);
661         if(m->name == NULL) {
662                 printf("                ->name     == NULL\n");
663         } else {
664                 printf("                ->name     == %p\n", m->name);
665                 printf("                ->name     == <%s>\n", m->name);
666         }
667
668         printf("  &dfilter_macro->text     == %p\n", &m->text);
669         if(m->text == NULL) {
670                 printf("                ->text     == NULL\n");
671         } else {
672                 printf("                ->text     == %p\n", m->text);
673                 printf("                ->text     == <%s>\n", m->text);
674         }
675
676         printf("  &dfilter_macro->usable   == %p\n", &m->usable);
677         printf("                ->usable   == %u\n", m->usable);
678
679         printf("  &dfilter_macro->parts    == %p\n", &m->parts);
680
681         if(m->parts == NULL) {
682                 printf("                ->parts    == NULL\n");
683         } else {
684                 int i = 0;
685
686                 while (m->parts[i]) {
687                         printf("                ->parts[%d] == %p\n", i, m->parts[i]);
688                         printf("                ->parts[%d] == <%s>\n", i, m->parts[i]);
689                         i++;
690                 }
691                 printf("                ->parts[%d] == NULL\n", i);
692         }
693
694         printf("  &dfilter_macro->args_pos == %p\n", &m->args_pos);
695         if(m->args_pos == NULL) {
696                 printf("                ->args_pos == NULL\n");
697         } else {
698                 printf("                ->args_pos == %p\n", m->args_pos);
699                 /*printf("                ->args_pos == <%?>\n", m->args_pos);*/
700         }
701
702         printf("  &dfilter_macro->argc     == %p\n", &m->argc);
703         printf("                ->argc     == %d\n", m->argc);
704
705         printf("  &dfilter_macro->priv     == %p\n", &m->priv);
706         if(m->priv == NULL) {
707                 printf("                ->priv     == NULL\n");
708         } else {
709                 printf("                ->priv     == %p\n", m->priv);
710                 printf("                ->priv     == <%s>\n", (char *)m->priv);
711         }
712
713         printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
714 }
715 #endif
716