#!/usr/bin/perl
#
# Copyright (c) 1994-2019 Carnegie Mellon University. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. The name "Carnegie Mellon University" must not be used to
# endorse or promote products derived from this software without
# prior written permission. For permission or any legal
# details, please contact
# Carnegie Mellon University
# Center for Technology Transfer and Enterprise Creation
# 4615 Forbes Avenue
# Suite 302
# Pittsburgh, PA 15213
# (412) 268-7393, fax: (412) 268-7395
# innovation@andrew.cmu.edu
#
# 4. Redistributions of any form whatsoever must retain the following
# acknowledgment:
# "This product includes software developed by Computing Services
# at Carnegie Mellon University (http://www.cmu.edu/computing/)."
#
# CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
# AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
# OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# Copyright (c) 2017 Nic Bernstein, Onlight, Inc.
#
require 5;
package config2sample;
use strict;
use warnings;
my $mode = 0;
my $save = "";
my $enums = "";
our $opt = '';
#
our %ftags = (
'R' => '', # (1) Times Roman
);
our @fstack = ();
our $tabs = 0;
our $spaces = 0;
our $inl = 0;
our $section = 0;
my $blank = "";
# XXX get rid of cvs id cruft
my $version = "\$Revision: 1.6 $blank";
sub print_values {
my @values = eval $_[0];
my $v;
my $sep = ' ';
my $result = 'Allowed values:';
foreach $v (@values) {
$result .= "$sep\\fI$v\\fR";
$sep = ", ";
}
print print_man2rst("$result");
}
sub switch_font {
my $font = shift;
my $tags = "";
our %ftags;
our @fstack;
# If font is numeric, convert to letter format
if ($font =~ /^\d+$/) {
if ($font <= 4) {
$font = ("R", "I", "B", "BI")[$font - 1];
}
else {
die("invalid numeric font value, '$font'\n");
}
}
# normal font
if ($font ne "P") {
if (scalar(@fstack) > 0) {
# end last font
$tags .= $ftags{pop(@fstack)};
}
if (defined($ftags{$font})) {
# start new font, push new font on stack
if (length $tags > 0 && $font ne 'R') {
$tags .= '\ ';
}
$tags .= $ftags{$font};
push(@fstack, $font);
}
else {
# push unknown font on stack
push(@fstack, 'U'); # unknown
warn("unknown font '", $font, "'\n");
}
}
# previous font
elsif (scalar(@fstack) > 0) {
# pop current font off stack, end current font
$tags .= $ftags{pop(@fstack)};
if ($#fstack >= 0) {
# if there was a previous font, start previous font
if (length $tags > 0 && $fstack[0] ne 'R') {
$tags .= '\ ';
}
$tags .= $ftags{$fstack[0]};
}
}
return $tags;
}
sub print_man2rst {
$_ = shift;
our $tabs; # How many tabstops to indent this line
our $spaces; # How many spaces we indent this line (plus tabs)
our $section; # Whether we're already in a "Section"
our $inl; # Indent Next Line this many tabstops
our $opt; # The current option
our $file; # The file we're working on
my $i = 0;
my $prefix = '';
my $result = '';
# If we're already in a section, and tabs is 0, set it to 1
if ($section == 1 && $tabs == 0) {
$tabs = 1;
$inl = 0;
}
# We have a hanging indent from the last pass
if ($inl > 0) {
$tabs = $inl;
}
# Title
if (/^\.TH\s+(\S+).*$/) {
$file = lc $1;
my $headlen = length $file;
my $header = "\n\n## cyrusman:: $file(5)\n\n";
$header .= "## _imap-reference-manpages-configs-$file:\n\n#";
$header .= '=' x $headlen . "\n#$file\n#" . '=' x $headlen . "\n";
$_ = $header;
}
# Section Header, zero all of our special indenters
elsif (/^\.SH\s*(.*)$/) {
if ($1 eq 'NAME') {
return;
}
my $headlen = length $1;
$_ = "\n#" . $1 . "\n#" . '=' x $headlen . "\n";
$section = 1;
$tabs = 0;
$spaces = 0;
$inl = 0;
}
# Clean up the NAME section
elsif (/^(\S+)\s+\\\-\s+(.*)/) {
if ($1 eq $file) {
$_ = $2;
}
}
# New Paragraph
elsif (/^\.PP/) {
if ($opt eq '') {
$tabs--;
}
$spaces = 0;
$inl = 0;
$_ = '#';
}
# Indent
elsif (/^\s*\.in \+(\d+)/) {
$spaces += $1;
return;
}
# Hanging indent with tag and indent value
elsif (/^\s*\.IP\s+(\S*)\s+(\d+)/) {
$inl = $tabs + int($2/5);
$_ = $1;
}
# Hanging indent with tag
elsif (/^\s*\.IP\s+(\S*)/) {
$inl = $tabs + 1;
$_ = $1;
}
# Hanging Indent
elsif (/^\s*\.IP|\.TP$/) {
$inl = $tabs + 1;
return;
}
# Line break
elsif (/^\s*\.br|\.sp/) {
if ($section == 1) {
return;
}
$_ = '';
}
# Comment, with text
elsif (m|^\s*\.\\"\s*\S+|) {
$spaces = 0;
$inl = 0;
return;
}
# Bare comments, which may mean blank line
elsif (m|^\s*\.\\"$|) {
#$spaces = 0;
$inl--;
if ($section == 1) {
return;
}
$_ = '';
}
# Relative margin indent start
elsif (/^\s*\.RS\s+(\d+)/) {
$inl = $tabs +int($1/5);
$spaces = 0;
return;
}
# Relative margin indent end
elsif (/^\s*\.RE/) {
$tabs--;
$inl = 0;
$spaces = 0;
return;
}
# Traditional Bold/Italic alternating markup. This is hackish...
elsif (/^\s*\.BI\s+(.*)/) {
$_ = $1;
s/\"//g;
s/\\\-(\w)\s+(\w+)/.. option:: -$1 $2/;
}
# Now clean up the font markup...
while (/\\f(\w|\(\w\w|\[\S+\])/) {
my $font = $1;
my $esc = '';
$font =~ s/^\((\w\w)/$1/; # \f(xx
$font =~ s/^\[(\S+)\]/$1/; # \f[xxx]
s/\\f(\w|\(\w\w|\[\S+\])/&switch_font('R')/e;
}
# Unescape hyphens and backslashes
s/\\-/\-/g;
s/\\\\/\\/g;
for ($i=$tabs;$i>0;$i--) {
$prefix .= " ";
}
for ($i=$spaces;$i>0;$i--) {
$prefix .= ' ';
}
if (/^$/) {
$result = "\n";
} else {
$result = "#$prefix$_\n";
}
$prefix = '';
return $result;
}
$version =~ s/.Revision: (.*) /$1/;
print "## auto-generated by config2sample $version\n";
while (<>) {
if ($mode == 0) {
# look for { option, default, type [enums] }; don't output until we
# hit a comment
# n.b. we explicitly do not match deprecated options here
if (m|
{ # opening curly
\s* # skip leading whitespace
\"(.*?)\" # $1: option name (don't capture quotes)
\s*,\s* # comma, optional whitespace
\"?(.*?)\"? # $2: default value (don't capture quotes)
\s*,\s* # comma, optional whitespace
(.*?) # $3: option type
\s* # optional whitespace
(\(.*\))? # $4: list of permitted values, n.b. perl syntax!
\s*,\s* # comma, optional whitespace
\"[^\"]+\" # last modified version
\s* # optional whitespace
} # closing curly
|x) {
$opt = $1;
my $def = $2 eq "NULL" ? "<none>" : $2;
if ($def eq "") { $def = "<empty string>" }
# Drop a tag into the output so we can find this section
# later, from an include directive.
$save .= "#$opt: $def\n";
$enums = $4;
$tabs--;
my $label = ucfirst($opt);
my $lablen = length($label);
print "# $label\n# " . '-' x $lablen . "\n";
}
# look for single-line comment
elsif (m|/\*\s*(.*)\s*\*/|) {
if ($opt ne '') { $tabs++; }
print print_man2rst("$1");
if ($enums) { print_values($enums); }
$opt = '';
$tabs--;
print "$save\n"; $save = "";
}
# look for /* to shift into passthrough mode;
elsif (m|/\*\s*(.*)|) {
if ($opt ne '') { $tabs++; }
print print_man2rst($1);
$mode = 1;
}
else {
chomp;
}
} elsif ($mode == 1) {
# passthru; look for */ to end; print current cached option
# header if any
if (m|\s*(.*)\*/|) {
print print_man2rst($1);
if ($enums) { print_values($enums); }
$mode = 0; # back to search mode
$tabs = 0;
$opt = '';
print "$save\n"; $save = "";
} elsif (m|\s*(.*)\s*|) {
my $stripped = $1;
chomp($stripped);
print print_man2rst($stripped);
}
}
}