#!/usr/bin/perl -n # SPDX-License-Identifier: GPL-2.0+ # Copyright 2016 Red Hat, Inc. # # This tool formats enums along with their Gtk-Doc comments from a header # file and produces a Docbook refentry suitable for inclusion in the D-Bus # API rederence documentation. # # The output differs from Gtk-Doc: only enums are considered and are # printed along with the values that are need to stay stable. use strict; use warnings; our $name; our $desc; our $choice; our @choices; our $val; BEGIN { my $id = shift @ARGV or die "Missing ID"; my $nm = shift @ARGV or die "Missing title"; print < $nm 3 $nm $nm END } # Increment a value keeping the format (e.g. 0x000666 is incremented to 0x000667, # while 9 becomes 10. sub inc { my $val = shift; if ($val =~ /^-?\d+$/) { my $len = length $val; if ($val =~ /^-/ && ($val + 1 == 0)) { $len = $len - 1 } return sprintf "%0${len}d", $val + 1; } elsif ($val =~ /^0x(.+)$/) { my $len = length $1; return sprintf "0x%0${len}x", hex ($1) + 1; } die "'$val' used in previous enum value can not be incremented"; } # The Gtk-Doc to docbook translation happens here. We don't support # everything Gtk-Doc does. sub fmt { $_ = shift; s/\#([^\s\.]+)/$1<\/link>/gm; s/\s*(.*)/$1<\/para>/gm; $_; } chomp; if (/^\/\*\*$/) { # Start of a documentation comment $name = ''; $desc = ''; $choice = undef; @choices = (); } elsif (/^ \* (.+):$/) { # The name die "Duplicate name '$1': already processing '$name'" if $name; $name = $1; } elsif (/^ \* @(\S+):\s+(.*)$/) { # The enum choice documentation $choice = $1; die "Documentation for '$1' already seen" if grep { $_->[0] eq $choice } @choices; push @choices, [ $choice, $2 ] } elsif (/^ \*\s+(.*)$/) { # Text. Either a choice documentation, a description or continuation of either if (defined $choice) { my ($this) = grep { $_->[0] eq $choice } @choices; $this->[1] .= " $1"; } elsif (defined $desc) { $desc .= " " if $desc; $desc .= $1; } } elsif (/^ \*$/) { # A separator line. Either starts the description or breaks a paragraph. $desc .= "\n" if $desc; $choice = undef; } elsif (/^ \*+\/$/) { # End of the doc comment $choice = undef; } elsif (/^typedef enum/) { # Start of an enum $val = -1; } elsif (/^\s+(\S+)\s+=\s+([^,\s]+)/) { # A choice with a literal value next unless @choices; die "Saw enum value '$1', but didn't see start of enum before" unless defined $val; $val = $2; my ($this) = grep { $_->[0] eq $1 } @choices; die "Documentation for value '$1' missing" unless $this; $this->[2] = "= $val"; } elsif (/^\s+([^,\s]+),?$/) { # A choice without a literal value next unless @choices; die "Saw enum value '$1', but didn't see start of enum before" unless defined $val; my ($this) = grep { $_->[0] eq $1 } @choices; die "Documentation for value '$1' missing" unless $this; $val = inc $val; $this->[2] = "= $val"; } elsif (/^\} ([^;]+);/) { # End of an enum next unless defined $name; die "Name of the enum '$1' different than documented '$name'" if $1 ne $name; @choices = grep { $_->[0] !~ /_LAST$/ } @choices; foreach (@choices) { die "'$_->[0]' documented, but not present in enum" unless defined $_->[2] } $desc = fmt $desc; print < enum $name $name $desc Values END foreach (@choices) { my ($name, $desc, $val) = map { fmt $_ } @$_; print < $name $val $desc END print < END $name = undef; $desc = undef; $choice = undef; $val = undef; @choices = (); } else { # Only care about other lines if we're parsing an enum next unless $val; s/\/\*.*\*\///g; die "Unexpected input '$_' while parsing enum" unless /^\s*$/; } END { print < END }