GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
############################################################################# ## #W BibTeX.gi GAPDoc Frank Lübeck ## ## #Y Copyright (C) 2000, Frank Lübeck, Lehrstuhl D für Mathematik, #Y RWTH Aachen ## ## The files BibTeX.g{d,i} contain a parser for BibTeX files and some ## functions for printing BibTeX entries in different formats. ## ## normalize author/editor name lists: last-name, initial(s) of first ## name(s) and ... ## see Lamport: LaTeX App.B 1.2 InstallGlobalFunction(NormalizedNameAndKey, function(str) local isutf8, nbsp, ini, new, pp, p, a, i, names, norm, keyshort, keylong, res, utf8initial; utf8initial := function(s) local i, n; i := 1; while Length(s) <= i and s[i] in "-.{}\\\"\'`\003" do i := i+1; od; if i > Length(s) then return fail; fi; n := UNICODE_RECODE.UnicodeUTF8Char(s, i); return Encode(Unicode([n]),"UTF-8"); end; # do almost nothing if already list of strings (e.g., from BibXMLext tools if IsString(str) then isutf8 := (Unicode(str) <> fail); # first normalize white space inside braces { ... } and change # spaces to non-spaces (removed below) nbsp := '\003'; new := ""; pp := 0; p := Position(str, '{'); while p <> fail do Append(new, str{[pp+1..p-1]}); pp := PositionMatchingDelimiter(str, "{}", p); a := NormalizedWhitespace(str{[p..pp]}); for i in [1..Length(a)] do if a[i] = ' ' then a[i] := nbsp; fi; od; Append(new, a); p := Position(str, '{', pp); od; if Length(new)>0 then str := Concatenation(new, str{[pp+1..Length(str)]}); fi; # split into names: names := []; pp := 0; p := PositionSublist(str, "and"); while p <> fail do # "and" is only delimiter if surrounded by white space if not (str[p-1] in WHITESPACE and Length(str)>p+2 and str[p+3] in WHITESPACE) then p := PositionSublist(str, "and", p); else Add(names, str{[pp+1..p-2]}); pp := p+3; p := PositionSublist(str, "and", pp); fi; od; Add(names, str{[pp+1..Length(str)]}); # normalize a single name norm := function(str) local n, i, lnam, j, fnam, fnamfull; # special case "et. al." if str="others" then return ["others", "", ""]; fi; # first some normalization on the string RemoveCharacters(str,"[]"); str := SubstitutionSublist(str, "\\~", "BSLTILDE"); str := SubstitutionSublist(str, "~", " "); str := SubstitutionSublist(str, "BSLTILDE", "\\~"); str := SubstitutionSublist(str, ".", ". "); StripBeginEnd(str, WHITESPACE); n := SplitString(str, "", WHITESPACE); # check if in "lastname, firstname" notation # find last "," i := Length(n); while i>0 and n[i]<>"," and n[i][Length(n[i])] <> ',' do i := i-1; od; if i>0 then # last name lnam := ""; for j in [1..i] do Append(lnam, n[j]); if j < i then Add(lnam, ' '); fi; lnam := Filtered(lnam, x-> x<>','); od; # first name initials fnam := ""; for j in [i+1..Length(n)] do if isutf8 then ini := utf8initial(n[j]); else ini := First(n[j], x-> not x in WHITESPACE and not x in "-.{}\\\"\'`\003"); if ini <> fail then ini := [ini]; fi; fi; if ini <> fail then Append(fnam, ini); Append(fnam, ". "); fi; od; fnamfull := JoinStringsWithSeparator(n{[i+1..Length(n)]}, " "); else # last name is last including words not starting with # capital letters i := Length(n); while i>1 and First(n[i-1], a-> a in LETTERS) in SMALLLETTERS do i := i-1; od; # last name lnam := ""; for j in [i..Length(n)] do Append(lnam, n[j]); if j < Length(n) then Add(lnam, ' '); fi; od; # first name initials fnam := ""; for j in [1..i-1] do if isutf8 then ini := utf8initial(n[j]); else ini := First(n[j], x-> not x in WHITESPACE and not x in "-.{}\\\"\'`\003"); if ini <> fail then ini := [ini]; fi; fi; if ini <> fail then Append(fnam, ini); Append(fnam, ". "); fi; od; fnamfull := JoinStringsWithSeparator(n{[1..i-1]}, " "); fi; for j in [1..Length(lnam)] do if lnam[j] = '\003' then lnam[j] := ' '; fi; od; for j in [1..Length(fnamfull)] do if fnamfull[j] = '\003' then fnamfull[j] := ' '; fi; od; while Length(fnam) > 0 and fnam[Length(fnam)] in WHITESPACE do fnam := fnam{[1..Length(fnam)-1]}; od; return [lnam, fnam, fnamfull]; end; names := List(names, norm); else names := str; fi; keyshort := ""; keylong := ""; res := ""; for a in names do if Length(res)>0 then Append(res, " and "); fi; Append(res, a[1]); Append(res, ", "); Append(res, a[2]); if a[1] = "others" then Add(keyshort, '+'); else p := 1; while p <= Length(a[1]) and not a[1][p] in CAPITALLETTERS do p := p+1; od; if p > Length(a[1]) then p := 1; fi; if a[1][p] in LETTERS then Add(keyshort, a[1][p]); else Add(keyshort, 'X'); fi; Append(keylong, STRING_LOWER(Filtered(a[1]{[p..Length(a[1])]}, x-> x in LETTERS))); fi; od; if Length(keyshort)>3 then keyshort := keyshort{[1,2]}; Add(keyshort, '+'); fi; return [res, keyshort, keylong, names]; end); ## <#GAPDoc Label="ParseBibFiles"> ## <ManSection > ## <Func Arg="bibfile1[, bibfile2[, ...]]" Name="ParseBibFiles" /> ## <Func Arg="str1[, str2[, ...]]" Name="ParseBibStrings" /> ## <Returns>list <C>[list of bib-records, list of abbrevs, list of ## expansions]</C></Returns> ## <Description> ## The first function parses the files <A>bibfile1</A> and so on (if a file ## does not ## exist the extension <C>.bib</C> is appended) in &BibTeX; format ## and returns a list as follows: <C>[entries, strings, texts]</C>. ## Here <C>entries</C> is a list of records, one record for each ## reference contained in <A>bibfile</A>. Then <C>strings</C> is ## a list of abbreviations defined by <C>@string</C>-entries in ## <A>bibfile</A> and <C>texts</C> is a list which contains in the ## corresponding position the full text for such an abbreviation. ## <P/> ## The second function does the same, but the input is given as &GAP; strings ## <A>str1</A> and so on.<P/> ## ## The records in <C>entries</C> store key-value pairs of a &BibTeX; ## reference in the form <C>rec(key1 = value1, ...)</C>. The names ## of the keys are converted to lower case. The type of the ## reference (i.e., book, article, ...) and the citation key are ## stored as components <C>.Type</C> and <C>.Label</C>. The records ## also have a <C>.From</C> field that says that the data are read ## from a &BibTeX; source.<P/> ## ## As an example consider the following &BibTeX; file. ## ## <Listing Type="doc/test.bib"> ## @string{ j = "Important Journal" } ## @article{ AB2000, Author= "Fritz A. First and Sec, X. Y.", ## TITLE="Short", journal = j, year = 2000 } ## </Listing> ## ## <Example> ## gap> bib := ParseBibFiles("doc/test.bib"); ## [ [ rec( From := rec( BibTeX := true ), Label := "AB2000", ## Type := "article", author := "Fritz A. First and Sec, X. Y." ## , journal := "Important Journal", title := "Short", ## year := "2000" ) ], [ "j" ], [ "Important Journal" ] ] ## </Example> ## </Description> ## </ManSection> ## <#/GAPDoc> ## InstallGlobalFunction(ParseBibFiles, function(arg) local s, entries, stringlabels, strings, str, file; s := Filtered(arg, x-> IsList(x) and not IsString(x)); if Length(s) > 0 then entries := s[1][1]; stringlabels := s[1][2]; strings := s[1][3]; arg := Filtered(arg, IsString); else entries := []; stringlabels := []; strings := []; fi; for file in arg do str := StringFile(file); if str=fail then str := StringFile(Concatenation(file, ".bib")); fi; if str=fail then Info(InfoBibTools, 1, "#W WARNING: Cannot find bib-file ", file, "[.bib]\n"); return fail; fi; ParseBibStrings(str, [entries, stringlabels, strings]); od; return [entries, stringlabels, strings]; end); InstallGlobalFunction(ParseBibStrings, function(arg) local s, entries, stringlabels, strings, p, r, pb, Type, ende, comp, pos, str; s := Filtered(arg, x-> IsList(x) and not IsString(x)); if Length(s) > 0 then entries := s[1][1]; stringlabels := s[1][2]; strings := s[1][3]; arg := Filtered(arg, IsString); else entries := []; stringlabels := []; strings := []; fi; for str in arg do # find entries p := Position(str, '@'); while p<>fail do r := rec(); # type pb := Position(str, '{', p); s := LowercaseString(StripBeginEnd(str{[p+1..pb-1]}, WHITESPACE)); p := pb; if s = "string" then # a string is normalized and stored for later substitutions pb := Position(str, '=', p); Add(stringlabels, LowercaseString(StripBeginEnd(str{[p+1..pb-1]}, WHITESPACE))); p := pb; pb := PositionMatchingDelimiter(str, "{}", p); s := StripBeginEnd(str{[p+1..pb-1]}, WHITESPACE); if (s[1]='\"' and s[Length(s)]='\"') or (s[1]='{' and s[Length(s)]='}') then s := s{[2..Length(s)-1]}; fi; Add(strings, s); p := pb; else # type and label of entry r := rec(From := rec(BibTeX := true), Type := s); # end of bibtex entry, for better recovery from errors ende := PositionMatchingDelimiter(str, "{}", p); pb := Position(str, ',', p); if not IsInt(pb) or pb > ende then # doesn't seem to be a correct entry, ignore p := Position(str, '@', ende); continue; fi; r.Label := StripBeginEnd(str{[p+1..pb-1]}, WHITESPACE); p := pb; # get the components pb := Position(str, '=', p); while pb<>fail and pb < ende do comp := LowercaseString(StripBeginEnd(str{[p+1..pb-1]}, Concatenation(",", WHITESPACE))); pb := pb+1; while str[pb] in WHITESPACE do pb := pb+1; od; p := pb; if str[p] = '\"' then pb := Position(str, '\"', p); # if double quote is escaped, then go to next one while str[pb-1]='\\' do pb := Position(str, '\"', pb); od; r.(comp) := str{[p+1..pb-1]}; elif str[p] = '{' then pb := PositionMatchingDelimiter(str, "{}", p); r.(comp) := str{[p+1..pb-1]}; else pb := p+1; while (not str[pb] in WHITESPACE) and str[pb] <> ',' and str[pb] <> '}' do pb := pb+1; od; s := str{[p..pb-1]}; # number if Int(s)<>fail then r.(comp) := s; else # abbrev string, look up and substitute s := LowercaseString(s); pos := Position(stringlabels, s); if pos=fail then r.(comp) := Concatenation("STRING-NOT-KNOWN: ", s); else r.(comp) := strings[pos]; fi; fi; fi; p := pb+1; pb := Position(str, '=', p); od; Add(entries, r); fi; p := Position(str, '@', p); od; od; return [entries, stringlabels, strings]; end); ## <#GAPDoc Label="NormalizeNameAndKey"> ## <ManSection > ## <Func Arg="namestr" Name="NormalizedNameAndKey" /> ## <Returns>list of strings and names as lists</Returns> ## <Func Arg="r" Name="NormalizeNameAndKey" /> ## <Returns>nothing</Returns> ## <Description> ## The argument <A>namestr</A> must be a string describing an author or a list ## of authors as described in the &BibTeX; documentation in <Cite Key="La85" ## Where="Appendix B 1.2"/>. The function <Ref Func="NormalizedNameAndKey" ## /> returns a list of the form [ normalized name string, short key, long ## key, names as lists]. The first entry is a normalized form ## of the input where names are written as <Q>lastname, first name ## initials</Q>. The second and third entry are the name parts of a short and ## long key for the bibliography entry, formed from the (initials of) last ## names. The fourth entry is a list of lists, one for each name, where a ## name is described by three strings for the last name, the first name ## initials and the first name(s) as given in the input. <P/> ## ## The function <Ref Func="NormalizeNameAndKey"/> gets as argument <A>r</A> ## a record for a bibliography entry as returned by <Ref Func="ParseBibFiles" ## />. It substitutes <C>.author</C> and <C>.editor</C> fields of <A>r</A> by ## their normalized form, the original versions are stored in fields ## <C>.authororig</C> and <C>.editororig</C>.<P/> ## ## Furthermore a short and a long citation key is generated and stored ## in components <C>.printedkey</C> (only if no <C>.key</C> is already ## bound) and <C>.keylong</C>.<P/> ## ## We continue the example from <Ref Func="ParseBibFiles" />. ## ## <Example> ## gap> bib := ParseBibFiles("doc/test.bib");; ## gap> NormalizedNameAndKey(bib[1][1].author); ## [ "First, F. A. and Sec, X. Y.", "FS", "firstsec", ## [ [ "First", "F. A.", "Fritz A." ], [ "Sec", "X. Y.", "X. Y." ] ] ] ## gap> NormalizeNameAndKey(bib[1][1]); ## gap> bib[1][1]; ## rec( From := rec( BibTeX := true ), Label := "AB2000", ## Type := "article", author := "First, F. A. and Sec, X. Y.", ## authororig := "Fritz A. First and Sec, X. Y.", ## journal := "Important Journal", keylong := "firstsec2000", ## printedkey := "FS00", title := "Short", year := "2000" ) ## </Example> ## </Description> ## </ManSection> ## <#/GAPDoc> ## InstallGlobalFunction(NormalizeNameAndKey, function(b) local yy, y, names, nn; if IsBound(b.year) then if IsInt(b.year) then yy := String(b.year); y := String(b.year mod 100); else yy := b.year; y := b.year{[Length(b.year)-1, Length(b.year)]}; fi; else yy := ""; y := ""; fi; for names in ["author", "editor"] do if IsBound(b.(names)) then nn := NormalizedNameAndKey(b.(names)); if nn[1] <> b.(names) then b.(Concatenation(names, "orig")) := b.(names); b.(names) := nn[1]; fi; if not IsBound(b.key) then b.printedkey := Concatenation(nn[2], y); fi; if not IsBound(b.keylong) then b.keylong := Concatenation(nn[3], yy); fi; fi; od; if not IsBound(b.keylong) then b.keylong := "xxx"; fi; if not (IsBound(b.key) or IsBound(b.printedkey)) then b.printedkey := "xxx"; fi; end); # small utility BindGlobal("AndToCommaNames", function(str) local n, p, i; str := NormalizedWhitespace(str); n := 0; p := PositionSublist(str, " and "); while p <> fail do n := n+1; p := PositionSublist(str, " and ", p); od; for i in [1..n-1] do str := SubstitutionSublist(str, " and ", ", ", false); od; if n > 1 then str := SubstitutionSublist(str, " and ", ", and ", false); fi; return str; end); # print out a bibtex entry, the ordering of fields is normalized and # type and field names are in lowercase, also some formatting is done # arg: entry[, abbrevs, texts] where abbrevs and texts are lists # of same length abbrevs[i] is string macro for texts[i] InstallGlobalFunction(StringBibAsBib, function(arg) local r, abbrevs, texts, res, ind, fieldlist, pos, lines, comp; # scan arguments r := arg[1]; if Length(arg)>2 then abbrevs := arg[2]; texts := arg[3]; else abbrevs := []; texts := []; fi; res := ""; if not IsBound(r.Label) then Info(InfoBibTools, 1, "#W WARNING: no .Label in Bib-record"); Info(InfoBibTools, 2, ":\n", r); Info(InfoBibTools, 1, "\n"); return fail; fi; ind := RepeatedString(' ', 22); fieldlist := [ "author", "editor", "booktitle", "title", "journal", "month", "organization", "institution", "publisher", "school", "edition", "series", "volume", "number", "address", "year", "pages", "chapter", "crossref", "note", "notes", "howpublished", "key", "coden", "fjournal", "isbn", "issn", "location", "mrclass", "mrnumber", "mrreviewer", "organisation", "reviews", "source", "url", "keywords" ]; Append(res, Concatenation("@", r.Type, "{ ", r.Label)); for comp in Concatenation(fieldlist, Difference(NamesOfComponents(r), Concatenation(fieldlist, ["From", "Type", "Label","authorAsList", "editorAsList"]) )) do if IsBound(r.(comp)) then Append(res, Concatenation(",\n ", comp, " = ", ListWithIdenticalEntries(16-Length(comp), ' '))); pos := Position(texts, r.(comp)); if pos <> fail then Append(res, abbrevs[pos]); else Append(res, "{"); lines := FormatParagraph(r.(comp), SizeScreen()[1]-26, "both", [ind, ""]); Append(res, lines{[Length(ind)+1..Length(lines)-1]}); Append(res, "}"); fi; fi; od; Append(res, "\n}\n"); return res; end); InstallGlobalFunction(PrintBibAsBib, function(arg) PrintFormattedString(CallFuncList(StringBibAsBib, arg)); end); ## <#GAPDoc Label="WriteBibFile"> ## <ManSection > ## <Func Arg="bibfile, bib" Name="WriteBibFile" /> ## <Returns>nothing</Returns> ## <Description> ## This is the converse of <Ref Func="ParseBibFiles"/>. Here ## <A>bib</A> either must have a format as list of three lists ## as it is returned by <Ref ## Func="ParseBibFiles"/>. Or <A>bib</A> can be a record as returned ## by <Ref Func="ParseBibXMLextFiles"/>. ## A &BibTeX; file <A>bibfile</A> is written ## and the entries are formatted in a uniform way. All given ## abbreviations are used while writing this file.<P/> ## ## We continue the example from <Ref Func="NormalizeNameAndKey"/>. ## The command ## ## <Example> ## gap> WriteBibFile("nicer.bib", bib); ## </Example> ## ## produces a file <F>nicer.bib</F> as follows: ## ## <Listing Type="nicer.bib"> ## @string{j = "Important Journal" } ## ## @article{ AB2000, ## author = {First, F. A. and Sec, X. Y.}, ## title = {Short}, ## journal = j, ## year = {2000}, ## authororig = {Fritz A. First and Sec, X. Y.}, ## keylong = {firstsec2000}, ## printedkey = {FS00} ## } ## </Listing> ## </Description> ## </ManSection> ## <#/GAPDoc> ## InstallGlobalFunction(WriteBibFile, function(file, bib) local p, b3, a, b, pos, f; if IsRecord(bib) and IsBound(bib.entries) and IsBound(bib.strings) then b := bib; bib := []; bib[1] := List(b.entries, a-> RecBibXMLEntry(a, "BibTeX", b.strings)); bib[2] := List(b.strings, a-> a[1]); bib[3] := List(b.strings, a-> a[2]); fi; # collect abbrevs p := []; SortParallel(bib[3], bib[2]); b3 := Immutable(bib[3]); IsSet(b3); for a in bib[1] do for b in NamesOfComponents(a) do pos := Position(b3, a.(b)); if pos <> fail then Add(p, pos); fi; od; od; p := Set(p); f := function() local i, a; Print("\n\n"); # the `string's for i in p do Print("@string{", bib[2][i], " = \"", b3[i], "\" }\n"); od; Print("\n\n"); for a in bib[1] do PrintBibAsBib(a, bib[2], b3); od; end; PrintTo1(file, f); end); # a utility for translating LaTeX macros for non ascii characters into # HTML entities; also removing {}'s, and "\-" hyphenation hints. BindGlobal("LaTeXToHTMLString", function(str) local trans_bs, trans_qq, i, pos; # macros for accents starting with '\', add new entries - somehow more # frequent ones first - as they become necessary trans_bs := [ ["\\\"a","ä"], ["\\\"o","ö"], ["\\\"u","ü"], ["\\\"{a}","ä"], ["\\\"{o}","ö"], ["\\\"{u}","ü"], ["\\\"{s}","ß"], ["\\\"s","ß"], ["\\3","ß"], ["\\ss","ß"], ["\\\"A","Ä"], ["\\\"O","Ö"], ["\\\"U","Ü"], ["\\'e","é"], ["\\`e","è"], ["\\'E","É"], ["\\`E","È"], ["\\'a","á"], ["\\`a","à"], ["\\c{c}", "ç"], ["\\c c", "ç"], # long Hungarian umlaut, substituted by unicode entity # (see http://www.unicode.org/charts/) ["\\H{o}", "ő"], ["\\H o", "ő"], ["\\'A","Á"], ["\\'I","Í"], ["\\'O","Ó"], ["\\'U","Ú"], ["\\'i","í"], ["\\'o","ó"], ["\\'u","ú"], ["\\`A","À"], ["\\`I","Ì"], ["\\`O","Ò"], ["\\`U","Ù"], ["\\`i","ì"], ["\\`o","ò"], ["\\`u","ù"] ]; # and some starting with '"' from 'german' styles trans_qq := [ ["\"a","ä"], ["\"o","ö"], ["\"u","ü"], ["\"s","ß"], ["\"A","Ä"], ["\"O","Ö"], ["\"U","Ü"] ]; i := 0; pos := Position(str, '\\'); while pos <> fail and i < Length(trans_bs) do i := i + 1; str := ReplacedString(str, trans_bs[i][1], trans_bs[i][2]); pos := Position(str, '\\'); od; i := 0; pos := Position(str, '\"'); while pos <> fail and i < Length(trans_qq) do i := i + 1; str := ReplacedString(str, trans_qq[i][1], trans_qq[i][2]); pos := Position(str, '\"'); od; # throw away {}'s and "\-"'s if Position(str, '{') <> fail then str := Filtered(str, c-> c <> '{' and c <> '}'); fi; str := ReplacedString(str, "\\-", ""); return str; end); ## arg: r[, escape] (with escape = false it is assumed that entries are ## already HTML) InstallGlobalFunction(StringBibAsHTML, function(arg) local r, i, str, res, esc, key, mrnumber, booklike; r := arg[1]; if Length(arg)=2 then esc := arg[2]; else if IsBound(r.From) and IsBound(r.From.BibXML) and r.From.BibXML = true then esc := false; else esc := true; fi; fi; if not IsBound(r.Label) then Info(InfoBibTools, 1, "#W WARNING: no .Label in Bib-record"); Info(InfoBibTools, 2, ":\n", r); Info(InfoBibTools, 1, "\n"); return fail; fi; # some details are set differently for book-like references if r.Type in [ "book", "booklet", "manual", "techreport", "mastersthesis", "phdthesis", "proceedings" ] then booklike := true; else booklike := false; fi; res := ""; # remove SGML markup characters in entries and translate # LaTeX macros for accented characters to HTML, remove {}'s if esc = true then r := ShallowCopy(r); for i in NamesOfComponents(r) do if IsString(r.(i)) then str := ""; GAPDoc2HTMLProcs.PCDATAFILTER(rec(content := r.(i)), str); if str <> r.(i) then r.(i) := str; fi; r.(i) := LaTeXToHTMLString(r.(i)); if i in ["title", "subtitle", "booktitle"] then r.(i) := Filtered(r.(i), x -> not x in "{}"); fi; fi; od; fi; if IsBound(r.key) then key := r.key; elif IsBound(r.printedkey) then key := r.printedkey; else key := r.Label; fi; if IsBound(r.mrnumber) then mrnumber:= r.mrnumber; if ' ' in mrnumber then mrnumber:= mrnumber{ [ 1 .. Position( mrnumber, ' ' ) - 1 ] }; fi; Append(res, Concatenation( "<p class='BibEntry'>\n[<span class='BibKeyLink'><a href=\"http://www.ams.org/mathscinet-getitem?mr=", mrnumber, "\">", key, "</a></span>] ")); else Append(res, Concatenation("<p class='BibEntry'>\n[<span class='BibKey'>", key, "</span>] ")); fi; # standard BibTeX-styles typeset a type if not given if r.Type = "phdthesis" and not IsBound(r.type) then r := ShallowCopy(r); r.type := "Ph.D. thesis"; elif r.Type = "mastersthesis" and not IsBound(r.type) then r := ShallowCopy(r); r.type := "Master's thesis"; fi; # we assume with the "," delimiters that at least one of .author, # .editor or .title exist if IsBound(r.author) then Append(res, Concatenation("<b class='BibAuthor'>", AndToCommaNames(r.author),"</b>")); fi; if IsBound(r.editor) then if PositionSublist( r.editor, " and " ) = fail then Append(res, Concatenation(" (<span class='BibEditor'>", AndToCommaNames(r.editor), "</span>, Ed.)")); else Append(res, Concatenation(" (<span class='BibEditor'>", AndToCommaNames(r.editor), "</span>, Eds.)")); fi; fi; if IsBound(r.title) then if ForAny(["author", "editor"], a-> IsBound(r.(a))) then Add(res, ','); fi; Append(res, Concatenation("\n <i class='BibTitle'>", r.title, "</i>")); fi; if IsBound(r.booktitle) then Append( res, ",\n " ); if r.Type in ["inproceedings", "incollection"] then Append(res, " in "); fi; Append(res, Concatenation(" <i class='BibBooktitle'>", r.booktitle, "</i>")); fi; if IsBound(r.subtitle) then Append(res, Concatenation("\n <i class='BibSubtitle'>–", r.subtitle, "</i>")); fi; if IsBound(r.journal) then Append(res, Concatenation(",\n <span class='BibJournal'>", r.journal, "</span>")); fi; if IsBound(r.type) then Append(res, Concatenation(",\n <span class='BibType'>", r.type, "</span>")); fi; if IsBound(r.organization) then Append(res, Concatenation(",\n <span class='BibOrganization'>", r.organization, "</span>")); fi; if IsBound(r.institution) then Append(res, Concatenation(",\n <span class='BibOrganization'>", r.institution, "</span>")); fi; if IsBound(r.publisher) then Append(res, Concatenation(",\n <span class='BibPublisher'>", r.publisher, "</span>")); fi; if IsBound(r.school) then Append(res, Concatenation(",\n <span class='BibSchool'>", r.school, "</span>")); fi; if IsBound(r.edition) then Append(res, Concatenation(",\n <span class='BibEdition'>", r.edition, " edition", "</span>")); fi; if IsBound(r.series) then Append(res, Concatenation(",\n <span class='BibSeries'>", r.series, "</span>")); fi; if IsBound(r.volume) then Append(res, Concatenation(",\n <em class='BibVolume'>", r.volume, "</em>")); fi; if IsBound(r.number) then Append(res, Concatenation(" (<span class='BibNumber'>", r.number, "</span>)")); fi; if IsBound(r.address) then Append(res, Concatenation(",\n <span class='BibAddress'>", r.address, "</span>")); fi; if IsBound(r.year) then Append(res, Concatenation("\n (<span class='BibYear'>", r.year, "</span>)")); fi; if IsBound(r.pages) then if booklike then Append(res, Concatenation(",\n <span class='BibPages'>", r.pages, " pages</span>")); else Append(res, Concatenation(",\n <span class='BibPages'>", r.pages, "</span>")); fi; fi; if IsBound(r.chapter) then Append(res, Concatenation(",\n <span class='BibChapter'>Chapter ", r.chapter, "</span>")); fi; if IsBound(r.note) then Append(res, Concatenation("<br />\n(<span class='BibNote'>", r.note, "</span>", ")")); fi; if IsBound(r.notes) then Append(res, Concatenation("<br />\n(<span class='BibNotes'>", r.notes, "</span>", ")")); fi; if IsBound(r.howpublished) then Append(res, Concatenation(",\n<span class='BibHowpublished'>", r.howpublished, "</span>")); fi; if IsBound(r.BUCHSTABE) then Append(res, Concatenation("<br />\nEinsortiert unter ", r.BUCHSTABE, ".<br />\n")); fi; if IsBound(r.LDFM) then Append(res, Concatenation("Signatur ", r.LDFM, ".<br />\n")); fi; if IsBound(r.BUCHSTABE) and i>=0 then Append(res, Concatenation("<a href=\"HTMLldfm", r.BUCHSTABE, ".html#", i, "\"><span style=\"color: red;\">BibTeX Eintrag</span></a>\n<br />")); elif not ( IsBound( r.BUCHSTABE ) or IsBound( r.LDFM ) ) then Append( res, ".\n" ); fi; Append(res, "</p>\n\n"); return res; end); InstallGlobalFunction(PrintBibAsHTML, function(arg) PrintFormattedString(CallFuncList(StringBibAsHTML, arg)); end); ## arg: r[, ansi] (for link to BibTeX) InstallGlobalFunction(StringBibAsText, function(arg) local r, ansi, str, txt, s, f, field, booklike; r := arg[1]; ansi := rec( Bib_reset := TextAttr.reset, Bib_author := Concatenation(TextAttr.bold, TextAttr.1), ## Bib_editor := ~.Bib_author, Bib_title := TextAttr.4, ## Bib_subtitle := ~.Bib_title, Bib_journal := "", Bib_volume := TextAttr.4, Bib_Label := TextAttr.3, Bib_edition := ["", ""], Bib_year := ["", ""], Bib_note := ["(", ")"], Bib_chapter := ["Chapter ", ""], ); ansi.Bib_editor := ansi.Bib_author; ansi.Bib_subtitle := ansi.Bib_title; if Length(arg) = 2 and arg[2] <> true then for f in RecNames(arg[2]) do ansi.(f) := arg[2].(f); od; elif IsBound(r.From) and IsBound(r.From.options) and IsBound(r.From.options.ansi) then for f in RecNames(r.From.options.ansi) do ansi.(f) := r.From.options.ansi.(f); od; else for f in RecNames(ansi) do ansi.(f) := ""; od; fi; # some details are set differently for book-like references if r.Type in [ "book", "booklet", "manual", "techreport", "mastersthesis", "phdthesis", "proceedings" ] then booklike := true; else booklike := false; fi; if not IsBound(r.Label) then Info(InfoBibTools, 1, "#W WARNING: no .Label in Bib-record"); Info(InfoBibTools, 2, ":\n", r); Info(InfoBibTools, 1, "\n"); return; fi; str := ""; # helper adds markup txt := function(arg) local field, s, pp, pre, post; field := arg[1]; if Length(arg) > 1 then s := arg[2]; elif IsBound(r.(field)) then s := r.(field); else return; fi; if not IsBound(ansi.(Concatenation("Bib_", field))) then Append(str, s); else pp := ansi.(Concatenation("Bib_", field)); if not IsString(pp) then pre := pp[1]; post := pp[2]; else pre := pp; post := ansi.Bib_reset; fi; Append(str, pre); Append(str, s); Append(str, post); fi; end; if IsBound(r.key) then s := r.key; elif IsBound(r.printedkey) then s := r.printedkey; else s := r.Label; fi; Add(str, '['); txt("Label", s); Append(str, "] "); # we assume with the "," delimiters that at least one of .author, # .editor or .title exist txt("author"); if IsBound(r.editor) then Append(str, " ("); txt("editor"); if PositionSublist( r.editor, " and " ) = fail then Append(str, ", Ed.)"); else Append(str, ", Eds.)"); fi; fi; if IsBound(r.title) then if IsBound(r.author) or IsBound(r.editor) then Append(str, ", "); fi; txt("title"); fi; if IsBound(r.booktitle) then Append(str, ", "); if r.Type in ["inproceedings", "incollection"] then Append(str, " in "); fi; txt("booktitle"); fi; if IsBound(r.subtitle) then Append(str, "–"); txt("subtitle"); fi; # standard BibTeX-styles typeset a type if not given if r.Type = "phdthesis" and not IsBound(r.type) then r := ShallowCopy(r); r.type := "Ph.D. thesis"; elif r.Type = "mastersthesis" and not IsBound(r.type) then r := ShallowCopy(r); r.type := "Master's thesis"; fi; for field in [ "journal", "type", "organization", "institution", "publisher", "school", "edition", "series", "volume", "number", "address", "year", "pages", "chapter", "note", "notes", "howpublished" ] do if IsBound(r.(field)) then if field = "year" then Append(str, " ("); txt(field); Append(str, ")"); continue; elif field = "pages" then if booklike then Append(str, ", "); txt(field); Append(str, " pages"); else ## Append(str, ", p. "); Append(str, ", "); txt(field); fi; continue; elif field = "edition" then Append(str, ", "); txt(field); Append(str, " edition"); continue; elif field in ["note", "notes"] then Append(str, ",\n ("); txt(field); Append(str, ")"); continue; elif field = "chapter" then Append(str, ", Chapter "); txt(field); continue; else Append(str, ", "); fi; txt(field); fi; od; # some LDFM specific if IsBound(r.BUCHSTABE) then Append(str, Concatenation(", Einsortiert unter ", r.BUCHSTABE)); fi; if IsBound(r.LDFM) then Append(str, Concatenation(", Signatur ", r.LDFM)); fi; ## str := FormatParagraph(Filtered(str, x-> not x in "{}"), 72); Add(str, '.'); if Unicode(str, "UTF-8") <> fail then str := FormatParagraph(str, SizeScreen()[1]-4, WidthUTF8String); else str := FormatParagraph(str, SizeScreen()[1]-4); fi; Add(str, '\n'); return str; end); InstallGlobalFunction(PrintBibAsText, function(arg) PrintFormattedString(CallFuncList(StringBibAsText, arg)); end); ## <#GAPDoc Label="SearchMRSection"> ## <Section Label="MathSciNet"> ## <Heading>Getting &BibTeX; entries from ## <Package>MathSciNet</Package></Heading> ## We provide utilities to access the <URL ## ><Link>http://www.ams.org/mathscinet/</Link><LinkText><Package> ## MathSciNet</Package></LinkText></URL> ## data base from within GAP. One condition for this to work is that the ## <Package>IO</Package>-package <Cite Key="IO"/> is available. The other is, ## of course, that you use these functions from a computer which has access to ## <Package>MathSciNet</Package>.<P/> ## ## Please note, that the usual license for <Package>MathSciNet</Package> ## access does not allow for automated searches in the database. Therefore, ## only use the <Ref Func="SearchMR" /> function for single queries, as you ## would do using your webbrowser.<P/> ## ## <ManSection > ## <Func Arg="qurec" Name="SearchMR" /> ## <Func Arg="bib" Name="SearchMRBib" /> ## <Returns>a list of strings, a string or <K>fail</K></Returns> ## <Description> ## The first function <Ref Func="SearchMR"/> provides the same functionality ## as the Web interface <URL ## ><Link>http://www.ams.org/mathscinet/</Link><LinkText><Package> ## MathSciNet</Package></LinkText></URL>. The query strings must be given as ## a record, and the following components of this record are recognized: ## <C>Author</C>, <C>AuthorRelated</C>, <C>Title</C>, <C>ReviewText</C>, ## <C>Journal</C>, <C>InstitutionCode</C>, <C>Series</C>, <C>MSCPrimSec</C>, ## <C>MSCPrimary</C>, <C>MRNumber</C>, <C>Anywhere</C>, <C>References</C> ## and <C>Year</C>. ## <P/> ## Furthermore, the component <C>type</C> can be specified. It can be one of ## <C>"bibtex"</C> (the default if not given), <C>"pdf"</C>, <C>"html"</C> and ## probably others. In the last cases the function returns a string with ## the correspondig PDF-file or web page from <Package>MathSciNet</Package>. ## In the first case the <Package>MathSciNet</Package> interface returns a web ## page with &BibTeX; entries, for convenience this function returns a list ## of strings, each containing the &BibTeX; text for a single result entry. ## <P/> ## The format of a <C>.Year</C> component can be either a four digit number, ## optionally preceded by one of the characters <C>'<'</C>, ## <C>'>'</C> or <C>'='</C>, or it can be two four digit numbers ## separated by a <C>-</C> to specify a year range.<P/> ## ## The function <Ref Func="SearchMRBib"/> gets a record of a parsed &BibTeX; ## entry as input as returned by <Ref Func="ParseBibFiles"/> or <Ref ## Func="ParseBibStrings"/>. It tries to generate some sensible input from this ## information for <Ref Func="SearchMR"/> and calls that function. <P/> ## ## <Example> ## gap> ll := SearchMR(rec(Author:="Gauss", Title:="Disquisitiones"));; ## gap> ll2 := List(ll, HeuristicTranslationsLaTeX2XML.Apply);; ## gap> bib := ParseBibStrings(Concatenation(ll2));; ## gap> bibxml := List(bib[1], StringBibAsXMLext);; ## gap> bib2 := ParseBibXMLextString(Concatenation(bibxml));; ## gap> for b in bib2.entries do ## > PrintFormattedString(StringBibXMLEntry(b, "Text")); od; ## [Gau95] Gauss, C. F., Disquisitiones arithmeticae, Academia ## Colombiana de Ciencias Exactas, Físicas y Naturales, Bogotá, ## Colección Enrique Pérez Arbeláez [Enrique Pérez Arbeláez ## Collection], 10 (1995), xliv+495 pages, (Translated from the Latin ## by Hugo Barrantes Campos, Michael Josephy and Ángel Ruiz Zúñiga, ## With a preface by Ruiz Zúñiga). ## ## [Gau86] Gauss, C. F., Disquisitiones arithmeticae, Springer-Verlag, ## New York (1986), xx+472 pages, (Translated and with a preface by ## Arthur A. Clarke, Revised by William C. Waterhouse, Cornelius ## Greither and A. W. Grootendorst and with a preface by Waterhouse). ## ## [Gau66] Gauss, C. F., Disquisitiones arithmeticae, Yale University ## Press, New Haven, Conn.-London, Translated into English by Arthur A. ## Clarke, S. J (1966), xx+472 pages. ## ## </Example> ## </Description> ## ## ## </ManSection> ## ## ## </Section> ## <#/GAPDoc> SEARCHMRHOST := "www.ams.org"; ## SEARCHMRHOST := "ams.math.uni-bielefeld.de"; if not IsBound(SingleHTTPRequest) then SingleHTTPRequest := 0; fi; InstallGlobalFunction(SearchMR, function(r) local trans, uri, i, l, res, extr, a, b; trans := [["Author", "AUCN"], ["AuthorRelated","ICN"], ["Title","TI"], ["ReviewText","RT"],["Journal","JOUR"],["InstitutionCode","IC"], ["Series","SE"],["MSCPrimSec","CC"],["MSCPrimary","PC"], ["MRNumber","MR"],["Anywhere","ALLF"],["References","REFF"]]; if LoadPackage("IO") <> true then Print("SearchMR not available because IO package not available.\n"); return fail; fi; if not IsBound(r.type) then r.type := "bibtex"; fi; uri := Concatenation("/mathscinet/search/publications.html?fmt=", r.type); if IsBound(r.Year) then if '-' in r.Year then extr := SplitString(r.Year,"","- "); Append(uri, "&dr=yearrange&yearRangeFirst="); Append(uri, extr[1]); Append(uri, "&yearRangeSecond="); Append(uri, extr[2]); else Append(uri, "&dr=pubyear&arg3="); Append(uri, Filtered(r.Year, c-> not c in "<>=")); if r.Year[1] = '<' then Append(uri, "&yrop=lt"); elif r.Year[1] = '>' then Append(uri, "&yrop=gt"); else Append(uri, "&yrop=eq"); fi; fi; fi; i := 4; for a in trans do if IsBound(r.(a[1])) then if IsString(r.(a[1])) then l := [r.(a[1])]; else l := r.(a[1]); fi; for b in l do Append(uri, Concatenation("&pg", String(i), "=", a[2], "&s", String(i), "=", Encode(Unicode(b),"URL"))); if i = 9 then break; else i := i+1; fi; od; fi; if i = 9 then break; fi; od; # get all entries Append(uri, "&extend=1"); res := SingleHTTPRequest(SEARCHMRHOST, 80, "GET", uri, rec(), false, false); while res.statuscode = 302 do res := SingleHTTPRequest(SEARCHMRHOST, 80, "GET", res.header.location, rec(), false, false); od; if not IsBound(res.body) then Info(InfoBibTools, 1, "Cannot reach MathSciNet service."); return fail; fi; if r.type = "bibtex" then i := PositionSublist(res.body, "<pre>\n@", i); extr := []; while i <> fail do Add(extr, res.body{[i+5..PositionSublist(res.body, "</pre>", i)-1]}); i := PositionSublist(res.body, "<pre>\n@", i); od; return extr; else return res.body; fi; end); # args: record[, type] # records like entry from ParseBibStrings/Files, default for type is "bibtex" InstallGlobalFunction(SearchMRBib, function(arg) local nn, tt, r, a, f; a := arg[1]; if IsBound(a.mrnumber) then r := rec(MRNumber := a.mrnumber); if ' ' in r.MRNumber then r.MRNumber := r.MRNumber{[1..Position(r.MRNumber, ' ')-1]}; fi; else a := ShallowCopy(a); for f in RecNames(a) do if IsString(a.(f)) then a.(f) := HeuristicTranslationsLaTeX2XML.Apply(a.(f)); fi; od; if IsBound(a.author) then a.author := SubstitutionSublist(a.author, "~", " "); nn := NormalizedNameAndKey(a.author)[4]; elif IsBound(a.editor) then a.editor := SubstitutionSublist(a.editor, "~", " "); nn := NormalizedNameAndKey(a.editor)[4]; else nn := [[""]]; fi; # up to three longest words from title tt := SubstitutionSublist(a.title, "{", ""); tt := SubstitutionSublist(tt, "}", ""); tt := NormalizedWhitespace(tt); tt := WordsString(tt); SortParallel(List(tt, w-> 1000-Length(w)), tt); tt := tt{[1..Minimum(3, Length(tt))]}; r := rec( Author := List(nn, a->a[1]), Title := tt); fi; if Length(arg) > 1 then r.type := arg[2]; fi; return SearchMR(r); end); if SingleHTTPRequest = 0 then Unbind(SingleHTTPRequest); fi; ## <#GAPDoc Label="LabelsFromBibTeX"> ## <ManSection > ## <Func Arg="path, keys, bibfiles, style" Name="LabelsFromBibTeX" /> ## <Returns>a list of pairs of strings <C>[key, label]</C></Returns> ## <Description> ## This function uses <C>bibtex</C> to determine the ordering of a list ## of references and a label for each entry which is typeset in a ## document citing these references. ## <P/> ## The argument <A>path</A> is a directory specified as string or ## directory object. The argument <A>bibfiles</A> must be a list of files ## in &BibTeX; format, each specified by a path relative to the ## first argument, or an absolute path (starting with <C>'/'</C>) or ## relative to the &GAP; roots (starting with <C>"gap://"</C>). The list ## <A>keys</A> must contain strings which occur as keys in the given ## &BibTeX; files. Finally the string <A>style</A> must be the name of a ## bibliography style (like <C>"alpha"</C>). <P/> ## ## The list returned by this function contains pairs <C>[key, label]</C> ## where <C>key</C> is one of the entries of <A>keys</A> and <C>label</C> ## is a string used for citations of the bibliography entry in a ## document. These pairs are ordered as the reference list produced by ## &BibTeX;. ## <Example> ## gap> f := Filename(DirectoriesPackageLibrary("gapdoc","doc"), "test.bib");; ## gap> LabelsFromBibTeX(".", ["AB2000"], [f], "alpha"); ## [ [ "AB2000", "FS00" ] ] ## </Example> ## </Description> ## </ManSection> ## <#/GAPDoc> ## InstallGlobalFunction(LabelsFromBibTeX, function(path, keys, bibfiles, style) local aux, flist, poss, d, dstr, auxfile, out, res, lab, pos, k, n, i; aux := ""; # keys of citations for k in keys do Append(aux, "\\citation{"); Append(aux, k); Append(aux, "}\n"); od; if IsString(path) then path := Directory(path); fi; # file names of bib-files flist := []; for n in bibfiles do if Length(n) > 0 and n[1] = '/' then Add(flist, n); elif Length(n) > 5 and n{[1..6]} = "gap://" then Add(flist, FilenameGAP(n)); else Add(flist, n); fi; od; poss := Positions(flist, fail); if Length(poss) > 0 then Error("Cannot generate path for bibfiles ",bibfiles{poss},".\n"); return fail; fi; Append(aux, "\\bibdata{"); Append(aux, JoinStringsWithSeparator(flist, ",")); Append(aux, "}\n"); # bibstyle of result Append(aux, "\\bibstyle{"); Append(aux, style); Append(aux, "}\n"); # write out, call bibtex, filter \bibitem lines from result d := DirectoryTemporary(); dstr := Filename(d, ""); auxfile := Filename(d, "temp.aux"); FileString(auxfile, aux); Exec(Concatenation("(export TEXMFOUTPUT=", dstr, "; cd ", Filename(path, ""), "; bibtex ", dstr, "/temp > /dev/null 2>&1 ; ", "grep '^\\\\bibitem' ", dstr, "/temp.bbl > ", dstr, "/out )")); out := StringFile(Filename(d, "out")); if out = fail then Error("Call of 'bibtex' was not successful.\n"); return fail; fi; # clean temporary directory for n in DirectoryContents(d) do if not n in [".", ".."] then RemoveFile(Filename(d, n)); fi; od; # ??? RemoveDir(Filename(d,"")); # parse result out := SplitString(out, "", "\n"); res := []; for i in [1..Length(out)] do n := out[i]; if n[9] = '[' then pos := Position(n, ']', 9); lab := n{[10..pos-1]}; pos := Position(n, '{', pos); else lab := String(i); pos := Position(n, '{', 8); fi; Add(res, [n{[pos+1..PositionMatchingDelimiter(n, "{}", pos)-1]}, lab]); od; return res; end);