Add environment checks. Add a topic to our refspec.
[metze/wireshark/wip.git] / tools / convert_expert_add_info_format.pl
1 #!/usr/bin/env perl
2 #
3 # Copyright 2013 Michael Mann (see AUTHORS file)
4 #
5 # A program to help convert the "old" expert_add_info_format API calls into filterable "items" that
6 # use the other expert API calls.  The program requires 2 passes.  "Pass 1" (generate) collects 
7 # the eligible expert_add_info_format calls and outputs the necessary data into a delimited
8 # file.  "Pass 2" (fix-all) takes the data from the delimited file and replaces the
9 # expert_add_info_format calls with filterable "expert info" calls as well as 
10 # generating a separate files for the  ei variable declarations and array data.
11 # The ei "file" can be copy/pasted into the dissector where appropriate
12 #
13 # Note that the output from "Pass 1" won't always be a perfect conversion for "Pass 2", so
14 # "human interaction" is needed as an intermediary to verify and update the delimited file
15 # before "Pass 2" is done.
16 #
17 # Delimited file field format:
18 # <convert expert_add_info_format_call[1-4]><add ei variable[0|1]><ei var><[GROUP]><[SEVERITY]><[FIELDNAME]><[EXPERTABBREV]>
19 # <pinfo var><proto_item var><tvb var><offset><length><params>
20 #
21 # convert proto_tree_add_text_call enumerations:
22 # 1  - expert_add_info
23 # 2  - expert_add_info_format
24 # 3  - proto_tree_add_expert
25 # 4  - proto_tree_add_expert_format
26 #
27 # Usage: convert_expert_add_info_format.pl action=<generate|fix-all> <file or files>
28 #
29 # Based off of convert_proto_tree_add_text.pl
30 #
31 # $Id$
32 #
33 # Wireshark - Network traffic analyzer
34 # By Gerald Combs <gerald@wireshark.org>
35 # Copyright 1998 Gerald Combs
36 #
37 # This program is free software; you can redistribute it and/or
38 # modify it under the terms of the GNU General Public License
39 # as published by the Free Software Foundation; either version 2
40 # of the License, or (at your option) any later version.
41 #
42 # This program is distributed in the hope that it will be useful,
43 # but WITHOUT ANY WARRANTY; without even the implied warranty of
44 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
45 # GNU General Public License for more details.
46 #
47 # You should have received a copy of the GNU General Public License
48 # along with this program; if not, write to the Free Software
49 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
50 #
51
52 use strict;
53 use warnings;
54
55 use Getopt::Long;
56
57 my %EXPERT_SEVERITY = ('PI_COMMENT' => "PI_COMMENT",
58                                            'PI_CHAT' => "PI_CHAT",
59                                            'PI_NOTE' => "PI_NOTE",
60                                            'PI_WARN' => "PI_WARN",
61                                            'PI_ERROR' => "PI_ERROR");
62
63 my %EXPERT_GROUPS = ('PI_CHECKSUM' => "PI_CHECKSUM",
64                                            'PI_SEQUENCE' => "PI_SEQUENCE",
65                                            'PI_RESPONSE_CODE' => "PI_RESPONSE_CODE",
66                                            'PI_REQUEST_CODE' => "PI_REQUEST_CODE",
67                                            'PI_UNDECODED' => "PI_UNDECODED",
68                                            'PI_REASSEMBLE' => "PI_REASSEMBLE",
69                                            'PI_MALFORMED' => "PI_MALFORMED",
70                                            'PI_DEBUG' => "PI_DEBUG",
71                                            'PI_PROTOCOL' => "PI_PROTOCOL",
72                                            'PI_SECURITY' => "PI_SECURITY",
73                                            'PI_COMMENTS_GROUP' => "PI_COMMENTS_GROUP");
74
75 my @expert_list;
76 my $protabbrev = "";
77
78 # Perl trim function to remove whitespace from the start and end of the string
79 sub trim($)
80 {
81         my $string = shift;
82         $string =~ s/^\s+//;
83         $string =~ s/\s+$//;
84         return $string;
85 }
86
87 # ---------------------------------------------------------------------
88 #
89 # MAIN
90 #
91 my $helpFlag  = '';
92 my $action    = 'generate';
93 my $register  = '';
94
95 my $result = GetOptions(
96                                                 'action=s' => \$action,
97                                                 'register' => \$register,
98                                                 'help|?'   => \$helpFlag
99                                                 );
100
101 if (!$result || $helpFlag || !$ARGV[0]) {
102         usage();
103 }
104
105 sub usage {
106         print "\nUsage: $0 [--action=generate|fix-all|find-all] FILENAME [...]\n\n";
107                 print "  --action = generate (default)\n";
108                 print "    generate - create a delimited file (FILENAME.expert_add_info_input) with\n";
109                 print "               expert_add_info_format fields in FILENAME(s)\n";
110                 print "    fix-all  - Use delimited file (FILENAME.expert_add_info_input) to convert\n";
111                 print "               expert_add_info_format to \"filterable\" expert API\n";
112                 print "               Also generates FILENAME.ei to be copy/pasted into\n";
113                 print "               the dissector where appropriate\n\n";
114                 print "  --register = generate ei_register_info and expert register function calls\n\n";
115
116         exit(1);
117 }
118
119 #
120 # XXX Outline general algorithm here
121 #
122 my $found_total = 0;
123 my $protabbrev_index;
124 my $line_number = 0;
125
126 while (my $fileName = $ARGV[0]) {
127         shift;
128         my $fileContents = '';
129
130         die "No such file: \"$fileName\"\n" if (! -e $fileName);
131
132         # delete leading './'
133         $fileName =~ s{ ^ \. / } {}xo;
134
135         #determine PROTABBREV for dissector based on file name format of (dirs)/packet-PROTABBREV.c
136         $protabbrev_index = rindex($fileName, "packet-");
137         if ($protabbrev_index == -1) {
138                 print "$fileName doesn't fit format of packet-PROTABBREV.c\n";
139                 next;
140         }
141
142         $protabbrev = substr($fileName, $protabbrev_index+length("packet-"));
143         $protabbrev_index = rindex($protabbrev, ".");
144         if ($protabbrev_index == -1) {
145                 print "$fileName doesn't fit format of packet-PROTABBREV.c\n";
146                 next;
147         }
148         $protabbrev = lc(substr($protabbrev, 0, $protabbrev_index));
149
150         # Read in the file (ouch, but it's easier that way)
151         open(FCI, "<", $fileName) || die("Couldn't open $fileName");
152         while (<FCI>) {
153                 $fileContents .= $_;
154         }
155         close(FCI);
156
157         if ($action eq "generate") {
158                 generate_eis(\$fileContents, $fileName);
159         }
160
161         if ($action eq "fix-all") {
162                 # Read in the ei "input" file
163                 $line_number = 0;
164                 my $errors = 0;
165                 open(FCI, "<", $fileName . ".expert_add_info_input") || die("Couldn't open $fileName.expert_add_info_input");
166                 while(my $line=<FCI>){
167                         my @expert_item = split(/;|\n/, $line);
168
169                         $line_number++;
170                         $errors += verify_line(@expert_item);
171
172                         push(@expert_list, \@expert_item);
173                 }
174                 close(FCI);
175
176                 if ($errors > 0) {
177                         print "Aborting conversion.\n";
178                         exit(-1);
179                 }
180
181                 fix_expert_add_info_format(\$fileContents, $fileName);
182
183                 # Write out the ei data
184                 output_ei_data($fileName);
185
186                 # Write out the changed version to a file
187                 open(FCO, ">", $fileName . ".expert_add_info_format");
188                 print FCO "$fileContents";
189                 close(FCO);
190         }
191
192 } # while
193
194 exit $found_total;
195
196 # ---------------------------------------------------------------------
197 # Sanity check the data in the .proto_tree_input file
198 sub verify_line {
199         my( @expert_item) = @_;
200         my $errors = 0;
201
202         #do some basic error checking of the file
203         if (($expert_item[0] eq "1") ||
204                 ($expert_item[0] eq "2") ||
205                 ($expert_item[0] eq "3") ||
206                 ($expert_item[0] eq "4")) {
207                 #expert info conversions
208                 if (!($expert_item[2] =~ /^ei_/)) {
209                         print "$line_number: Poorly formed ei_ variable ($expert_item[2])!\n";
210                         $errors++;
211                 }
212         } else {
213                 print "$line_number: Bad conversion value!\n";
214                 $errors++;
215         }
216
217         if ($expert_item[1] eq "1") {
218                 if (!($expert_item[2] =~ /^ei_/)) {
219                         print "$line_number: Poorly formed ei_ variable ($expert_item[2])!\n";
220                         $errors++;
221                 }
222                 if (!exists($EXPERT_SEVERITY{$expert_item[4]})) {
223                         print "$line_number: Expert severity value '$expert_item[5]' unknown!\n";
224                         $errors++;
225                 }
226                 if (!exists($EXPERT_GROUPS{$expert_item[3]})) {
227                         print "$line_number: Expert group value '$expert_item[4]' unknown!\n";
228                         $errors++;
229                 }
230
231         } elsif ($expert_item[1] ne "0") {
232                         print "$line_number: Bad ei variable generation value!\n";
233                         $errors++;
234         }
235
236         return $errors;
237 }
238
239 sub generate_eis {
240         my( $fileContentsRef, $fileName) = @_;
241         my @args;
242         my $num_items = 0;
243         my @temp;
244         my $str_temp;
245         my $pat;
246
247         $pat = qr /
248                                 (
249                                         (?:expert_add_info_format)\s* \(
250                                         (([^[\,;])*\,){4,}
251                                         [^;]*
252                                         \s* \) \s* ;
253                                 )
254                         /xs;
255
256         while ($$fileContentsRef =~ / $pat /xgso) {
257
258                 my @expert_item = (1, 1, "ei_name", "GROUP", "SEVERITY", "fieldfullname", "fieldabbrevname", 
259                                                          "pinfo", "item", "tvb", "offset", "length", "params");
260                 my $arg_loop = 5;
261                 my $str = "${1}\n";
262                 $str =~ tr/\t\n\r/ /d;
263                 $str =~ s/ \s+ / /xg;
264                 #print "$fileName: $str\n";
265
266                 @args = split(/,/, $str);
267                 #printf "ARGS(%d): %s\n", scalar @args, join("# ", @args);
268                 $args[0] =~ s/expert_add_info_format\s*\(\s*//;
269
270                 $expert_item[7] = $args[0];                     #pinfo
271                 $expert_item[8] = trim($args[1]);       #item
272                 $expert_item[3] = trim($args[2]);       #GROUP
273                 $expert_item[4] = trim($args[3]);       #SEVERITY
274                 $expert_item[5] = trim($args[4]);       #fieldfullname
275                 $expert_item[5] =~ s/\"//;
276
277                 #XXX - conditional?
278                 $expert_item[5] =~ s/\"\s*\)\s*;$//;
279                 $expert_item[5] =~ s/\"$//;
280
281                 #params
282                 $expert_item[12] = "";
283                 while ($arg_loop < scalar @args) {
284                         $expert_item[12] .= trim($args[$arg_loop]);
285                         if ($arg_loop+1 < scalar @args) {
286                                 $expert_item[12] .= ", ";
287                         }
288                         $arg_loop += 1;
289                 }
290                 $expert_item[12] =~ s/\s*\)\s*;$//;
291
292                 #ei variable name
293                 $expert_item[2] = sprintf("ei_%s_%s", $protabbrev, lc($expert_item[5]));
294                 $expert_item[2] =~ s/\s+|-|:/_/g;
295
296                 #field abbreviated name
297                 $expert_item[6] = sprintf("%s.%s", $protabbrev, lc($expert_item[5]));
298                 $expert_item[6] =~ s/\s+|-|:/_/g;
299
300                 push(@expert_list, \@expert_item);
301
302                 $num_items += 1;
303         }
304
305         if ($num_items > 0) {
306                 open(FCO, ">", $fileName . ".expert_add_info_input");
307                 for my $item (@expert_list) {
308                         print FCO join(";", @{$item}), "\n";
309                 }
310                 close(FCO);
311         }
312 }
313
314 # ---------------------------------------------------------------------
315 # Find all expert_add_info_format calls and replace them with the data
316 # found in expert_list
317 sub fix_expert_add_info_format {
318         my( $fileContentsRef, $fileName) = @_;
319         my $found = 0;
320         my $pat;
321
322         $pat = qr /
323                                 (
324                                         (?:expert_add_info_format)\s* \(
325                                         (([^[\,;])*\,){4,}
326                                         [^;]*
327                                         \s* \) \s* ;
328                                 )
329                         /xs;
330
331         $$fileContentsRef =~ s/ $pat /patsub($found, $1)/xges;
332 }
333
334 # ---------------------------------------------------------------------
335 # Format expert info functions with expert_list data
336 sub patsub {
337         my $item_str;
338
339         #print $expert_list[$_[0]][2] . " = ";
340         #print $#{$expert_list[$_[0]]}+1;
341         #print "\n";
342
343         if ($expert_list[$_[0]][0] eq "1") {
344                 $item_str = sprintf("expert_add_info(%s, %s, &%s);",
345                                                  $expert_list[$_[0]][7], $expert_list[$_[0]][8], $expert_list[$_[0]][2]);
346         } elsif ($expert_list[$_[0]][0] eq "2") {
347                 $item_str = sprintf("expert_add_info_format(%s, %s, &%s, \"%s\"",
348                                                  $expert_list[$_[0]][7], $expert_list[$_[0]][8],
349                                                  $expert_list[$_[0]][2], $expert_list[$_[0]][5]);
350                 if (($#{$expert_list[$_[0]]}+1 > 12 ) && ($expert_list[$_[0]][12] ne "")) {
351                         $item_str .= ", $expert_list[$_[0]][12]";
352                 }
353                 $item_str .= ");";
354         } elsif ($expert_list[$_[0]][0] eq "3") {
355                 $item_str = sprintf("proto_tree_add_expert(%s, %s, &%s, %s, %s, %s);",
356                                                  $expert_list[$_[0]][8], $expert_list[$_[0]][7],
357                                                  $expert_list[$_[0]][2], $expert_list[$_[0]][9],
358                                                  $expert_list[$_[0]][10], $expert_list[$_[0]][11]);
359         } elsif ($expert_list[$_[0]][0] eq "4") {
360                 $item_str = sprintf("proto_tree_add_expert_format(%s, %s, &%s, %s, %s, %s, \"%s\"",
361                                                  $expert_list[$_[0]][8], $expert_list[$_[0]][7], $expert_list[$_[0]][2],
362                                                  $expert_list[$_[0]][9], $expert_list[$_[0]][10],
363                                                  $expert_list[$_[0]][11], $expert_list[$_[0]][5]);
364                 if (($#{$expert_list[$_[0]]}+1 > 12) && ($expert_list[$_[0]][12] ne "")) {
365                         $item_str .= ", $expert_list[$_[0]][12]";
366                 }
367                 $item_str .= ");";
368         }
369
370         $_[0] += 1;
371
372         return $item_str;
373 }
374
375 # ---------------------------------------------------------------------
376 # Output the ei variable declarations and expert array.  For now, write them to a file.
377 # XXX - Eventually find the right place to add it to the modified dissector file
378 sub output_ei_data {
379         my( $fileName) = @_;
380         my %eis = ();
381         my $index;
382         my $key;
383
384         #add ei to hash table to prevent against (accidental) duplicates
385         for ($index=0;$index<@expert_list;$index++) {
386                 if ($expert_list[$index][1] eq "1") {
387                         $eis{$expert_list[$index][2]} = $expert_list[$index][2];
388                 }
389         }
390
391         open(FCO, ">", $fileName . ".ei");
392
393         print FCO "/* Generated from convert_expert_add_info_format.pl */\n";
394
395         foreach $key (keys %eis) {
396                 print FCO "static expert_field $key = EI_INIT;\n";
397         }
398         print FCO "\n\n";
399
400         if ($register ne "") {
401                 print FCO "  static ei_register_info ei[] = {\n";
402         }
403
404         %eis = ();
405         for ($index=0;$index<@expert_list;$index++) {
406                 if ($expert_list[$index][1] eq "1") {
407                         if (exists($eis{$expert_list[$index][2]})) {
408                                 print "duplicate ei entry '$expert_list[$index][2]' found!  Aborting conversion.\n";
409                                 exit(-1);
410                         }
411                         $eis{$expert_list[$index][2]} = $expert_list[$index][2];
412
413                         print FCO "      { &$expert_list[$index][2], { \"$expert_list[$index][6]\", $expert_list[$index][3], ";
414                         print FCO "$expert_list[$index][4], \"$expert_list[$index][5]\", EXPFILL }},\r\n";
415                 }
416         }
417
418         if ($register ne "") {
419                 print FCO "  };\n\n\n";
420                 print FCO "  expert_module_t* expert_$protabbrev;\n\n";
421                   
422                 print FCO "  expert_$protabbrev = expert_register_protocol(proto_$protabbrev);\n";
423                 print FCO "  expert_register_field_array(expert_$protabbrev, ei, array_length(ei));\n\n";
424         }
425
426
427         close(FCO);
428 }