asciidoc-pve.in: add some real functionality
[pve-docs.git] / scan-adoc-refs
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5 use IO::File;
6 use JSON;
7
8 use Data::Dumper;
9
10 my $environments = {
11 default => 1,
12 wiki => 1,
13 manvolnum => 1,
14 pvelogo => 0, # ignore
15 };
16
17 my $resolve_skip_files = {
18 default => {},
19 wiki => { 'pve-admin-guide.adoc' => 1 },
20 manvolnum => {},
21 };
22
23 my $fileinfo = {};
24
25 my $start_env = [];
26 foreach my $e (keys %$environments) {
27 push @$start_env, $e if $environments->{$e};
28 }
29
30 my $env_stack = [$start_env];
31 my $env_name_stack = [];
32
33 sub reset_environment_stack {
34 $env_stack = [$start_env];
35 $env_name_stack = [];
36 }
37
38 sub push_environment {
39 my ($env, $not) = @_;
40
41 die "undefined environment '$env'\n" if !defined($environments->{$env});
42
43 # FIXME: this seems wrong (nested env?)?
44 return if !$environments->{$env}; # do not track
45
46 if ($not) {
47 my $new_env = [];
48 foreach my $e (@{$env_stack->[-1]}) {
49 if ($e ne $env) {
50 push @$new_env, $e;
51 }
52 }
53 die "empty environment" if !scalar($new_env);
54 push @$env_stack, $new_env;
55 } else {
56 push @$env_stack, [$env];
57 }
58
59 push @$env_name_stack, $env;
60 }
61
62 sub pop_environment {
63 my ($env) = @_;
64
65 die "undefined environment '$env'\n" if !defined($environments->{$env});
66
67 return if !$environments->{$env}; # do not track
68
69 pop @$env_stack;
70 my $res = pop @$env_name_stack;
71
72 die "environment missmatch ($res != $env)\n" if $res ne $env;
73 }
74
75 sub register_include {
76 my ($filename, $include_filename, $env_list) = @_;
77
78 return if $include_filename !~ m/\.adoc$/; # skip attributes.txt
79
80 foreach my $e (@$env_list) {
81 $fileinfo->{include}->{$e}->{$filename}->{$include_filename} = 1;
82 }
83 }
84
85 sub register_blockid {
86 my ($filename, $blockid, $env_list) = @_;
87
88 foreach my $e (@$env_list) {
89 my $fn = $fileinfo->{blockid}->{$e}->{$blockid};
90 die "blockid '$blockid' already defined in $fn"
91 if defined($fn);
92 $fileinfo->{blockid}->{$e}->{$blockid} = $filename;
93 }
94 }
95
96 sub scan_adoc_file {
97 my ($filename) = @_;
98
99 reset_environment_stack();
100
101 # print "SCAN $filename\n";
102
103 my $fh = IO::File->new("$filename", "r") or
104 die "unable to open file '$filename' - $!\n";
105
106 my $env_last_line = {};
107
108 while (defined (my $line = <$fh>)) {
109 if ($line =~ m/^if(n?)def::(\S+)\[(.*)\]\s*$/) {
110 my ($not, $env, $text) = ($1, $2, $3);
111 die "unsuported ifdef usage - implement me" if $text;
112 push_environment($env, $not);
113 next;
114 } elsif ($line =~ m/^endif::(\S+)\[(.*)\]\s*$/) {
115 my ($env, $text) = ($1, $2);
116 die "unsuported ifdef usage - implement me" if $text;
117 pop_environment($env);
118 next;
119 } elsif ($line =~ m/^include::(\S+)\[.*\]\s*$/) {
120 register_include($filename, $1, $env_stack->[-1]);
121 next;
122 }
123
124 # try to detect titles
125 foreach my $e (@{$env_stack->[-1]}) {
126 my $title = $fileinfo->{titles}->{$e}->{$filename};
127 next if defined($title);
128
129 if (($line =~ m/^=====+/) || ($line =~ m/^-----+/)) {
130 $fileinfo->{titles}->{$e}->{$filename} = $env_last_line->{$e};
131 }
132 $env_last_line->{$e} = $line;
133 chomp $env_last_line->{$e};
134 }
135
136 # fixme: also scan <<>>
137
138 while ($line =~ m/xref:([^\s\[\]]+)\[([^\]]*)\]/g) {
139 # print "$filename xref:$1 [$2]\n";
140 }
141
142 if ($line =~ m/^\[\[(.*)\]\]\s*$/) {
143 my $blockid = $1;
144 die "implement me" if $blockid =~m/,/;
145 register_blockid($filename, $blockid, $env_stack->[-1]);
146 }
147 }
148 }
149
150 my $scanned_files = {};
151 while (my $filename = shift) {
152 next if $filename !~ m/\.adoc$/; # skip attributes.txt
153 next if $scanned_files->{$filename};
154
155 scan_adoc_file($filename);
156 $scanned_files->{$filename} = 1;
157 }
158
159 sub resolve_link_target {
160 my ($env, $filename) = @_;
161
162 my $include_hash = $fileinfo->{include}->{$env};
163
164 my $repeat = 1;
165
166 while ($repeat) {
167 $repeat = 0;
168 foreach my $fn (keys %$include_hash) {
169 next if $resolve_skip_files->{$env}->{$fn};
170 if ($include_hash->{$fn}->{$filename}) {
171 $filename = $fn;
172 $repeat = 1;
173 last;
174 }
175 }
176 }
177
178 return $filename;
179 }
180
181 # now resolve blockids
182 foreach my $e (@$start_env) {
183 my $blockid_hash = $fileinfo->{blockid}->{$e};
184 foreach my $blockid (keys %$blockid_hash) {
185 my $fn = resolve_link_target($e, $blockid_hash->{$blockid});
186 if ($e eq 'wiki') {
187 my $title = $fileinfo->{titles}->{$e}->{$fn};
188 $title =~ s/\s/_/g;
189 $title =~ s/\{pve\}/Proxmox VE/g;
190 die "found not title for '$fn' in env '$e'" if !$title;
191 $fileinfo->{blockid_target}->{$e}->{$blockid} = "link:/wiki/$title#$blockid";
192 } else {
193 $fileinfo->{blockid_target}->{$e}->{$blockid} = $fn;
194 }
195 }
196 }
197
198
199 print to_json($fileinfo, { pretty => 1 } );