#!/usr/local/bin/perl
#
# $Id: docbook-filter,v 1.5 1998/04/29 13:23:59 ssb Exp $
#
# +----------------------------------------------------------------------+
# | PHP HTML Embedded Scripting Language Version 3.0                     |
# +----------------------------------------------------------------------+
# | Copyright (c) 1997,1998 PHP Development Team (See Credits file)      |
# +----------------------------------------------------------------------+
# | This program is free software; you can redistribute it and/or modify |
# | it under the terms of one of the following licenses:                 |
# |                                                                      |
# |  A) the GNU General Public License as published by the Free Software |
# |     Foundation; either version 2 of the License, or (at your option) |
# |     any later version.                                               |
# |                                                                      |
# |  B) the PHP License as published by the PHP Development Team and     |
# |     included in the distribution in the file: LICENSE                |
# |                                                                      |
# | This program is distributed in the hope that it will be useful,      |
# | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
# | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
# | GNU General Public License for more details.                         |
# |                                                                      |
# | You should have received a copy of both licenses referred to here.   |
# | If you did not, or have any questions about PHP licensing, please    |
# | contact core@php.net.                                                |
# +----------------------------------------------------------------------+
# | Authors: Stig Sther Bakken <ssb@guardian.no>                        |
# +----------------------------------------------------------------------+
#
# This script originally came with sgmls.
# Rewritten to generate Linuxdoc SGML from "phpdoc" PHP reference DTD.
#

# {{{ variables

$backslash_in_data = "\\";
$prog = $0; $prog =~ s|.*/||;
$level = 0;

$doctype= "PHPDOC";

$document_title = "(NO TITLE)";

$indent = 0;

#$direct{"VAR"} = "tt";
#$direct{"FUNC"} = "tt";
$direct{"P"} = "para";

$direct_empty{"NEWLINE"} = "newline";

@store_in = ();

%converted_markup = ();

$'current_argument = '';
$'current_function = '';
$'current_section = '';

@'function_list = ();
%'function_name = ();
%'function_category = ();
%'function_firstver = ();
%'function_returns  = ();
%'function_arg_text = ();
%'function_text = ();
%'categories = ();
%'optional = ();
%'varargs = ();

%'postprocess = ();

# }}}

# {{{ main loop

while (<STDIN>) {
    chop;
    $command = substr($_, 0, 1);
    substr($_, 0, 1) = "";
    if ($command eq '(') {
	&start_element($_);
	$level++;
    }
    elsif ($command eq ')') {
	$level--;
	&end_element($_);
	foreach $key (keys %attribute_value) {
	    @splitkey = split($;, $key);
	    if ($splitkey[0] == $level) {
		delete $attribute_value{$key};
		delete $attribute_type{$key};
	    }
	}
    }
    elsif ($command eq '-') {
	&unescape_data($_);
	&data($_);
    }
    elsif ($command eq 'A') {
	@field = split(/ /, $_, 3);
	$attribute_type{$level,$field[0]} = $field[1];
	&unescape_data($field[2]);
	$attribute_value{$level,$field[0]} = $field[2];
    }
    elsif ($command eq '&') {
	&entity($_);
    }
    elsif ($command eq 'D') {
	@field = split(/ /, $_, 4);
	$data_attribute_type{$field[0], $field[1]} = $field[2];
	&unescape_data($field[3]);
	$data_attribute_value{$field[0], $field[1]} = $field[3];
    }
    elsif ($command eq 'N') {
	$notation{$_} = 1;
	if (defined($sysid)) {
	    $notation_sysid{$_} = $sysid;
	    undef($sysid);
	}
	if (defined($pubid)) {
	    $notation_pubid{$_} = $pubid;
	    undef($pubid);
	}
    }
    elsif ($command eq 'I') {
        @field = split(/ /, $_, 3);
	$entity_type{$field[0]} = $field[1];
	&unescape($field[2]);
	# You may want to substitute \e for \ if the type is CDATA.
	$entity_text{$field[0]} = $field[2];
	$entity_code{$field[0]} = 'I';
    }
    elsif ($command eq 'E') {
	@field = split(/ /, $_);
	$entity_code{$field[0]} = 'E';
	$entity_type{$field[0]} = $field[1];
	$entity_notation{$field[0]} = $field[2];
	if (defined(@files)) {
	    foreach $i (0..$#files) {
		$entity_filename{$field[0], $i} = $files[$i];
	    }
	    undef(@files);
	}
	if (defined($sysid)) {
	    $entity_sysid{$field[0]} = $sysid;
	    undef($sysid);
	}
	if (defined($pubid)) {
	    $entity_pubid{$field[0]} = $pubid;
	    undef($pubid);
	}
    }
    elsif ($command eq 'S') {
	$entity_code{$_} = 'S';
	if (defined(@files)) {
	    foreach $i (0..$#files) {
		$entity_filename{$_, $i} = $files[$i];
	    }
	    undef(@files);
	}
	if (defined($sysid)) {
	    $entity_sysid{$_} = $sysid;
	    undef($sysid);
	}
	if (defined($pubid)) {
	    $entity_pubid{$_} = $pubid;
	    undef($pubid);
	}
    }
    elsif ($command eq '?') {
	&unescape($_);
	&pi($_);
    }
    elsif ($command eq 'L') {
	@field = split(/ /, $_);
	$lineno = $field[0];
	if ($#field >= 1) {
	    &unescape($field[1]);
	    $filename = $field[1];
	}
    }
    elsif ($command eq 'V') {
	@field = split(/ /, $_, 2);
	&unescape($field[1]);
	$environment{$field[0]} = $field[1];
    }
    elsif ($command eq '{') {
	&start_subdoc($_);
    }
    elsif ($command eq '}') {
	&end_subdoc($_);
    }
    elsif ($command eq 'f') {
	&unescape($_);
	push(@files, $_);
    }
    elsif ($command eq 'p') {
	&unescape($_);
	$pubid = $_;
    }
    elsif ($command eq 's') {
	&unescape($_);
	$sysid = $_;
    }
    elsif ($command eq 'C') {
	$conforming = 1;
    }
    else {
	warn "$prog:$ARGV:$.: unrecognized command \`$command'\n";
    }
}

