SCP: Added hierarchical help capability (from Timothe Litt)
This commit is contained in:
parent
e65aa904e7
commit
00afa58bc4
5 changed files with 1666 additions and 14 deletions
514
helpx
Normal file
514
helpx
Normal file
|
@ -0,0 +1,514 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
# Extract utility for SimH help text
|
||||||
|
|
||||||
|
|
||||||
|
# Copyright (c) 2013, Timothe Litt
|
||||||
|
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
# copy of this software and associated documentation files (the "Software"),
|
||||||
|
# to deal in the Software without restriction, including without limitation
|
||||||
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
# and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
# Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
# THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
# Except as contained in this notice, the name of the author shall not be
|
||||||
|
# used in advertising or otherwise to promote the sale, use or other dealings
|
||||||
|
# in this Software without prior written authorization from the author.
|
||||||
|
|
||||||
|
use warnings;
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
# This utility attempts to read the C source of an emulator device and convert
|
||||||
|
# its help function to the structured help format. Manual editing of the result
|
||||||
|
# will be required, but the mechanical work is done by the tool.
|
||||||
|
#
|
||||||
|
# A template for organizing the help into standard topics/subtopics is inserted
|
||||||
|
# along with the translation. At this writing, everything is experimental, so
|
||||||
|
# the 'standard' format may change. Nonetheless, this is suitable for experimentation.
|
||||||
|
|
||||||
|
use File::Basename;
|
||||||
|
|
||||||
|
my $prg = basename $0;
|
||||||
|
|
||||||
|
my $rtn;
|
||||||
|
my $ifn = '-';
|
||||||
|
my $ofn = '-';
|
||||||
|
my $line;
|
||||||
|
my $update;
|
||||||
|
|
||||||
|
while (@ARGV) {
|
||||||
|
if( $ARGV[0] eq '--' ) {
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
if( $ARGV[0] eq '-dev' ) {
|
||||||
|
shift;
|
||||||
|
$rtn = shift;
|
||||||
|
$rtn .= "_help";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if( $ARGV[0] eq '-u' ) {
|
||||||
|
$update = 1;
|
||||||
|
shift;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
last if( $ARGV[0] !~ /^-/ );
|
||||||
|
|
||||||
|
printf STDERR << "USAGE_";
|
||||||
|
Usage:
|
||||||
|
$prg -u -dev devname -func rtn infile outfile
|
||||||
|
|
||||||
|
devname is used to look for the existing help function name.
|
||||||
|
E.g. if the routine is cr_help, use -dev cr.
|
||||||
|
|
||||||
|
Alternatively, use -func to specify the full function name.
|
||||||
|
|
||||||
|
infile and outfile default to - (stdin and stdout)
|
||||||
|
|
||||||
|
$prg will attempt to produce a sensible device help string, although you
|
||||||
|
should expect that the result will require manual editing. Complex C
|
||||||
|
constructs (preprocessor conditionals, if statements that generate strings)
|
||||||
|
are not automatically translated, but the old code will be preserved.
|
||||||
|
|
||||||
|
However, as it may have been partially translated, the result may not compile.
|
||||||
|
|
||||||
|
A template is installed so that you can move your information into the standard
|
||||||
|
sections.
|
||||||
|
|
||||||
|
Source code in the help function is reformatted - not to any particular
|
||||||
|
style, but as a consequence of how it is tokenized and parsed. Use your
|
||||||
|
favorite pretty-printer if you don't like the results.
|
||||||
|
|
||||||
|
Normally, just the help function is output. -u will output the entire file
|
||||||
|
(-u = "update")
|
||||||
|
|
||||||
|
USAGE_
|
||||||
|
|
||||||
|
exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unless( defined $rtn ) {
|
||||||
|
die "The help function must be specified with -func or -dev; -help for usage\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( @ARGV ) {
|
||||||
|
$ifn = shift;
|
||||||
|
}
|
||||||
|
if( @ARGV ) {
|
||||||
|
$ofn = shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
open( STDIN, "<$ifn" ) or
|
||||||
|
die "Unable to open $ifn for input: $!\n";
|
||||||
|
|
||||||
|
open( STDOUT, ">$ofn" ) or
|
||||||
|
die "Unable to open $ofn for output: $!\n";
|
||||||
|
|
||||||
|
$line = "";
|
||||||
|
while( <STDIN> ) {
|
||||||
|
|
||||||
|
# Look for the help function
|
||||||
|
if( /^(?:static\s+)?t_stat\s+$rtn\s*\(/ ) {
|
||||||
|
$line = $_;
|
||||||
|
while( $line !~ /\{/ && $line !~ /\)\s*;/ ) {
|
||||||
|
my $cont = <STDIN>;
|
||||||
|
if( !defined $cont ) {
|
||||||
|
die "EOF in function definition\n";
|
||||||
|
}
|
||||||
|
$line .= $cont;
|
||||||
|
}
|
||||||
|
if( $line =~ /\)\s*;/ ) { # Just a prototype
|
||||||
|
if( $update ) {
|
||||||
|
print $line;
|
||||||
|
}
|
||||||
|
$line = "";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
# Process the function body
|
||||||
|
my $f = $line;
|
||||||
|
my $b = '';
|
||||||
|
my $bl = 1;
|
||||||
|
my( %vargs, @vargs );
|
||||||
|
my $help = '';
|
||||||
|
my $comments = '';
|
||||||
|
|
||||||
|
# Each statement in the body
|
||||||
|
while (1) {
|
||||||
|
my ($tok, $val) = gettok();
|
||||||
|
last if( !defined $tok );
|
||||||
|
|
||||||
|
if ($tok eq '{') { # Track brace level
|
||||||
|
$bl++;
|
||||||
|
$b .= $tok;
|
||||||
|
} elsif ($tok eq '}') {
|
||||||
|
die "Unmatched }\n" if ( --$bl < 0 );
|
||||||
|
$b .= $tok;
|
||||||
|
last if (!$bl); # End of function
|
||||||
|
} elsif ($tok eq 'word' && $val eq 'fprintf') {
|
||||||
|
# fprintf ( st, "string" ,args );
|
||||||
|
# Save embedded comments, but don't confuse the parse.
|
||||||
|
($tok, $val) = gettok(\$comments);
|
||||||
|
if( $tok ne '(' ) {
|
||||||
|
$b .= " $val";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
($tok, $val) = gettok(\$comments);
|
||||||
|
if( $tok ne 'word' || $val ne 'st' ) {
|
||||||
|
$b .= "fprintf ($val";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
($tok, $val) = gettok(\$comments);
|
||||||
|
if( $tok ne ',' ) {
|
||||||
|
$b .= "fprintf (st$val";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
($tok, $val) = gettok(\$comments);
|
||||||
|
if( $tok ne 'QS' ) {
|
||||||
|
$b .= "fprintf (st, $val";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
# Concatenate adjacent strings
|
||||||
|
my $string = '';
|
||||||
|
while( $tok eq 'QS' ) {
|
||||||
|
$string .= substr( $val, 1, length( $ val ) -2);
|
||||||
|
($tok, $val) = gettok(\$comments);
|
||||||
|
}
|
||||||
|
# Check for format codes. plain %s is all that can be automated
|
||||||
|
if ($string =~ /(%[^%s])/) {
|
||||||
|
print STDERR "Line $.: Unsupported format code $1 in help string. Please convert to %s\n";
|
||||||
|
}
|
||||||
|
# Rework argument list
|
||||||
|
my $arg = '';
|
||||||
|
my @vlist;
|
||||||
|
my $pl = 1; # Paren level
|
||||||
|
while( $tok eq ',' ) {
|
||||||
|
($tok, $val) = gettok(\$comments);
|
||||||
|
while( $tok ne ',' ) {
|
||||||
|
if( $tok eq '(' ) {
|
||||||
|
$pl++;
|
||||||
|
} elsif( $tok eq ')' ) {
|
||||||
|
die "Unmatched )" if( --$pl < 0);
|
||||||
|
last if( !$pl );
|
||||||
|
}
|
||||||
|
$arg .= " $val";
|
||||||
|
($tok, $val) = gettok(\$comments);
|
||||||
|
}
|
||||||
|
if( !length $arg ) {
|
||||||
|
print STDERR "Line $.: null argument to fprintf in $rtn\n";
|
||||||
|
$string = "<<NULL>>";
|
||||||
|
}
|
||||||
|
unless( exists $vargs{$arg} ) { # Assign each unique arg an index
|
||||||
|
$vargs{$arg} = @vargs;
|
||||||
|
push @vargs, $arg;
|
||||||
|
}
|
||||||
|
push @vlist, $vargs{$arg}; # Remember offset in this list
|
||||||
|
$arg = '';
|
||||||
|
}
|
||||||
|
die "Line $.: Missing ')' in fprintf\n" if( $tok ne ')' );
|
||||||
|
($tok, $val) = gettok(\$comments);
|
||||||
|
die "Line $.: Missing ';' in fprintf\n" if( $tok ne ';' );
|
||||||
|
|
||||||
|
# Replace each escape with positional %s in new list.
|
||||||
|
my $n = 0;
|
||||||
|
$string =~ s/%([.\dlhs# +Lqjzt-]*[diouxXeEfFgGaAcspnm%])/
|
||||||
|
sprintf "%%%us",$vlist[$n++]+1/eg;
|
||||||
|
$help .= $string;
|
||||||
|
next;
|
||||||
|
} elsif ($tok eq 'word' && $val =~ /^fprint_(set|show|reg)_help(?:_ex)?$/) {
|
||||||
|
my %alt = ( set => "\$Set commands",
|
||||||
|
show => "\$Show commmands",
|
||||||
|
reg => "\$Registers" );
|
||||||
|
$b .= "/* Use \"$alt{$1}\" topic instead:\n";
|
||||||
|
do {
|
||||||
|
$b .= " $val";
|
||||||
|
($tok, $val) = gettok (\$comments);
|
||||||
|
} while ($tok ne ';');
|
||||||
|
$b .= ";\n*/\n";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Random function body content
|
||||||
|
|
||||||
|
$b .= " $val";
|
||||||
|
}
|
||||||
|
# End of function - output new one
|
||||||
|
print $f; # Function header
|
||||||
|
print "const char helpString[] =\n";
|
||||||
|
|
||||||
|
print << 'TEMPLATE_';
|
||||||
|
/* Template for re-arranging your help.
|
||||||
|
* Lines marked with '+' in the translation seemed to be indented and will
|
||||||
|
* indent 4 columns for each '+'. See scp_help.h for a worked-out example.
|
||||||
|
* The '*'s in the next line represent the standard text width of a help line */
|
||||||
|
/****************************************************************************/
|
||||||
|
" Insert your device summary here. Keep it short. Be sure to put a leading\n"
|
||||||
|
" space at the start of each line. Blank lines do appear in the output;\n"
|
||||||
|
" don't add extras.\n"
|
||||||
|
"1 Hardware Description\n"
|
||||||
|
" The details of the hardware. Feeds & speeds are OK here.\n"
|
||||||
|
"2 Models\n"
|
||||||
|
" If the device was offered in distinct models, a subtopic for each\n"
|
||||||
|
"3 Model A\n"
|
||||||
|
" Description of model A\n"
|
||||||
|
"3 Model B\n"
|
||||||
|
" Description of model B\n"
|
||||||
|
"2 $Registers\n"
|
||||||
|
" The register list of the device will automagically display above this\n"
|
||||||
|
" line. Add any special notes.\n"
|
||||||
|
"1 Configuration\n"
|
||||||
|
" How to configure the device under SimH. Use subtopics\n"
|
||||||
|
" if there is a lot of detail.\n"
|
||||||
|
"2 $Set commands\n"
|
||||||
|
" The SET commands for the device will automagically display above\n"
|
||||||
|
" this line. Add any special notes.\n"
|
||||||
|
"2 OSNAME1\n"
|
||||||
|
" Operating System-specif configuration details\n"
|
||||||
|
" If the device needs special configuration for a particular OS, a subtopic\n"
|
||||||
|
" for each such OS goes here.\n"
|
||||||
|
"2 Files\n"
|
||||||
|
" If the device uses external files (tapes, cards, disks, configuration)\n"
|
||||||
|
" Create a subtopic for each here.\n"
|
||||||
|
"3 Config file 1\n"
|
||||||
|
" Description.\n"
|
||||||
|
"2 Examples\n"
|
||||||
|
" Provide usable examples for configuring complex devices.\n"
|
||||||
|
" If the examples are more than a couple of lines, make a subtopic for each.\n"
|
||||||
|
"1 Operation\n"
|
||||||
|
" How to operate the device under SimH. Attach, runtime events\n"
|
||||||
|
" (e.g. how to load cards or mount a tape)\n"
|
||||||
|
"1 Monitoring\n"
|
||||||
|
" How to obtain and interpret status\n"
|
||||||
|
"2 $Show commands\n"
|
||||||
|
" The SHOW commands for the device will automagically display above\n"
|
||||||
|
" this line. Add any special notes.\n"
|
||||||
|
"1 Restrictions\n"
|
||||||
|
" If some aspects of the device aren't emulated or some host\n"
|
||||||
|
" host environments that aren't (fully) supported, list them here.\n"
|
||||||
|
"1 Debugging\n"
|
||||||
|
" Debugging information - provided by the device. Tips for common problems.\n"
|
||||||
|
"1 Related Devices\n"
|
||||||
|
" If devices are configured or used together, list the other devices here.\n"
|
||||||
|
" E.G. The DEC KMC/DUP are two hardware devices that are closely related;\n"
|
||||||
|
" The KMC controlls the DUP on behalf of the OS.\n"
|
||||||
|
|
||||||
|
/* **** Your converted help text starts hare **** */
|
||||||
|
|
||||||
|
TEMPLATE_
|
||||||
|
|
||||||
|
my @lines = split /(\\n|\n)/, $help;
|
||||||
|
while( @lines ) {
|
||||||
|
my $line = shift @lines;
|
||||||
|
my $term = shift @lines;
|
||||||
|
if ($term eq "\\n") {
|
||||||
|
$line .= $term;
|
||||||
|
$term = "\n";
|
||||||
|
}
|
||||||
|
if( $line =~ s/^(\s+)// ) {
|
||||||
|
$line = ('+' x ((length( $1 ) +3)/4)) . $line;
|
||||||
|
} else {
|
||||||
|
$line = ' ' . $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
print " \"$line\"\n" ;
|
||||||
|
}
|
||||||
|
print " ;\n";
|
||||||
|
print $b; # Stuff from body of old function
|
||||||
|
if( length $comments ) {
|
||||||
|
print "\n$comments";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Call scp_help
|
||||||
|
|
||||||
|
print "\nreturn scp_help (st, dptr, uptr, helpString, cptr";
|
||||||
|
|
||||||
|
%vargs = reverse %vargs;
|
||||||
|
while( @vargs ) {
|
||||||
|
print ",\n " . shift( @vargs );
|
||||||
|
}
|
||||||
|
|
||||||
|
print ");\n}\n";
|
||||||
|
} else {
|
||||||
|
if( $update ) {
|
||||||
|
print $_;
|
||||||
|
}
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit (0);
|
||||||
|
|
||||||
|
my @pending;
|
||||||
|
sub nextc {
|
||||||
|
if( @pending ) {
|
||||||
|
my $c = shift @pending;
|
||||||
|
return $c;
|
||||||
|
}
|
||||||
|
return getc;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub gettoken {
|
||||||
|
my $c;
|
||||||
|
my $ql = 0;
|
||||||
|
my $cl = 0;
|
||||||
|
my $tok = '';
|
||||||
|
|
||||||
|
while( defined(($c = nextc())) ) {
|
||||||
|
if( $cl ) {
|
||||||
|
if( $c eq '*' ) {
|
||||||
|
$c = nextc;
|
||||||
|
die "EOF in comment\n" if( !defined $c );
|
||||||
|
|
||||||
|
if ($c eq '/') {
|
||||||
|
$tok .= '*/';
|
||||||
|
return ('comment', $tok);
|
||||||
|
}
|
||||||
|
push @pending, $c;
|
||||||
|
$c = '*';
|
||||||
|
}
|
||||||
|
$tok .= $c;
|
||||||
|
next;
|
||||||
|
} elsif( $c eq '/' ) {
|
||||||
|
$c = nextc;
|
||||||
|
if( $c eq '*' ) {
|
||||||
|
if (length $tok) {
|
||||||
|
push @pending, '/', '*';
|
||||||
|
return ('word', $tok);
|
||||||
|
}
|
||||||
|
$cl = 1;
|
||||||
|
$tok = '/*';
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
push @pending, $c;
|
||||||
|
$c = '/';
|
||||||
|
}
|
||||||
|
if( $ql ) {
|
||||||
|
if( $c eq '\\' ) {
|
||||||
|
$c = nextc;
|
||||||
|
die "EOF in string\n" if( !defined $c );
|
||||||
|
|
||||||
|
$tok .= "\\$c"; # eval "\"\\$c\"";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if( $c eq $ql ) {
|
||||||
|
$tok .= $ql;
|
||||||
|
return ("QS", $tok);
|
||||||
|
}
|
||||||
|
$tok .= $c;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if( $c eq '"' || $c eq "'" ) {
|
||||||
|
$ql = $c;
|
||||||
|
$tok = $c;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if ($c =~ /^\s$/) {
|
||||||
|
if( length $tok ) {
|
||||||
|
return ('word', $tok);
|
||||||
|
}
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if ($c =~ /^\w$/) {
|
||||||
|
$tok .= $c;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if( length $tok ) {
|
||||||
|
push @pending, $c;
|
||||||
|
return ('word', $tok);
|
||||||
|
}
|
||||||
|
if ($c eq '-') {
|
||||||
|
$c = nextc;
|
||||||
|
if( $c =~ /^[>=-]$/ ) {
|
||||||
|
return ('op', "-$c");
|
||||||
|
}
|
||||||
|
push @pending, $c;
|
||||||
|
return ('op', '-');
|
||||||
|
}
|
||||||
|
if( $c eq '<' ) {
|
||||||
|
$c = nextc;
|
||||||
|
if( $c eq '=' ) {
|
||||||
|
return ('op', "<$c");
|
||||||
|
}
|
||||||
|
if( $c eq '<' ) {
|
||||||
|
my $c2 = nextc;
|
||||||
|
if( $c2 eq '=' ) {
|
||||||
|
return ('op', "<<=");
|
||||||
|
}
|
||||||
|
push @pending, $c2;
|
||||||
|
return ('op', '<<');
|
||||||
|
}
|
||||||
|
push @pending, $c;
|
||||||
|
return ('op', '<');
|
||||||
|
}
|
||||||
|
if( $c eq '>' ) {
|
||||||
|
$c = nextc;
|
||||||
|
if( $c eq '=' ) {
|
||||||
|
return ('op', ">$c");
|
||||||
|
}
|
||||||
|
if( $c eq '>' ) {
|
||||||
|
my $c2 = nextc;
|
||||||
|
if( $c2 eq '=' ) {
|
||||||
|
return ('op', ">>=");
|
||||||
|
}
|
||||||
|
push @pending, $c2;
|
||||||
|
return ('op', '>>');
|
||||||
|
}
|
||||||
|
push @pending, $c;
|
||||||
|
return ('op', '>');
|
||||||
|
}
|
||||||
|
if( $c eq '=' ) {
|
||||||
|
$c = nextc;
|
||||||
|
if( $c eq '=' ) {
|
||||||
|
return ('op', '==');
|
||||||
|
}
|
||||||
|
push @pending, $c;
|
||||||
|
return ('op', '=');
|
||||||
|
}
|
||||||
|
if ($c =~ m,^[!*+/%&^|]$,) {
|
||||||
|
my $c2 = nextc;
|
||||||
|
if( $c2 eq '=' ) {
|
||||||
|
return ('op', "$c$c2");
|
||||||
|
}
|
||||||
|
push @pending, $c2;
|
||||||
|
return ('op', $c);
|
||||||
|
}
|
||||||
|
if( $c =~ /^[&|]$/ ) {
|
||||||
|
my $c2 = nextc;
|
||||||
|
if( $c2 eq $c ) {
|
||||||
|
return ('op', "$c$c");
|
||||||
|
}
|
||||||
|
push @pending, $c2;
|
||||||
|
return ('op', $c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($c =~ /^[#}]$/ ) {
|
||||||
|
return ($c, "\n$c");
|
||||||
|
}
|
||||||
|
return ($c, ($c =~ /^[{;]$/? "$c\n" : $c));
|
||||||
|
}
|
||||||
|
return (undef, '<<EOF>>');
|
||||||
|
}
|
||||||
|
|
||||||
|
sub gettok {
|
||||||
|
my $comments = $_[0];
|
||||||
|
|
||||||
|
while( 1 ) {
|
||||||
|
my( $token, $value ) = gettoken();
|
||||||
|
return ($token, $value) if( !defined $token );
|
||||||
|
|
||||||
|
if( $token eq 'comment' && $comments ) {
|
||||||
|
$$comments .= $value . "\n";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
return ($token, $value);
|
||||||
|
}
|
||||||
|
}
|
897
scp.c
897
scp.c
|
@ -228,10 +228,13 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include <direct.h>
|
#include <direct.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#else
|
#else
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
|
||||||
#if defined(HAVE_DLOPEN) /* Dynamic Readline support */
|
#if defined(HAVE_DLOPEN) /* Dynamic Readline support */
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
@ -472,6 +475,7 @@ static double sim_time;
|
||||||
static uint32 sim_rtime;
|
static uint32 sim_rtime;
|
||||||
static int32 noqueue_time;
|
static int32 noqueue_time;
|
||||||
volatile int32 stop_cpu = 0;
|
volatile int32 stop_cpu = 0;
|
||||||
|
static char **sim_argv;
|
||||||
t_value *sim_eval = NULL;
|
t_value *sim_eval = NULL;
|
||||||
FILE *sim_log = NULL; /* log file */
|
FILE *sim_log = NULL; /* log file */
|
||||||
FILEREF *sim_log_ref = NULL; /* log file file reference */
|
FILEREF *sim_log_ref = NULL; /* log file file reference */
|
||||||
|
@ -899,6 +903,7 @@ if (!sim_quiet) {
|
||||||
if (sim_dflt_dev == NULL) /* if no default */
|
if (sim_dflt_dev == NULL) /* if no default */
|
||||||
sim_dflt_dev = sim_devices[0];
|
sim_dflt_dev = sim_devices[0];
|
||||||
|
|
||||||
|
sim_argv = argv;
|
||||||
cptr = getenv("HOME");
|
cptr = getenv("HOME");
|
||||||
if (cptr == NULL) {
|
if (cptr == NULL) {
|
||||||
cptr = getenv("HOMEPATH");
|
cptr = getenv("HOMEPATH");
|
||||||
|
@ -1259,17 +1264,17 @@ void fprint_show_help (FILE *st, DEVICE *dptr)
|
||||||
fprint_show_help_ex (st, dptr, TRUE);
|
fprint_show_help_ex (st, dptr, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
t_stat help_dev_help (FILE *st, DEVICE *dptr, UNIT *uptr, char *cptr)
|
t_stat help_dev_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
|
||||||
{
|
{
|
||||||
char gbuf[CBUFSIZE];
|
char gbuf[CBUFSIZE];
|
||||||
CTAB *cmdp;
|
CTAB *cmdp;
|
||||||
|
|
||||||
if (*cptr) {
|
if (*cptr) {
|
||||||
cptr = get_glyph (cptr, gbuf, 0);
|
char *gptr = get_glyph (cptr, gbuf, 0);
|
||||||
if ((cmdp = find_cmd (gbuf))) {
|
if ((cmdp = find_cmd (gbuf))) {
|
||||||
if (cmdp->action == &exdep_cmd) {
|
if (cmdp->action == &exdep_cmd) {
|
||||||
if (dptr->help)
|
if (dptr->help) /* Shouldn't this pass cptr so the device knows which command invoked? */
|
||||||
dptr->help (st, dptr, uptr, 0, cptr);
|
return dptr->help (st, dptr, uptr, flag, gptr);
|
||||||
else
|
else
|
||||||
fprintf (st, "No help available for the %s %s command\n", cmdp->name, sim_dname(dptr));
|
fprintf (st, "No help available for the %s %s command\n", cmdp->name, sim_dname(dptr));
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
|
@ -1286,23 +1291,22 @@ if (*cptr) {
|
||||||
fprint_attach_help_ex (st, dptr, FALSE);
|
fprint_attach_help_ex (st, dptr, FALSE);
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
fprintf (st, "No %s help is available for the %s device\n", cmdp->name, dptr->name);
|
|
||||||
if (dptr->help)
|
if (dptr->help)
|
||||||
dptr->help (st, dptr, uptr, 0, cptr);
|
return dptr->help (st, dptr, uptr, flag, cptr);
|
||||||
|
fprintf (st, "No %s help is available for the %s device\n", cmdp->name, dptr->name);
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
if (MATCH_CMD (gbuf, "REGISTERS") == 0) {
|
if (MATCH_CMD (gbuf, "REGISTERS") == 0) {
|
||||||
fprint_reg_help_ex (st, dptr, FALSE);
|
fprint_reg_help_ex (st, dptr, FALSE);
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
fprintf (st, "No %s help is available for the %s device\n", gbuf, dptr->name);
|
|
||||||
if (dptr->help)
|
if (dptr->help)
|
||||||
dptr->help (st, dptr, uptr, 0, cptr);
|
return dptr->help (st, dptr, uptr, flag, cptr);
|
||||||
|
fprintf (st, "No %s help is available for the %s device\n", gbuf, dptr->name);
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
if (dptr->help) {
|
if (dptr->help) {
|
||||||
dptr->help (st, dptr, uptr, 0, cptr);
|
return dptr->help (st, dptr, uptr, flag, cptr);
|
||||||
return SCPE_OK;
|
|
||||||
}
|
}
|
||||||
if (dptr->description)
|
if (dptr->description)
|
||||||
fprintf (st, "%s %s help\n", dptr->description (dptr), dptr->name);
|
fprintf (st, "%s %s help\n", dptr->description (dptr), dptr->name);
|
||||||
|
@ -1321,6 +1325,8 @@ char gbuf[CBUFSIZE];
|
||||||
CTAB *cmdp;
|
CTAB *cmdp;
|
||||||
|
|
||||||
GET_SWITCHES (cptr);
|
GET_SWITCHES (cptr);
|
||||||
|
if (sim_switches & SWMASK ('F'))
|
||||||
|
flag = flag | SCP_HELP_FLAT;
|
||||||
if (*cptr) {
|
if (*cptr) {
|
||||||
cptr = get_glyph (cptr, gbuf, 0);
|
cptr = get_glyph (cptr, gbuf, 0);
|
||||||
if ((cmdp = find_cmd (gbuf))) {
|
if ((cmdp = find_cmd (gbuf))) {
|
||||||
|
@ -1337,9 +1343,9 @@ if (*cptr) {
|
||||||
if (dptr == NULL)
|
if (dptr == NULL)
|
||||||
return SCPE_2MARG;
|
return SCPE_2MARG;
|
||||||
}
|
}
|
||||||
r = help_dev_help (stdout, dptr, uptr, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
|
r = help_dev_help (stdout, dptr, uptr, flag, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
|
||||||
if (sim_log)
|
if (sim_log)
|
||||||
help_dev_help (sim_log, dptr, uptr, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
|
help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, (cmdp->action == &set_cmd) ? "SET" : "SHOW");
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1419,6 +1425,7 @@ if (*cptr) {
|
||||||
else {
|
else {
|
||||||
DEVICE *dptr;
|
DEVICE *dptr;
|
||||||
UNIT *uptr;
|
UNIT *uptr;
|
||||||
|
t_stat r;
|
||||||
|
|
||||||
dptr = find_unit (gbuf, &uptr);
|
dptr = find_unit (gbuf, &uptr);
|
||||||
if (dptr == NULL) {
|
if (dptr == NULL) {
|
||||||
|
@ -1431,9 +1438,10 @@ if (*cptr) {
|
||||||
fprintf (sim_log, "Device %s is currently disabled\n", dptr->name);
|
fprintf (sim_log, "Device %s is currently disabled\n", dptr->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
help_dev_help (stdout, dptr, uptr, cptr);
|
r = help_dev_help (stdout, dptr, uptr, flag, cptr);
|
||||||
if (sim_log)
|
if (sim_log)
|
||||||
help_dev_help (stdout, dptr, uptr, cptr);
|
help_dev_help (sim_log, dptr, uptr, flag | SCP_HELP_FLAT, cptr);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -7127,3 +7135,864 @@ if (sim_deb && (dptr->dctrl & dbits)) {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hierarchical help presentation
|
||||||
|
*
|
||||||
|
* Device help can be presented hierarchically by calling
|
||||||
|
*
|
||||||
|
* t_stat scp_help (FILE *st, struct sim_device *dptr,
|
||||||
|
* struct sim_unit *uptr, int flag, const char *help, char *cptr)
|
||||||
|
*
|
||||||
|
* or one of its three cousins from the device HELP routine.
|
||||||
|
*
|
||||||
|
* *help is the pointer to the structured help text to be displayed.
|
||||||
|
*
|
||||||
|
* The format and usage, and some helper macros can be found in scp_help.h
|
||||||
|
* If you don't use the macros, it is not necessary to #include "scp_help.h".
|
||||||
|
*
|
||||||
|
* Actually, if you don't specify a DEVICE pointer and don't include
|
||||||
|
* other device references, it can be used for non-device help.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define blankch(x) ((x) == ' ' || (x) == '\t')
|
||||||
|
|
||||||
|
typedef struct topic {
|
||||||
|
uint32 level;
|
||||||
|
char *title;
|
||||||
|
char *label;
|
||||||
|
struct topic *parent;
|
||||||
|
struct topic **children;
|
||||||
|
uint32 kids;
|
||||||
|
char *text;
|
||||||
|
size_t len;
|
||||||
|
uint32 flags;
|
||||||
|
uint32 kidwid;
|
||||||
|
#define HLP_MAGIC_TOPIC 1
|
||||||
|
} TOPIC;
|
||||||
|
|
||||||
|
static volatile struct {
|
||||||
|
const char *error;
|
||||||
|
size_t block;
|
||||||
|
size_t line;
|
||||||
|
} help_where = { "", 0, 0 };
|
||||||
|
jmp_buf (help_env);
|
||||||
|
#define FAIL(why,text) { help_where.error = #text; longjmp (help_env, (why)); }
|
||||||
|
|
||||||
|
/* Add to topic text.
|
||||||
|
* Expands text buffer as necessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void appendText (TOPIC *topic, const char *text, size_t len) {
|
||||||
|
char *newt;
|
||||||
|
if (!len) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
newt = (char *)realloc (topic->text, topic->len + len +1);
|
||||||
|
if (!newt) {
|
||||||
|
FAIL (SCPE_MEM, No memory);
|
||||||
|
}
|
||||||
|
topic->text = newt;
|
||||||
|
memcpy (newt + topic->len, text, len);
|
||||||
|
topic->len +=len;
|
||||||
|
newt[topic->len] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release memory held by a topic and its children.
|
||||||
|
*/
|
||||||
|
static void cleanHelp (TOPIC *topic) {
|
||||||
|
TOPIC *child;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
free (topic->title);
|
||||||
|
free (topic->text);
|
||||||
|
free (topic->label);
|
||||||
|
for (i = 0; i < topic->kids; i++) {
|
||||||
|
child = topic->children[i];
|
||||||
|
cleanHelp (child);
|
||||||
|
free (child);
|
||||||
|
}
|
||||||
|
free (topic->children);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build a help tree from a string.
|
||||||
|
* Handles substitutions, formatting.
|
||||||
|
*/
|
||||||
|
static TOPIC *buildHelp (TOPIC *topic, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, const char *htext, va_list ap) {
|
||||||
|
char *end;
|
||||||
|
size_t n, ilvl;
|
||||||
|
#define VSMAX 100
|
||||||
|
char *vstrings[VSMAX];
|
||||||
|
size_t vsnum = 0;
|
||||||
|
char *astrings[VSMAX+1];
|
||||||
|
size_t asnum = 0;
|
||||||
|
char *const *hblock;
|
||||||
|
const char *ep;
|
||||||
|
t_bool excluded = FALSE;
|
||||||
|
|
||||||
|
/* variable arguments consumed table.
|
||||||
|
* The scheme used allows arguments to be accessed in random
|
||||||
|
* order, but for portability, all arguments must be char *.
|
||||||
|
* If you try to violate this, there ARE machines that WILL break.
|
||||||
|
*/
|
||||||
|
|
||||||
|
memset (vstrings, 0, VSMAX * sizeof (char *));
|
||||||
|
memset (astrings, 0, VSMAX * sizeof (char *));
|
||||||
|
astrings[asnum++] = (char *) htext;
|
||||||
|
|
||||||
|
for (hblock = astrings; (htext = *hblock) != NULL; hblock++) {
|
||||||
|
help_where.block = hblock - astrings;
|
||||||
|
help_where.line = 0;
|
||||||
|
while (*htext) {
|
||||||
|
const char *start;
|
||||||
|
|
||||||
|
help_where.line++;
|
||||||
|
if (isspace (*htext) || *htext == '+') {/* Topic text, indented topic text */
|
||||||
|
if (excluded) { /* Excluded topic text */
|
||||||
|
while (*htext && *htext != '\n') {
|
||||||
|
htext++;
|
||||||
|
}
|
||||||
|
if (*htext)
|
||||||
|
++htext;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ilvl = 1;
|
||||||
|
appendText (topic, " ", 4); /* Basic indentation */
|
||||||
|
if (*htext == '+') { /* More for each + */
|
||||||
|
while (*htext == '+') {
|
||||||
|
ilvl++;
|
||||||
|
appendText (topic, " ", 4);
|
||||||
|
htext++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (*htext && *htext != '\n' && isspace (*htext)) {
|
||||||
|
htext++;
|
||||||
|
}
|
||||||
|
if (!*htext) { /* Empty after removing leading spaces */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start = htext;
|
||||||
|
while (*htext) { /* Process line for substitutions */
|
||||||
|
if (*htext == '%') {
|
||||||
|
appendText (topic, start, htext - start); /* Flush up to escape */
|
||||||
|
switch (*++htext) { /* Evaluate escape */
|
||||||
|
case 'U':
|
||||||
|
if (dptr) {
|
||||||
|
char buf[129];
|
||||||
|
n = uptr? uptr - dptr->units: 0;
|
||||||
|
sprintf (buf, "%s%u", dptr->name, (int)n);
|
||||||
|
appendText (topic, buf, strlen (buf));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
if (dptr) {
|
||||||
|
appendText (topic, dptr->name, strlen (dptr->name));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'S':
|
||||||
|
appendText (topic, sim_name, strlen (sim_name));
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
appendText (topic, "%", 1);
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
appendText (topic, "+", 1);
|
||||||
|
break;
|
||||||
|
default: /* Check for vararg # */
|
||||||
|
if (isdigit (*htext)) {
|
||||||
|
n = 0;
|
||||||
|
while (isdigit (*htext)) {
|
||||||
|
n += (n * 10) + (*htext++ - '0');
|
||||||
|
}
|
||||||
|
if (( *htext != 'H' && *htext != 's') ||
|
||||||
|
n == 0 || n >= VSMAX)
|
||||||
|
FAIL (SCPE_ARG, Invalid escape);
|
||||||
|
while (n > vsnum) { /* Get arg pointer if not cached */
|
||||||
|
vstrings[vsnum++] = va_arg (ap, char *);
|
||||||
|
}
|
||||||
|
start = vstrings[n-1]; /* Insert selected string */
|
||||||
|
if (*htext == 'H') { /* Append as more input */
|
||||||
|
if (asnum >= VSMAX) {
|
||||||
|
FAIL (SCPE_ARG, Too many blocks);
|
||||||
|
}
|
||||||
|
astrings[asnum++] = (char *)start;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ep = start;
|
||||||
|
while (*ep) {
|
||||||
|
if (*ep == '\n') {
|
||||||
|
ep++; /* Segment to \n */
|
||||||
|
appendText (topic, start, ep - start);
|
||||||
|
if (*ep) { /* More past \n, indent */
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < ilvl; i++) {
|
||||||
|
appendText (topic, " ", 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start = ep;
|
||||||
|
} else {
|
||||||
|
ep++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appendText (topic, start, ep-start);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FAIL (SCPE_ARG, Invalid escape);
|
||||||
|
} /* switch (escape) */
|
||||||
|
start = ++htext;
|
||||||
|
continue; /* Current line */
|
||||||
|
} /* if (escape) */
|
||||||
|
if (*htext == '\n') { /* End of line, append last segment */
|
||||||
|
htext++;
|
||||||
|
appendText (topic, start, htext - start);
|
||||||
|
break; /* To next line */
|
||||||
|
}
|
||||||
|
htext++; /* Regular character */
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} /* topic text line */
|
||||||
|
if (isdigit (*htext)) { /* Topic heading */
|
||||||
|
TOPIC **children;
|
||||||
|
TOPIC *newt;
|
||||||
|
char nbuf[100];
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
start = htext;
|
||||||
|
while (isdigit (*htext)) {
|
||||||
|
n += (n * 10) + (*htext++ - '0');
|
||||||
|
}
|
||||||
|
if ((htext == start) || !n) {
|
||||||
|
FAIL (SCPE_ARG, Invalid topic heading);
|
||||||
|
}
|
||||||
|
if (n <= topic->level) { /* Find level for new topic */
|
||||||
|
while (n <= topic->level) {
|
||||||
|
topic = topic->parent;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (n > topic->level +1) { /* Skipping down more than 1 */
|
||||||
|
FAIL (SCPE_ARG, Level not contiguous); /* E.g. 1 3, not reasonable */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (*htext && (*htext != '\n') && isspace (*htext)) {
|
||||||
|
htext++;
|
||||||
|
}
|
||||||
|
if (!*htext || (*htext == '\n')) { /* Name missing */
|
||||||
|
FAIL (SCPE_ARG, Missing topic name);
|
||||||
|
}
|
||||||
|
start = htext;
|
||||||
|
while (*htext && (*htext != '\n')) {
|
||||||
|
htext++;
|
||||||
|
}
|
||||||
|
if (start == htext) { /* Name NULL */
|
||||||
|
FAIL (SCPE_ARG, Null topic name);
|
||||||
|
}
|
||||||
|
excluded = FALSE;
|
||||||
|
if (*start == '?') { /* Conditional topic? */
|
||||||
|
size_t n = 0;
|
||||||
|
start++;
|
||||||
|
while (isdigit (*start)) { /* Get param # */
|
||||||
|
n += (n * 10) + (*start++ - '0');
|
||||||
|
}
|
||||||
|
if (!*start || *start == '\n'|| n == 0 || n >= VSMAX)
|
||||||
|
FAIL (SCPE_ARG, Invalid parameter number);
|
||||||
|
while (n > vsnum) { /* Get arg pointer if not cached */
|
||||||
|
vstrings[vsnum++] = va_arg (ap, char *);
|
||||||
|
}
|
||||||
|
end = vstrings[n-1]; /* Check for True */
|
||||||
|
if (!end || !(toupper (*end) == 'T' || *end == '1')) {
|
||||||
|
excluded = TRUE; /* False, skip topic this time */
|
||||||
|
if (*htext)
|
||||||
|
htext++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newt = (TOPIC *) calloc (sizeof (TOPIC), 1);
|
||||||
|
if (!newt) {
|
||||||
|
FAIL (SCPE_MEM, No memory);
|
||||||
|
}
|
||||||
|
newt->title = (char *) malloc ((htext - start)+1);
|
||||||
|
if (!newt->title) {
|
||||||
|
free (newt);
|
||||||
|
FAIL (SCPE_MEM, No memory);
|
||||||
|
}
|
||||||
|
memcpy (newt->title, start, htext - start);
|
||||||
|
newt->title[htext - start] = '\0';
|
||||||
|
if (*htext)
|
||||||
|
htext++;
|
||||||
|
|
||||||
|
if (newt->title[0] == '$') {
|
||||||
|
newt->flags |= HLP_MAGIC_TOPIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
children = (TOPIC **) realloc (topic->children,
|
||||||
|
(topic->kids +1) * sizeof (TOPIC *));
|
||||||
|
if (!children) {
|
||||||
|
free (newt->title);
|
||||||
|
free (newt);
|
||||||
|
FAIL (SCPE_MEM, No memory);
|
||||||
|
}
|
||||||
|
topic->children = children;
|
||||||
|
topic->children[topic->kids++] = newt;
|
||||||
|
newt->level = n;
|
||||||
|
newt->parent = topic;
|
||||||
|
n = strlen (newt->title);
|
||||||
|
if (n > topic->kidwid) {
|
||||||
|
topic->kidwid = n;
|
||||||
|
}
|
||||||
|
sprintf (nbuf, ".%u", topic->kids);
|
||||||
|
n = strlen (topic->label) + strlen (nbuf) + 1;
|
||||||
|
newt->label = (char *) malloc (n);
|
||||||
|
if (!newt->label) {
|
||||||
|
free (newt->title);
|
||||||
|
topic->children[topic->kids -1] = NULL;
|
||||||
|
free (newt);
|
||||||
|
FAIL (SCPE_MEM, No memory);
|
||||||
|
}
|
||||||
|
sprintf (newt->label, "%s%s", topic->label, nbuf);
|
||||||
|
topic = newt;
|
||||||
|
continue;
|
||||||
|
} /* digits introducing a topic */
|
||||||
|
if (*htext == ';') { /* Comment */
|
||||||
|
while (*htext && *htext != '\n')
|
||||||
|
htext++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FAIL (SCPE_ARG, Unknown line type); /* Unknown line */
|
||||||
|
} /* htext not at end */
|
||||||
|
memset (vstrings, 0, VSMAX * sizeof (char *));
|
||||||
|
vsnum = 0;
|
||||||
|
} /* all strings */
|
||||||
|
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create prompt string - top thru current topic
|
||||||
|
* Add prompt at end.
|
||||||
|
*/
|
||||||
|
static char *helpPrompt ( TOPIC *topic, const char *pstring, t_bool oneword ) {
|
||||||
|
char *prefix;
|
||||||
|
char *newp, *newt;
|
||||||
|
|
||||||
|
if (topic->level == 0) {
|
||||||
|
prefix = (char *) calloc (2,1);
|
||||||
|
if (!prefix) {
|
||||||
|
FAIL (SCPE_MEM, No memory);
|
||||||
|
}
|
||||||
|
prefix[0] = '\n';
|
||||||
|
} else {
|
||||||
|
prefix = helpPrompt (topic->parent, "", oneword);
|
||||||
|
}
|
||||||
|
|
||||||
|
newp = (char *) malloc (strlen (prefix) + 1 + strlen (topic->title) + 1 +
|
||||||
|
strlen (pstring) +1);
|
||||||
|
if (!newp) {
|
||||||
|
free (prefix);
|
||||||
|
FAIL (SCPE_MEM, No memory);
|
||||||
|
}
|
||||||
|
strcpy (newp, prefix);
|
||||||
|
if (topic->level != 0)
|
||||||
|
strcat (newp, " ");
|
||||||
|
newt = (topic->flags & HLP_MAGIC_TOPIC)?
|
||||||
|
topic->title+1: topic->title;
|
||||||
|
if (oneword) {
|
||||||
|
char *np = newp + strlen (newp);
|
||||||
|
while (*newt) {
|
||||||
|
*np++ = blankch (*newt)? '_' : *newt;
|
||||||
|
newt++;
|
||||||
|
}
|
||||||
|
*np = '\0';
|
||||||
|
} else {
|
||||||
|
strcat (newp, newt);
|
||||||
|
}
|
||||||
|
if (*pstring && *pstring != '?')
|
||||||
|
strcat (newp, " ");
|
||||||
|
strcat (newp, pstring);
|
||||||
|
free (prefix);
|
||||||
|
return newp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void displayMagicTopic (FILE *st, struct sim_device *dptr, TOPIC *topic) {
|
||||||
|
char tbuf[CBUFSIZE];
|
||||||
|
size_t i, skiplines;
|
||||||
|
#ifdef _WIN32
|
||||||
|
FILE *tmp;
|
||||||
|
char *tmpnam;
|
||||||
|
do {
|
||||||
|
int fd;
|
||||||
|
tmpnam = _tempnam (NULL, "simh");
|
||||||
|
fd = _open (tmpnam, _O_CREAT | _O_RDWR | _O_EXCL, _S_IREAD | _S_IWRITE);
|
||||||
|
if (fd != -1) {
|
||||||
|
tmp = _fdopen (fd, "w+");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (1);
|
||||||
|
#else
|
||||||
|
FILE *tmp = tmpfile();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!tmp) {
|
||||||
|
fprintf (st, "Unable to create temporary file: %s\n", strerror (errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topic->title)
|
||||||
|
fprintf (st, "%s\n", topic->title+1);
|
||||||
|
|
||||||
|
skiplines = 0;
|
||||||
|
if (!strcmp (topic->title+1, "Registers")) {
|
||||||
|
fprint_reg_help (tmp, dptr) ;
|
||||||
|
skiplines = 1;
|
||||||
|
} else if (!strcmp (topic->title+1, "Set commands")) {
|
||||||
|
fprint_set_help (tmp, dptr);
|
||||||
|
skiplines = 3;
|
||||||
|
} else if (!strcmp (topic->title+1, "Show commands")) {
|
||||||
|
fprint_show_help (tmp, dptr);
|
||||||
|
skiplines = 3;
|
||||||
|
}
|
||||||
|
rewind (tmp);
|
||||||
|
|
||||||
|
/* Discard leading blank lines/redundant titles */
|
||||||
|
|
||||||
|
for (i =0; i < skiplines; i++) {
|
||||||
|
fgets (tbuf, sizeof (tbuf), tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (fgets (tbuf, sizeof (tbuf), tmp)) {
|
||||||
|
if (tbuf[0] != '\n') {
|
||||||
|
fputs (" ", st);
|
||||||
|
}
|
||||||
|
fputs (tbuf, st);
|
||||||
|
}
|
||||||
|
fclose (tmp);
|
||||||
|
#ifdef _WIN32
|
||||||
|
remove (tmpnam);
|
||||||
|
free (tmpnam);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Flatten and display help for those who say they prefer it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static t_stat displayFlatHelp (FILE *st, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, int32 flag,
|
||||||
|
TOPIC *topic, va_list ap ) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (topic->flags & HLP_MAGIC_TOPIC) {
|
||||||
|
fprintf (st, "\n%s ", topic->label);
|
||||||
|
displayMagicTopic (st, dptr, topic);
|
||||||
|
} else {
|
||||||
|
fprintf (st, "\n%s %s\n", topic->label, topic->title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Topic text (for magic topics, follows for explanations)
|
||||||
|
* It's possible/reasonable for a magic topic to have no text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (topic->text)
|
||||||
|
fputs (topic->text, st);
|
||||||
|
|
||||||
|
for (i = 0; i < topic->kids; i++) {
|
||||||
|
displayFlatHelp (st, dptr, uptr, flag, topic->children[i], ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HLP_MATCH_AMBIGUOUS (~0u)
|
||||||
|
#define HLP_MATCH_WILDCARD (~1U)
|
||||||
|
#define HLP_MATCH_NONE 0
|
||||||
|
static int matchHelpTopicName (TOPIC *topic, const char *token) {
|
||||||
|
size_t i, match;
|
||||||
|
char cbuf[CBUFSIZE], *cptr;
|
||||||
|
|
||||||
|
if (!strcmp (token, "*")) {
|
||||||
|
return HLP_MATCH_WILDCARD;
|
||||||
|
}
|
||||||
|
match = 0;
|
||||||
|
for (i = 0; i < topic->kids; i++) {
|
||||||
|
strcpy (cbuf,topic->children[i]->title +
|
||||||
|
((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
|
||||||
|
cptr = cbuf;
|
||||||
|
while (*cptr) {
|
||||||
|
if (blankch (*cptr)) {
|
||||||
|
*cptr++ = '_';
|
||||||
|
} else {
|
||||||
|
*cptr = toupper (*cptr);
|
||||||
|
cptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!strncmp (cbuf, token, strlen (token))) {
|
||||||
|
if (match) {
|
||||||
|
return HLP_MATCH_AMBIGUOUS;
|
||||||
|
}
|
||||||
|
match = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main help routine
|
||||||
|
* Takes a va_list
|
||||||
|
*/
|
||||||
|
|
||||||
|
t_stat scp_vhelp (FILE *st, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, int32 flag,
|
||||||
|
const char *help, char *cptr, va_list ap) {
|
||||||
|
|
||||||
|
TOPIC top = { 0, NULL, NULL, &top, NULL, 0, NULL, 0, 0};
|
||||||
|
TOPIC *topic = ⊤
|
||||||
|
int failed;
|
||||||
|
size_t match;
|
||||||
|
size_t i;
|
||||||
|
char *p;
|
||||||
|
t_bool flat_help = FALSE;
|
||||||
|
char cbuf [CBUFSIZE], gbuf[CBUFSIZE];
|
||||||
|
|
||||||
|
static const char attach_help[] = { " ATTACH" };
|
||||||
|
static const char brief_help[] = { "%s help. Type <CR> to exit, HELP for navigation help" };
|
||||||
|
static const char onecmd_help[] = { "%s help." };
|
||||||
|
static const char help_help[] = {
|
||||||
|
/****|***********************80 column width guide********************************/
|
||||||
|
" This help command provides hierarchical help. To see more information, type\n"
|
||||||
|
" an offered subtopic name. To move back a level, just type <CR>.\n"
|
||||||
|
" To review the current topic/subtopic, type \"?\".\n"
|
||||||
|
" To view all subtopics, type \"*\".\n"
|
||||||
|
" To exit help at any time, type EXIT.\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((failed = setjmp (help_env)) != 0) {
|
||||||
|
fprintf (stderr, "\nHelp was unable to process the help for this device.\n"
|
||||||
|
"Error in block %u line %u: %s\n"
|
||||||
|
"Please contact the device maintainer.\n",
|
||||||
|
(int)help_where.block, (int)help_where.line, help_where.error);
|
||||||
|
cleanHelp (&top);
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compile string into navigation tree */
|
||||||
|
|
||||||
|
/* Root */
|
||||||
|
|
||||||
|
if (dptr) {
|
||||||
|
p = dptr->name;
|
||||||
|
flat_help = (dptr->flags & DEV_FLATHELP) != 0;
|
||||||
|
} else {
|
||||||
|
p = sim_name;
|
||||||
|
}
|
||||||
|
top.title = (char *) malloc (strlen (p) + ((flag & SCP_HELP_ATTACH)? sizeof (attach_help)-1: 0) +1);
|
||||||
|
for (i = 0; p[i]; i++ ) {
|
||||||
|
top.title[i] = toupper (p[i]);
|
||||||
|
}
|
||||||
|
top.title[i] = '\0';
|
||||||
|
if (flag & SCP_HELP_ATTACH) {
|
||||||
|
strcpy (top.title+i, attach_help);
|
||||||
|
}
|
||||||
|
|
||||||
|
top.label = (char *) malloc (sizeof ("1"));
|
||||||
|
strcpy (top.label, "1");
|
||||||
|
|
||||||
|
flat_help = flat_help || !sim_ttisatty() || (flag & SCP_HELP_FLAT);
|
||||||
|
|
||||||
|
if (flat_help) {
|
||||||
|
flag |= SCP_HELP_FLAT;
|
||||||
|
if (sim_ttisatty()) {
|
||||||
|
fprintf (st, "%s help.\nThis help is also available in hierarchical form.\n", top.title);
|
||||||
|
} else {
|
||||||
|
fprintf (st, "%s help.\n", top.title);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fprintf (st, ((flag & SCP_HELP_ONECMD)? onecmd_help: brief_help), top.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add text and subtopics */
|
||||||
|
|
||||||
|
(void) buildHelp (&top, dptr, uptr, help, ap);
|
||||||
|
|
||||||
|
/* Go to initial topic if provided */
|
||||||
|
|
||||||
|
while (cptr && *cptr) {
|
||||||
|
cptr = get_glyph (cptr, gbuf, 0);
|
||||||
|
if (!gbuf[0]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!strcmp (gbuf, "HELP")) { /* HELP (about help) */
|
||||||
|
fputs (help_help, st);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
match = matchHelpTopicName (topic, gbuf);
|
||||||
|
if (match == HLP_MATCH_WILDCARD) {
|
||||||
|
displayFlatHelp (st, dptr, uptr, flag, topic, ap);
|
||||||
|
cleanHelp (&top);
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
if (match == HLP_MATCH_AMBIGUOUS) {
|
||||||
|
fprintf (st, "\n%s is ambiguous in %s\n", gbuf, topic->title);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (match == HLP_MATCH_NONE) {
|
||||||
|
fprintf (st, "\n%s is not available in %s\n", gbuf, topic->title);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
topic = topic->children[match-1];
|
||||||
|
}
|
||||||
|
cptr = NULL;
|
||||||
|
|
||||||
|
if (flat_help) {
|
||||||
|
displayFlatHelp (st, dptr, uptr, flag, topic, ap);
|
||||||
|
cleanHelp (&top);
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive loop displaying help */
|
||||||
|
|
||||||
|
while (TRUE) {
|
||||||
|
char *pstring;
|
||||||
|
const char *prompt[2] = {"? ", "Subtopic? "};
|
||||||
|
|
||||||
|
/* Some magic topic names for help from data structures */
|
||||||
|
|
||||||
|
if (topic->flags & HLP_MAGIC_TOPIC) {
|
||||||
|
fputc ('\n', st);
|
||||||
|
displayMagicTopic (st, dptr, topic);
|
||||||
|
} else {
|
||||||
|
fprintf (st, "\n%s\n", topic->title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Topic text (for magic topics, follows for explanations)
|
||||||
|
* It's possible/reasonable for a magic topic to have no text.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (topic->text)
|
||||||
|
fputs (topic->text, st);
|
||||||
|
|
||||||
|
if (topic->kids) {
|
||||||
|
size_t w = 0;
|
||||||
|
char *p;
|
||||||
|
char tbuf[CBUFSIZE];
|
||||||
|
|
||||||
|
fprintf (st, "\n Additional information available:\n\n");
|
||||||
|
for (i = 0; i < topic->kids; i++) {
|
||||||
|
strcpy (tbuf, topic->children[i]->title +
|
||||||
|
((topic->children[i]->flags & HLP_MAGIC_TOPIC)? 1 : 0));
|
||||||
|
for (p = tbuf; *p; p++) {
|
||||||
|
if (blankch (*p))
|
||||||
|
*p = '_';
|
||||||
|
}
|
||||||
|
w += 4 + topic->kidwid;
|
||||||
|
if (w > 80) {
|
||||||
|
w = 4 + topic->kidwid;
|
||||||
|
fputc ('\n', st);
|
||||||
|
}
|
||||||
|
fprintf (st, " %-*s", topic->kidwid, tbuf);
|
||||||
|
}
|
||||||
|
fprintf (st, "\n\n");
|
||||||
|
if (flag & SCP_HELP_ONECMD) {
|
||||||
|
pstring = helpPrompt (topic, "", TRUE);
|
||||||
|
fprintf (st, "To view additional topics, type HELP %s topicname\n", pstring+1);
|
||||||
|
free (pstring);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sim_ttisatty() || (flag & SCP_HELP_ONECMD))
|
||||||
|
break;
|
||||||
|
|
||||||
|
reprompt:
|
||||||
|
if (!cptr || !*cptr) {
|
||||||
|
pstring = helpPrompt (topic, prompt[topic->kids != 0], FALSE);
|
||||||
|
|
||||||
|
cptr = read_line_p (pstring, cbuf, sizeof (cbuf), stdin);
|
||||||
|
free (pstring);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cptr) /* EOF, exit help */
|
||||||
|
break;
|
||||||
|
|
||||||
|
cptr = get_glyph (cptr, gbuf, 0);
|
||||||
|
if (!strcmp (gbuf, "*")) { /* Wildcard */
|
||||||
|
displayFlatHelp (st, dptr, uptr, flag, topic, ap);
|
||||||
|
gbuf[0] = '\0'; /* Displayed all subtopics, go up */
|
||||||
|
}
|
||||||
|
if (!gbuf[0]) { /* Blank, up a level */
|
||||||
|
if (topic->level == 0)
|
||||||
|
break;
|
||||||
|
topic = topic->parent;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp (gbuf, "?")) { /* ?, repaint current topic */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp (gbuf, "HELP")) { /* HELP (about help) */
|
||||||
|
fputs (help_help, st);
|
||||||
|
goto reprompt;
|
||||||
|
}
|
||||||
|
if (!strcmp (gbuf, "EXIT") || !strcmp (gbuf, "QUIT")) { /* EXIT (help) */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* String - look for that topic */
|
||||||
|
|
||||||
|
if (!topic->kids) {
|
||||||
|
fprintf (st, "No additional help at this level.\n");
|
||||||
|
cptr = NULL;
|
||||||
|
goto reprompt;
|
||||||
|
}
|
||||||
|
match = matchHelpTopicName (topic, gbuf);
|
||||||
|
if (match == HLP_MATCH_AMBIGUOUS) {
|
||||||
|
fprintf (st, "%s is ambiguous, please type more of the topic name\n", gbuf);
|
||||||
|
cptr = NULL;
|
||||||
|
goto reprompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match == HLP_MATCH_NONE) {
|
||||||
|
fprintf (st, "Help for %s is not available\n", gbuf);
|
||||||
|
cptr = NULL;
|
||||||
|
goto reprompt;
|
||||||
|
}
|
||||||
|
/* Found, display subtopic */
|
||||||
|
|
||||||
|
topic = topic->children[match-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free structures and return */
|
||||||
|
|
||||||
|
cleanHelp (&top);
|
||||||
|
|
||||||
|
return SCPE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* variable argument list shell - most commonly used
|
||||||
|
*/
|
||||||
|
|
||||||
|
t_stat scp_help (FILE *st, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, int32 flag,
|
||||||
|
const char *help, char *cptr, ...) {
|
||||||
|
t_stat r;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start (ap, cptr);
|
||||||
|
r = scp_vhelp (st, dptr, uptr, flag, help, cptr, ap);
|
||||||
|
va_end (ap);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 01
|
||||||
|
/* Read help from a file
|
||||||
|
*
|
||||||
|
* Not recommended due to OS-dependent issues finding the file, + maintenance.
|
||||||
|
* Don't hardcode any path - just name.hlp - so there's a chance the file can
|
||||||
|
* be found.
|
||||||
|
*/
|
||||||
|
|
||||||
|
t_stat scp_vhelpFromFile (FILE *st, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, int32 flag,
|
||||||
|
const char *helpfile,
|
||||||
|
char *cptr, va_list ap) {
|
||||||
|
FILE *fp;
|
||||||
|
char *help, *p;
|
||||||
|
t_offset size, n;
|
||||||
|
int c;
|
||||||
|
t_stat r;
|
||||||
|
|
||||||
|
fp = sim_fopen (helpfile, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
if (sim_argv && *sim_argv[0]) {
|
||||||
|
char fbuf[(4*PATH_MAX)+1]; /* PATH_MAX is ridiculously small on some platforms */
|
||||||
|
char *d = NULL;
|
||||||
|
|
||||||
|
/* Try to find a path from argv[0]. This won't always
|
||||||
|
* work (one reason files are probably not a good idea),
|
||||||
|
* but we might as well try. Some OSs won't include a
|
||||||
|
* path. Having failed in the CWD, try to find the location
|
||||||
|
* of the executable. Failing that, try the 'help' subdirectory
|
||||||
|
* of the executable. Failing that, we're out of luck.
|
||||||
|
*/
|
||||||
|
strncpy (fbuf, sim_argv[0], sizeof (fbuf));
|
||||||
|
if ((p = match_ext (fbuf, "EXE"))) {
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
|
if ((p = strrchr (fbuf, '\\'))) {
|
||||||
|
p[1] = '\0';
|
||||||
|
d = "%s\\";
|
||||||
|
} else {
|
||||||
|
if ((p = strrchr (fbuf, '/'))) {
|
||||||
|
p[1] = '\0';
|
||||||
|
d = "%s/";
|
||||||
|
#ifdef VMS
|
||||||
|
} else {
|
||||||
|
if ((p = strrchr (fbuf, ']'))) {
|
||||||
|
p[1] = '\0';
|
||||||
|
d = "[%s]";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p && (strlen (fbuf) + strlen (helpfile) +1) <= sizeof (fbuf)) {
|
||||||
|
strcat (fbuf, helpfile);
|
||||||
|
fp = sim_fopen (fbuf, "r");
|
||||||
|
}
|
||||||
|
if (!fp && p && (strlen (fbuf) + strlen (d) + sizeof ("help") +
|
||||||
|
strlen (helpfile) +1) <= sizeof (fbuf)) {
|
||||||
|
sprintf (p+1, d, "help");
|
||||||
|
strcat (p+1, helpfile);
|
||||||
|
fp = sim_fopen (fbuf, "r");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fp == NULL) {
|
||||||
|
fprintf (stderr, "Unable to open %s\n", helpfile);
|
||||||
|
return SCPE_UNATT;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = sim_fsize_ex (fp); /* Estimate; line endings will vary */
|
||||||
|
|
||||||
|
help = (char *) malloc ((size_t) size +1);
|
||||||
|
if (!help) {
|
||||||
|
fclose (fp);
|
||||||
|
return SCPE_MEM;
|
||||||
|
}
|
||||||
|
p = help;
|
||||||
|
n = 0;
|
||||||
|
|
||||||
|
while ((c = fgetc (fp)) != EOF) {
|
||||||
|
if (++n > size) {
|
||||||
|
#define XPANDQ 512
|
||||||
|
p = (char *) realloc (help, ((size_t)size) + XPANDQ +1);
|
||||||
|
if (!p) {
|
||||||
|
fclose (fp);
|
||||||
|
return SCPE_MEM;
|
||||||
|
}
|
||||||
|
help = p;
|
||||||
|
size += XPANDQ;
|
||||||
|
p += n;
|
||||||
|
}
|
||||||
|
*p++ = c;
|
||||||
|
}
|
||||||
|
*p++ = '\0';
|
||||||
|
|
||||||
|
fclose (fp);
|
||||||
|
|
||||||
|
r = scp_vhelp (st, dptr, uptr, flag, help, cptr, ap);
|
||||||
|
free (help);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
t_stat scp_helpFromFile (FILE *st, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, int32 flag,
|
||||||
|
const char *helpfile, char *cptr, ...) {
|
||||||
|
t_stat r;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start (ap, cptr);
|
||||||
|
r = scp_vhelpFromFile (st, dptr, uptr, flag, helpfile, cptr, ap);
|
||||||
|
va_end (ap);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
11
scp.h
11
scp.h
|
@ -155,6 +155,17 @@ void _sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...);
|
||||||
#define sim_debug(dbits, dptr, ...) if (sim_deb && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__); else (void)0
|
#define sim_debug(dbits, dptr, ...) if (sim_deb && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__); else (void)0
|
||||||
#endif
|
#endif
|
||||||
void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr);
|
void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr);
|
||||||
|
#define SCP_HELP_FLAT (1u << 31) /* Force flat help when prompting is not possible */
|
||||||
|
#define SCP_HELP_ONECMD (1u << 30) /* Display one topic, do not prompt */
|
||||||
|
#define SCP_HELP_ATTACH (1u << 29) /* Top level topic is ATTACH help */
|
||||||
|
t_stat scp_help (FILE *st, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, int32 flag, const char *help, char *cptr, ...);
|
||||||
|
t_stat scp_vhelp (FILE *st, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, int32 flag, const char *help, char *cptr, va_list ap);
|
||||||
|
t_stat scp_helpFromFile (FILE *st, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, int32 flag, const char *help, char *cptr, ...);
|
||||||
|
t_stat scp_vhelpFromFile (FILE *st, struct sim_device *dptr,
|
||||||
|
struct sim_unit *uptr, int32 flag, const char *help, char *cptr, va_list ap);
|
||||||
|
|
||||||
/* Global data */
|
/* Global data */
|
||||||
|
|
||||||
|
|
256
scp_help.h
Normal file
256
scp_help.h
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
/* scp_help.h: hierarchical help definitions
|
||||||
|
|
||||||
|
Copyright (c) 2013, Timothe Litt
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall not be
|
||||||
|
used in advertising or otherwise to promote the sale, use or other dealings
|
||||||
|
in this Software without prior written authorization from the author.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SCP_HELP_H_
|
||||||
|
#define SCP_HELP_H_ 0
|
||||||
|
|
||||||
|
/* The SCP structured help uses help text that defines a hierarchy of information
|
||||||
|
* organized into topics and subtopics. This is modelled on the VMS help command.
|
||||||
|
*
|
||||||
|
* This arrangement allows the long help messages being used in many devices to be
|
||||||
|
* organized to be easier to approach and navigate.
|
||||||
|
*
|
||||||
|
* The helpx utility (a Perl script) will read a device source file
|
||||||
|
* that conforms to the previous conventions, and produce a template and
|
||||||
|
* initial translation into the format described here.
|
||||||
|
*
|
||||||
|
* The structure of the help text is:
|
||||||
|
*
|
||||||
|
* Lines beginning with whitespace are displayed as part of the current topic, except:
|
||||||
|
* * The leading white space is replaced by a standard indentation of 4 spaces.
|
||||||
|
* Additional indentation, where appropriate, can be obtained with '+', 4 spaces each.
|
||||||
|
*
|
||||||
|
* * The following % escapes are recognized:
|
||||||
|
* * %D - Inserts the name of the device (e.g. "DTA").
|
||||||
|
* * %U - Inserts the name of the unit (e.g. "DTA0").
|
||||||
|
* * %S - Inserts the current simulator name (e.g. "PDP-10")
|
||||||
|
* * %#s - Inserts the string suppled in the "#"th optional argument to the help
|
||||||
|
* routine. # starts with 1. Any embedded newlines will cause following
|
||||||
|
* text to be indented.
|
||||||
|
* * %#H - Appends the #th optional argument to the help text. Use to add common
|
||||||
|
* help to device specific help. The text is placed AFTER the current help
|
||||||
|
* string, and after any previous %H inclusions. Parameter numbers restart
|
||||||
|
* with the new string, following the last parameter used by the previous tree.
|
||||||
|
* * %% - Inserts a literal %.
|
||||||
|
* * %+ - Inserts a literal +.
|
||||||
|
* - Any other escape is reserved and will cause an exception. However, the goal
|
||||||
|
* is to provide help, not a general formatting facility. Use sprintf to a
|
||||||
|
* local buffer, and pass that as a string if more general formatting is required.
|
||||||
|
*
|
||||||
|
* Lines beginning with a number introduce a subtopic of the device. The number indicates
|
||||||
|
* the subtopic's place in the help hierarchy. Topics offered as Additional Information
|
||||||
|
* under the device's main topic are at level 1. Their sub-topics are at level 2, and
|
||||||
|
* so on. Following the number is a string that names the sub-topic. This is displayed,
|
||||||
|
* and what the user types to access the information. Whitespace in the topic name is
|
||||||
|
* typed as an underscore (_). Topic names beginning with '$' invoke other kinds of help,
|
||||||
|
* These are:
|
||||||
|
* $Registers - Displays the device register help
|
||||||
|
* $Set commands - Displays the standard SET command help.
|
||||||
|
* $Show commands - Displays the standard SHOW command help.
|
||||||
|
*
|
||||||
|
* For these special topics, any text that you provide will be added after
|
||||||
|
* the output from the system routines. This allows you to add more information, or
|
||||||
|
* an introduction to subtopics with more detail.
|
||||||
|
*
|
||||||
|
* Topic names that begin with '?' are conditional topics.
|
||||||
|
* Some devices adopt radically different personalities at runtime,
|
||||||
|
* e.g. when attached to a processor with different bus.
|
||||||
|
* In rare cases, it's better not to include help that doesn't apply.
|
||||||
|
* For these cases, ?#, where # is a 1-based parameter number, can be used
|
||||||
|
* to selectively include a topic. If the specified parameter is TRUE
|
||||||
|
* (a string with the value "T", "t" or '1'), the topic will be visible.
|
||||||
|
* If the parameter is FALSE (NULL, or a string with any other value),
|
||||||
|
* the topic will not be visible.
|
||||||
|
*
|
||||||
|
* If it can be determined at compile time whether the topic in question
|
||||||
|
* is needed, #ifdef around those lines of the help is a better choice.
|
||||||
|
*
|
||||||
|
* If both $ and ? are used, ? comes first.
|
||||||
|
*
|
||||||
|
* Guidelines:
|
||||||
|
* Help should be concise and easy to understand.
|
||||||
|
*
|
||||||
|
* The main topic should be short - less than a sceenful when presented with the
|
||||||
|
* subtopic list.
|
||||||
|
*
|
||||||
|
* Keep line lengths to 76 columns or less.
|
||||||
|
*
|
||||||
|
* Follow the subtopic naming conventions (under development) for a consistent style:
|
||||||
|
*
|
||||||
|
* At the top level, the device should be summarized in a few sentences.
|
||||||
|
* The subtopics for detail should be:
|
||||||
|
* Hardware Description - The details of the hardware. Feeds & speeds are OK here.
|
||||||
|
* Models - If the device was offered in distinct models, a subtopic for each.
|
||||||
|
* Registers - Register descriptions
|
||||||
|
*
|
||||||
|
* Configuration - How to configure the device under SimH. SET commands.
|
||||||
|
* Operating System - If the device needs special configuration for a particular
|
||||||
|
* OS, a subtopic for each such OS goes here.
|
||||||
|
* Files - If the device uses external files (tapes, cards, disks, configuration)
|
||||||
|
* A subtopic for each here.
|
||||||
|
* Examples - Provide usable examples for configuring complex devices.
|
||||||
|
*
|
||||||
|
* Operation - How to operate the device under SimH. Attach, runtime events
|
||||||
|
* (e.g. how to load cards or mount a tape)
|
||||||
|
*
|
||||||
|
* Monitoring - How to obtain status (SHOW commands)
|
||||||
|
*
|
||||||
|
* Restrictions - If some aspects of the device aren't emulated, list them here.
|
||||||
|
*
|
||||||
|
* Debugging - Debugging information
|
||||||
|
*
|
||||||
|
* Related Devices - If devices are configured or used together, list the other devices here.
|
||||||
|
* E.G. The DEC KMC/DUP are two hardware devices that are closely related;
|
||||||
|
* The KMC controlls the DUP on behalf of the OS.
|
||||||
|
*
|
||||||
|
* This text can be created by any convenient means. It can be mechanically extracted from the device
|
||||||
|
* source, read from a file, or simply entered as a string in the help routine. To facilitate the latter,
|
||||||
|
* this file defines two convenience macros:
|
||||||
|
*
|
||||||
|
* L(text) - provides a string with a leading space and a trailing \n. Enter a line of topic text.
|
||||||
|
* T(n, NAME) - provides a string with the topic level n and the topic name NAME, and a trailing \n.
|
||||||
|
*
|
||||||
|
* These are concatenated normally, e.g.
|
||||||
|
const char *const help =
|
||||||
|
L (The %D device is interesting)
|
||||||
|
L (It has lots of help options)
|
||||||
|
T (1, TOPIC 1)
|
||||||
|
L (And this is topic 1)
|
||||||
|
;
|
||||||
|
*
|
||||||
|
* API:
|
||||||
|
* To make use of this type of help in your device, create (or replace) a help routine with one that
|
||||||
|
* calls scp_help. Most of the arguments are the same as those of the device help routine.
|
||||||
|
*
|
||||||
|
* t_stat scp_help (FILE *st, struct sim_device *dptr,
|
||||||
|
* struct sim_unit *uptr, int flag, const char *help, char *cptr, ...)
|
||||||
|
*
|
||||||
|
* If you need to pass the variable argument list from another routine, use:
|
||||||
|
*
|
||||||
|
* t_stat scp_vhelp (FILE *st, struct sim_device *dptr,
|
||||||
|
* struct sim_unit *uptr, int flag, const char *help, char *cptr, va_list ap)
|
||||||
|
*
|
||||||
|
* To obtain the help from an external file (Note this reads the entire file into memory):
|
||||||
|
* t_stat scp_helpFromFile (FILE *st, struct sim_device *dptr,
|
||||||
|
* struct sim_unit *uptr, int flag, const char *helpfile, char *cptr, ...)
|
||||||
|
* and for va_list:
|
||||||
|
* t_stat scp_vhelpFromFile (FILE *st, struct sim_device *dptr,
|
||||||
|
* struct sim_unit *uptr, int flag, const char *helpfile, char *cptr, va_list ap) {
|
||||||
|
*
|
||||||
|
* dptr and uptr are only used if the %D and/or %U escapes are encountered.
|
||||||
|
* help is the help text; helpfile is the help file name.
|
||||||
|
*
|
||||||
|
* flag is usually the flag from the help command dispatch. SCP_HELP_FLAT is set in non-interactive
|
||||||
|
* environments. When this flag, or DEV_FLATHELP in DEVICE.flags is set, the entire help text
|
||||||
|
* will be flattened and displayed in outline form.
|
||||||
|
*
|
||||||
|
* Help files are easier to edit, but can become separated from the SimH executable. Finding them
|
||||||
|
* at runtime can also be a challenge. SimH tries...but the project standard is to embed help
|
||||||
|
* as strings in the device. (It may be easier to develop help as a file before converting it
|
||||||
|
* to a string.)
|
||||||
|
*
|
||||||
|
* Lines beginning with ';' will be ignored.
|
||||||
|
*
|
||||||
|
* Here is a worked-out example:
|
||||||
|
*
|
||||||
|
;****************************************************************************
|
||||||
|
The Whizbang 100 is a DMA line printer controller used on the Whizbang 1000
|
||||||
|
and Gurgle 1200 processor familes of the Obsolete Hardware Corporation.
|
||||||
|
1 Hardware Description
|
||||||
|
The Whizbang 100 is specified to operate "any printer you and a friend can
|
||||||
|
lift", and speeds up to 0.5 C.
|
||||||
|
|
||||||
|
The controller requires a refrigerator-sized box, consumes 5.5KW, and is
|
||||||
|
liquid cooled. It uses GBL (Granite Baked Logic).
|
||||||
|
|
||||||
|
Painted a cool blue, it consistently won industrial design awards, even
|
||||||
|
as mechanically, it was less than perfect. Plumbers had full employment.
|
||||||
|
2 Models
|
||||||
|
The Whizbang 100 model G was commissioned by a government agency, which
|
||||||
|
insisted on dull gray paint, and speeds limited to 11 MPH.
|
||||||
|
|
||||||
|
The Whizbang 100 Model X is powered by the improbability drive, and is
|
||||||
|
rarely seen once installed.
|
||||||
|
2 $Registers
|
||||||
|
The two main registers are the Print Control register and the Print Data
|
||||||
|
register. The Print Maintenance register is usually broken.
|
||||||
|
3 Print Control register
|
||||||
|
Bit 0 turns the attached printer on when set, off when clear.
|
||||||
|
Bit 1 ejects the current page
|
||||||
|
Bit 2 ejects the operator
|
||||||
|
Bit 3 enables interrupts
|
||||||
|
3 Print data register
|
||||||
|
The print data register is thiry-seven bits wide, and accepts data in
|
||||||
|
elephantcode, the precursor to Unicode. Paper advance is accomplished
|
||||||
|
with the Rocket Return and Page Trampoline characters.
|
||||||
|
1 Configuration
|
||||||
|
The Whizbang 100 requires 4 address slots on the LooneyBus.
|
||||||
|
+ SET WHIZBANG LUNA 11
|
||||||
|
will assign the controller to its default bus address.
|
||||||
|
2 $Set commands
|
||||||
|
The output codeset can be ASCII or elephantcode
|
||||||
|
+ SET WHIZBANG CODESET ASCII
|
||||||
|
+ SET WHIZBANG CODESET ELEPHANTCODE
|
||||||
|
|
||||||
|
The VFU (carriage control tape) is specifed with
|
||||||
|
+ SET WHIZBANG TAPE vfufile
|
||||||
|
2 WOS
|
||||||
|
Under WOS, the device will only work at LooneyBus slot 9
|
||||||
|
2 RTG
|
||||||
|
The RTG driver has been lost. It is not known if the
|
||||||
|
Whizbang will operate correctly.
|
||||||
|
2 Files
|
||||||
|
The VFU is programmed with an ASCII text file. Each line of the
|
||||||
|
file corresponds to a line of the form. Enter the channel numbers
|
||||||
|
as base 33 roman numerals.
|
||||||
|
2 Examples
|
||||||
|
TBS
|
||||||
|
1 Operation
|
||||||
|
Specify the host file to receive output using the
|
||||||
|
+ATTACH WHIZBANG filespec
|
||||||
|
command.
|
||||||
|
1 Monitoring
|
||||||
|
The Whizbang has no lights or switches. The model X may be located
|
||||||
|
with the
|
||||||
|
+SHOW WHIZBANG LOCATION
|
||||||
|
simulator command.
|
||||||
|
2 $Show commands
|
||||||
|
1 Restrictions
|
||||||
|
The emulator is limited to a single Whizbang controller.
|
||||||
|
1 Debugging
|
||||||
|
The only implemented debugging command is
|
||||||
|
+ SET WHIZBANG DEBUG=PRAY
|
||||||
|
To stop:
|
||||||
|
+ SET WHIZBANG NODEBUG=PRAY
|
||||||
|
1 Related Devices
|
||||||
|
See also the Whizbang paper shredder (SHRED).
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define T(level, text) #level " " #text "\n"
|
||||||
|
#define L(text) " " #text "\n"
|
||||||
|
|
||||||
|
#endif
|
|
@ -391,6 +391,7 @@ struct sim_device {
|
||||||
#define DEV_S_TYPE 3 /* Width of Type Field */
|
#define DEV_S_TYPE 3 /* Width of Type Field */
|
||||||
#define DEV_V_SECTORS 7 /* Unit Capacity is in 512byte sectors */
|
#define DEV_V_SECTORS 7 /* Unit Capacity is in 512byte sectors */
|
||||||
#define DEV_V_DONTAUTO 8 /* Do not auto detach already attached units */
|
#define DEV_V_DONTAUTO 8 /* Do not auto detach already attached units */
|
||||||
|
#define DEV_V_FLATHELP 9 /* Use traditional (unstructured) help */
|
||||||
#define DEV_V_UF_31 12 /* user flags, V3.1 */
|
#define DEV_V_UF_31 12 /* user flags, V3.1 */
|
||||||
#define DEV_V_UF 16 /* user flags */
|
#define DEV_V_UF 16 /* user flags */
|
||||||
#define DEV_V_RSV 31 /* reserved */
|
#define DEV_V_RSV 31 /* reserved */
|
||||||
|
@ -401,6 +402,7 @@ struct sim_device {
|
||||||
#define DEV_DEBUG (1 << DEV_V_DEBUG) /* device supports SET DEBUG command */
|
#define DEV_DEBUG (1 << DEV_V_DEBUG) /* device supports SET DEBUG command */
|
||||||
#define DEV_SECTORS (1 << DEV_V_SECTORS) /* capacity is 512 byte sectors */
|
#define DEV_SECTORS (1 << DEV_V_SECTORS) /* capacity is 512 byte sectors */
|
||||||
#define DEV_DONTAUTO (1 << DEV_V_DONTAUTO) /* Do not auto detach already attached units */
|
#define DEV_DONTAUTO (1 << DEV_V_DONTAUTO) /* Do not auto detach already attached units */
|
||||||
|
#define DEV_FLATHELP (1 << DEV_V_FLATHELP) /* Use traditional (unstructured) help */
|
||||||
#define DEV_NET 0 /* Deprecated - meaningless */
|
#define DEV_NET 0 /* Deprecated - meaningless */
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue