GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W gapbibl.g GAP 4 package `browse' Thomas Breuer
##
#Y Copyright (C) 2006, Lehrstuhl D für Mathematik, RWTH Aachen, Germany
##
## <#GAPDoc Label="Bibliography_section">
## <Section Label="sec:gapbibl">
## <Heading>Overview of Bibliographies</Heading>
##
## The function <Ref Func="BrowseBibliography"/> can be used to turn the
## contents of bibliography files in BibTeX or BibXMLext format
## (see <Ref Sect="The BibXMLext Format" BookName="gapdoc"/>)
## into a Browse table,
## such that one can scroll in the list, search for entries, sort by year,
## sort and categorize by authors etc.
## <P/>
## The default bibliography used by <Ref Func="BrowseBibliography"/> is the
## bibliography of &GAP; related publications,
## see <Cite Key="GAPBibliography"/>.
## The &Browse; package contains a (perhaps outdated) version of this
## bibliography.
## One can get an updated version as follows.
## <P/>
## <C>wget -N http://www.gap-system.org/Doc/Bib/gap-publishednicer.bib</C>
## <P/>
## The columns of the Browse table that is shown by
## <Ref Func="BrowseBibliography"/> can be customized,
## two examples for that are given by the functions
## <Ref Func="BrowseBibliographySporadicSimple" BookName="atlasrep"/>
## and <Ref Func="BrowseBibliographyGapPackages"/>.
## <P/>
## The function <Ref Func="BrowseMSC"/> shows an overview of the
## AMS Mathematics Subject Classification codes.
##
## <#Include Label="BrowseBibliography">
## <#Include Label="BrowseBibliographyGapPackages">
## <#Include Label="BrowseMSC">
##
## </Section>
## <#/GAPDoc>
##
if not IsBound( HeuristicTranslationsLaTeX2XML ) then
HeuristicTranslationsLaTeX2XML:= 0;
fi;
#############################################################################
##
#F BrowseData.LoadMSCData()
##
## reads the files `bibl/mscdata2010.txt' and `bibl/mscdata_old.txt' of the
## package and sets the components of the records `BrowseData.MSCData' and
## `BrowseData.MSCData_old'.
##
## The component names of `BrowseData.MSCData' are the valid MSC codes,
## the values are their descriptions according to the file
## `classifications2010.pdf'.
##
## The component names of `BrowseData.MSCData_old' are the outdated MSC
## codes, the values are their descriptions.
##
## If one of the source files is not available then `false' is returned,
## otherwise `true'.
##
BrowseData.LoadMSCData:= function()
local file, len, pos1, pos2, pos3;
if not IsBound( BrowseData.MSCData ) then
file:= Filename( DirectoriesPackageLibrary( "browse", "bibl" ),
"mscdata2010.txt" );
if file = fail then
return false;
fi;
file:= StringFile( file );
len:= Length( file );
BrowseData.MSCData:= rec();
pos1:= 1;
pos2:= Position( file, '\n' );
while pos2 <> fail do
if pos1 + 5 < len and file[ pos1 + 5 ] = ' '
and file[ pos1 ] <> '%' then
BrowseData.MSCData.( file{ [ pos1 .. pos1+4 ] } ):=
file{ [ pos1+6 .. pos2-1 ] };
fi;
pos1:= pos2+1;
pos2:= Position( file, '\n', pos2 );
od;
fi;
if not IsBound( BrowseData.MSCData_old ) then
file:= Filename( DirectoriesPackageLibrary( "browse", "bibl" ),
"mscdata_old.txt" );
if file = fail then
return false;
fi;
file:= StringFile( file );
len:= Length( file );
BrowseData.MSCData_old:= rec();
pos1:= 1;
pos2:= Position( file, '\n' );
while pos2 <> fail do
if pos1 + 5 < len and file[ pos1 + 5 ] = ' '
and file[ pos1 ] <> '%' then
BrowseData.MSCData_old.( file{ [ pos1 .. pos1+4 ] } ):=
file{ [ pos1+6 .. pos2-1 ] };
fi;
pos1:= pos2+1;
pos2:= Position( file, '\n', pos2 );
od;
fi;
return true;
end;
#############################################################################
##
#F BrowseData.CompareMSCcode( <mrcode1>, <mrcode2> )
##
## The codes have length five,
## the first two entries are digits,
## the third entry is '-' (comes first) or an uppercase letter,
## the last two entries are "XX" or "xx" (come first) or two digits.
## So we can replace 'x' and 'X' by '*' (these two cannot compete) and then
## compare with GAP's standard function '\<'.
##
BrowseData.CompareMSCcode:= function( mrcode1, mrcode2 )
if mrcode1[4] in "xX" then
mrcode1:= Concatenation( mrcode1{ [ 1 .. 3 ] }, "**" );
fi;
if mrcode2[4] in "xX" then
mrcode2:= Concatenation( mrcode2{ [ 1 .. 3 ] }, "**" );
fi;
return mrcode1 < mrcode2;
end;
#############################################################################
##
#F BrowseData.MSCDescription( <mrclass> )
##
BrowseData.MSCDescription:= function( mrclass )
local descr, pos;
# Secondary classifications are enclosed in brackets.
mrclass:= ReplacedString( mrclass, "(", "" );
mrclass:= ReplacedString( mrclass, ")", "" );
# If the descriptions are not yet loaded then read the file.
if not BrowseData.LoadMSCData() then
return [ mrclass, "MSC descriptions not available" ];
elif IsBound( BrowseData.MSCData.( mrclass ) ) then
return [ mrclass, BrowseData.MSCData.( mrclass ) ];
elif IsBound( BrowseData.MSCData_old.( mrclass ) ) then
descr:= BrowseData.MSCData_old.( mrclass );
pos:= PositionNthOccurrence( descr, '|', 2 );
return [ Concatenation( mrclass, "*" ),
descr{ [ pos+2 .. Length( descr ) ] } ];
else
return [ mrclass, "unknown MSC code" ];
fi;
end;
#############################################################################
##
#F BrowseData.MSCString( <mrclasses> )
##
BrowseData.MSCString:= function( mrclasses )
local mscinfo, class, val, info, render, result, i;
if not BrowseData.LoadMSCData() then
return "MSC descriptions not available";
fi;
mscinfo:= [];
for class in SplitString( mrclasses, " " ) do
class:= ReplacedString( class, "(", "" );
class:= ReplacedString( class, ")", "" );
if Length( class ) = 5 then
# There is always the first level.
val:= BrowseData.MSCDescription(
Concatenation( class{ [ 1, 2 ] }, "-XX" ) );
info:= [ Concatenation( String( val[1], -11 ), val[2] ) ];
if class[4] <> 'X' then
# There is another level.
if class[3] = '-' then
# There is level 3 but not 2.
info[2]:= "";
val:= BrowseData.MSCDescription( class );
info[3]:= Concatenation( " ", String( val[1], -7 ), val[2] );
elif class[4] = 'x' then
# There is level 2 but not 3.
val:= BrowseData.MSCDescription( class );
info[2]:= Concatenation( " ", String( val[1], -9 ), val[2] );
info[3]:= "";
else
# There are levels 2 and 3.
val:= BrowseData.MSCDescription(
Concatenation( class{ [ 1 .. 3 ] }, "xx" ) );
info[2]:= Concatenation( " ", String( val[1], -9 ), val[2] );
val:= BrowseData.MSCDescription( class );
info[3]:= Concatenation( " ", String( val[1], -7 ), val[2] );
fi;
fi;
Add( mscinfo, info );
fi;
od;
Sort( mscinfo );
render:= l -> JoinStringsWithSeparator( Filtered( l, x -> x <> "" ), "\n" );
result:= render( mscinfo[1] );
for i in [ 2 .. Length( mscinfo ) ] do
Append( result, "\n" );
if mscinfo[i][1] = mscinfo[i-1][1] then
if mscinfo[i][2] = mscinfo[i-1][2] then
Append( result, mscinfo[i][3] );
else
Append( result, render( mscinfo[i]{ [ 2, 3 ] } ) );
fi;
else
Append( result, render( mscinfo[i] ) );
fi;
od;
return result;
end;
#############################################################################
##
#F BrowseMSC()
##
## <#GAPDoc Label="BrowseMSC">
## <ManSection>
## <Func Name="BrowseMSC" Arg=""/>
##
## <Returns>
## nothing.
## </Returns>
##
## <Description>
## This function shows the currently valid MSC codes in a browse table that
## is categorized by the <C>..-XX</C> and the <C>...xx</C> codes.
## (Use <B>X</B> for expanding all categories or <B>x</B> for expanding the
## currently selected category.)
## Due to the categorization, only two columns of the table are visible,
## showing the codes and their descriptions.
## </Description>
## </ManSection>
## <#/GAPDoc>
##
BindGlobal( "BrowseMSC", function()
local winwidth, mrwidth, descrwidth, matrix, listtosort,
mrclass, middle, top, descr, shortdescr, pos, r;
if not BrowseData.LoadMSCData() then
return;
fi;
if not IsBound( BrowseData.MSCDataDescr ) then
BrowseData.MSCDataDescr:= rec();
fi;
winwidth:= NCurses.getmaxyx( 0 )[2];
mrwidth:= 5;
descrwidth:= winwidth - mrwidth - 7;
matrix:= [];
listtosort:= [];
for mrclass in RecNames( BrowseData.MSCData ) do
if mrclass[3] = '-' then
middle:= Concatenation( mrclass{ [ 1 .. 3 ] }, "XX" );
else
middle:= Concatenation( mrclass{ [ 1 .. 3 ] }, "xx" );
fi;
top:= Concatenation( mrclass{ [ 1, 2 ] }, "-XX" );
descr:= BrowseData.SimplifiedString(
HeuristicTranslationsLaTeX2XML.Apply(
BrowseData.MSCData.( mrclass ) ) );
if mrclass[4] in "xX" then
shortdescr:= descr;
pos:= PositionSublist( shortdescr, " {" );
if pos <> fail then
shortdescr:= shortdescr{ [ 1 .. pos - 1 ] };
fi;
pos:= PositionSublist( shortdescr, " [" );
if pos <> fail then
shortdescr:= shortdescr{ [ 1 .. pos - 1 ] };
fi;
BrowseData.MSCDataDescr.( mrclass ):= shortdescr;
fi;
descr:= rec( rows:= SplitString( ReplacedString( FormatParagraph(
descr, descrwidth, "left" ), "\n", " \n" ), "\n" ),
align:= "tl" );
Add( matrix, [ mrclass, middle, top, descr ] );
Add( listtosort, mrclass );
od;
SortParallel( listtosort, matrix, BrowseData.CompareMSCcode );
# Create the default table.
r:= rec(
work:= rec(
align:= "tl",
header:= t -> BrowseData.HeaderWithRowCounter( t,
"MSC classification",
Length( matrix ) ),
widthCol:= [ , mrwidth,, mrwidth,, mrwidth,, descrwidth ],
sepRow:= "-",
sepCol:= [ "| ", " | ", " | ", " | ", " |" ],
SpecialGrid:= BrowseData.SpecialGridLineDraw,
main:= matrix,
CategoryValues:= function( t, i, j )
if j = 2 then
return [ Concatenation( t.work.main[ i/2 ][ j/2 ], " ",
Concatenation( t.work.main[ i/2 ][3].rows ) ) ];
elif j in [ 4, 6 ] then
return [ Concatenation( t.work.main[ i/2 ][ j/2 ], " ",
BrowseData.MSCDataDescr.( t.work.main[ i/2 ][ j/2 ] ) ) ];
else
return [ Concatenation( t.work.main[ i/2 ][ j/2 ].rows ) ];
fi;
end,
),
dynamic:= rec(
initialSteps:= "scrscXscrsc",
),
);
NCurses.BrowseGeneric( r );
end );
#############################################################################
##
#F BrowseBibliography( [<bibfiles>] )
##
## <#GAPDoc Label="BrowseBibliography">
## <ManSection>
## <Func Name="BrowseBibliography" Arg="[bibfiles]"/>
##
## <Returns>
## a record as returned by
## <Ref Func="ParseBibXMLExtFiles" BookName="gapdoc"/>.
## </Returns>
##
## <Description>
## This function shows the list of bibliography entries in the files
## given by <A>bibfiles</A>, which may be a string or a list of strings
## (denoting a filename or a list of filenames, respectively)
## or a record (see below for the supported components).
## <P/>
## If no argument is given then the file <F>bibl/gap-publishednicer.bib</F>
## in the &Browse; package directory is taken,
## and <C>"GAP Bibliography"</C> is used as the header.
## <P/>
## Another perhaps interesting data file that should be available in the
## &GAP; distribution is <F>doc/manualbib.xml</F>.
## This file can be located as follows.
## <P/>
## <Example><![CDATA[
## gap> file:= Filename( DirectoriesLibrary( "doc" ), "manualbib.xml" );;
## ]]></Example>
## <P/>
## Both &BibTeX; format and the &XML; based extended format provided by the
## &GAPDoc; package are supported by <Ref Func="BrowseBibliography"/>, see
## Chapter <Ref Chap="Utilities for Bibliographies" BookName="gapdoc"/>.
## <P/>
## In the case of &BibTeX; format input, first a conversion to the extended
## format takes place, via <Ref Func="StringBibAsXMLext" BookName="gapdoc"/>
## and <Ref Func="ParseBibXMLextString" BookName="gapdoc"/>.
## Note that syntactically incorrect entries are rejected in this conversion
## –this is signaled with <Ref Var="InfoBibTools" BookName="gapdoc"/>
## warnings– and that only a subset of the possible &LaTeX; markup is
## recognized –other markup appears in the browse table except that
## the leading backslash is removed.
## <P/>
## In both cases of input, the problem arises
## that in visual mode, currently we can show only ASCII characters
## (and the symbols in <C>NCurses.lineDraw</C>, but these are handled
## differently, see Section <Ref Subsect="ssec:ncursesLines"/>).
## Therefore, we use the function
## <Ref Func="SimplifiedUnicodeString" BookName="gapdoc"/>
## for replacing other unicode characters by ASCII text.
## <P/>
## The return value is a record as returned by
## <Ref Func="ParseBibXMLExtFiles" BookName="gapdoc"/>,
## its <C>entries</C> component corresponds to the bibliography entries
## that have been <Q>clicked</Q> in visual mode.
## This record can be used as input for
## <Ref Func="WriteBibFile" BookName="gapdoc"/> or
## <Ref Func="WriteBibXMLextFile" BookName="gapdoc"/>,
## in order to produce a bibliography file,
## or it can be used as input for
## <Ref Func="StringBibXMLEntry" BookName="gapdoc"/>,
## in order to produce strings from the entries, in various formats.
## <P/>
## The full functionality of the function
## <Ref Func="NCurses.BrowseGeneric"/> is available.
## <P/>
## <Example><![CDATA[
## gap> # sort and categorize by year, scroll down, expand a category row
## gap> BrowseData.SetReplay( "scrrscsedddddxdddddQ" );
## gap> BrowseBibliography();;
## gap> # sort & categorize by authors, expand all category rows, scroll down
## gap> BrowseData.SetReplay( "scscXseddddddQ" );
## gap> BrowseBibliography();;
## gap> # sort and categorize by journal, search for a journal name, expand
## gap> BrowseData.SetReplay( Concatenation( "scrrrsc/J. Algebra",
## > [ NCurses.keys.ENTER ], "nxdddQ" ) );
## gap> BrowseBibliography();;
## gap> BrowseData.SetReplay( false );
## ]]></Example>
## <P/>
## <E>Implementation remarks</E>:
## The browse table has a dynamic header (showing the number of entries,
## which can vary when the table is restricted), no footer and row labels;
## one row of column labels is given by the descriptions of the table
## columns (authors, title, year, journal, MSC code).
## <P/>
## Row and column separators are drawn as grids
## (cf. <Ref Func="NCurses.Grid"/>) composed from the special characters
## described in Section <Ref Subsect="ssec:ncursesLines"/>,
## using the component <C>work.SpecialGrid</C> of the browse table,
## see <Ref Var="BrowseData"/>.
## <P/>
## For categorizing by authors (or by MSC codes), the sort parameter
## <C>"split rows on categorizing"</C> is set to <C>"yes"</C>,
## so the authors (codes) are distributed to different category rows,
## hence each entry appears once for each of its authors (or its MSC codes)
## in the categorized table.
## When a data row or an entry in a data row is selected,
## <Q>click</Q> adds the corresponding bibliographhy entry to the result.
## <P/>
## The width of the title column is preset; usually titles are too long for
## one line, and the contents of this column is formatted as a paragraph,
## using the function <Ref Func="FormatParagraph" BookName="gapdoc"/>.
## For the authors and journal columns, maximal widths are prescribed,
## and <Ref Func="FormatParagraph" BookName="gapdoc"/> is used for longer
## entries.
## <P/>
## For four columns, the sort parameters are defined as follows:
## The <E>authors</E> and <E>MSC code</E> columns do not become hidden
## when the table is categorized according to this column,
## sorting by the <E>year</E> yields a <E>de</E>scending order,
## and the category rows arising from these columns and the <E>journal</E>
## column show the numbers of the data rows that belong to them.
## <P/>
## Those standard modes in <Ref Var="BrowseData"/> where an entry or a row
## of the table is selected have been extended by three new actions,
## which open a pager showing the &BibTeX;, HTML, and Text format of the
## selected entry, respectively.
## The corresponding user inputs are the <B>vb</B>, <B>vh</B>,
## and <B>vt</B>.
## If the <E>MSC code</E> column is available then also the user input
## <B>vm</B> is admissible; it opens a pager showing the descriptions of the
## MSC codes attached to the selected entry.
## <P/>
## This function requires some of the utilities provided by the
## &GAP; package <Package>GAPDoc</Package>
## (see <Cite Key="GAPDoc"/>),
## such as <Ref Func="FormatParagraph" BookName="gapdoc"/>,
## <Ref Func="NormalizeNameAndKey" BookName="gapdoc"/>,
## <Ref Func="NormalizedNameAndKey" BookName="gapdoc"/>,
## <Ref Func="ParseBibFiles" BookName="gapdoc"/>,
## <Ref Func="ParseBibXMLextFiles" BookName="gapdoc"/>,
## <Ref Func="ParseBibXMLextString" BookName="gapdoc"/>,
## <Ref Func="RecBibXMLEntry" BookName="gapdoc"/>, and
## <Ref Func="StringBibAsXMLext" BookName="gapdoc"/>.
## <P/>
## The code can be found in the file <F>app/gapbibl.g</F> of the package.
## <P/>
## The browse table can be customized by entering a record as the argument
## of <Ref Func="BrowseBibliography"/>,
## with the following supported components.
## <List>
## <Mark><C>files</C></Mark>
## <Item>
## a nonempty list of filenames containing the data to be shown;
## there is no default for this component.
## </Item>
## <Mark><C>filesshort</C></Mark>
## <Item>
## a list of the same length as the <C>files</C> component,
## the entries are strings which are shown in the <C>"sourcefilename"</C>
## column of the table (if this column is present);
## the default is the list of filenames.
## </Item>
## <Mark><C>filecontents</C></Mark>
## <Item>
## a list of the same length as the <C>files</C> component,
## the entries are strings which are shown as category values when the
## table is categorized by the <C>"sourcefilename"</C> column;
## the default is the list of filenames.
## </Item>
## <Mark><C>header</C></Mark>
## <Item>
## is the constant part of the header shown above the browse table,
## the default is the first filename.
## </Item>
## <Mark><C>columns</C></Mark>
## <Item>
## is a list of records that are valid as the second argument of
## <Ref Func="DatabaseAttributeAdd"/>,
## where the first argument is a database id enumerator created from the
## bibliography entries in the files in question.
## Each entry (and also the corresponding identifier) of this database id
## enumerator is a list of records obtained from
## <Ref Func="ParseBibXMLextFiles" BookName="gapdoc"/> and
## <Ref Func="RecBibXMLEntry" BookName="gapdoc"/>,
## or from <Ref Func="ParseBibFiles" BookName="gapdoc"/>,
## such that the list elements are regarded as equal, in the sense that
## their fingerprints (see below) are equal.
## The records in the <C>columns</C> list are available for constructing
## the desired browse table, the actual appearance is controlled by the
## <C>choice</C> component described below.
## Columns showing authors, title, year, journal, MSC code, and filename
## are predefined and need not be listed here.
## </Item>
## <Mark><C>choice</C></Mark>
## <Item>
## a list of strings denoting the <C>identifier</C> components of those
## columns that shall actually be shown in the table, the default is
## <C>[ "authors", "title", "year", "journal", "mrclass" ]</C>.
## </Item>
## <Mark><C>fingerprint</C></Mark>
## <Item>
## a list of strings denoting component names of the entries of the
## database id enumerator that is constructed from the data (see above);
## two data records are regarded as equal if the values of these
## components are equal; the default is
## <C>[ "mrnumber", "title", "authorAsList", "editorAsList", "author" ]</C>.
## </Item>
## <Mark><C>sortKeyFunction</C></Mark>
## <Item>
## either <K>fail</K> or a function that takes a record as returned by
## <Ref Func="RecBibXMLEntry" BookName="gapdoc"/> and returns a list
## that is used for comparing and thus sorting the records;
## the default is <K>fail</K>, which means that the rows of the table
## appear in the same ordering as in the source files.
## </Item>
## </List>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
BindGlobal( "BrowseBibliography", function( arg )
local columns, choice, fingerprint, sortKeyFunction, files, filesshort,
filecontents, header, extend_entries, infocache, entries, mapping,
entities, file, parse, r, result, identifier, ids, idenum, entry,
id, pos, authorswidth, l, titlewidth, journalwidth, mrclasswidth,
modes, newactions, showaction, mode, sel_action, table, ret;
# Get the arguments.
columns:= [];
choice:= [ "authors", "title", "year", "journal", "mrclass" ];
fingerprint:= [ "mrnumber", "title", "authorAsList", "editorAsList",
"author", "year", "pages" ];
sortKeyFunction:= fail;
if Length( arg ) = 0 then
files:= [ Filename( DirectoriesPackageLibrary( "browse", "bibl" ),
"gap-publishednicer.bib" ) ];
filesshort:= [ BrowseData.StrippedPath( files[1] ) ];
filecontents:= [ "gap-publishednicer.bib" ];
header:= "GAP Bibliography";
elif Length( arg ) = 1 and IsString( arg[1] ) then
files:= arg;
filesshort:= [ BrowseData.StrippedPath( files[1] ) ];
filecontents:= filesshort;
header:= files[1];
elif Length( arg ) = 1 and IsList( arg[1] )
and not IsEmpty( arg[1] )
and ForAll( arg[1], IsString ) then
files:= arg[1];
filesshort:= List( files, BrowseData.StrippedPath );
filecontents:= filesshort;
header:= files[1];
if 1 < Length( files ) then
header:= Concatenation( header, ", ..." );
fi;
elif Length( arg ) = 1 and IsRecord( arg[1] )
and IsBound( arg[1].files ) then
files:= arg[1].files;
if not IsList( files ) or IsEmpty( files )
or not ForAll( files, IsString ) then
Error( "<files> must be a nonempty list of strings" );
fi;
if IsBound( arg[1].filesshort ) then
filesshort:= arg[1].filesshort;
else
filesshort:= List( files, BrowseData.StrippedPath );
fi;
if IsBound( arg[1].filecontents ) then
filecontents:= arg[1].filecontents;
else
filecontents:= filesshort;
fi;
if IsBound( arg[1].header ) and IsString( arg[1].header ) then
header:= arg[1].header;
elif Length( files ) = 1 then
header:= files[1];
else
header:= Concatenation( files[1], ", ..." );
fi;
if IsBound( arg[1].columns ) then
columns:= ShallowCopy( arg[1].columns );
fi;
if IsBound( arg[1].choice ) then
choice:= ShallowCopy( arg[1].choice );
fi;
if IsBound( arg[1].fingerprint ) then
fingerprint:= ShallowCopy( arg[1].fingerprint );
fi;
if IsBound( arg[1].sortKeyFunction ) then
sortKeyFunction:= arg[1].sortKeyFunction;
fi;
else
Error( "usage: BrowseBibliography( [<bibfiles>] )" );
fi;
extend_entries:= function( entry, strings, file )
local r;
r:= RecBibXMLEntry( entry, "Text", strings );
r.sourcefilename:= file;
Add( entries, [ r, entry ] );
end;
# Switch off GAPDoc info messages.
infocache:= List( [ InfoGAPDoc, InfoBibTools ],
c -> rec( class:= c, oldlevel:= InfoLevel( c ) ) );
Perform( infocache, function( r ) SetInfoLevel( r.class, 0 ); end );
# Parse the data files.
entries:= [];
mapping:= [];
entities:= [];
for file in files do
if 3 < Length( file )
and file{ [ Length( file )-3 .. Length( file ) ] } = ".xml" then
# Convert the XML structure to records,
# memorize the source.
parse:= ParseBibXMLextFiles( file );
if parse <> fail then
for entry in parse.entries do
extend_entries( entry, parse.strings, file );
od;
Append( mapping, parse.strings );
Append( entities, parse.entities );
fi;
else
# First convert the BibTeX format into XMLext format,
# apply some heuristics in order to improve this,
# and then create the records.
parse:= ParseBibFiles( file );
if parse <> fail then
for entry in parse[1] do
for r in RecNames( entry ) do
if IsString( entry.( r ) ) then
entry.( r ):= HeuristicTranslationsLaTeX2XML.Apply(
entry.( r ) );
fi;
od;
entry:= StringBibAsXMLext( entry, parse[2], parse[3]);
if entry <> fail then
entry:= ParseBibXMLextString( entry ).entries[1];
extend_entries( entry, parse[2], file );
fi;
od;
Append( mapping, TransposedMat( parse{ [ 2, 3 ] } ) );
fi;
fi;
od;
# Reset the changed GAPDoc info levels.
Perform( infocache,
function( r ) SetInfoLevel( r.class, r.oldlevel ); end );
result:= rec( entries:= [],
strings:= [],
entities:= [], );
if IsEmpty( entries ) then
return result;
fi;
# Sort the entries if required.
# (If no file argument is given then sort the entries by authors;
# this was the behaviour before the source file was sorted.)
if sortKeyFunction <> fail then
SortParallel( List( entries, e -> sortKeyFunction( e[1] ) ), entries );
elif Length( arg ) = 0 then
SortParallel( List( entries, e -> e[1].author ), entries );
fi;
# Construct a database id enumerator from the contents.
# (Deal with duplicates by collecting entries with the same identifier.)
identifier:= function( entry )
local id, fld;
id:= rec();
for fld in fingerprint do
if IsBound( entry.( fld ) ) then
id.( fld ):= entry.( fld );
fi;
od;
return id;
end;
ids:= [];
idenum:= [];
for entry in entries do
id:= identifier( entry[1] );
pos:= Position( ids, id );
if pos = fail then
Add( ids, id );
Add( idenum, [ entry ] );
else
Add( idenum[ pos ], entry );
fi;
od;
idenum:= DatabaseIdEnumerator( rec(
identifiers:= idenum,
entry:= function( dbidenum, id ) return id; end,
) );
# Construct database attributes for the columns to be displayed.
authorswidth:= 35;
DatabaseAttributeAdd( idenum, rec(
identifier:= "authors",
type:= "values",
create:= function( attr, id )
local normalize, auth;
normalize:= true;
id:= id[1][1];
if IsBound( id.authorAsList ) then
auth:= id.authorAsList;
elif IsBound( id.editorAsList ) then
auth:= id.editorAsList;
elif IsBound( id.author ) then
auth:= id.author;
elif IsBound( id.organization ) then
auth:= id.organization;
normalize:= false;
elif IsBound( id.editor ) then
auth:= id.editor;
elif IsBound( id.publisher ) then
auth:= id.publisher;
normalize:= false;
else
auth:= "(no author, organization, editor, publisher)";
normalize:= false;
fi;
if normalize and IsString( auth ) then
# The value comes from a `.bib' file;
# the value obtained from an `.xml' file need not be changed.
auth:= NormalizedNameAndKey( auth )[4];
fi;
if IsString( auth ) then
auth:= [ BrowseData.SimplifiedString( auth ) ];
else
auth:= List( auth, y -> BrowseData.SimplifiedString(
Concatenation( y[1], ", ", y[2] ) ) );
fi;
auth:= List( auth, NormalizedWhitespace );
if authorswidth < MaximumList( List( auth, Length ) ) then
auth:= Concatenation( List( auth, l -> SplitString( FormatParagraph(
BrowseData.SimplifiedString( l ), authorswidth, "left" ),
"\n" ) ) );
fi;
return auth;
end,
viewValue:= x -> rec( rows:= x, align:= "tl" ),
align:= "l",
widthCol:= authorswidth,
sortParameters:= [ "hide on categorizing", "no",
"add counter on categorizing", "yes",
"split rows on categorizing", "yes" ],
) );
titlewidth:= 40;
DatabaseAttributeAdd( idenum, rec(
identifier:= "title",
type:= "values",
create:= function( attr, id )
if IsBound( id[1][1].title ) then
return id[1][1].title;
elif IsBound( id[1][1].booktitle ) then
return id[1][1].booktitle;
else
return "(no title or booktitle)";
fi;
end,
viewValue:= x -> rec(
rows:= SplitString( ReplacedString( FormatParagraph(
BrowseData.SimplifiedString( x ),
titlewidth, "left" ), "\n", " \n" ), "\n" ),
align:= "tl" ),
align:= "l",
widthCol:= titlewidth,
) );
DatabaseAttributeAdd( idenum, rec(
identifier:= "year",
type:= "values",
create:= function( attr, id )
if IsBound( id[1][1].year ) then
return id[1][1].year;
else
return "(no year)";
fi;
end,
viewValue:= x -> rec( rows:= [ x ], align:= "tr" ),
align:= "r",
sortParameters:= [ "direction", "descending",
"add counter on categorizing", "yes" ],
) );
journalwidth:= 35;
DatabaseAttributeAdd( idenum, rec(
identifier:= "journal",
type:= "values",
create:= function( attr, id )
local jour;
if IsBound( id[1][1].journal ) then
jour:= id[1][1].journal;
elif IsBound( id[1][1].Type ) then
if id[1][1].Type in [ "incollection", "inproceedings" ]
and IsBound( id[1][1].booktitle ) then
jour:= Concatenation( "in: ", id[1][1].booktitle );
else
jour:= Concatenation( "(", id[1][1].Type, ")" );
fi;
else
jour:= "(no journal)";
fi;
jour:= [ NormalizedWhitespace( BrowseData.SimplifiedString( jour ) ) ];
if journalwidth < Length( jour[1] ) then
jour:= SplitString( ReplacedString( FormatParagraph( jour[1],
journalwidth, "left" ), "\n", " \n" ),
"\n" );
fi;
return jour;
end,
viewValue:= x -> rec( rows:= x, align:= "tl" ),
align:= "l",
widthCol:= journalwidth,
sortParameters:= [ "add counter on categorizing", "yes" ],
) );
mrclasswidth:= 7;
DatabaseAttributeAdd( idenum, rec(
identifier:= "mrclass",
type:= "values",
create:= function( attr, id )
if IsBound( id[1][1].mrclass ) then
return id[1][1].mrclass;
else
return "";
fi;
end,
viewValue:= x -> rec(
rows:= SplitString( x, " " ),
align:= "t" ),
viewSort:= BrowseData.CompareMSCcode,
categoryValue:= value -> List( SplitString( value, " " ),
function( x )
x:= BrowseData.MSCDescription( x );
return Concatenation( x[1], ": ", BrowseData.SimplifiedString(
HeuristicTranslationsLaTeX2XML.Apply( x[2] ) ) );
end ),
align:= "c",
sortParameters:= [ "hide on categorizing", "no",
"add counter on categorizing", "yes",
"split rows on categorizing", "yes" ],
widthCol:= mrclasswidth,
) );
DatabaseAttributeAdd( idenum, rec(
identifier:= "sourcefilename",
viewLabel:= "filename",
type:= "values",
create:= function( attr, id )
local rows, r;
rows:= [];
for r in id do
if IsBound( r[1].sourcefilename ) and
not r[1].sourcefilename in rows then
Add( rows, r[1].sourcefilename );
fi;
od;
return rows;
end,
viewValue:= value -> rec( rows:= DuplicateFreeList( List( value,
x -> BrowseData.ReplacedEntry( x, files, filesshort ) ) ),
align:= "tl" ),
categoryValue:= value -> List( value,
x -> BrowseData.ReplacedEntry( x, files, filecontents ) ),
align:= "l",
sortParameters:= [ "hide on categorizing", "no",
"add counter on categorizing", "yes",
"split rows on categorizing", "yes" ],
) );
# Add user defined columns.
for l in columns do
DatabaseAttributeAdd( idenum, l );
od;
# Construct the extended modes if necessary.
if not IsBound( BrowseData.defaults.work.customizedModes.gapbibl ) then
# Create a shallow copy of each default mode for `Browse', and add
# new actions to those modes where a row or an entry is selected:
# - vb: Show BibTeX format of the selected entry in a pager
# - vh: Show HTML format of the selected entry in a pager
# - vt: Show text format of the selected entry in a pager
modes:= List( BrowseData.defaults.work.availableModes,
BrowseData.ShallowCopyMode );
BrowseData.defaults.work.customizedModes.gapbibl:= modes;
newactions:= [ [ "vb", "BibTeX" ],
[ "vh", "HTML" ],
[ "vt", "Text" ] ];
showaction:= pair -> [ [ pair[1] ],
rec( helplines:= [ Concatenation( "show ", pair[2],
" format of the selected entry in a pager" ) ],
action:= function( t )
local pos, disp;
if t.dynamic.selectedEntry <> [ 0, 0 ] then
pos:= t.dynamic.indexRow[ t.dynamic.selectedEntry[1] ] / 2;
disp:= StringBibXMLEntry( t.work.entries[ pos ][1][2],
pair[2], t.work.mapping );
disp:= SimplifiedUnicodeString( Unicode( disp ), "ASCII" );
disp:= Encode( disp, GAPInfo.TermEncoding );
NCurses.hide_panel( t.dynamic.statuspanel );
NCurses.Pager( disp );
NCurses.show_panel( t.dynamic.statuspanel );
fi;
t.dynamic.changed:= true;
end ) ];
newactions:= List( newactions, showaction );
if "mrclass" in choice then
Add( newactions, [ [ "vm" ],
rec( helplines:= [ "show MSC info of the selected entry in a pager" ],
action:= function( t )
local pos, r, disp;
if t.dynamic.selectedEntry <> [ 0, 0 ] then
pos:= t.dynamic.indexRow[ t.dynamic.selectedEntry[1] ] / 2;
r:= t.work.entries[ pos ][1][1];
if IsBound( r.mrclass ) then
disp:= BrowseData.MSCString( r.mrclass );
else
disp:= "no MSC codes";
fi;
disp:= SimplifiedUnicodeString( Unicode( disp ), "ASCII" );
disp:= Encode( disp, GAPInfo.TermEncoding );
NCurses.hide_panel( t.dynamic.statuspanel );
NCurses.Pager( disp );
NCurses.show_panel( t.dynamic.statuspanel );
fi;
t.dynamic.changed:= true;
end ) ] );
fi;
for mode in modes do
if mode.name in [ "select_row", "select_entry",
"select_row_and_entry",
"select_column_and_entry" ] then
BrowseData.SetActions( mode, newactions );
fi;
od;
else
modes:= BrowseData.defaults.work.customizedModes.gapbibl;
fi;
sel_action:= rec(
helplines:= [ "add the BibTeX or XML entry to the result list" ],
action:= function( t )
if t.dynamic.selectedEntry <> [ 0, 0 ] then
Add( t.dynamic.Return,
t.dynamic.indexRow[ t.dynamic.selectedEntry[1] ] / 2 );
fi;
end );
# Construct the browse table.
table:= BrowseTableFromDatabaseIdEnumerator( idenum, [], choice,
t -> BrowseData.HeaderWithRowCounter( t, header,
Length( idenum.identifiers ) ) );
table.work.cacheEntries:= true;
table.work.Click:= rec(
select_entry:= sel_action,
select_row:= sel_action,
);
table.work.availableModes:= modes;
table.dynamic.Return:= [];
table.dynamic.activeModes:= [ First( modes, x -> x.name = "browse" ) ];
table.work.entries:= idenum.identifiers;
table.work.mapping:= mapping;
# Show the table.
ret:= DuplicateFreeList( NCurses.BrowseGeneric( table ) );
# Construct the return value.
if not IsEmpty( ret ) then
result.strings:= mapping;
result.entities:= entities;
result.entries:= List( idenum.identifiers{ ret }, x -> x[1][2] );
fi;
return result;
end );
if HeuristicTranslationsLaTeX2XML = 0 then
Unbind( HeuristicTranslationsLaTeX2XML );
fi;
#############################################################################
##
#F BrowseBibliographyGapPackages()
##
## <#GAPDoc Label="BrowseBibliographyGapPackages">
## <ManSection>
## <Func Name="BrowseBibliographyGapPackages" Arg=""/>
##
## <Returns>
## a record as returned by <Ref Func="BrowseBibliography"/>.
## </Returns>
##
## <Description>
## This function collects the information from the <C>*.bib</C> and
## <C>*bib.xml</C> files in those subdirectories of installed &GAP; packages
## which contain the package documentation,
## and shows it in a Browse table, using the function
## <Ref Func="BrowseBibliography"/>.
## <P/>
## <E>This function is experimental.</E>
## The result is not really satisfactory, for the following reasons.
## <P/>
## <List>
## <Item>
## Duplicate entries may occur,
## due to subtle differences in various source files.
## </Item>
## <Item>
## The source files may contain more than what is actually cited
## in the package manuals.
## </Item>
## <Item>
## It may happen that some <C>*.bib</C> or <C>*bib.xml</C> file is
## accidentally distributed with the package but is not intended to serve
## as package bibliography.
## </Item>
## <Item>
## The heuristics for rewriting &LaTeX; code is of course not perfect;
## thus strange symbols may occur in the Browse table.
## </Item>
## </List>
## </Description>
## </ManSection>
## <#/GAPDoc>
##
BindGlobal( "BrowseBibliographyGapPackages", function()
local info, name, r1, path, r2, subdir, pos, len, dir, file;
# Collect the '*.bib' and '*bib.xml' files from directories
# of installed packages.
info:= [];
for name in RecNames( GAPInfo.PackagesInfo ) do
# Run over the available versions of this package.
for r1 in GAPInfo.PackagesInfo.( name ) do
path:= r1.InstallationPath;
# Run over the documentation books.
for r2 in r1.PackageDoc do
subdir:= r2.PDFFile;
pos:= Positions( subdir, '/' );
len:= Length( pos );
if len <> 0 then
subdir:= subdir{ [ 1 .. pos[ len ] - 1 ] };
dir:= Concatenation( path, "/", subdir );
if IsDirectoryPath( dir ) then
dir:= Directory( dir );
# Run over the files in the directory for this book.
for file in Set( DirectoryContents( dir ) ) do
len:= Length( file );
if 7 < len and file{ [ len-6 .. len ] } = "bib.xml" then
Add( info, [ name, Filename( dir, file ) ] );
elif 4 < len and file{ [ len-3 .. len ] } = ".bib" then
# Omit files of the form '<name>.xml.bib',
# they are assumed to be generated from '<name>.xml'.
if not ( 8 < len and file{ [ len-7 .. len ] }
= ".xml.bib" ) then
Add( info, [ name, Filename( dir, file ) ] );
fi;
fi;
od;
fi;
fi;
od;
od;
od;
return BrowseBibliography( rec(
filesshort:= List( info, x -> Concatenation( x[1], ": ",
Reversed( SplitString( x[2], "/" ) )[1] ) ),
filecontents:= List( info, x -> Concatenation( "Package ", x[1] ) ),
files:= List( info, x -> x[2] ),
header:= "Bibliography of Gap Packages",
choice:= [ "authors", "title", "year", "journal", "sourcefilename" ],
sortKeyFunction:= BrowseData.SortKeyFunctionBibRec,
) );
end );
#############################################################################
##
## Add the Browse applications to the list shown by `BrowseGapData'.
##
BrowseGapDataAdd( "AMS Math. Subject Classif.", BrowseMSC, false, "\
an overview of the current AMS Mathematics Subject Classification codes" );
BrowseGapDataAdd( "GAP Bibliography", BrowseBibliography, true, "\
an overview of GAP related publications, in a browse table \
whose columns show authors, title, year, journal, and MSC code; \
the return value is a record encoding the clicked bibliography entries" );
BrowseGapDataAdd( "GAP Packages Bibliographies",
BrowseBibliographyGapPackages, true, "\
the contents of the *bib and *bib.xml files of installed GAP packages, \
shown in a browse table with columns for authors, title, year, journal, \
and filename; \
the return value is a record encoding the clicked bibliography entries" );
#############################################################################
##
#E