# }}}

# {{{ references and printing

$docbook .= "
 <reference>
  <title>$document_title</title>


";
#<descrip>
# output all functions in alphabetical order
foreach $function (sort @'function_list) {
    $docbook .= $'function_text{$function};
}

# fix references
while (($pattern, $code) = each %'postprocess) {
    $replace = eval($code);
    $docbook =~ s/$pattern/$replace/g;
}

$docbook .= " </reference>\n";

print $docbook;

print "
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:\"../manual.ced\"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
-->
";

# }}}

# {{{ sub unescape

sub unescape {
    $_[0] =~ s/\\([0-7][0-7]?[0-7]?|.)/&esc($1)/eg;
}

# }}}
# {{{ sub esc

sub esc {
    local($_) = $_[0];
    if ($_ eq '012' || $_ eq '12') {
	"";			# ignore RS
    }
    elsif (/^[0-7]/) {
	sprintf("%c", oct);
    }
    elsif ($_ eq 'n') {
	"\n";
    }
    elsif ($_ eq '|') {
	"";
    }
    elsif ($_ eq "\\") {
	"\\";
    }
    else {
	$_;
    }
}

# }}}
# {{{ sub unescape_data

sub unescape_data {
    local($sdata) = 0;
    $_[0] =~ s/\\([0-7][0-7]?[0-7]?|.)/&esc_data($1)/eg;
}

# }}}
# {{{ sub esc_data

sub esc_data {
    local($_) = $_[0];
    if ($_ eq '012' || $_ eq '12') {
	"";			# ignore RS
    }
    elsif (/^[0-7]/) {
	sprintf("%c", oct);
    }
    elsif ($_ eq 'n') {
	"\n";
    }
    elsif ($_ eq '|') {
	$sdata = !$sdata;
	"";
    }
    elsif ($_ eq "\\") {
	$sdata ? "\\" : $backslash_in_data;
    }
    else {
	$_;
    }
}

# }}}
# {{{ sub start_element

sub start_element {
    local($gi) = $_[0];
    local($name,$cat,$firstin,$ret,$type,$id,$markup);
    if ($gi eq "FUNCDEF") {
	$name    = $attribute_value{$level,"NAME"};
	$cat     = $attribute_value{$level,"CAT"};
	$firstin = $attribute_value{$level,"FIRSTIN"};
	$ret     = $attribute_value{$level,"RET"};

	$'current_function = "\L$name";
	$id = "function:$'current_function";
	$'current_section = $id;

	push(@store_in, $id);
	push(@'function_list, $'current_function);

	$'function_name{$'current_function}     = $name;
	$'function_category{$'current_function} = $cat;
	$'function_firstver{$'current_function} = $firstin;
	$'function_returns{$'current_function}  = $ret;
	$'categories{$cat}++;

	@function_args = ();

	$indent = 2;
    }
    elsif ($gi eq "ARG") {
	$name    = $attribute_value{$level,"NAME"};
	$type    = $attribute_value{$level,"TYPE"};

	$'current_argument = "$'current_function:\L$name";
	if ($type =~ /^varargs$/i) {
	    $'varargs{$'current_argument} = 1;
	}
	else {
	    $'varargs{$'current_argument} = 0;
	}
	$opt = $attribute_value{$level,"OPTIONAL"};
	if ($opt eq "OPTIONAL") {
	    $'optional{$'current_argument} = 1;
	}
	else {
	    $'optional{$'current_argument} = 0;
	}
	$arg_type{$'current_argument} = "\L$type";
	$id = "argument:$'current_argument";

	push(@store_in, $id);
	push(@function_args, $name);

	$indent = 4;
    }
    elsif ($gi eq "FREF" || $gi eq "AREF") {
	$name = $attribute_value{$level,"NAME"};
	&data2(&postprocess("$gi:\L$name", "&insert_\L$gi('\L$name');"));
    }
    elsif ($gi eq "EXAMPLE") {
	$name = $attribute_value{$level,"NAME"};
	if ($name) {
	    &data2("<example>\n" .
		   " <title>$name</title>\n" .
		   " <programlisting>\n");
	} else {
	    &data2("<informalexample>\n" .
		   " <programlisting>\n");
	}
    }
    elsif ($gi eq $doctype) {
	$document_title = $attribute_value{$level,"TITLE"};
    }
    elsif ($direct{$gi}) {
	$tag = $direct{$gi};
	&data2("<$tag>");
    }
    elsif ($direct_empty{$gi}) {
	$tag = $direct_empty{$gi};
	&data2("<$tag>");
    }
}

