device: renew dhcp leases on awake for software devices
[NetworkManager.git] / libnm / generate-plugin-docs.pl
1 #!/usr/bin/env perl
2 # vim: ft=perl ts=2 sts=2 sw=2 et ai
3 # -*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License along
17 # with this program; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #
20 #
21 # Copyright 2014 Red Hat, Inc.
22 #
23
24 #
25 # The script parses nm-setting-*.c files and extracts documentation related
26 # to setting plugins. The documentation is in a simple format of lines
27 # "keyword: value". The documentation is enclosed between tags
28 # ---<plugin-name>--- and ---end---
29 # Recognized keywords are:
30 # "property: "     - property name
31 # "variable: "     - name of the variable used by the plugin
32 # "format: "       - format of the value in 'keyfile' plugin
33 # "default: "      - default value when variable is not used
34 # "values: "       - allowed values (e.g. for enumerations)
35 # "example: "      - example(s)
36 # "description: "  - description text
37 # Value is an arbitrary string that can span over multiple lines.
38 #
39 # ifcfg-rh specifics:
40 #  - mark NM extension variables with (+), e.g. variable: UUID(+)
41 #
42
43 use strict;
44 use warnings;
45 use v5.10;
46 #YAML:XS is based on libyaml C library and it is a good and fast YAML implementation.
47 #However it may not be present everywhere. So use YAML instead.
48 #use YAML::XS qw(Load);
49 use YAML qw(Load);
50
51 # global variables
52 my @keywords = ("property", "variable", "format", "values", "default", "example", "description");
53 my @source_files;
54 my @data;
55 my $fo;
56
57 (scalar @ARGV == 3) or die "Usage: $0 <plugin> <srcdir> <output-xml-file>\n";
58 my ($plugin, $srcdir, $output) = @ARGV;
59 my $start_tag = "---$plugin---\\s*\$";
60 my $end_tag   = '---end---';
61
62 # get source files to scan for documentation comments (nm-setting-<something>.c)
63 my $file = "$srcdir/Makefile.libnm-core";
64 open my $fh, '<', $file or die "Can't open $file: $!";
65 while (my $line = <$fh>) {
66   chomp $line;
67   my @strings = $line =~ /\/(nm-setting-[^.]*\.c)(?:\s|$)/g;
68   push @source_files, @strings
69 }
70 close $fh;
71
72 # open output file
73 open $fo, '>', $output or die "Can't open $output: $!";
74
75 # write XML header
76 write_header();
77
78 # write generated documenation for each setting
79 foreach my $c_file (@source_files) {
80   my $path = "$srcdir/$c_file";
81   my $setting_name = get_setting_name($path);
82   write_item("<setting name=\"$setting_name\">");
83   scan_doc_comments($path, $start_tag, $end_tag);
84   write_item("</setting>");
85 }
86
87 # write XML footer
88 write_footer();
89
90 # close output file
91 close $fo;
92
93
94 ### --- subroutines --- ###
95
96 # get setting name from NM_SETTING_*_SETTING_NAME constant in C header file
97 sub get_setting_name {
98   my $path = $_[0];
99   $path =~ s/c$/h/;  # use header file to find out setting name
100   open my $fh, '<', $path or die "Can't open $path: $!";
101   while (my $line = <$fh>) {
102     if ($line =~ /NM_SETTING_.+SETTING_NAME\s+\"(\S+)\"/) {
103       return $1;
104     }
105   }
106 }
107
108 # scan source setting file for documentation tags and write them to XML
109 sub scan_doc_comments {
110   my($setting_file, $start, $end) = @_;
111   open my $fi, '<', $setting_file or die "Can't open $setting_file: $!";
112   while (<$fi>) {
113     if (/$start/ .. /$end/) {
114       next if /$start/;
115       if (/$end/) {
116         process_data();
117       } else {
118         push @data, $_;
119       }
120       next;
121     }
122     # ignore text not inside marks
123   }
124   close $fi;
125 }
126
127 # process plugin property documentation comments (as a YAML document)
128 sub process_data {
129   return if not @data;
130   my $kwd_pat = join("|", @keywords);
131   my $yaml_literal_seq = "|\n";
132
133   foreach (@data) {
134     # make a proper YAML document from @data
135     $_ =~ s/^\s*\**\s+|\s+$//;  # remove leading spaces and *, and traling spaces
136     # Properly indent the text so that it is a valid YAML, and insert | (for literal text)
137     if ($_ =~ /^($kwd_pat):\s+/) {
138       # add | after "keyword:" that allows using literal text (YAML won't break on special character)
139       # http://learnxinyminutes.com/docs/yaml/ and http://www.yaml.org/spec/1.2/spec.html#id2795688
140       $_ =~ s/(^($kwd_pat):)/$1 $yaml_literal_seq/;
141     } else {
142       $_ = " " . $_;  # indent the text
143     }
144   }
145   my $str = join ("", @data);
146   my $yaml_data = Load($str);
147
148   # now write a line into the XML
149   my $name   = $yaml_data->{property}    // "";
150   my $var    = $yaml_data->{variable}    // $name;  # fallback to "property: "
151   my $format = $yaml_data->{format}      // "";
152   my $values = $yaml_data->{values}      // "";
153   my $def    = $yaml_data->{default}     // "";
154   my $exam   = $yaml_data->{example}     // "";
155   my $desc   = $yaml_data->{description} // "";
156
157   chomp($name, $var, $format, $values, $def, $exam, $desc);
158   escape_xml_chars($name, $var, $format, $values, $def, $exam, $desc);
159   my $foo = sprintf("<property name=\"%s\" variable=\"%s\" format=\"%s\" values=\"%s\" ".
160                     "default=\"%s\" example=\"%s\" description=\"%s\"/>",
161                     $name, $var, $format, $values, $def, $exam, $desc);
162   write_item($foo);
163   @data = ();
164 }
165
166 # - XML handling -
167 sub write_header {
168   (my $header =
169     qq{<?xml version=\"1.0\"?>
170        <!DOCTYPE nm-$plugin-docs [
171        ]>
172
173        <nm-$plugin-docs>
174   }) =~ s/^ {7}//mg;
175   print {$fo} $header;
176 }
177
178 sub write_footer {
179   my $footer = "</nm-$plugin-docs>";
180   print {$fo} $footer;
181 }
182
183 sub write_item {
184   my $str = join("", @_);
185   print {$fo} $str, "\n";
186 }
187
188 sub escape_xml_chars {
189   # http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Predefined%5Fentities%5Fin%5FXML
190   foreach my $val (@_) {
191     $val =~ s/&/&amp;/sg;
192     $val =~ s/</&lt;/sg;
193     $val =~ s/>/&gt;/sg;
194     $val =~ s/"/&quot;/sg;
195     $val =~ s/'/&apos;/sg;
196   }
197 }
198