summaryrefslogtreecommitdiffstats
path: root/pidl/lib/Parse/Pidl/Wireshark/Conformance.pm
diff options
context:
space:
mode:
Diffstat (limited to 'pidl/lib/Parse/Pidl/Wireshark/Conformance.pm')
-rw-r--r--pidl/lib/Parse/Pidl/Wireshark/Conformance.pm510
1 files changed, 510 insertions, 0 deletions
diff --git a/pidl/lib/Parse/Pidl/Wireshark/Conformance.pm b/pidl/lib/Parse/Pidl/Wireshark/Conformance.pm
new file mode 100644
index 0000000..7f7ef18
--- /dev/null
+++ b/pidl/lib/Parse/Pidl/Wireshark/Conformance.pm
@@ -0,0 +1,510 @@
+###################################################
+# parse an Wireshark conformance file
+# Copyright jelmer@samba.org 2005
+# released under the GNU GPL
+
+=pod
+
+=head1 NAME
+
+Parse::Pidl::Wireshark::Conformance - Conformance file parser for Wireshark
+
+=head1 DESCRIPTION
+
+This module supports parsing Wireshark conformance files (*.cnf).
+
+=head1 FILE FORMAT
+
+Pidl needs additional data for Wireshark output. This data is read from
+so-called conformance files. This section describes the format of these
+files.
+
+Conformance files are simple text files with a single command on each line.
+Empty lines and lines starting with a '#' character are ignored.
+Arguments to commands are separated by spaces.
+
+The following commands are currently supported:
+
+=over 4
+
+=item I<TYPE> name dissector ft_type base_type mask valsstring alignment
+
+Register new data type with specified name, what dissector function to call
+and what properties to give header fields for elements of this type.
+
+=item I<NOEMIT> type
+
+Suppress emitting a dissect_type function for the specified type
+
+=item I<PARAM_VALUE> type param
+
+Set parameter to specify to dissector function for given type.
+
+=item I<HF_FIELD> hf title filter ft_type base_type valsstring mask description
+
+Generate a custom header field with specified properties.
+
+=item I<HF_RENAME> old_hf_name new_hf_name
+
+Force the use of new_hf_name when the parser generator was going to
+use old_hf_name.
+
+This can be used in conjunction with HF_FIELD in order to make more than
+one element use the same filter name.
+
+=item I<ETT_FIELD> ett
+
+Register a custom ett field
+
+=item I<STRIP_PREFIX> prefix
+
+Remove the specified prefix from all function names (if present).
+
+=item I<PROTOCOL> longname shortname filtername
+
+Change the short-, long- and filter-name for the current interface in
+Wireshark.
+
+=item I<FIELD_DESCRIPTION> field desc
+
+Change description for the specified header field. `field' is the hf name of the field.
+
+=item I<IMPORT> dissector code...
+
+Code to insert when generating the specified dissector. @HF@ and
+@PARAM@ will be substituted.
+
+=item I<INCLUDE> filename
+
+Include conformance data from the specified filename in the dissector.
+
+=item I<TFS> hf_name "true string" "false string"
+
+Override the text shown when a bitmap boolean value is enabled or disabled.
+
+=item I<MANUAL> fn_name
+
+Force pidl to not generate a particular function but allow the user
+to write a function manually. This can be used to remove the function
+for only one level for a particular element rather than all the functions and
+ett/hf variables for a particular element as the NOEMIT command does.
+
+=item I<CODE START>/I<CODE END>
+Begin and end a section of code to be put directly into the generated
+source file for the dissector.
+
+=item I<HEADER START>/I<HEADER END>
+Begin and end a section of code to be put directly into the generated
+header file for the dissector.
+
+=back
+
+=head1 EXAMPLE
+
+ INFO_KEY OpenKey.Ke
+
+=cut
+
+package Parse::Pidl::Wireshark::Conformance;
+
+require Exporter;
+use vars qw($VERSION);
+$VERSION = '0.01';
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(ReadConformance ReadConformanceFH valid_ft_type valid_base_type);
+
+use strict;
+use warnings;
+
+use Parse::Pidl qw(fatal warning error);
+use Parse::Pidl::Util qw(has_property);
+use Parse::Pidl::Typelist qw(addType);
+
+sub handle_type($$$$$$$$$$)
+{
+ my ($pos,$data,$name,$dissectorname,$ft_type,$base_type,$mask,$valsstring,$alignment) = @_;
+
+ unless(defined($alignment)) {
+ error($pos, "incomplete TYPE command");
+ return;
+ }
+
+ unless ($dissectorname =~ /.*dissect_.*/) {
+ warning($pos, "dissector name does not contain `dissect'");
+ }
+
+ unless(valid_ft_type($ft_type)) {
+ warning($pos, "invalid FT_TYPE `$ft_type'");
+ }
+
+ unless (valid_base_type($base_type)) {
+ warning($pos, "invalid BASE_TYPE `$base_type'");
+ }
+
+ $dissectorname =~ s/^\"(.*)\"$/$1/g;
+
+ if (not ($dissectorname =~ /;$/)) {
+ warning($pos, "missing semicolon");
+ }
+
+ $data->{types}->{$name} = {
+ NAME => $name,
+ POS => $pos,
+ USED => 0,
+ DISSECTOR_NAME => $dissectorname,
+ FT_TYPE => $ft_type,
+ BASE_TYPE => $base_type,
+ MASK => $mask,
+ VALSSTRING => $valsstring,
+ ALIGNMENT => $alignment
+ };
+
+ addType({
+ NAME => $name,
+ TYPE => "CONFORMANCE",
+ BASEFILE => "conformance file",
+ DATA => {
+ NAME => $name,
+ TYPE => "CONFORMANCE",
+ ALIGN => $alignment
+ }
+ });
+}
+
+sub handle_tfs($$$$$)
+{
+ my ($pos,$data,$hf,$trues,$falses) = @_;
+
+ unless(defined($falses)) {
+ error($pos, "incomplete TFS command");
+ return;
+ }
+
+ $data->{tfs}->{$hf} = {
+ TRUE_STRING => $trues,
+ FALSE_STRING => $falses
+ };
+}
+
+sub handle_hf_rename($$$$)
+{
+ my ($pos,$data,$old,$new) = @_;
+
+ unless(defined($new)) {
+ warning($pos, "incomplete HF_RENAME command");
+ return;
+ }
+
+ $data->{hf_renames}->{$old} = {
+ OLDNAME => $old,
+ NEWNAME => $new,
+ POS => $pos,
+ USED => 0
+ };
+}
+
+sub handle_param_value($$$$)
+{
+ my ($pos,$data,$dissector_name,$value) = @_;
+
+ unless(defined($value)) {
+ error($pos, "incomplete PARAM_VALUE command");
+ return;
+ }
+
+ $data->{dissectorparams}->{$dissector_name} = {
+ DISSECTOR => $dissector_name,
+ PARAM => $value,
+ POS => $pos,
+ USED => 0
+ };
+}
+
+sub valid_base_type($)
+{
+ my $t = shift;
+ return 0 unless($t =~ /^BASE_.*/);
+ return 1;
+}
+
+sub valid_ft_type($)
+{
+ my $t = shift;
+ return 0 unless($t =~ /^FT_.*/);
+ return 1;
+}
+
+sub handle_hf_field($$$$$$$$$$)
+{
+ my ($pos,$data,$index,$name,$filter,$ft_type,$base_type,$valsstring,$mask,$blurb) = @_;
+
+ unless(defined($blurb)) {
+ error($pos, "incomplete HF_FIELD command");
+ return;
+ }
+
+ unless(valid_ft_type($ft_type)) {
+ warning($pos, "invalid FT_TYPE `$ft_type'");
+ }
+
+ unless(valid_base_type($base_type)) {
+ warning($pos, "invalid BASE_TYPE `$base_type'");
+ }
+
+ $data->{header_fields}->{$index} = {
+ INDEX => $index,
+ POS => $pos,
+ USED => 0,
+ NAME => $name,
+ FILTER => $filter,
+ FT_TYPE => $ft_type,
+ BASE_TYPE => $base_type,
+ VALSSTRING => $valsstring,
+ MASK => $mask,
+ BLURB => $blurb
+ };
+}
+
+sub handle_strip_prefix($$$)
+{
+ my ($pos,$data,$x) = @_;
+
+ push (@{$data->{strip_prefixes}}, $x);
+}
+
+sub handle_noemit($$$)
+{
+ my ($pos,$data,$type) = @_;
+
+ if (defined($type)) {
+ $data->{noemit}->{$type} = 1;
+ } else {
+ $data->{noemit_dissector} = 1;
+ }
+}
+
+sub handle_manual($$$)
+{
+ my ($pos,$data,$fn) = @_;
+
+ unless(defined($fn)) {
+ warning($pos, "incomplete MANUAL command");
+ return;
+ }
+
+ $data->{manual}->{$fn} = 1;
+}
+
+sub handle_protocol($$$$$$)
+{
+ my ($pos, $data, $name, $longname, $shortname, $filtername) = @_;
+
+ $data->{protocols}->{$name} = {
+ LONGNAME => $longname,
+ SHORTNAME => $shortname,
+ FILTERNAME => $filtername
+ };
+}
+
+sub handle_fielddescription($$$$)
+{
+ my ($pos,$data,$field,$desc) = @_;
+
+ unless(defined($desc)) {
+ warning($pos, "incomplete FIELD_DESCRIPTION command");
+ return;
+ }
+
+ $data->{fielddescription}->{$field} = {
+ DESCRIPTION => $desc,
+ POS => $pos,
+ USED => 0
+ };
+}
+
+sub handle_import
+{
+ my $pos = shift @_;
+ my $data = shift @_;
+ my $dissectorname = shift @_;
+
+ unless(defined($dissectorname)) {
+ error($pos, "no dissectorname specified");
+ return;
+ }
+
+ $data->{imports}->{$dissectorname} = {
+ NAME => $dissectorname,
+ DATA => join(' ', @_),
+ USED => 0,
+ POS => $pos
+ };
+}
+
+sub handle_ett_field
+{
+ my $pos = shift @_;
+ my $data = shift @_;
+ my $ett = shift @_;
+
+ unless(defined($ett)) {
+ error($pos, "incomplete ETT_FIELD command");
+ return;
+ }
+
+ push (@{$data->{ett}}, $ett);
+}
+
+sub handle_include
+{
+ my $pos = shift @_;
+ my $data = shift @_;
+ my $fn = shift @_;
+
+ unless(defined($fn)) {
+ error($pos, "incomplete INCLUDE command");
+ return;
+ }
+
+ ReadConformance($fn, $data);
+}
+
+my %field_handlers = (
+ TYPE => \&handle_type,
+ NOEMIT => \&handle_noemit,
+ MANUAL => \&handle_manual,
+ PARAM_VALUE => \&handle_param_value,
+ HF_FIELD => \&handle_hf_field,
+ HF_RENAME => \&handle_hf_rename,
+ ETT_FIELD => \&handle_ett_field,
+ TFS => \&handle_tfs,
+ STRIP_PREFIX => \&handle_strip_prefix,
+ PROTOCOL => \&handle_protocol,
+ FIELD_DESCRIPTION => \&handle_fielddescription,
+ IMPORT => \&handle_import,
+ INCLUDE => \&handle_include
+);
+
+sub ReadConformance($$)
+{
+ my ($f,$data) = @_;
+ my $ret;
+
+ open(IN,"<$f") or return undef;
+
+ $ret = ReadConformanceFH(*IN, $data, $f);
+
+ close(IN);
+
+ return $ret;
+}
+
+sub ReadConformanceFH($$$)
+{
+ my ($fh,$data,$f) = @_;
+
+ my $incodeblock = 0;
+ my $inheaderblock = 0;
+
+ my $ln = 0;
+
+ foreach (<$fh>) {
+ $ln++;
+ next if (/^#.*$/);
+ next if (/^$/);
+
+ s/[\r\n]//g;
+
+ if ($_ eq "CODE START") {
+ if ($incodeblock) {
+ warning({ FILE => $f, LINE => $ln },
+ "CODE START inside CODE section");
+ }
+ if ($inheaderblock) {
+ error({ FILE => $f, LINE => $ln },
+ "CODE START inside HEADER section");
+ return undef;
+ }
+ $incodeblock = 1;
+ next;
+ } elsif ($_ eq "CODE END") {
+ if (!$incodeblock) {
+ warning({ FILE => $f, LINE => $ln },
+ "CODE END outside CODE section");
+ }
+ if ($inheaderblock) {
+ error({ FILE => $f, LINE => $ln },
+ "CODE END inside HEADER section");
+ return undef;
+ }
+ $incodeblock = 0;
+ next;
+ } elsif ($incodeblock) {
+ if (exists $data->{override}) {
+ $data->{override}.="$_\n";
+ } else {
+ $data->{override} = "$_\n";
+ }
+ next;
+ } elsif ($_ eq "HEADER START") {
+ if ($inheaderblock) {
+ warning({ FILE => $f, LINE => $ln },
+ "HEADER START inside HEADER section");
+ }
+ if ($incodeblock) {
+ error({ FILE => $f, LINE => $ln },
+ "HEADER START inside CODE section");
+ return undef;
+ }
+ $inheaderblock = 1;
+ next;
+ } elsif ($_ eq "HEADER END") {
+ if (!$inheaderblock) {
+ warning({ FILE => $f, LINE => $ln },
+ "HEADER END outside HEADER section");
+ }
+ if ($incodeblock) {
+ error({ FILE => $f, LINE => $ln },
+ "CODE END inside HEADER section");
+ return undef;
+ }
+ $inheaderblock = 0;
+ next;
+ } elsif ($inheaderblock) {
+ if (exists $data->{header}) {
+ $data->{header}.="$_\n";
+ } else {
+ $data->{header} = "$_\n";
+ }
+ next;
+ }
+
+ my @fields = /([^ "]+|"[^"]+")/g;
+
+ my $cmd = $fields[0];
+
+ shift @fields;
+
+ my $pos = { FILE => $f, LINE => $ln };
+
+ next unless(defined($cmd));
+
+ if (not defined($field_handlers{$cmd})) {
+ warning($pos, "Unknown command `$cmd'");
+ next;
+ }
+
+ $field_handlers{$cmd}($pos, $data, @fields);
+ }
+
+ if ($incodeblock) {
+ warning({ FILE => $f, LINE => $ln },
+ "Expecting CODE END");
+ return undef;
+ }
+
+ return 1;
+}
+
+1;