# }}}
# {{{ sub end_element

sub end_element {
    local($gi) = $_[0];
    local($arg,$func,$ret,$cat,$firstin);
    local($markup) = "";
    local($prepend, $append);

    if ($gi eq "FUNCDEF") {
	$func = $'current_function;
	$ret = $'function_returns{$func};
	$cat = $'function_category{$func};
	$fver = $'function_firstver{$func};
	$name = $'function_name{$func};

	$funcid = $func;
	$funcid =~ s/_/-/g;
	$markup = "  <refentry id=\"ref.function.$funcid\">
   <refnamediv>
    <refname>$name</refname>
    <refpurpose></refpurpose>
   </refnamediv>
   <refsect1>
    <title>$name()</title>
    <funcsynopsis>
     <funcdef>\L$ret <function>$name</function></funcdef>
";
	$opt = 0;
	$first = 1;
	# XXX FIXME: optional parameters are not handled
	foreach $arg (@function_args) {
	    $ind = "$func:\L$arg";
	    $type = $arg_type{$ind};
	    $markup .= "     <paramdef>$type <parameter>$arg</parameter></paramdef>\n";
	}
	if (!@function_args) {
	    $markup .= "     <void>\n";
	}
	$markup .= "    </funcsynopsis>\n";
	$markup .= $converted_markup{$store_in[$#store_in]};
	$markup .= "\n   </refsect1>\n  </refentry>\n\n\n";

	$'function_text{$func} = $markup;

	pop @store_in;
	$'current_function = undef;
    }
    elsif ($gi eq "ARG") {
	pop @store_in;
	$'current_argument = undef;
    }
    elsif ($gi eq "EXAMPLE") {
	$name = $attribute_value{$level,"NAME"};
	if ($name) {
	    &data2("</programlisting></example>\n");
	} else {
	    &data2("</programlisting></informalexample>\n");
	}
    }
    elsif ($direct{$gi}) {
	$tag = $direct{$gi};
	&data2("</$tag>");
    }
    elsif ($direct_empty{$gi}) {
	# no end tag
    }

    if ($markup) {
	data($markup);
    }
}

# }}}
# {{{ sub data

sub data {
    local($data) = $_[0];
    return &data2($data);
}

sub data2 {
    local($data) = $_[0];
#    return if (!$data || $data =~ /^\s+$/);
    local($context) = $store_in[$#store_in];
#    print STDERR "|$context| $data\n";
    $converted_markup{$context} .= $data;
}

# }}}
# {{{ sub pi

# A processing instruction.
sub pi {
    local($data) = $_[0];
    # XXX
}

# }}}
# {{{ sub entity

# A reference to an external entity.

sub entity {
    local($name) = $_[0];
    # XXX
}

# }}}
# {{{ sub start_subdoc

sub start_subdoc {
    local($name) = $_[0];
    # XXX
}

# }}}
# {{{ sub end_subdoc

sub end_subdoc {
    local($name) = $_[0];
    # XXX
}

# }}}
# {{{ sub postprocess

sub postprocess {
    local($string, $code) = @_;
    local($pattern) = "@@\{$string\}@@";
    $'postprocess{$pattern} = $code;
    $pattern;
}

# }}}
# {{{ sub insert_aref

sub insert_aref {
    local($arg) = @_;
    "<parameter>$arg</parameter>";
}

# }}}
# {{{ sub insert_fref

sub insert_fref {
    my $func = shift;
    my $fname = $'function_name{$func} || $func;
    my $funcid = $func;
    $funcid =~ s/_/-/g;
    "<link linkend=\"ref.function.$funcid\">$func</link>";
}

# }}}
