|
#############################################################################
##
## This file is part of GAP, a system for computational discrete algebra.
## This file's authors include Frank Celler / Frank Lübeck.
##
## Copyright of GAP belongs to its developers, whose names are too numerous
## to list here. Please refer to the COPYRIGHT file for details.
##
## SPDX-License-Identifier: GPL-2.0-or-later
##
## The files helpdef.g{d,i} contain the `default' help book handler
## functions, which implement access of GAP's online help to help documents
## produced from `gapmacro.tex'- .tex and .msk files using buildman.pe,
## tex, pdftex and convert.pl.
##
## The function which converts the TeX sources to text for the "screen"
## viewer is outsourced into `helpt2t.g{d,i}'.
##
################ ???????????????????????????? ###############################
#############################################################################
##
#F GapLibToc2Gap( <tocfile> ) . . . . . . . . . . . . . reading .toc file
##
## reads a manual.toc file of GAP library book and returns list of entries
## of form [[chapnr, secnr], pagenr]. Used in `default' ReadSix.
##
## This allows to use xdvi and acroread/xpdf conveniently as help
## browser.
##
InstallGlobalFunction(GapLibToc2Gap, function(file)
local stream, str, getarg, p, l, res, a, s, pos, r;
stream := StringStreamInputTextFile(file);
if stream=fail then
return fail;
fi;
str := ReadAll(stream);
CloseStream(stream);
# get next argument in {...} after pos (need to handle nested {}'s)
getarg := function(str, pos)
local l, level, p;
l := Length(str);
while l >= pos and str[pos] <> '{' do
pos := pos + 1;
od;
level := 0;
p := pos+1;
while true do
if p > l then
break;
elif str[p] = '{' then
level := level+1;
elif str[p] = '}' then
if level = 0 then
break;
else
level := level-1;
fi;
fi;
p := p+1;
od;
return [pos, p];
end;
p := Position(str, '\\');
l := Length(str);
res := [];
while p <> fail do
# read one .toc entry
if p+12 < l and (str{[p..p+12]} = "\\chapcontents" or
str{[p..p+11]} = "\\seccontents") then
a := [getarg(str, p+12)];
Add(a, getarg(str, a[1][2]+1));
Add(a, getarg(str, a[2][2]+1));
p := Position(str, '\\', a[3][2]);
s := str{[a[1][1]+1..a[1][2]-1]};
# bibliography, index,.. are numberless chapters and seem not available
# in help index for library books
if Length(s)>0 and ForAll(s, x-> x in "0123456789" or
x='.') then
pos := Position(s, '.');
if pos=fail then
# chapter entry
r := [[Int(s), 0]];
else
# chapter and section number
r := [[Int(s{[1..pos-1]}), Int(s{[pos+1..Length(s)]})]];
fi;
# don't need the header again
## Add(r, str{[a[2][1]+1..a[2][2]-1]});
# page number
Add(r, Int(str{[a[3][1]+1..a[3][2]-1]}));
Add(res, r);
fi;
else
p := Position(str, '\\', p);
fi;
od;
return res;
end);
## here are more functions which are used by the `default' handler
## functions (see their use below).
#############################################################################
##
#F HELP_CHAPTER_INFO( <book>, <chapter> ) . . . . get info about a chapter
##
## this is a helper function for `HELP_SHOW_SECTIONS'
BindGlobal("HELP_CHAPTER_BEGIN", Immutable("\\Chapter"));
BindGlobal("HELP_SECTION_BEGIN", Immutable("\\Section"));
BindGlobal("HELP_FAKECHAP_BEGIN", Immutable("%\\FakeChapter"));
BindGlobal("HELP_PRELCHAPTER_BEGIN", Immutable("\\PreliminaryChapter"));
InstallGlobalFunction(HELP_CHAPTER_INFO, function( book, chapter )
local info, filename, stream, poss, secnum, pos, line;
# get the book info
info := HELP_BOOK_INFO(book);
# read in a chapter
if not IsBound(info.secposs[chapter]) then
filename := Filename( info.directories, info.filenames[chapter] );
if filename = fail then
Error("help file ", info.filenames[chapter], " for help book '", book.bookname, "' not found");
return fail;
fi;
stream := StringStreamInputTextFile(filename);
if stream = fail then
Error("help file ", filename, " does not exist or is not readable");
return fail;
fi;
poss := [];
secnum := 0;
repeat
pos := PositionStream(stream);
line := ReadLine(stream);
if line <> fail then
if MATCH_BEGIN( line, HELP_SECTION_BEGIN ) then
secnum := secnum + 1;
poss[secnum] := pos;
elif MATCH_BEGIN( line, HELP_CHAPTER_BEGIN ) or
MATCH_BEGIN( line, HELP_PRELCHAPTER_BEGIN ) then
info.chappos[chapter] := pos;
elif MATCH_BEGIN( line, HELP_FAKECHAP_BEGIN ) then
info.chappos[chapter] := pos;
fi;
fi;
until IsEndOfStream(stream);
CloseStream(stream);
info.secposs[chapter] := Immutable(poss);
fi;
# return the info
return [ info.chappos[chapter], info.secposs[chapter] ];
end);
InstallGlobalFunction(HELP_PRINT_SECTION_URL, function(arg)
local book, hnb, d, pos, chapter, section, fn, path;
book := HELP_BOOK_INFO(arg[1]);
if book=fail then
Error("this book does not exist");
fi;
hnb := HELP_KNOWN_BOOKS;
# the path as string
book := hnb[2][Position(hnb[1], SIMPLE_STRING(book.bookname))][3];
if IsDirectory(book) then
d := book![1];
else
d := book;
fi;
if Last(d) = '/' then
d := d{[1..Length(d)-1]};
fi;
# find `doc'
pos:=Length(d)-2;
while pos>0 and (d[pos]<>'d' or d[pos+1]<>'o' or d[pos+2]<>'c') do
pos:=pos-1;
od;
#see if it is only `doc', if yes skip
if pos+2=Length(d) then
# it ends in doc, replace `doc' by `htm'
d:=Concatenation(d{[1..pos-1]},"htm");
else
# insert htm after doc
d:=Concatenation(d{[1..pos+2]},"/htm",d{[pos+3..Length(d)]});
fi;
chapter:=String(arg[2]);
while Length(chapter)<3 do
chapter:=Concatenation("0",chapter);
od;
section:=arg[3];
# first try to find a file-per-chapter .htm file
fn := Concatenation("CHAP", chapter, ".htm");
if IsDirectory(book) then
path := Filename([Directory(d)], fn);
else
path := Filename(List(GAPInfo.RootPaths, Directory),
Concatenation(d, "/", fn));
fi;
if path = fail then
# now try to find a file-per-section .htm file
section:=String(section);
while Length(section)<3 do
section:=Concatenation("0",section);
od;
fn := Concatenation("C", chapter, "S", section, ".htm");
if IsDirectory(book) then
path := Filename([Directory(d)], fn);
else
path := Filename(List(GAPInfo.RootPaths, Directory),
Concatenation(d, "/", fn));
fi;
fi;
if path <> fail and not IsString(section) and section>0 then
# we must have found a file-per-chapter .htm file above
section:=String(section);
while Length(section)<3 do
section:=Concatenation("0",section);
od;
path:=Concatenation(path,"#SECT",section);
fi;
return path;
end);
# now the handlers
atomic HELP_REGION do # acquire lock for HELP_BOOK_HANDLER
## the default ReadSix function for books in gapmacro format
## (need to parse a text file in this case, this function still
## looks pretty long winded)
HELP_BOOK_HANDLER.default.ReadSix := function(stream)
local fname, readNumber, pos, n, c, s, x, f, line, subline,
c1, c2, i, name, num, s1, sec, s2, j, x1,
w, f1, res, toc;
# name of file
fname := ShallowCopy(stream![2]);
# numbers
readNumber := function( str )
local n;
while pos<=Length(str) and str[pos] = ' ' do
pos := pos+1;
od;
n := 0;
while pos<=Length(str) and str[pos] <> '.' do
n := n * 10 + (Position("0123456789", str[pos])-1);
pos := pos+1;
od;
pos := pos+1;
return n;
end;
c := []; s := []; x := []; f := [];
repeat
line := ReadLine(stream);
if line <> fail then
RemoveCharacters(line, "\r");
subline:=line{[3..Length(line)-1]} ;
if line[1] = 'C' then
Add( c, subline);
elif line[1] = 'S' then
Add( s, subline);
elif line[1] = 'I' then
Add( x, subline);
elif line[1] = 'F' then
if ForAll([Maximum(Length(f)-10, 1)..Length(f)],
i-> f[i] <> subline) then
Add( f, subline);
fi;
else
Print( "#W corrupted 'manual.six': ", line );
Print( "#W (in stream: ", stream, ")\n");
break;
fi;
fi;
until IsEndOfStream(stream);
CloseStream(stream);
# parse the chapters information
c1 := [];
c2 := [];
for line in c do
# first the filename
pos := Position( line, ' ' );
name := line{[1..pos-1]};
# then the chapter number
num := readNumber(line);
# then the chapter name
while pos <= Length(line) and line[pos] = ' ' do pos := pos+1; od;
# store that information in <c1> and <c2>
c1[num] := name;
c2[num] := line{[pos..Length(line)]};
od;
# parse the sections information
s1 := List( c1, x -> [] );
for line in s do
# chapter and section number
pos := 1;
num := readNumber(line);
sec := readNumber(line);
# then the section name
while pos < Length(line) and line[pos] = ' ' do pos := pos+1; od;
# store the information in <s1>
s1[num][sec] := line{[pos..Length(line)]};
if pos = Length(line) then
Print("#W Empty section name ", num, ".", sec,"\n");
fi;
od;
# convert sections and chapters to lower case
s2 := [];
for i in [ 1 .. Length(s1) ] do
for j in [ 1 .. Length(s1[i]) ] do
Add( s2, [ s1[i][j], SIMPLE_STRING(s1[i][j]), "S", i, j ] );
od;
od;
for i in [ 1 .. Length(c2) ] do
Add( s2, [ c2[i], SIMPLE_STRING(c2[i]), "C", i, 0 ] );
od;
# parse the index information
x1 := [];
for line in x do
# chapter and section number
pos := 1;
num := readNumber(line);
sec := readNumber(line);
# then the index entry
while pos <= Length(line) and line[pos] = ' ' do pos := pos+1; od;
# store the information in <x1>
w := line{[pos..Length(line)]};
Add( x1, [ w, SIMPLE_STRING(w), "I", num, sec ] );
od;
# parse the function information
f1 := [];
for line in f do
# chapter and section number
pos := 1;
num := readNumber(line);
sec := readNumber(line);
# then the index entry
while pos <= Length(line) and line[pos] = ' ' do pos := pos+1; od;
# store the information in <x1>
w := line{[pos..Length(line)]};
Add( f1, [ w, SIMPLE_STRING(w), "F", num, sec ] );
od;
res := rec(
formats := ["text", "url"],
filenames := Immutable(c1),
# the following three are not made immutable to allow change of names (if it
# is found out that several sections have the same name).
chapters := c2,
sections := s1,
secposs := [],
chappos := [],
entries := Concatenation(s2, x1, f1)
);
# trying to read page numbers from manual.toc file
fname{[Length(fname)-2..Length(fname)]} := "toc";
toc := GapLibToc2Gap(fname);
if toc <> fail then
res.pagenumbers := toc;
fname{[Length(fname)-2..Length(fname)]} := "dvi";
if IsExistingFile( fname ) = true then
res.dvifile := ShallowCopy(fname);
Add(res.formats, "dvi");
fi;
fname{[Length(fname)-2..Length(fname)]} := "pdf";
if IsExistingFile( fname ) = true then
res.pdffile := ShallowCopy(fname);
Add(res.formats, "pdf");
fi;
fi;
res.directories := [ Directory(fname{[1..Length(fname)-10]}) ];
return res;
end;
HELP_BOOK_HANDLER.default.ShowChapters := function( book )
local info, chap;
info := HELP_BOOK_INFO(book);
if info = fail then
Print( "Help: unknown book \"", book, "\"\n" );
return false;
fi;
# print the chapters
chap := ShallowCopy(info.chapters);
Sort(chap);
return Concatenation(
[ FILLED_LINE( "Table of Chapters", info.bookname, '_' ) ],
chap,
[ "" ]
);
end;
HELP_BOOK_HANDLER.default.ShowSections := function( book )
local info, lines, chap, sec;
info := HELP_BOOK_INFO(book);
if info = fail then
Print( "Help: unknown book \"", book, "\"\n" );
return false;
fi;
# print the sections
lines := [ FILLED_LINE( "Table of Sections", info.bookname, '_' ) ];
for chap in [ 1 .. Length(info.chapters) ] do
Add( lines, info.chapters[chap] );
for sec in [ 1 .. Length(info.sections[chap]) ] do
Add(lines,Concatenation(" ",info.sections[chap][sec]));
od;
od;
Add( lines, "" );
return lines;
end;
HELP_BOOK_HANDLER.default.MatchPrevChap := function(book, entrynr)
local info, chnr, nr;
info := HELP_BOOK_INFO(book);
chnr := info.entries[entrynr][4];
if info.entries[entrynr][3] <> "C" or chnr = 1 then
nr := First([1..Length(info.entries)], i-> info.entries[i]{[3,4]} =
["C", chnr]);
else
nr := First([1..Length(info.entries)], i-> info.entries[i]{[3,4]} =
["C", chnr-1]);
fi;
return [info, nr];
end;
HELP_BOOK_HANDLER.default.MatchNextChap := function(book, entrynr)
local info, chnr, nr;
info := HELP_BOOK_INFO(book);
chnr := info.entries[entrynr][4] + 1;
nr := First([1..Length(info.entries)], i-> info.entries[i]{[3,4]} =
["C", chnr]);
return [info, nr];
end;
HELP_BOOK_HANDLER.default.MatchPrev := function(book, entrynr)
local info, entry, chnr, secnr, nr;
info := HELP_BOOK_INFO(book);
entry := info.entries[entrynr];
chnr := entry[4];
secnr := entry[5];
if secnr > 1 then
nr := First([1..Length(info.entries)], i-> info.entries[i]{[3,4,5]}
= ["S", chnr, secnr-1]);
elif secnr = 1 then
nr := First([1..Length(info.entries)], i-> info.entries[i]{[3,4]}
= ["C", chnr]);
elif secnr = 0 then
nr := First(Reversed([1..Length(info.entries)]), i->
info.entries[i][3] = "S" and
info.entries[i][4] = chnr-1);
fi;
return [info, nr];
end;
HELP_BOOK_HANDLER.default.MatchNext := function(book, entrynr)
local info, entry, chnr, secnr, nr;
info := HELP_BOOK_INFO(book);
entry := info.entries[entrynr];
chnr := entry[4];
secnr := entry[5];
nr := First([1..Length(info.entries)], i-> info.entries[i]{[3,4,5]}
= ["S", chnr, secnr+1]);
if nr = fail then
nr := First([1..Length(info.entries)], i-> info.entries[i]{[3,4,5]}
= ["C", chnr+1, 0]);
fi;
return [info, nr];
end;
##
## The default search for matches is easy, just MATCH_BEGIN (if frombegin
## = true), resp. IS_SUBSTRING is used for content of second position in
## .entries. The topic is assumed to be normalized already.
##
HELP_BOOK_HANDLER.default.SearchMatches := function (book, topic, frombegin)
local info, exact, match, rank, m, i;
info := HELP_BOOK_INFO(book);
exact := [];
match := [];
rank := [];
for i in [1..Length(info.entries)] do
if topic=info.entries[i][2] then
Add(exact, i);
elif frombegin = true then
m := MATCH_BEGIN_COUNT(info.entries[i][2], topic);
if m >= 0 then
Add(match, i);
Add(rank, -m);
fi;
else
if IS_SUBSTRING(info.entries[i][2], topic) then
Add(match, i);
fi;
fi;
od;
# sort by rank if applicable
if frombegin = true then
SortParallel(rank, match);
fi;
return [exact, match];
end;
## the `default' handler for HelpData delegates to functions from above
HELP_BOOK_HANDLER.default.HelpData := function(book, entrynr, type)
local info, entry, chnr, secnr, pos, r;
info := HELP_BOOK_INFO(book);
entry := info.entries[entrynr];
chnr := entry[4];
secnr := entry[5];
# we handle the special type "ref" for cross references first
if type = "ref" then
return HELP_BOOK_HANDLER.HelpDataRef(info, entrynr);
fi;
if type = "secnr" then
r := "";
Append(r, String(chnr));
if secnr <> 0 then
Add(r, '.');
Append(r, String(secnr));
fi;
return [[chnr, secnr, 0], r];
fi;
if not type in info.formats then
return fail;
fi;
if type = "text" then
if entry[3] = "F" then
return HELP_PRINT_SECTION_TEXT(info, chnr, secnr, entry[1]);
else
return HELP_PRINT_SECTION_TEXT(info, chnr, secnr);
fi;
fi;
if type = "url" then
return HELP_PRINT_SECTION_URL(info, chnr, secnr);
fi;
if type = "dvi" then
pos := PositionSorted(info.pagenumbers, [[chnr, secnr],-1]);
if IsBound(info.pagenumbers[pos]) and info.pagenumbers[pos][1] =
[chnr, secnr] then
return rec(file := info.dvifile, page := info.pagenumbers[pos][2]);
fi;
fi;
if type = "pdf" then
pos := PositionSorted(info.pagenumbers, [[chnr, secnr],-1]);
if IsBound(info.pagenumbers[pos]) and info.pagenumbers[pos][1] =
[chnr, secnr] then
return rec(file := info.pdffile, page := info.pagenumbers[pos][2]);
fi;
fi;
return fail;
end;
HELP_BOOK_HANDLER.default.SubsectionNumber := function(info, entrynr)
return info.entries[entrynr]{[4,5]};
end;
od; # end of atomic HELP_REGION
[ Dauer der Verarbeitung: 0.7 Sekunden
(vorverarbeitet)
]
|