#! /usr/bin/perl -w
# Determine the path to the McStas system directory. This must be done
# in the BEGIN block so that it can be used in a "use lib" statement
# afterwards.
use Config;
use Cwd;
BEGIN {
# default configuration (for all high level perl scripts)
if($ENV{"MCSTAS"}) {
$MCSTAS::sys_dir = $ENV{"MCSTAS"};
} else {
if ($Config{'osname'} eq 'MSWin32') {
$MCSTAS::sys_dir = "c:\\mcstas\\lib";
} else {
$MCSTAS::sys_dir = "/usr/local/lib/mcstas";
}
}
$MCSTAS::perl_dir = "$MCSTAS::sys_dir/tools/perl";
}
use lib $MCSTAS::perl_dir;
require "mcstas_config.perl";
use FileHandle;
use File::Basename;
require "mcrunlib.pl";
my $is_single_file= 0; # true when doc requested for a single component
my $is_user_lib = 0; # true when doc requested for a directory
my $lib_dir = $MCSTAS::sys_dir;
my $out_file = "index.html"; # default name for output of catalog
my $use_local = 0; # true when also looking into current path
my $single_comp_name = 0; # component single name
my $browser = $MCSTAS::mcstas_config{'BROWSER'};
my $is_forced = 0; # true when force re-writting of existing HTML
sub show_header { # output in text mode
my ($d) = @_;
my ($i);
print "######## $d->{'type'}: $d->{'name'} #####################\n";
if ($d->{'type'} eq "Instrument") {
print "[Site]: $d->{'site'}\n";
}
print "[Author]: $d->{'identification'}{'author'}\n";
print "[Origin]: $d->{'identification'}{'origin'}\n";
print "[Date]: $d->{'identification'}{'date'}\n";
print "[Version]:$d->{'identification'}{'version'}\n";
for $i (@{$d->{'identification'}{'history'}}) {
print "[Modified by]: $i\n";
}
print "\n";
print $d->{'identification'}{'short'};
print "######## Input parameters: ##############################\n";
for $i (@{$d->{'inputpar'}}) {
if(defined($d->{'parhelp'}{$i}{'default'})) {
print "<$i=$d->{'parhelp'}{$i}{'default'}>: ";
} else {
print "<$i>: ";
}
if($d->{'parhelp'}{$i}) {
print "[$d->{'parhelp'}{$i}{'unit'}] "
if $d->{'parhelp'}{$i}{'unit'};
print "$d->{'parhelp'}{$i}{'text'}"
if $d->{'parhelp'}{$i}{'text'}; # text finishes by \n
print("\n");
} else {
print("<Undocumented>\n");
}
}
if (@{$d->{'outputpar'}}) {
print "\n######## Output parameters: #############################\n";
for $i (@{$d->{'outputpar'}}) {
print "<$i>: ";
if($d->{'parhelp'}{$i}) {
print "[$d->{'parhelp'}{$i}{'unit'}] "
if $d->{'parhelp'}{$i}{'unit'};
print "$d->{'parhelp'}{$i}{'text'}"
if $d->{'parhelp'}{$i}{'text'}; # text finishes by \n
print("\n");
} else {
print("<Undocumented>\n");
}
}
}
if($d->{'description'}) {
print "\n######## Description: ###################################\n";
print $d->{'description'};
}
print "\n#########################################################\n";
}
#
# Output the start of the main component index HTML table
# parameters: ($filehandle, $toolbar);
sub html_main_start {
my ($f, $toolbar) = @_;
print $f <<END;
<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">
<HTML>
<HEAD>
<META NAME="GENERATOR" CONTENT="McDoc">
<TITLE>McStas : Components/Instruments Library </TITLE>
</HEAD>
<BODY>
$toolbar
<CENTER><H1>Components and Instruments from the Library for <i>McStas</i></H1></CENTER>
<P> Names in <B>Boldface</B> denote components that are properly
documented with comments in the source code.</P>
END
}
#
# Output the HTML table row describing component with information in
# $d to file handle $f.
# parameters: ($data, $filehandle, $basename)
sub html_table_entry {
my ($d, $f, $bn, $vn) = @_;
print $f "<TR>\n";
print $f "<TD> ";
print $f "<B>" if %{$d->{'parhelp'}};
if ($d->{'type'} eq "Instrument") {
print $f "$d->{'site'} <A HREF=\"$vn.html\">$d->{'name'}</A> ($d->{'path'})";
} else {
print $f "<A HREF=\"$vn.html\">$d->{'name'}</A>";
}
print $f "</B>" if %{$d->{'parhelp'}};
print $f "</TD>\n";
print $f "<TD>$d->{'identification'}{'origin'}</TD>\n";
print $f "<TD>$d->{'identification'}{'author'}</TD>\n";
print $f "<TD>";
print $f "<A HREF=\"$bn.$d->{'ext'}\">$d->{'ext'}</A>";
print $f "</TD>\n";
print $f "<TD>$d->{'identification'}{'short'}</TD>\n";
print $f "</TR>\n\n";
}
#
# Output the end of the main component index HTML table
# parameters: ($filehandle, $toolbar);
sub html_main_end {
my ($f, $toolbar) = @_;
my $date = gmtime;
print $f <<END;
<P>This Component list was updated on $date.
<HR WIDTH="100%">
<CENTER>
[ <A HREF="http://www.ill.fr/tas/mcstas/"><I>McStas</I> at ILL</A>
| <A href="http://www.mcstas.org/"><I>McStas</I> at Risø</A> ]
</CENTER>
<P><BR>
<ADDRESS>
Generated by McDoc,
Maintained by Emmanuel Farhi <<a href="mailto:farhi\@ill.fr">farhi\@ill.fr</a>>
and Peter Willendrup <<a href="mailto:peter.willendrup\@risoe.dk">peter.willendrup\@risoe.dk</a>>.
Contact us for any comments.
</ADDRESS>
</BODY></HTML>
END
}
#
# Output the HTML table for either input or output parameters.
#
sub gen_param_table {
my ($f, $ps, $qs) = @_;
my $i;
# Avoid outputting empty table.
unless(@$ps) {
print $f "None.\n";
return;
}
print $f "<TABLE BORDER=1>\n";
print $f "<TR><TH>Name</TH> <TH>Unit</TH> <TH>Description</TH> <TH>Default</TH></TR>\n";
for $i (@$ps) {
my $default = $qs->{$i}{'default'};
print $f "<TR> <TD>";
print $f "<B>" unless defined($default);
print $f "$i";
print $f "</B>" unless defined($default);
print $f "</TD>\n";
if($qs->{$i}{'unit'} && $qs->{$i}{'text'}) {
print $f " <TD>$qs->{$i}{'unit'}</TD>\n";
print $f " <TD>$qs->{$i}{'text'}</TD>\n";
} else {
print $f " <TD></TD> <TD></TD>\n";
}
print $f "<TD ALIGN=RIGHT>", defined($default) ?
$default : " ", "</TD> </TR>\n";
}
print $f "</TABLE>\n\n";
}
#
# Generate description web page from component with information in $d.
# parameters: ($data, $basename, $name);
sub gen_html_description {
my ($d, $bn, $n) = @_;
my $f = new FileHandle;
my $toolbar = <<'TB_END';
<P ALIGN=CENTER>
[ <A href="#id">Identification</A>
| <A href="#desc">Description</A>
| <A href="#ipar">Input parameters</A>
| <A href="#opar">Output parameters</A>
| <A href="#links">Links</A> ]
</P>
TB_END
my $is_opened = 0;
my $valid_name= "";
$n=~ s|.comp\Z||; # remove trailing extension
$n=~ s|.cmp\Z||; # remove trailing extension
$n=~ s|.com\Z||; # remove trailing extension
$n=~ s|.instr\Z||; # remove trailing extension
$valid_name = $bn;
if (open($f, ">$bn.html")) { # use component location
$is_opened = 1;
}
if (((not $is_opened) && $is_forced) || (not -f "$valid_name.html")) {
if (open($f, ">$n.html")) { # create locally
$is_opened = 1;
$valid_name = $n;
}
}
if ($is_single_file) {
$out_file = "$valid_name.html";
}
if ($is_opened) {
print $f "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n";
print $f "<HTML><HEAD>\n";
if ($d->{'type'} eq "Instrument") {
print $f "<TITLE>McStas: $d->{'name'} $d->{'type'} at $d->{'site'}</TITLE>\n";
} else {
print $f "<TITLE>McStas: $d->{'name'} $d->{'type'}</TITLE>\n";
}
print $f "<LINK REV=\"made\" HREF=\"mailto:peter.willendrup\@risoe.dk\">\n";
print $f "</HEAD>\n\n";
print $f "<BODY>\n\n$toolbar\n";
print $f "<H1>The <CODE>$d->{'name'}</CODE> $d->{'type'}</H1>\n\n";
print $f "$d->{'identification'}{'short'}\n\n";
print $f "<H2><A NAME=id></A>Identification</H2>\n";
print $f "\n<UL>\n";
if ($d->{'type'} eq "Instrument") {
print $f " <LI> <B>Site: $d->{'site'}</B>\n";
}
print $f " <LI> <B>Author:</B>$d->{'identification'}{'author'}</B>\n";
print $f " <LI> <B>Origin:</B>$d->{'identification'}{'origin'}</B>\n";
print $f " <LI> <B>Date:</B>$d->{'identification'}{'date'}</B>\n";
print $f " <LI> <B>Version:</B>$d->{'identification'}{'version'}</B>\n";
if(@{$d->{'identification'}{'history'}}) {
my $entry;
print $f " <LI> <B>Modification history:</B> <UL>\n";
for $entry (@{$d->{'identification'}{'history'}}) {
print $f " <LI> $entry\n";
}
print $f " </UL>\n";
}
print $f "</UL>\n";
if($d->{'description'}) {
print $f "<H2><A NAME=desc></A>Description</H2>\n";
print $f "\n<PRE>\n$d->{'description'}</PRE>\n";
if ($bn =~ m/obsolete/i || $n =~ m/obsolete/i) {
print $f "WARNING: <B>This is an obsolete $d->{'type'}.";
print $f "Please avoid usage whenever possible.</B>\n";
}
if ($bn =~ m/contrib/i || $n =~ m/contrib/i) {
print $f "WARNING: <B>This is a contributed $d->{'type'}.</B>\n";
}
}
print $f "\n<H2><A NAME=ipar></A>Input parameters</H2>\n";
if(@{$d->{'inputpar'}}) {
print $f "Parameters in <B>boldface</B> are required;\n";
print $f "the others are optional.\n";
}
gen_param_table($f, $d->{'inputpar'}, $d->{'parhelp'});
if (@{$d->{'outputpar'}}) {
print $f "\n<H2><A NAME=opar></A>Output parameters</H2>\n";
gen_param_table($f, $d->{'outputpar'}, $d->{'parhelp'});
}
print $f "\n<H2><A NAME=links></A>Links</H2>\n\n<UL>\n";
print $f " <LI> <A HREF=\"$d->{'path'}\">Source code</A> ";
print $f "for <CODE>$d->{'name'}.$d->{'ext'}</CODE>.\n";
# Additional links from component comment header go here.
my $link;
for $link (@{$d->{'links'}}) {
print $f " <LI> $link";
}
print $f "</UL>\n";
print $f "<HR>\n$toolbar\n<ADDRESS>\n";
print $f "Generated automatically by McDoc, Peter Willendrup\n";
print $f "<<A HREF=\"mailto:peter.willendrup\@risoe.dk\">";
print $f "peter.willendrup\@risoe.dk</A>> /\n";
my $date = gmtime;
print $f "$date";
print $f "</ADDRESS>\n";
print $f "</BODY></HTML>\n";
close $f;
} else {
if (not -f "$valid_name.html") {
print "mcdoc: Cannot open $valid_name.html. Use -f option to force.\n";
}
}
return $valid_name;
}
#
# Add component with info in $d to web page handle $f, and generate
# stand-alone documentation page. $bn is the base name (file name
# without trailing .comp).
# parameters: ($data, $filehandle, $basename, $name);
sub add_comp_html {
my ($d, $f, $bn, $n) = @_;
my $vn;
$vn = gen_html_description($d, $bn, $n);
if ($f) { html_table_entry($d, $f, $bn, $vn); }
}
#
# Add a whole section of components, given the section directory name.
# parameters: ($lib_dir, $section, $section_header, $filehandle);
sub add_comp_section_html {
my ($lib, $sec, $header, $filehandle) = @_;
my $sec_orig = $sec;
if ($sec =~ "local") { $sec = getcwd(); $single_comp_name = basename($single_comp_name); $is_forced=1; } # local components
$sec = "$lib/$sec" unless -d $sec;
if(opendir(DIR, $sec)) {
my @comps = readdir(DIR);
closedir DIR;
if ($is_forced) {
# test if the given comp/instr name is an actual file name
if (-f "$single_comp_name") {
push @comps, $single_comp_name;
}
}
return unless @comps;
if ($filehandle) {
print $filehandle <<END;
<P><A NAME="$sec_orig"></A>
$header
<TABLE BORDER COLS=5 WIDTH="100%" NOSAVE>
<TR>
<TD><B><I>Name</I></B></TD>
<TD WIDTH="10%"><B><I>Origin</I></B></TD>
<TD WIDTH="10%"><B><I>Author(s)</I></B></TD>
<TD><B><I>Source code</I></B></TD>
<TD><B><I>Description</I></B></TD>
</TR>
END
} # end if filehandle
my ($comp, $name);
my $single_comp_name_base;
# extract the requested comp/instr name to look for, removing possible path
$single_comp_name_base= basename($single_comp_name);
for $name (sort(@comps)) {
my $comp = "$sec/$name";
my $does_match = 0;
my $name_base;
my $basename;
next if (-d "$name"); # skip directories
# extract the scanned comp/instr name from lib, removing possible path
$name_base = basename($name); # with extension
if ($single_comp_name_base =~ /^(.*)\.(com|comp|cmp|instr)$/) {
# requested doc name includes extension: search exact match
if($name_base =~ $single_comp_name_base) {
$does_match = 2;
}
} elsif ($name_base =~ $single_comp_name_base) {
# requested doc name does not contain an extension: search all matches
$does_match = 1;
}
# skip non comp/instr
if ($comp !~ /^(.*)\.(com|comp|cmp|instr)$/)
{
if ($comp !~ /^(.*)\.(htm|html)$/ && $does_match) {
print STDOUT "mcdoc: $comp (not a component/instrument)\n";
}
next
} else { $basename = $1; } # without extension
if (($is_single_file && $does_match)
|| (not $is_single_file)) {
$data = component_information($comp);
if (not defined($data)) {
print STDERR "mcdoc: Failed to get information for component/instrument '$comp'";
} else {
print STDOUT "mcdoc: $comp\n";
if ($is_single_file) { $data->{'path'} = $comp; }
else { $data->{'path'} = $name; }
if ($is_single_file && $browser =~ "text") {
show_header($data); # display single comp as text
if ($sec =~ m/obsolete/i) {
print "WARNING: This is an obsolete $data->{'type'}. \n";
print " Please avoid usage whenever possible.\n";
}
if ($sec =~ m/contrib/i) {
print "WARNING: This is a contributed $data->{'type'}. \n";
}
} else {
add_comp_html($data, $filehandle, $basename, $name);
}
}
}
last if $does_match == 2;
} # end for
if ($filehandle) {
print $filehandle <<END;
</TABLE>
END
} # end if filehandle
} #end if open DIR
}
# Start of main ===============================
my $index = 0;
my $file;
my $show_website = 0;
my $show_manual = 0;
my $show_compman = 0;
my $show_tutorial = 0;
for($i = 0; $i < @ARGV; $i++) {
$_ = $ARGV[$i];
# Options specific to mcdoc.
if(/^--show$/i || /^-s$/i || /^--html$/i) {
$browser = $MCSTAS::mcstas_config{'BROWSER'};
} elsif(/^--text$/i || /^-t$/i) {
$browser = "text";
} elsif(/^--web$/i || /^-w$/i) {
$show_website = 1;
} elsif(/^--manual$/i || /^-m$/i) {
$show_manual = 1;
} elsif(/^--comp$/i || /^-c$/i) {
$show_compman = 1;
} elsif(/^--tutorial$/i) {
$show_tutorial = 1;
} elsif(/^--local$/i) {
$use_local = 1;
} elsif(/^--force$/i || /^-f$/i) {
$is_forced = 1;
} elsif(/^--help$/i || /^-h$/i || /^-v$/i) {
print "Usage: mcdoc [options] <dir|file>\n";
print "Generate/show component/instrument documentation using $browser\n";
print " -f --force Force re-writting of existing HTML doc locally\n";
print " -h --help Show this help\n";
print " -l --tools Display the McStas tools list\n";
print " -m --manual Open the McStas User manual\n";
print " -c --comp Open the McStas Component manual\n";
print " -t --text For single component, display as text\n";
print " -w --web Open the McStas web page http://www.mcstas.org/\n";
print " --tutorial Open the McStas tutorial from the local McStas library\n";
print "SEE ALSO: mcstas, mcdoc, mcplot, mcrun, mcgui, mcresplot, mcstas2vitess\n";
print "DOC: Please visit http://www.mcstas.org/\n";
exit;
} elsif(/^--tools$/i || /^-l$/) {
print "McStas Tools\n";
print " mcstas Main instrument compiler\n";
print " mcrun Instrument maker and execution utility\n";
print " mcgui Graphical User Interface instrument builder\n";
print " mcdoc Component library documentation generator/viewer\n";
print " mcplot Simulation result viewer\n";
print " mcdisplay Instrument geometry viewer\n";
print " mcresplot Instrument resolution function viewer\n";
print " mcstas2vitess McStas to Vitess component translation utility\n";
print " mcconvert Matlab <-> Scilab script conversion tool\n";
print "When used with the -h flag, all tools display a specific help.\n";
print "SEE ALSO: mcstas, mcdoc, mcplot, mcrun, mcgui, mcresplot, mcstas2vitess\n";
print "DOC: Please visit http://www.mcstas.org/\n";
exit;
} else {
$file = $ARGV[$i];
$index++;
}
} # end for
if ($show_website) {
# open the index.html
my $cmd = "$MCSTAS::mcstas_config{'BROWSER'} http://www.mcstas.org/ ";
print "mcdoc: Starting $cmd\n"; system("$cmd\n");
die "mcdoc: web site done.\n";
}
if ($show_manual) {
# open the manual using embedded acroread plugin
$cmd = "$MCSTAS::mcstas_config{'BROWSER'} $MCSTAS::sys_dir/doc/mcstas-manual.pdf";
print "mcdoc: Starting $cmd\n"; system("$cmd\n");
die "mcdoc: User manual done.\n";
}
if ($show_compman) {
# open the component manual
$cmd = "$MCSTAS::mcstas_config{'BROWSER'} $MCSTAS::sys_dir/doc/mcstas-components.pdf";
print "mcdoc: Starting $cmd\n"; system("$cmd\n");
die "mcdoc: Component manual done.\n";
}
if ($show_tutorial) {
# open the index.html
$cmd = "$MCSTAS::mcstas_config{'BROWSER'} $MCSTAS::sys_dir/doc/tutorial/html/tutorial.html";
print "mcdoc: Starting $cmd\n"; system("$cmd\n");
die "mcdoc: Tutorial done.\n";
}
# if 'file' is given
if ($index > 0) {
if (-d $file) { $lib_dir = $file; } # get doc of the given dir
else { $is_single_file=1; $single_comp_name = $file; } # search locally and in lib
$use_local=1; # will also search locally
}
my $filehandle = 0;
my @sections;
my %section_headers;
if (not $is_single_file) {
# Open the local documentation file
$filehandle = new FileHandle;
my $no_lib_write = 0;
if (not open($filehandle, ">$lib_dir/$out_file")) { $no_lib_write = 1; }
if ($no_lib_write) {
my $no_local_write = 0;
if (not open($filehandle, ">$out_file")) { $no_local_write = 1; }
if ($no_local_write) {
$filehandle = 0; # will not write the catalog
print STDERR "mcdoc: Could not open $out_file for writing.\n";
}
} else {
$out_file = "$lib_dir/$out_file";
}
if (not $filehandle) {
if (-f "$lib_dir/$out_file") {
$out_file = "$lib_dir/$out_file";
}
elsif (not -f $out_file) {
print STDERR "mcdoc: Could not find the $out_file library catalog.\n";
}
}
}
if ($use_local) {
# define local and lib sections
@sections = ("sources", "optics", "samples", "monitors",
"misc", "contrib", "obsolete","examples","local","data","share","doc");
%section_headers =
("sources" => '<B><FONT COLOR="#FF0000">Sources</FONT></B>',
"optics" => '<B><FONT COLOR="#FF0000">Optics</FONT></B>',
"samples" => '<B><FONT COLOR="#FF0000">Samples</FONT></B>',
"monitors" => '<B><FONT COLOR="#FF0000">Detectors</FONT> and monitors</B>',
"contrib" => '<B><FONT COLOR="#FF0000">Contributed</FONT> components</B>',
"misc" => '<B><FONT COLOR="#FF0000">Misc</FONT></B>',
"obsolete" => '<B><FONT COLOR="#FF0000">Obsolete</FONT> (avoid usage whenever possible)</B>',
"examples" => '<B><FONT COLOR="#FF0000">Instrument Examples</FONT></B>',
"local" => '<B><FONT COLOR="#FF0000">Local components</FONT></B>',
"data" => '<B><FONT COLOR="#FF0000">Data files</FONT></B>',
"share" => '<B><FONT COLOR="#FF0000">Shared libraries</FONT></B>',
"doc" => '<B><FONT COLOR="#FF0000">Documentation</FONT></B>');
} else {
# define lib sections
@sections = ("sources", "optics", "samples", "monitors", "misc", "contrib","examples");
%section_headers =
("sources" => '<B><FONT COLOR="#FF0000">Sources</FONT></B>',
"optics" => '<B><FONT COLOR="#FF0000">Optics</FONT></B>',
"samples" => '<B><FONT COLOR="#FF0000">Samples</FONT></B>',
"monitors" => '<B><FONT COLOR="#FF0000">Detectors</FONT> and monitors</B>',
"contrib" => '<B><FONT COLOR="#FF0000">Contributed</FONT> components</B>',
"misc" => '<B><FONT COLOR="#FF0000">Misc</FONT></B>',
"obsolete" => '<B><FONT COLOR="#FF0000">Obsolete</FONT> (avoid usage whenever possible)</B>',
"examples" => '<B><FONT COLOR="#FF0000">Instrument Examples</FONT></B>',);
}
my @tblist = map "<A href=\"#$_\">$_</A>", @sections;
my $toolbar = "<P ALIGN=CENTER>\n [ " . join("\n | ", @tblist) . " ]\n</P>\n";
$toolbar .= "<P ALIGN=CENTER>\n [ <a href=\"$MCSTAS::sys_dir/doc/mcstas-manual.pdf\">User Manual</a>
| <a href=\"$MCSTAS::sys_dir/doc/mcstas-components.pdf\">Component Manual</a>
| <a href=\"$MCSTAS::sys_dir/doc/tutorial/html/tutorial.html\">McStas tutorial</a>
| <a href=\"$MCSTAS::sys_dir/data\">Data files</a> ]\n</P>\n";
if ($filehandle) {
html_main_start($filehandle, $toolbar);
}
# open each section, look for comps, add entry in index.html,
# and generate comp doc
my $sec;
my $is_forced_orig = $is_forced;
for $sec (@sections) {
add_comp_section_html($lib_dir, $sec, $section_headers{$sec}, $filehandle);
$is_forced = $is_forced_orig; # may have been changed globally (sec == local)
}
if ($filehandle) {
html_main_end($filehandle, $toolbar);
close($filehandle);
}
if (-f $out_file) {
if ($browser ne "text") {
# open the index.html
my $cmd = "$MCSTAS::mcstas_config{'BROWSER'} $out_file";
print "mcdoc: Starting $cmd\n"; system("$cmd\n");
}
}
syntax highlighted by Code2HTML, v. 0.9.1