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