SSL utlmrkup.g
Sprache: unbekannt
|
|
#############################################################################
##
#W utlmrkup.g GAP 4 package CTblLib Thomas Breuer
##
## This file contains utility functions for creating HTML files.
## They are used for the web pages on
## - decomposition matrices,
## - the contents of the GAP Character Table Library,
## - the contents of the MFER database,
## - the contents of parts of the AtlasRep database.
##
#############################################################################
##
#V MarkupGlobals
##
## The constant 'MarkupGlobals.CompareMark' is used in 'HTMLFooter' and
## 'PrintToIfChanged'.
##
MarkupGlobals := rec(
HTML:= rec(
\+ := "+",
\- := "−",
lt := "<",
leq := "≤",
ast := "∗",
cdot := " ⋅ ",
rightarrow:= "→",
sub := [ "<sub>", "</sub>" ],
super := [ "<sup>", "</sup>" ],
center:= [ "<center>", "</center>" ],
bold := [ "<strong>", "</strong>" ],
dot := ".",
splitdot := ":",
times := " × ",
wreath := " ≀ ",
xi := "ξ",
Z := "ℤ",
outerbrackets:= [ "", "" ],
),
LaTeX:= rec(
\+ := "+",
\- := "-",
lt := "<",
leq := "\\leq",
ast := "\\ast",
cdot := " \\cdot ",
rightarrow:= "\\rightarrow",
sub := [ "_{", "}" ],
super := [ "^{", "}" ],
center:= [ "\n\\begin{center}\n", "\n\\end{center}\n" ],
bold := [ "\\textbf{", "}" ],
dot := ".",
splitdot := ":",
times := " \\times ",
wreath := " \\wr ",
xi := "\\xi",
Z := "\\texttt{{\\ensuremath{\\mathbb Z}}}",
outerbrackets:= [ "", "" ],
),
MathJax:= rec(
\+ := "+",
\- := "-",
lt := "<",
leq := "\\leq",
ast := "\\ast",
cdot := " \\cdot ",
rightarrow:= "\\rightarrow",
sub := [ "_{", "}" ],
super := [ "^{", "}" ],
center:= [ "\n\\begin{center}\n", "\n\\end{center}\n" ],
bold := [ "\\textbf{", "}" ],
dot := ".",
splitdot := ":",
times := " \\times ",
wreath := " \\wr ",
xi := "\\xi",
Z := "\\texttt{{\\ensuremath{\\mathbb Z}}}",
outerbrackets:= [ "\\(", "\\)" ],
),
CompareMark:= "File created automatically by GAP on ",
);
#############################################################################
##
#F MarkupFactoredNumber( <n>, <global> )
##
## This is used in 'ctbltoc/gap/htmltbl.g'.
##
MarkupFactoredNumber:= function( n, global )
if global = "LaTeX" then
global:= MarkupGlobals.LaTeX;
elif global = "HTML" then
global:= MarkupGlobals.HTML;
fi;
if not IsPosInt( n ) then
Error( "<n> must be a positive integer" );
elif n = 1 then
return "1";
fi;
# Loop over the prime factors and the corresponding exponents.
return ReplacedString(
JoinStringsWithSeparator(
List( Collected( Factors( n ) ),
pair -> Concatenation(
String( pair[1] ), global.super[1],
String( pair[2] ), global.super[2] ) ),
global.cdot ),
Concatenation( global.super[1], "1", global.super[2] ), "" );
end;
#############################################################################
##
#F NormalizedNameOfGroup( <name>, <global> )
##
## Let <name> be a string describing a group structure,
## and <global> be one of "HTML", "LaTex", "MathJax", or a component of
## 'MarkupGlobals'.
## This function proceeds as follows.
## - If <name> consists of two group names that are combined with '" < "'
## or '" -> "' then treat the parts separately;
## this occurs in names used in the MFER package.
## - If <name> contains the character '/' that is not surrounded by
## digit characters then just return <name>;
## this occurs for table identifiers such as 'P1/G1/L1/V1/ext2'.
## - If name ends with 'M<n>' or 'N<n>' or 'N<n><char>' then keep this
## suffix and normalize the part until this suffix,
## *except* if 'M<n>' stands for a Mathieu group.
## - In all other cases, it turns <name> into a tree describing the
## hierarchy given by the substrings " < " and " -> "
## (only on the outermost level) and brackets,
## then splits the strings that occur in this tree at
## the following characters.
## ',' (appears in some MFER strings),
## 'x' (for direct product),
## '.' and ':' (for product and semidirect product, respectively),
## '_' (for a subscript),
## '^' (for an exponent),
## where the weakest binding is treated first.
## - Then the strings that occur in the resulting tree are converted:
## numbers following a capital letter are turned into subscripts,
## and the characters '+', '-' are turned into superscripts.
## - Finally, this tree is imploded into a string, where the characters at
## which the input was split are replaced by the relevant entries of
## <global>.
##
NormalizedNameOfGroup:= function( name, global )
local extractbrackets, split, convertstring, convertatoms, concatenate,
pos, result, i;
if IsString( global ) and IsBound( MarkupGlobals.( global ) ) then
global:= MarkupGlobals.( global );
fi;
extractbrackets:= function( str )
local tree, brackets, pos, minpos, b, closeb, closepos, open;
tree:= [];
brackets:= [ "([{", ")]}" ];
while str <> "" do
pos:= List( brackets[1], b -> Position( str, b ) );
minpos:= Minimum( pos );
if minpos <> fail then
b:= str[ minpos ];
closeb:= brackets[2][ Position( brackets[1], b ) ];
closepos:= minpos+1;
open:= 0;
while closepos <= Length( str )
and ( str[ closepos ] <> closeb or open <> 0 ) do
if str[ closepos ] = b then
open:= open+1;
elif str[ closepos ] = closeb then
open:= open-1;
fi;
closepos:= closepos + 1;
od;
if closepos > Length( str ) then
return fail;
fi;
Append( tree,
[ str{ [ 1 .. minpos-1 ] },
rec( op:= b,
contents:= extractbrackets( str{ [ minpos+1
.. closepos-1 ] } ) ) ] );
str:= str{ [ closepos+1 .. Length( str ) ] };
else
Add( tree, str );
str:= "";
fi;
od;
return tree;
end;
split:= function( tree )
local i, splitchar, found, entry, pos;
tree:= ShallowCopy( tree );
for i in [ 1 .. Length( tree ) ] do
entry:= tree[i];
if IsRecord( tree[i] ) then
if IsBound( entry.contents ) then
tree[i]:= rec( op:= entry.op, contents:= split( entry.contents ) );
else
tree[i]:= rec( op:= entry.op, left:= split( entry.left ),
right:= split( entry.right ) );
fi;
fi;
od;
for splitchar in ",x.:_^" do # weakest binding first!
for i in [ 1 .. Length( tree ) ] do
entry:= tree[i];
if IsString( entry ) then
pos:= Position( entry, splitchar );
if pos <> fail then
return [ rec( op:= splitchar,
left:= split( Concatenation( tree{ [ 1 .. i-1 ] },
[ entry{ [ 1 .. pos-1 ] } ] ) ),
right:= split( Concatenation( [ entry{ [ pos+1
.. Length( entry ) ] } ],
tree{ [ i+1 .. Length( tree ) ] } ) ) ) ];
fi;
fi;
od;
od;
for i in [ 1 .. Length( tree ) ] do
entry:= tree[i];
if IsString( entry ) then
pos:= PositionSublist( entry, "wr" );
if pos <> fail then
return [ rec( op:= "wreath",
left:= split( Concatenation( tree{ [ 1 .. i-1 ] },
[ entry{ [ 1 .. pos-1 ] } ] ) ),
right:= split( Concatenation( [ entry{ [ pos+2
.. Length( entry ) ] } ],
tree{ [ i+1 .. Length( tree ) ] } ) ) ) ];
fi;
fi;
od;
return tree;
end;
#T If we want to replace '"L2(4)"' and not '"L2"' then
#T first we have to implode locally, in order to get "(4)";
#T this is done by the following function.
#T Afterwards, we have to implode locally the two parts in question.
# concatenatenumberbrackets:= function( tree )
# local i;
#
# for i in [ 1 .. Length( tree ) ] do
# if IsRecord( tree[i] ) then
# if tree[i].op = '^' and Length( tree[i].left ) = 1
# and Length( tree[i].right ) = 1
# and IsString( tree[i].left[1] )
# and Int( tree[i].left[1] ) <> fail
# and IsString( tree[i].right[1] )
# and Int( tree[i].right[1] ) <> fail then
# tree[i]:= Concatenation( tree[i].left[1], global.super[1],
# tree[i].right[1], global.super[2] );
# elif tree[i].op = '_' and Length( tree[i].left ) = 1
# and Length( tree[i].right ) = 1
# and IsString( tree[i].left[1] )
# and Int( tree[i].left[1] ) <> fail
# and IsString( tree[i].right[1] )
# and Int( tree[i].right[1] ) <> fail then
# tree[i]:= Concatenation( tree[i].left[1], global.sub[1],
# tree[i].right[1], global.sub[2] );
# elif tree[i].op = '(' and Length( tree[i].contents ) = 1
# and IsString( tree[i].contents[1] )
# and Int( tree[i].contents[1] ) <> fail then
# tree[i]:= Concatenation( "(", tree[i].contents[1], ")" );
# elif IsBound( tree[i].contents ) then
# concatenatenumberbrackets( tree[i].contents );
# else
# concatenatenumberbrackets( tree[i].left );
# concatenatenumberbrackets( tree[i].right );
# fi;
# fi;
# od;
#
# return tree;
# end;
convertstring:= function( str )
local digits, letters, lower, special, pos, len, string, dig;
NormalizeWhitespace( str );
digits := "0123456789";
letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
lower := "abcdefghijklmnopqrstuvwxyz";
# translate special cases
special:= TransposedMat( [
[ "McL", Concatenation( "M", global.super[1], "c",
global.super[2], "L" ) ],
[ "F3+", Concatenation( "F", global.sub[1], "3+", global.sub[2] ) ],
[ "Fi24'", Concatenation( "Fi", global.sub[1], "24", global.sub[2],
global.super[1], "'", global.super[2] ) ],
[ "2E6", Concatenation( global.super[1], "2", global.super[2],
"E", global.sub[1], "6", global.sub[2] ) ],
[ "2F4", Concatenation( global.super[1], "2", global.super[2],
"F", global.sub[1], "4", global.sub[2] ) ],
[ "3D4", Concatenation( global.super[1], "3", global.super[2],
"D", global.sub[1], "4", global.sub[2] ) ],
] );
pos:= Position( special[1], str );
if pos <> fail then
string:= special[2][ pos ];
if StartsWith( string, "^" ) then
# This happens only in the LaTeX situation.
string:= Concatenation( "{}", string );
fi;
return string;
fi;
# general heuristics
pos:= 1;
len:= Length( str );
string:= "";
# initial digits (happens if 'str' consists oly of digits)
while pos <= len and str[ pos ] in digits do
Add( string, str[ pos ] );
pos:= pos + 1;
od;
while pos <= len do
# copy letter part
if str[ pos ] in letters then
while pos <= len and str[ pos ] in letters do
Add( string, str[ pos ] );
pos:= pos + 1;
od;
fi;
# following digits become subscripts
if pos <= len and str[ pos ] in digits then
Append( string, global.sub[1] );
while pos <= len and str[ pos ] in digits do
Add( string, str[ pos ] );
pos:= pos + 1;
od;
Append( string, global.sub[2] );
fi;
# A following '+' or '-' becomes a superscript if it is the last letter
# except if it is the only letter
# (and except for '"F3+"' but this has been handled above ...).
if pos = len and str[ pos ] in "+-" then
if pos = 1 then
Append( string, global.( [ str[ pos ] ] ) );
pos:= pos + 1;
else
Append( string, global.super[1] );
Append( string, global.( [ str[ pos ] ] ) );
pos:= pos + 1;
Append( string, global.super[2] );
fi;
fi;
if pos <= len and not IsAlphaChar( str[ pos ] ) then
while pos <= len do
if str[ pos ] <> '_' then
Add( string, str[ pos ] );
pos:= pos + 1;
else
pos:= pos + 1;
Append( string, global.sub[1] );
while pos <= len and str[ pos ] in digits do
Add( string, str[ pos ] );
pos:= pos + 1;
od;
Append( string, global.sub[2] );
fi;
od;
fi;
od;
return string;
end;
convertatoms:= function( tree )
local i, entry;
for i in [ 1 .. Length( tree ) ] do
entry:= tree[i];
if IsString( entry ) then
tree[i]:= convertstring( tree[i] );
elif IsBound( entry.contents ) then
convertatoms( entry.contents );
else
convertatoms( entry.left );
convertatoms( entry.right );
fi;
od;
return tree;
end;
# Concatenate the translated parts.
concatenate:= function( tree )
local result, entry, right;
result:= [];
for entry in tree do
if IsString( entry ) then
Add( result, entry );
elif IsBound( entry.contents ) then
if entry.op = '(' then
Add( result,
Concatenation( "(", concatenate( entry.contents ), ")" ) );
elif entry.op = '[' then
Add( result,
Concatenation( "[", concatenate( entry.contents ), "]" ) );
elif entry.op = '{' then
Add( result,
Concatenation( "{", concatenate( entry.contents ), "}" ) );
fi;
else
if entry.op = '^' then
# Deal with superscripts
# (remove brackets around the superscripts if they are unique).
right:= concatenate( entry.right );
if Length( right ) > 0 and right[1] = '('
and right[ Length( right ) ] = ')'
and Number( right, x -> x = '(' ) = 1 then
right:= right{ [ 2 .. Length( right ) - 1 ] };
fi;
Add( result, Concatenation( concatenate( entry.left ),
global.super[1], right, global.super[2] ) );
elif entry.op = '_' then
# Deal with subscripts
# (remove brackets around the subscripts if they are unique).
right:= concatenate( entry.right );
if Length( right ) > 0 and
( ( right[1] = '{' and right[ Length( right ) ] = '}'
and Number( right, x -> x = '{' ) = 1 )
or ( right[1] = '(' and right[ Length( right ) ] = ')'
and Number( right, x -> x = '(' ) = 1 ) ) then
right:= right{ [ 2 .. Length( right ) - 1 ] };
fi;
Add( result, Concatenation( concatenate( entry.left ),
global.sub[1], right, global.sub[2] ) );
elif entry.op = 'x' then
right:= concatenate( entry.right );
if Length( right ) = 0 then
Add( result, Concatenation( concatenate( entry.left ), "x" ) );
else
Add( result, Concatenation( concatenate( entry.left ),
global.times, concatenate( entry.right ) ) );
fi;
elif entry.op = "wreath" then
Add( result, Concatenation( concatenate( entry.left ),
global.wreath, concatenate( entry.right ) ) );
elif entry.op = '.' then
Add( result, Concatenation( concatenate( entry.left ),
global.dot, concatenate( entry.right ) ) );
elif entry.op = ':' then
Add( result, Concatenation( concatenate( entry.left ),
global.splitdot, concatenate( entry.right ) ) );
elif entry.op = ',' then
Add( result, Concatenation( concatenate( entry.left ),
", ", concatenate( entry.right ) ) );
else
Error( "unexpected entry.op" );
fi;
fi;
od;
return Concatenation( result );
end;
# If <name> consists of two group names that are combined with '" < "'
# or '" -> "' then treat the parts separately.
pos:= PositionSublist( name, " < " );
if pos <> fail then
return Concatenation(
global.outerbrackets[1],
NormalizedNameOfGroup( name{ [ 1 .. pos-1 ] }, global ),
" ", global.lt, " ",
NormalizedNameOfGroup( name{ [ pos+3 .. Length( name ) ] },
global ),
global.outerbrackets[2] );
fi;
pos:= PositionSublist( name, " -> " );
if pos <> fail then
return Concatenation(
global.outerbrackets[1],
NormalizedNameOfGroup( name{ [ 1 .. pos-1 ] }, global ),
" ", global.rightarrow, " ",
NormalizedNameOfGroup( name{ [ pos+4 .. Length( name ) ] },
global ),
global.outerbrackets[2] );
fi;
# Replace <name> by structure information known to CTblLib.
if IsBound( StructureDescriptionCharacterTableName ) then
name:= ValueGlobal( "StructureDescriptionCharacterTableName" )( name );
fi;
# If <name> contains the character '/' that is not surrounded by
# digit characters then just return <name>.
pos:= Position( name, '/' );
if pos <> fail and pos <> 1 and pos <> Length( name ) and not
( IsDigitChar( name[ pos-1 ] ) and IsDigitChar( name[ pos+1 ] ) ) then
return ShallowCopy( name );
fi;
# If name ends with 'M<n>' or 'N<n>' or 'N<n><char>' or 'C<n><char>'
# then keep this suffix and normalize the part until this suffix,
# *except* if 'M<n>' stands for a Mathieu group.
pos:= Length( name );
while pos > 0 and IsDigitChar( name[ pos ] ) do
pos:= pos - 1;
od;
if pos < Length( name ) and pos > 0 and name <> "3^6:2M12" and
( name[ pos ] = 'N' or
name = "M12C4" or
( name[ pos ] = 'M' and pos > 1 and not name[ pos-1 ] in ".:x" ) ) then
return Concatenation( NormalizedNameOfGroup( name{ [ 1 .. pos-1 ] },
global ),
name{ [ pos .. Length( name ) ] } );
fi;
pos:= Length( name ) - 1;
while pos > 0 and IsDigitChar( name[ pos ] ) do
pos:= pos - 1;
od;
if pos < Length( name ) - 1 and pos > 1 and name[ pos ] in "CN" then
return Concatenation( NormalizedNameOfGroup( name{ [ 1 .. pos-1 ] },
global ),
name{ [ pos .. Length( name ) ] } );
fi;
# Hack for a few names which contain proper subnames '<name>N<p>':
# If there is an outer round bracket then recurse with its contents.
result:= extractbrackets( NormalizedWhitespace( name ) );
if Length( result ) = 3 and result[1] = "" and result[3] = ".2" and
IsRecord( result[2] ) and result[2].op = '(' then
return Concatenation( "(",
NormalizedNameOfGroup( name{ [ 2 .. Length( name ) - 3 ] },
global ), ").2" );
fi;
# Now apply the translation rules.
result:= concatenate( convertatoms( split( extractbrackets(
NormalizedWhitespace( name ) ) ) ) );
for i in [ 1 .. 3 ] do
result:= ReplacedString( result,
Concatenation( ".2<sub>", String( i ), "'</sub>" ),
Concatenation( ".2<sub>", String( i ), "</sub>'" ) );
od;
return Concatenation(
global.outerbrackets[1], result, global.outerbrackets[2] );
end;
#############################################################################
##
#F HTMLStandardTable( <header>, <matrix>, <tblclass>, <colclasses> )
##
## <header>
## must be 'fail' (if no table header is wanted) or a list of strings,
## its entries are turned into <th> elements
## (with the appropriate alignments),
## <matrix>
## must be a nonempty list of lists of strings,
## the rows are turned into <tr> elements,
## the entries are turned into <td> elements
## where unbound and empty entries are represented by " "
## (with the appropriate alignments),
## <tblclass>
## must be a style class for the table itself,
## <colclasses>
## must be a list of style classes for the <th> and <td> elements
## (typically defining the alignments of the columns).
##
HTMLStandardTable:= function( header, matrix, tblclass, colclasses )
local str, i, ncols, row;
str:= Concatenation( "<table class=\"", tblclass, "\">\n" );
ncols:= Maximum( List( matrix, Length ) );
if IsList( header ) and not IsEmpty( header ) then
ncols:= Maximum( ncols, Length( header ) );
Append( str, "<tr class=\"firstrow\">\n" );
for i in [ 1 .. ncols ] do
if IsBound( colclasses[i] ) then
Append( str, "<th class=\"" );
Append( str, colclasses[i] );
Append( str, "\">" );
else
Append( str, "<th>" );
fi;
if not IsBound( header[i] ) or IsEmpty( header[i] ) then
Append( str, " " );
else
Append( str, header[i] );
fi;
Append( str, "</th>\n" );
od;
Append( str, "</tr>\n" );
fi;
for row in matrix do
Append( str, "<tr>\n" );
for i in [ 1 .. ncols ] do
if IsBound( colclasses[i] ) then
Append( str, "<td class=\"" );
Append( str, colclasses[i] );
Append( str, "\">" );
else
Append( str, "<td>" );
fi;
if not IsBound( row[i] ) or row[i] = "" then
Append( str, " " );
else
Append( str, row[i] );
fi;
Append( str, "</td>\n" );
od;
Append( str, "</tr>\n" );
od;
Append( str, "</table>\n" );
return str;
end;
#############################################################################
##
#F HTMLHeader( <titlestring>, <stylesheetpath>, <commonheading>, <heading> )
##
## For the given four strings,
## 'HTMLHeader' returns the string that prints as follows.
##
## <?xml version="1.0" encoding="UTF-8"?>
##
## <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
## "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
##
## <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
## <head>
## <title>
## <titlestring>
## </title>
## <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
## <link rel="stylesheet" type="text/css" href="<stylesheetpath>" />
## </head>
## <body>
## <h5 class="pleft"><span class="Heading">
## <commonheading>
## </span></h5>
## <h3 class="pcenter"><span class="Heading">
## <heading>
## </span></h3>
##
HTMLHeader:= function( titlestring, stylesheetpath, commonheading, heading )
local str;
str:= "";
# Append the document type stuff.
Append( str, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n" );
Append( str, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" );
Append( str, " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\n" );
Append( str, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n" );
# Append the head part, which contains the title.
Append( str, "<head>\n" );
Append( str, "<title>\n" );
Append( str, titlestring );
Append( str, "\n</title>\n" );
Append( str, "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\"/>\n" ); # needed to display symbols properly
if IsString( stylesheetpath ) then
# Support a list of style sheet paths.
stylesheetpath:= [ stylesheetpath ];
fi;
if IsList( stylesheetpath ) and ForAll( stylesheetpath, IsString ) then
Append( str, Concatenation( List( stylesheetpath,
path -> Concatenation(
"<link rel=\"stylesheet\" type=\"text/css\" href=\"",
path, "\" />\n" ) ) ) );
fi;
Append( str, "</head>\n" );
# Append the body begin, with font specifications.
Append( str, "<body>\n" );
if commonheading <> fail then
Append( str, "<h5 class=\"pleft\"><span class=\"Heading\">" );
Append( str, commonheading );
Append( str, "\n</span></h5>\n" );
fi;
if heading <> fail then
Append( str, "<h3 class=\"pcenter\"><span class=\"Heading\">" );
Append( str, heading );
Append( str, "\n</span></h3>\n" );
fi;
# Return the result.
return str;
end;
#############################################################################
##
#F HTMLFooter()
##
## Let <datestr> be a string describing the current date,
## as is returned by 'CurrentDateTimeString' (which belongs to 'AtlasRep'
## and therefore cannot be used here in general).
## 'HTMLFooter' returns the string that prints as follows.
##
## <hr/>
## <p class="foot">File created by GAP on <datestr>.</p>
##
## </body>
## </html>
##
HTMLFooter:= function( )
local date, name, out, pos, str;
# Create a string that shows the current date.
# (This is done as in AtlasRep's function 'CurrentDateTimeString'.)
date:= "unknown";
name:= Filename( DirectoriesSystemPrograms(), "date" );
if name <> fail then
date:= "";
out:= OutputTextString( date, true );
Process( DirectoryCurrent(), name, InputTextNone(), out,
[ "-u", "+%s" ] );
CloseStream( out );
# Strip the trailing newline character.
Unbind( date[ Length( date ) ] );
# Transform to a format that is compatible with
# 'StringDate' and 'StringTime'.
date:= Int( date );
date:= Concatenation( StringDate( Int( date / 86400 ) ),
", ",
StringTime( 1000 * ( date mod 86400 ) ),
" UTC" );
pos:= Position( date, ',' );
if pos <> fail then
date:= date{ [ 1 .. pos-1 ] };
fi;
fi;
str:= "";
# Append a horizontal line.
Append( str, "\n<hr/>\n" );
# Append the line about the file creation.
Append( str, "<p class=\"foot\">" );
Append( str, MarkupGlobals.CompareMark );
Append( str, date );
Append( str, ".</p>\n\n" );
# Append the closing brackets.
Append( str, "</body>\n" );
Append( str, "</html>\n" );
# Return the result.
return str;
end;
#############################################################################
##
#F PrintToIfChanged( <filename>, <str> );
##
## Let <filename> be a filename, and <str> be a string.
## If no file with name <filename> exists or if the contents of the file
## with name <filename> is different from <str>, up to the ''last changed''
## line, <str> is printed to the file.
## Otherwise nothing is done.
##
PrintToIfChanged := function( filename, str )
local mark, oldfile, contents, pos, diffstr,
diff, out, tmpfile;
mark:= MarkupGlobals.CompareMark;
# Check whether the file exists in the web directory.
if IsExistingFile( filename ) then
# Check whether the contents of the file differs from 'str'.
oldfile:= filename;
contents:= AGR.StringFile( filename );
pos:= PositionSublist( contents, mark );
if pos <> fail
and pos = PositionSublist( str, mark )
and contents{ [ 1 .. pos-1 ] } = str{ [ 1 .. pos-1 ] } then
return Concatenation( "unchanged: ", filename );
fi;
fi;
# The file does not yet exist or the info has changed,
# so print a new file, and produce a 'diff' string if applicable.
diffstr:= "";
if IsBound( oldfile ) then
diffstr:= "\n";
diff:= Filename( DirectoriesSystemPrograms(), "diff" );
if diff <> fail and IsExecutableFile( diff ) then
out:= OutputTextString( diffstr, true );
SetPrintFormattingStatus( out, false );
tmpfile:= TmpName();
FileString( tmpfile, str );
Process( DirectoryCurrent(), diff, InputTextNone(), out,
[ oldfile, tmpfile ] );
CloseStream( out );
RemoveFile( tmpfile );
fi;
fi;
if FileString( filename, str ) = fail then
Error( "cannot write file '", filename, "'" );
fi;
return Concatenation( "replaced: ", filename, diffstr );
end;
#############################################################################
##
#E
[ Verzeichnis aufwärts0.62unsichere Verbindung
Übersetzung europäischer Sprachen durch Browser
]
|
2026-03-28
|