3 # Script to convert Exim FAQ into wiki markup - moin flavour
5 # $Cambridge: exim/doc/doc-scripts/f2wiki,v 1.1 2004/11/12 14:48:08 nm4 Exp $
17 foreach my $str (@in) {
18 $str =~ tr/0-9A-Za-z _//cd;
21 return join('/', @bits);
26 sub mkwikifilename ($) {
29 $wn =~ s/([^A-Za-z0-9])/sprintf("_%02x",ord($1))/eg;
36 sub format_wiki_question_header ($) {
41 for($i =0; ($i <= $#lines); $i++) {
42 unless (defined($lines[$i])) {
46 $lines[$i] =~ s/^\s+//;
48 return('= ' . join(' ', @lines) . ' =');
57 my $qtag = $meta->qtags->{$qref};
59 warn "Unknown qtag $qref\n";
63 return join(':', '[', $qref->{wikiname}, $qref . ']');
68 sub wiki_markup ($$) {
72 $s =~ s/@\\/\@\@backslash\@\@/g; # @\ temporarily hidden
74 $s =~ s/\\#/ /g; # \# is a hard space
76 $s =~ s/\\\*\*([^*]*)\*\*\\/'''$1'''/g; # \**...**\ => bold
77 $s =~ s/\\\*([^*]*)\*\\/''$1''/g; # \*.....*\ => italic
78 $s =~ s/\\"([^"]*)"\\/`$1`/g; # \"....."\ => fixed pitch
79 $s =~ s/\\\$([^\$]*)\$\\/''\$$1''/g; # \$.....$\ => $italic
80 $s =~ s/\\\\([^\\]*)\\\\/<small>$1<\/small>/g; # \\.....\\ => small
81 $s =~ s/\\\(([^)]*)\)\\/''$1''/g; # \(.....)\ => italic
82 $s =~ s/\\-([^\\]*)-\\/'''-$1'''/g; # \-.....-\ => -bold
83 $s =~ s/\\\[([^]]*)\]\\/''$1''/gx; # \[.....]\ => <italic>
84 $s =~ s/\\\?(.*?)\?\\/$1/g; # \?.....?\ => URL
85 $s =~ s/\\\^\^([^^]*)\^\^\\/''$1''/g; # \^^...^^\ => italic
86 $s =~ s/\\\^([^^]*)\^\\/''$1''/g; # \^.....^\ => italic
87 $s =~ s/\\%([^%]*)%\\/'''$1'''/g; # \%.....%\ => bold
88 $s =~ s/\\\/([^\/]*)\/\\/''$1''/g; # \/...../\ => italic
89 $s =~ s/\\([^\\]+)\\/`$1`/g; # \.......\ => fixed pitch
91 $s =~ s"//([^/\"]*)//"''$1</i>"g; # //.....// => italic
92 $s =~ s/::([^:]*)::/''$1:''/g; # ::.....:: => italic:
94 $s =~ s/``(.*?)''/“$1”/g; # ``.....'' => quoted text
96 #$s =~ s/\s*\[\[br\]\]\s*/<br>/g; # [[br]] => <br>
98 $s =~ s/\@\@backslash\@\@/\\/g; # Put back single backslash
100 $s =~ s/^(\s*\(\d\)\s)/$1 /; # Extra space after (1), etc.
102 # Cross references within paragraphs
104 $s =~ s/Q(\d{4})(?!:)/wikiref($meta, $1)/xg;
106 # References to configuration samples
108 ##$s =~ s/\b([CFLS]\d\d\d)\b/<a href="$1.txt">$1<\/a>/g;
110 # Remove white space preceding a newline in the middle of paragraphs,
111 # to keep the file smaller (and for human reading when debugging).
120 sub clip_paragraph ($) {
127 # split off and throw initial para breaks
128 while (($#{$lines} >= 0) && (!defined($lines->[0]))) {
132 # if nothing else return
134 unless ($#{$lines} >= 0);
136 # deal with example chunks
137 if ($lines->[0] =~ /^(\=\=\>\s+)\S/) {
138 $offlen = length($1);
139 while (($#{$lines} >= 0) && (defined($lines->[0]))) {
140 my $txt = substr(shift @{$lines}, $offlen);
141 $ret .= (defined($ret)) ? "\n$txt" : $txt;
143 return ($ret, 'code');
147 # deal with rest - numeric lines first
148 if ($lines->[0] =~ /^(\s+\(\d+\)\s*)/) {
149 $offlen = length($1);
152 } elsif ($lines->[0] =~ /^(\s+)\S/) {
153 $offlen = length($1);
162 while (($#{$lines} >= 0) && (defined($lines->[0]))) {
165 substr(shift @{$lines}, $offlen);
170 return ($ret, $flags);
175 sub format_wiki_text ($$) {
182 while ($#lines >= 0) {
183 my($para, $flags) = clip_paragraph(\@lines);
184 if ($flags eq 'code') {
185 $out .= "{{{\n" . $para . "\n}}}\n";
186 } elsif ($flags eq 'numlist') {
187 $out .= ' 1. ' . wiki_markup($meta, $para) . "\n";
188 } elsif ($flags eq 'empty') {
190 $out .= wiki_markup($meta, $para) . "\n";
198 sub output_wiki_header ($$$) {
203 $fh->print(join("\n",
205 '#pragma section-numbers off',
206 '## Autogenerated by f2wiki',
207 join('', '["FAQ"] / [:',
208 $qset->{section}->{wikiname},
210 $qset->{section}->{title},
214 '[[Navigation(siblings)]]',
221 sub output_wiki_question ($$$$) {
227 $fh->print(join("\n",
228 ('= ' . $qset->{qtag} . ' ='),
232 format_wiki_text($meta, $lines),
239 sub output_wiki_answer ($$$$) {
245 $fh->print(join("\n",
247 format_wiki_text($meta, $lines),
253 sub output_wiki_trailer ($$$) {
258 $fh->print(join("\n",
260 '[[Navigation(siblings)]]',
262 join('', '["FAQ"] / [:',
263 $qset->{section}->{wikiname},
265 $qset->{section}->{title},
269 'CategoryFrequentlyAskedQuestions',
278 my $tfh = IO::File->new('FAQ', 'w');
279 foreach my $sect (values %{$meta->{sections}}) {
280 my $fh = IO::File->new($sect->{wikifile}, 'w');
281 $fh->print(join("\n",
283 '#pragma section-numbers off',
284 '## Autogenerated by f2wiki',
285 join('', '["FAQ"] / [:',
291 '[[Navigation(siblings,1)]]',
293 '[[Navigation(children)]]',
297 '= ' . $sect->{title} . ' =',
302 '/.*,,2,from="##qstart",to="##qend")]]'),
305 '[[Navigation(siblings,1)]]',
307 '[[Navigation(children)]]',
309 join('', '["FAQ"] / [:',
315 'CategoryFrequentlyAskedQuestions',
318 $tfh->print(' * [:', $sect->{wikiname}, ':', $sect->{title}, "]\n");
324 sub process_qset ($$$$) {
330 unless ($qset->{wikifile}) {
338 my $fh = IO::File->new($qset->{wikifile}, 'w') ||
339 die "$qset->{wikifile} OUT $!";
340 output_wiki_header($fh, $meta, $qset);
341 output_wiki_question($fh, $meta, $qset, $qlines);
342 output_wiki_answer($fh, $meta, $qset, $alines);
343 output_wiki_trailer($fh, $meta, $qset);
348 sub parse_faqsrc ($$) {
357 unless(defined($section)) {
358 unless (/^\d+\.\s/) {
362 $sectitle) = split(/\s+/, $_, 3);
363 $secnum =~ tr/0-9//cd;
364 my $wikiname = mkwikiname('FAQ', $sectitle);
365 my $wikifile = mkwikifilename($wikiname);
366 $meta->{sections}->{$secnum} =
369 wikiname => $wikiname,
370 wikifile => $wikifile,
378 $sect = $meta->{sections}->{$section};
380 } elsif (/^(Q\d+):/) {
382 my $wikiname = mkwikiname('FAQ', $sect->{title}, $qtag);
383 my $wikifile = mkwikifilename($wikiname);
384 my $qset = {section => $sect,
386 wikiname => $wikiname,
387 wikifile => $wikifile};
388 $meta->{qtags}->{$qtag} = $qset;
389 push(@{$sect->{qtags}}, $qset);
396 sub process_faqsrc ($$) {
403 my $clines = $qlines;
408 # skip preceding stuff....
409 unless(defined($qset)) {
410 next unless (/^Q\d+/);
414 # just skip section boundaries - we have done those before
416 } elsif (/^([QA]\d+):\s+(.+)$/) {
419 if (substr($1, 0, 1) eq 'Q') {
420 process_qset($meta, $qset, $qlines, $alines);
424 $qset = $meta->{qtags}->{$qtag};
428 push(@{$clines}, $line);
430 push(@{$clines}, undef);
432 push(@{$clines}, $_);
436 process_qset($meta, $qset, $qlines, $alines);
448 my $fh = IO::File->new(shift, 'r') || die $!;
450 parse_faqsrc($fh, $state);
452 # print Dumper($state);
453 process_faqsrc($fh, $state);