diff options
author | Hans Hagen <pragma@wxs.nl> | 2009-10-16 16:13:00 +0200 |
---|---|---|
committer | Hans Hagen <pragma@wxs.nl> | 2009-10-16 16:13:00 +0200 |
commit | 7f9b179ad5be5000f67192f283d20e7120402bd9 (patch) | |
tree | 18f83a8cbfe7fed1c2a6939fb4b2cf10473abbbe | |
parent | c878054f6360d50885dbdab96643a8f3ac61c46c (diff) | |
download | context-7f9b179ad5be5000f67192f283d20e7120402bd9.tar.gz |
beta 2009.10.16 16:13
93 files changed, 4460 insertions, 8499 deletions
diff --git a/doc/context/bib/bibmod-doc.pdf b/doc/context/bib/bibmod-doc.pdf Binary files differdeleted file mode 100644 index ea1db3160..000000000 --- a/doc/context/bib/bibmod-doc.pdf +++ /dev/null diff --git a/doc/context/bib/bibmod-doc.tex b/doc/context/bib/bibmod-doc.tex deleted file mode 100644 index 2ffdfa9f2..000000000 --- a/doc/context/bib/bibmod-doc.tex +++ /dev/null @@ -1,750 +0,0 @@ - -\usemodule[int-load] -\def\loadsetups{} -\setupinteraction[state=start] -\setupcolors[state=start] - -\usemodule[bib,set-11,mod-01] - - -\startXMLmapping[zero] -\processXMLfilegrouped{t-bib.xml} -\stopXMLmapping - -\setupitemize[each][packed] - -\setuphead[section][page=] - -\setupoutput[pdftex] - -\def\BIBTEX{Bib\TeX} -\def\MAPS{Maps} - - -\startbuffer[bibexample] -\startpublication[k=me, - t=manual, - a=Hoekwater, - y=2006, - s=TH2006, - n=1, - u=http://contextgarden.net/Bibliography] -\author{Taco}[T.]{}{Hoekwater} -\title{\CONTEXT\ Publication Module, The user documententation} -\pubyear{2006} -\note{In case you didn't know: it's the document you are reading now} -\pages{14} -\stoppublication -\stopbuffer - -\getbuffer[bibexample] - -\startmodule[type=tex] - -\startdocumentation - -\module - [ file=bibmod-doc, - version=2006.09.15, - title=Module Documentation, - subtitle=Bibliographies, - author={Taco Hoekwater}, - date=\currentdate, - copyright=Taco Hoekwater] - -\completecontent - -\section{Introduction} - -The bibliographic module (\type{t-bib.tex}) takes care of references -to publications and the typesetting of publication lists, as well as -providing an interface between \BIBTEX and \CONTEXT. This manual -documents version 2009.03.02. - -The bibliographic subsystem consists of the main module -\type{t-bib.tex}; four \BIBTEX\ styles (\type{cont-xx.bst}); and a set -of example configuration files (\type{bibl-xxx.tex}) that set up -specific formatting styles for both the citations and the list of -references. - - -\subsection{General overview} - -A typical input file obeys following structure: -\startitemize[n] -\item A call to \type{\usemodule[bib]}. -\item Optionally, a few setup commands for the bibliographic module. -\item A number of definitions of publications to be referenced in the -main text of the article. The source of these definitions can be -a combination of: - \startitemize - \item The \type{\jobname.bbl} file (automatically read at \type{\starttext}) - \item extra bbl files - \item a file or inline macros before \type{\starttext} - \stopitemize - These possibilities will be explained below. For now, it is - only important to realize that of all these definitions have to be known - {\it before} the first citation in the text. -\item \type{\starttext} -\item The body text, with a number of \type{\cite} and \type{\nocite} commands. -\item The list of publications, called using the command - \type{\placepublications} or the command\break \type{\completepublications}. -\item \type{\stoptext} -\stopitemize - -\section{Setup commands} - -Bibliographic references use a specific `style', a collection of rules -for the use of \type{\cite} as well as for the formatting that is -applied to the publication list. The \CONTEXT\ bibliographic module -expects you to define all of these style options in one single -file of which the names starts with the prefix \type{bibl-}. - -Unlike the normal situation in \LATEX, this style {\it also\/} -includes the formatting of the items themselves. Because of this, the -\type{.bbl} file is set up as a database of entries with fields. - -\subsection{Global settings: \type{\setuppublications}} - -The most important user-level command is -\type{\setuppublications}. Most of the options to this command -shoudl be set by the bibliography style file, but a few of them are of -immediate interest to the casual user as well. - -Like all setup commands, thus command should be given before -\type{\starttext}, as it sets up global information about the -bibliographic references used in the document. \CONTEXT\ needs this -information in order to function correctly. - -\setup{setuppublications} - -\starttabulate[|l|p|] -\NC alternative\NC This gives the name of a bibliography style. \crlf - The chosen style defines the other default options, the options - given in this documentation are the defaults as they are set up - by the `apa' style. When this argument is given, the newly set -style is read in first, before the other options are processed. Thus -allowing you to override specific settings from the chosen style.\NC\NR -\NC refcommand \NC the default option for \type{\cite}\NC \NR -\NC sorttype\NC How the publications in the final publication - list should be sorted. `cite' means: by the order in which - they were first cited in your text. `bbl' tells the - module to keep the relative ordering in which the publication - definitions were found\crlf - The current default for apa is `cite'\NC\NR -\NC criterium\NC Whether to list only the referenced - publications or all of them.\crlf - If this value is `all', then if `sorttype' equals `cite', this - means that all referred-to publications are listed - before all others, otherwise (if `sorttype' equals `bbl') you will - just get a typeset version of the used database(s).\crlf - The default for apa is `used'.\NC\NR -\NC numbering\NC Whether or not the publication list - should be labelled and if so, how. \type{yes} uses the item number in - the publication list as label. \type{short} uses the short - label. \type{bib} - uses the original number in the \BIBTEX\ database as a label. - Anything else turns labelling off.\crlf - The default for apa is `no'\NC\NR -\NC autohang\NC Whether or not the - hanging indent should be re-calculated based on the real size of the - label. This option only applies if numbering is turned on.\crlf - The default is `no'.\NC\NR -\NC monthconversion\NC The presentation form of any month field, if it - is entered in the database as a numeric value. The default is to - typeset the number without any conversion\NC\NR -\stoptabulate - -\subsection{How the entries are formatted: \type{\setuppublicationlist}} - -\setup{setuppublicationlist} - -The list of publications at the end of the article is comparable with -a sequence of normal \CONTEXT\ `list items' that behaves much like the -list that defines the table of contents. {\it In previous versions, it was -in fact implemented as a `normal' \CONTEXT\ list, but this is no -longer the true.\/} - -The module defines a set of extra options. These option names are static, they -do {\it not} change to follow the selected \CONTEXT\ interface language. - -The first two options provide default widths for `autohang': -\starttabulate[|l|p|] -\NC totalnumber\NC The total number of items in the following list (used for autohang).\NC\NR -\NC samplesize\NC The longest short label in the list (used for autohang)\NC\NR -\stoptabulate - -A third option can be used to overrule the use of \type{\title} as -heading for \type{\completepublications} - -\starttabulate[|l|p|] -\NC title\NC The sectioning command.\NC\NR -\stoptabulate - -A fourth option can be used to nullify the printing of `year suffixes' -in cases where the author(s) has written multiple works within a -single year. - -\starttabulate[|l|p|] -\NC maybeyear\NC either \type{on} or \type{off}. Default is \type{on}\NC\NR -\stoptabulate - -The other extra options are needed to control micro||typesetting -of things that are buried deep within macros. There is a separate -command to handle the larger layout options -(\type{\setuppublicationlayout}, explained below), but the options -here are the only way to make changes in the formatting used for -editors', authors', and article authors' names. -\starttabulate[|l|p|] -\NC author \NC command to typeset one author in the publication list.\NC \NR -\NC artauthor \NC command to typeset one article author in the publication list.\NC \NR -\NC editor \NC command to typeset one editor in the publication list.\NC \NR -\NC namesep \NC the separation between consecutive names (either - editors, authors or artauthors).\NC \NR -\NC lastnamesep \NC the separation before the last name in a list of names.\NC \NR -\NC firstnamesep \NC the separation following the fistname or inits - within a name in the publication list.\NC \NR -\NC juniorsep \NC likewise for `junior'.\NC \NR -\NC vonsep \NC likewise for `von'.\NC \NR -\NC surnamesep \NC likewise for surname.\NC \NR -\NC authoretallimit \NC Number of authors needed to trigger `et al.' handling.\NC \NR -\NC authoretaltext \NC Text to show at the end of an abbreviated list.\NC \NR -\NC authoretaldisplay \NC Number of authors to actually display in an abbreviated list.\NC \NR -\NC artauthoretallimit \NC Number of authors needed to trigger `et al.' handling.\NC \NR -\NC artauthoretaltext \NC Text to show at the end of an abbreviated list.\NC \NR -\NC artauthoretaldisplay \NC Number of authors to actually display in an abbreviated list.\NC \NR -\NC editoretallimit \NC Number of editors needed to trigger `et al.' handling.\NC \NR -\NC editoretaltext \NC Text to show at the end of an abbreviated list.\NC \NR -\NC editoretaldisplay \NC Number of editors to actually display in an abbreviated list.\NC \NR -\NC authorcommand \NC A three-argument macro to typeset the list of authors.\NC\NR -\NC artauthorcommand \NC A three-argument macro to typeset the list of authors.\NC\NR -\NC editorcommand \NC A three-argument macro to typeset the list of authors.\NC \NR -\stoptabulate - -The commands after `author' e.g. are predefined -macros that control how a single name is typeset. The four supplied -macros provide formatting that looks like this: - -{\setvalue{@@currentalternative}{data} -\starttabulate[|l|p|] -\NC\tex{invertedauthor}\NC \invertedauthor{Taco}{von}{Hoekwater}{T}{jr}\NC\NR -\NC\tex{invertedshortauthor}\NC \invertedshortauthor{Taco}{von}{Hoekwater}{T}{jr}\NC\NR -\NC\tex{normalauthor}\NC \normalauthor{Taco}{von}{Hoekwater}{T}{jr}\NC\NR -\NC\tex{normalshortauthor}\NC \normalshortauthor{Taco}{von}{Hoekwater}{T}{jr}\NC\NR -\stoptabulate -} -As you can see in the examples, there is a connection between certain -styles of displaying a name and the punctuation used. Punctuation in -this document has been set up by the `apa' style, and that style makes -sure that \type{\invertedshortauthor} looks good, since that is the default -command for `apa' style. (Keep in mind that the comma at the end of the -author will be inserted by either `namesep' or `lastnamesep'.) - -In case you are not happy with the predefined macros; it is quite simple to -define one of these macros yourself, it is a simple macro with 5 -arguments: firstnames, von-part, surname, inits, junior. - -For example, here is the definition of \type{\normalauthor}, -\starttyping -\def\normalauthor#1#2#3#4#5% - {\bibdoif{#1}{#1\bibalternative{firstnamesep}}% - \bibdoif{#2}{#2\bibalternative{vonsep}}% - #3% - \bibdoif{#5}{\bibalternative{surnamesep}#5\unskip}} -\stoptyping -but commands can be a lot simpler, like this: -\starttyping -\def\surnameonly#1#2#3#4#5{#3} -\setuppublicationlist[editor=\surnameonly] -\stoptyping - -The three-argument macro after `authorcommand' etc. can be used to -overrule the typesetting of the list of authors (normally done by the -internal macro \type{\dospecialbibinsert}). This is mostly a hook for -duplicated author lists in the publication list, that can be handled -like so: - -\starttyping -\def\oldlist{} -\def\AbbreviateAuthors#1#2#3% - {\xdef\newlist{#3}% - \ifx\oldlist\newlist \hbox to 2em{\hss---\hss}% - \else \dospecialbibinsert{#1}{#2}{#3}\fi - \global\let\oldlist\newlist } - -\setuppublicationlist - [artauthorcommand=\AbbreviateAuthors] -\stoptyping -The first argument is a list type `author', `artauthor', or `editor', -the second argument is the number of items that should be typeset, -and the third argument is a macro containing the commalist of persons, -in a form suitable for \tex{invertedauthor} and friends. - - -The following options are initialized depending on the -global settings for `numbering' and `autohang': -\starttabulate[|l|p|] -\NC width\NC Set to the calculated width of the largest label, but only if autohang is `yes'\NC\NR -\NC distance\NC Set to 0pt, but only if autohang is `yes'\NC\NR -\NC numbercommand\NC A command given in `setuppublications' if numbering is turned on, otherwise empty.\NC\NR -\NC textcommand\NC Set to a macro that outdents the body text if numbering is turned off, otherwise empty\NC\NR -\stoptabulate - - -\subsection{Setting citation options: \type{\setupcite}} - -The \type{\cite} command has a lot of alternatives, as could be seen -above in the setting of `refcommand'. And these alternatives have -their own options: - -\setup{setupcite} - -\starttabulate[|l|p|] -\NC andtext \NC separation between two authors (for \type{\cite[author]} styles)\NC \NR -\NC otherstext \NC text used for `et.al.' (for \type{\cite[author]} styles)\NC \NR -\NC namesep \NC the separation between consecutive authors (for \type{\cite[author]} styles)\NC \NR -\NC pubsep \NC separator between publication references in a - \type{\cite} command.\NC \NR -\NC lastpubsep \NC same, but for the - last publication in the list.\NC \NR -\NC left \NC left side of a \type{\cite} (like \type{[})\NC \NR -\NC inbetween \NC the separator between parts of a single citation.\NC\NR -\NC right \NC right side of a \type{\cite} (like \type{]})\NC \NR -\NC compress \NC Whether \type{\cite} should try to -compress it's argument list. \NC\NR -\stoptabulate -Not all options apply to all types of \type{\cite} commands. -E.g. `compress' does not apply to the citation -list for all options of \type{\cite}, since sometimes compression does -not make sense or is not possible. The `num' version compresses -into a condensed sorted list, and the various `author' styles try -to compress all publications by one author, but e.g. years are -never compressed. - -Likewise, `inbetween' only applies to three types: `authoryear' (a -space), `authoryears' (a comma followed by a space), and `num' (where -it is `--' (an endash), the character used to separate number ranges). - -\subsection{Setting up \BIBTEX: \type{\setupbibtex}} - -\BIBTEX\ bibliographic databases are converted into \type{.bbl} files, -and the generated file is just a more \TEX-minded representation of -the full database(s). - -The four \type{.bst} files do not do any actual formatting on the -entries, and they do not subset the database either. Instead, the -{\it entire} database is converted into \TEX-parseable records. About the -only thing the \type{.bst} files do is sorting the entries (and -\BIBTEX\ itself resolves any `STRING' specifications, of course). - -The module will read the created \type{\jobname.bbl} file -and select the parts that are needed for the current article. - -\setup{setupbibtex} - -\starttabulate[|l|p|] -\NC database\NC List of bibtex database file names to be - used. The module will write a very short \type{.aux} file instructing - \BIBTEX\ to create a (possibly very large) \type{\jobname.bbl} file, - that will be \type{\input} by the module (at \type{\starttext}).\NC\NR -\NC sort\NC How the publications in the - \BIBTEX\ database file should be sorted.\crlf - The default here is `no' (\type{cont-no.bst}), meaning no sorting at all. - `author' (\type{cont-au.bst}) sorts alphabetically on author and within that on year, - `title' (\type{cont-ti.bst}) sorts alphabetically on title and then on author and - year, and `short' (\type{cont-ab.bst}) sorts on the short key that is generated - by \BIBTEX. If \type{FILE} is given, it specifies an individual \type{.bst} file name used - by \BIBTEX.\NC\NR -\stoptabulate - -Starting with version 2006.08.08, the module registers \BIBTEX\ as a -program to be run by texexec, so you no longer need to run \BIBTEX\ by -hand (and in MkIV, the module runs \BIBTEX\ on the fly using Lua). - -Still, you may want to create the \type{\jobname.bbl} yourself. The -\type{.bbl} syntax is explained below. There is no default -database of course, and you do not {\it have} to use one: it is -perfectly OK to just \type{\input} a file with the bibliographic -records, as long as it has the right input syntax. Or even to include -the definitions themselves in the preamble of your document. - -\subsection{Borrowing publications: \type{\usepublications}} - -It is also possible to instruct the module to use the bibliographic -references belonging to another document. This is done by using the command -\type{\usepublications[files]}, where \type{files} is a list of other -\CONTEXT\ documents (without extension). - -\setup{usepublications} - -To be precise, this command will use the \type{.bbl} and \type{.tuo} -files from the other document(s), and will therefore not work if these -files cannot be found (the \type{.tuo} file is needed to get correct -page references for \type{\cite[page]}). - - -\subsection{Legacy database support} - -Old \BIBTEX\ databases tend to contain \LaTeX-specific commands and, -especially, command||definitions. To make it easier to handle these -databases, a support module that defines a simplified version of -\LaTeX's \type{\newcommand} is shipped alongside the bib module. -You can load this support code by adding -\starttyping -\usemodule[bibltx] -\stoptyping -to your document preamble. - -\section{Citations} - -Citations are normally handled through the \type{\cite} command. - -\type{\cite} has two basic appearances: - -\subsection{Default and explicit citations} - -\setup{cite} - -The single-argument form executes the style-defined default citation -command. This is the preferred way of usage, since some styles might -use numeric citations while others might use a variation of the -(author,year) style. - -The two-argument form allows you to manually select the style you want. - -\subsubsection{Citation types} - -Following is the full list of recognized keywords for \type{\cite}, -with a short explanation where the data comes from. Most of the -information that is usable within \type{\cite} comes from the argument -to \type{\startpublication}. This command is covered in detail below. - - -All of these options are {\it valid} in all publication styles, since -\CONTEXT\ always has the needed information available. But not all of -these are {\it sensible} in a particular style: using numbered references if -the list of publications itself is not numbered is not a good idea, for -instance. Also, some of the keys are somewhat strange and only -provided for future extensions. - -First, here are the simple ones: -\starttabulate[|l|l|p|] -\NC author\NC \cite[author][me] \NC(from `a')\hfil\NC\NR -\NC doi\NC \cite[doi][me]\NC (from `d')\hfil\NC\NR -\NC key\NC \cite[key][me]\NC (from `k')\hfil\NC\NR -\NC serial\NC \cite[serial][me]\NC (from `n')\hfil\NC\NR -\NC short\NC \cite[short][me]\NC (from `s')\hfil\NC\NR -\NC type\NC \cite[type][me]\NC (from `t')\hfil\NC\NR -\NC year\NC \cite[year][me]\NC (from `y')\hfil\NC\NR -\NC url\NC \cite[url][me]\NC (from `u')\hfil\NC\NR -\stoptabulate -Keep in mind that `n' is a database sequence number, and not -necesarily the same number that is used in the list of -publications. For instance, if `sorttype' is cite, the list will be -re-ordered, but the `n' value will remain the same. To get to the -number that is finally used, use -\starttabulate[|l|l|p|] -\NC num\NC \cite[num][me]\NC (this is a reference to - the sequence number used in the publication list)\hfil\NC\NR -\stoptabulate -If the list of publications is not numbered visually, there will still -be a number available. - -Three of the options are combinations: -\starttabulate[|l|l|p|] -\NC authoryear\NC \cite[authoryear][me]\NC(from `a' and `y')\hfil\NC\NR -\NC authoryears\NC \cite[authoryears][me]\NC(from `a' and `y')\hfil\NC\NR -\NC authornum\NC \cite[authornum][me]\NC(from `a' and `num')\hfil\NC\NR -\NC data\NC \vtop{\hsize .45\hsize \cite[data][me]}\NC The data content of the entry\hfil\NC\NR -\stoptabulate - -And the last one is a page reference to the page where the -the entry is typeset within the publication list. - -\starttabulate[|l|l|p|] -\NC page\NC \cite[page][me]\NC (a page reference)\hfil\NC\NR -\stoptabulate - -\subsection{Citations with local setups} - -\setup{citealt} - -The arguments in this form are inherited from \type{\setupcite}, -except for \type{extras}. The argument of `\type{extras}' is typeset -at the end of the reference, but before a potential `\type{right}', so -it can be used for e.g. page or chapter specifiers. - -\subsection{Invisible citations} - -\setup{nocite} - -This command registers the references in the argument list, but does -not generate typeset material. It can be used to force certain entries -from the database to appear in the typeset list of publications. - -\section{Placing the publication list} - -To typset the list of publications, use \type{\completepublications} -or \type{\placepublications} at the location in your text where you -want the publication list to appear. As is normal in \CONTEXT, -\type{\placepublications} gives you a raw list, and -\type{\completepublications} a list with a title. - - -The default for the publication list is to contain only the `locally' -referenced items, so if you want to use your own heading instead of -the default one, you most likely want to call -\type{\placepublications} with an explicit criterium, like so: -\starttyping -\placepublications[criterium=all] -\stoptyping - -If you use a numeric list style combined with multiple , each -\type{\placepublications} or \type{\completepublications} commands, -by default each one restarts the displayed number. If you do not -like that, you can add an option argument like so: -\starttyping -\placepublications[option=continue] -\stoptyping - - -The module uses the following defaults for the generated head: -\starttyping -\setupheadtext[en][pubs=References] -\setupheadtext[nl][pubs=Literatuur] -\setupheadtext[de][pubs=Literatur] -\setupheadtext[it][pubs=Bibliografia] -\setupheadtext[sl][pubs=Literatura] -\setupheadtext[fr][pubs=Bibliographie] -\stoptyping -These (or new ones) can be redefined as needed. - -\section{The bbl file} - -A typical bbl file consists of one initial command -(\type{\setuppublicationlist}) that sets some information -about the number of entries in the bbl file and the widths -of the labels for the list, and that command is followed by a number of -appearances of \type{\startpublication ... \stoppublication} - -The full appearance version of \type{\cite} -accepts a number of option keywords, and we saw earlier that -the argument of the \type{\startpublication} command -defines most of the things we can reference to. This section explains -the precise syntax for \type{\startpublication}. - -Each single block defines one bibliographic entry. I apologise -for the use of single||letter keys, but these have the advantage of -being a)\quad short and b)\quad safe w.r.t. the multi-lingual interface. - -\setup{startpublication} - -Here is the full example that has been used throughout this document: - -\typebuffer[bibexample] - -\subsection{Defining a publication} - -The list of commands that is allowed to appear between \type{\startpublication} -and \type{\stoppublication} is given below. - -Order within an entry is irrelevant, except for the relative ordering -within each of the three commands that might appear more than once: -\type{\artauthor}, \type{\author} and \type{\editor}. - -Most of these are `normal' \BIBTEX\ field names (in lowercase), but -some are extra special, either because they come from non-standard -databases that I know of, or because the bst file has pre-processed -the contents of the field. - -\subsubsection{Complex fields} - -The three fields that contain names are extra special, because they -have more than one argument. These are: \type{\artauthor}, -\type{\author} and \type{\editor}. These commands require three -arguments, and there can be two extra optional arguments as well. - - -\starttabulate[|l|l|p|] -\NC\tex{artauthor[]\{\}[]\{\}\{\}}\NC\tfx AUTHOR\NC For an author of any publication - that appears within a larger publication, like an article that appears - within a journal or as part of a proceedings. \NC\NR -\NC\tex{author[]\{\}[]\{\}\{\}}\NC\tfx AUTHOR\NC The author of a standalone - publication, like a monograph.\NC\NR -\NC\tex{editor[]\{\}[]\{\}\{\}}\NC\tfx EDITOR\NC The editor of e.g. - an edited volume.\NC\NR -\stoptabulate - -The argument lists have this form: - -\starttyping -\author[junior]{firstnames}[inits]{von}{surname} -\stoptyping - -and the meanings are as follows: -\starttabulate[|l|p|] -\NC \type{junior} \NC a designation of lineage, only used if confusion is possible (due to family members having the same name).\NC\NR -\NC \type{firstnames} \NC individual (given) name(s)\NC\NR -\NC \type{inits} \NC abbreviated form(s) of \type{firstnames}.\NC\NR -\NC \type{von} \NC any bits of the family name that are normally disregarded in sorting\NC\NR -\NC \type{surname} \NC remainder of the family (last) name\NC\NR -\stoptabulate - - -\subsubsection{Simple fields} - -Rather a large list, this is caused by the desire to support as many -existing \BIBTEX\ databases as possible. Please note that a few of -the fields have names that are not the same as in \BIBTEX, because a -1~on~1 mapping causes conflicts with predefined macro names in -\CONTEXT. - -\starttabulate[|l|p(2.5cm)|p|] -\NC\type{\abstract}\NC\tfx ABSTRACT\NC just text.\NC\NR -\NC\type{\annotate}\NC\tfx ANNOTATE \NC just text.\NC\NR -\NC\type{\arttitle}\NC\tfx TITLE\NC The title of a partial publication (one that has \type{\artauthor}s).\NC\NR -\NC\type{\assignee}\NC\tfx ASSIGNEE\NC Assigned person for a patent\NC\NR -\NC\type{\bibnumber}\NC\tfx NUMBER \NC \NC\NR -\NC\type{\bibtype}\NC\tfx TYPE \NC See the \BIBTEX\ - documentation for it's use. This is {\it not} related - to the type of entry that is used for deciding on the - layout.\NC\NR -\NC\type{\biburl}\NC\tfx URL \NC Location on the internet. \NC\NR -\NC\type{\chapter}\NC\tfx CHAPTER \NC the chapter number, if this entry is -referring to a smaller section of a publication. It might actually -be a part number or a (sub)section number. The field \type{\bibtype} (above) -differentiates between these.\NC\NR -\NC\type{\city}\NC\tfx CITY\NC city of publication.\NC\NR -\NC\type{\comment}\NC\tfx COMMENT\NC just text.\NC\NR -\NC\type{\country}\NC\tfx COUNTRY\NC country of publication.\NC\NR -\NC\type{\crossref}\NC\tfx CROSSREF\NC A cross-reference to another - bibliographic entry. It will insert a citation - to that entry, forcing it to be typeset as well.\NC\NR -\NC\type{\day}\NC\tfx DAY \NC Date of publication (for a patent)\NC\NR -\NC\type{\dayfiled}\NC\tfx DAYFILED\NC Filing date for a patent\NC\NR -\NC\type{\doi}\NC\tfx DOI \NC Document Object Identifier\NC\NR -\NC\type{\edition}\NC\tfx EDITION\NC The edition.\NC\NR -\NC\type{\eprint}\NC\tfx EPRINT\NC E-print information\NC\NR -\NC\type{\howpublished}\NC\tfx HOWPUBLISHED\NC \NC\NR -\NC\type{\isbn}\NC\tfx ISNB\NC isbn number (for books)\NC\NR -\NC\type{\issn}\NC\tfx ISSN\NC issn number (for journals)\NC\NR -\NC\type{\issue}\NC\tfx ISSUE\NC issue number (for journals)\NC\NR -\NC\type{\journal}\NC\tfx JOURNAL \NC The journal's name.\NC\NR -\NC\type{\keyword}\NC\tfx KEYWORD \NC just text (for use in indices).\NC\NR -\NC\type{\keywords}\NC\tfx KEYWORDS \NC just text (for use in indices).\NC\NR -\NC\type{\lang}\NC\tfx LANGUAGE \NC The language of the - current bibliographic record\NC\NR -\NC\type{\month}\NC\tfx MONTH\NC Month of publication\NC\NR -\NC\type{\monthfiled}\NC\tfx MONTHFILED\NC Filing month for a patent\NC\NR -\NC\type{\names}\NC\tfx NAMES\NC just text (for use in indices).\NC\NR -\NC\type{\nationality}\NC\tfx NATIONALITY\NC Nationality information for a patent\NC\NR -\NC\type{\note}\NC\tfx NOTE \NC just text (this is the `standard' \BIBTEX\ commentary field).\NC\NR -\NC\type{\notes}\NC\tfx NOTES \NC just text.\NC\NR -\NC\type{\organization}\NC\tfx ORGANIZATION\NC Like institute, but for e.g. companies.\NC\NR -\NC\type{\pages}\NC\tfx PAGES\NC Either the number of pages, or the page range - for a partial publication. The `t' key to startpublication - will decide automatically what is meant.\NC\NR -\NC\type{\pubname}\NC\tfx INSTITUTION,\crlf PUBLISHER,\crlf SCHOOL\NC Publisher or institution name.\NC\NR -\NC\type{\pubyear}\NC\tfx YEAR \NC Year of publication. Within this command, - the \BIBTEX\ bst files will sometimes insert the command - \type{\maybeyear}, which is needed to make sure that - the bbl file stay flexible enough to allow all styles of - formatting.\NC\NR -\NC\type{\revision}\NC\tfx REVISION \NC Release version\NC\NR -\NC\type{\series}\NC\tfx SERIES \NC Possible book series information.\NC\NR -\NC\type{\size}\NC\tfx SIZE \NC Size in KB of a PDF file (this came from - the NTG \MAPS\ database)\NC\NR -\NC\type{\thekey}\NC\tfx KEY \NC See the \BIBTEX\ - documentation for it's use. This is {\it not} related to - the key used for citing this entry.\NC\NR -\NC\type{\title}\NC\tfx TITLE,\crlf BOOKTITLE \NC The title of a book.\NC\NR -\NC\type{\volume}\NC\tfx VOLUME \NC Volume number for multi-part books or - journals.\NC\NR -\NC\type{\yearfiled}\NC\tfx YEARFILED\NC Filing year for a patent\NC\NR -\stoptabulate - -When the \type{\lang} field's content is a full word instead of a -two||letter code, correct processing depends on an auxiliary command -\type{\setbiblanguage}, to be used like this: -\starttyping -\setbiblanguage{English}{en} -\stoptyping -The first argument is a literal \type{\lang} argument, the second -argument has to be a two||letter language abbreviation understood by -\CONTEXT. - -Adding in one of your own fields is reasonably simple: - -\starttyping -\newbibfield[mycommand] -\stoptyping -This will define \type{\mycommand} for use within -a publication (plus \type{\bib@mycommand}, it's internal form) as -well as the command \type{\insertmycommand} that can be used -within \type{\setuppublicationlayout} to fetch the supplied -value (see below). - - -\section{Defining a publication type layout} - -Publication style files of course take care of setting defaults for the -commands as explained earlier, but the largest part of a such a -publication style is concerned with specifying layouts for various -types of publications. - -The command that does the work is \type{\setuppublicationlayout}: - -\setup{setuppublicationlayout} - -The first argument that is a publication (\BIBTEX\ entry) type, and -all publications that have this type given as argument to the `t' key -of \type{\startpublication} will be typeset by executing the commands -that appear in the group following the command. - -For example, here is a possible way to typeset an article: from \type{bibl-apa}: -\starttyping -\setuppublicationlayout[article]{% - \insertartauthors{}{ }{\insertthekey{}{ }{}}% - \insertpubyear{(}{). }{\unskip.}% - \insertarttitle{\bgroup }{\egroup. }{}% - \insertjournal{\bgroup \it}{\egroup} - {\insertcrossref{In }{}{}}% - \insertvolume - {, } - {\insertissue{(}{)}{}\insertpages{:}{.}{.}} - {\insertpages{, pages }{.}{.}}% - \insertnote{ }{.}{}% - \insertcomment{}{.}{}% -} -\stoptyping -For every command in the long list given in the previous paragraph, there is -a corresponding \type{\insertxxx} command. (As usual, \type{\author} -etc. are special: they have a macro called \type{\insertxxxs} -instead). All of these \type{\insertxxx} macros use the same logic: - -\starttyping -\insertartauthors{<before>}{<after>}{<not found>} -\stoptyping - -Sounds easy? It is! But it is also often tedious: database entries can -be tricky things: some without issue numbers, others without page -numbers, some even without authors. So, you often need to nest rather -a lot of commands in the \type{<not found>} section of the `upper' -command, and \type{\unskip} and \type{\ignorespaces} are good friends -as well. - -Incidentally, the distributed \type{bibl-xxx} files define layouts for -the `standard' publication types that are defined in the example -bibliography that comes with \BIBTEX. The list of possbile types is in -no way limited to that list, but it provides a reasonable starting -point. - -\section{References} - -\placepublications[criterium=all] - -\stopdocumentation - -\stopmodule - -\stoptext diff --git a/doc/context/scripts/perl/texshow.1 b/doc/context/scripts/perl/texshow.1 deleted file mode 100644 index 618928b8f..000000000 --- a/doc/context/scripts/perl/texshow.1 +++ /dev/null @@ -1,70 +0,0 @@ -.TH "texshow" "1" "Jul 2006" "ConTeXt" "CONTEXT" -.PP -.SH "NAME" -texshow \- ConTeXt command and parameter reference -.PP -.SH "SYNOPSIS" -\fBtexshow\fP [ \fIOPTION\fP ] [ \fICOMMAND\fP ] -[ \fILANGUAGE\fP ] -.PP -.SH "DESCRIPTION" -.PP -\fBtexshow\fP uses \fBperl\fP(1) and PerlTk (see \fBTk\fP(3pm)) to -show a overview of the commands and parameters of those\&. In the Tk -window you can search for a command and by switching the interface, -you can see the equivalent in another interface language\&. -.PP -\fICOMMAND\fP is a \fBConTeXt\fP command to search for in the -database\&. Note that Plain TeX commands are not (yet) in this database -although they can be used in \fBConTeXt\fP source files\&. -.PP -\fILANGUAGE\fP can be one of \fBcz\fP (Czech), \fBde\fP -(German), \fBen\fP (US-English) or \fBnl\fP (Dutch)\&. The default -language is English\&. -.PP -You can also set the interface language with the -\fB--interface\fP switch\&. -.PP -.SH "BINDINGS" -.PP -Most keyboard events are bound to the search widget, with the -exception of \fB<Page Up>\fP (aka \fB<Prior>\fP) and \fB<Page Down>\fP -(aka \fB<Next>\fP). \fB<Control-q>\fP, \fB<Control-x>\fP, and -\fB<Alt-F4>\fP quit the application\&. -.PP -.SH "OPTIONS" -.PP -.IP "\fB--help\fP" -Print a brief syntax summary -.IP "\fB--interface=\fP\fILANGUAGE\fP" -Primary interface language\&. -See DESCRIPTION for \fILANGUAGE\fP options\&. -.PP -.SH "FILES" -.PP -.IP "\fITEXMF/tex/context/interface/cont-\fIXX\fP\&.xml\fP" -The database -file\&. At this writing, \fIXX\fP can be one of \fBcz\fP (Czech), -\fBde\fP (German), \fBen\fP (English), \fBfr\fP (French), -\fBit\fP (Italian), \fBnl\fP (Dutch), or \fBro\fP (Romanian)\&. -Which file is used is determined by the -\fILANGUAGE\fP specified on the command line\&. -.PP -.SH "SEE ALSO" -.PP -\fBtexexec\fP(1), \fBpdfetex\fP(1), \fBpdftex\fP(1)\&. -.PP -Web page: <http://www\&.pragma-ade\&.com/> -.PP -.SH "AUTHOR" -.PP -This version of \fBtexshow\fP was written by Taco Hoekwater -<taco@elvenkind\&.com>\&. -.PP -This manpage was written by Tobias Burnus -<burnus@gmx\&.de> and C\&.M\&. Connelly -<c@eskimo\&.com>\&. -.PP -\fBtexshow\fP is part of \fBConTeXt\fP and is available -from <http://www\&.pragma-ade\&.com/pragma-ade/>\&. -.PP diff --git a/doc/context/scripts/perl/texshow.html b/doc/context/scripts/perl/texshow.html deleted file mode 100644 index a9af24284..000000000 --- a/doc/context/scripts/perl/texshow.html +++ /dev/null @@ -1,137 +0,0 @@ -<HTML><HEAD><TITLE>Manpage of texshow</TITLE> -</HEAD><BODY> -<H1>texshow</H1> -Section: CONTEXT (1)<BR>Updated: Jul 2006<BR><A HREF="#index">Index</A> -<A HREF="http://localhost/cgi-bin/man/man2html">Return to Main Contents</A><HR> - -<P> - -<A NAME="lbAB"> </A> -<H2>NAME </H2> - -texshow - ConTeXt command and parameter reference -<P> - -<A NAME="lbAC"> </A> -<H2>SYNOPSIS </H2> - -<B>texshow</B> [ <I>OPTION</I> ] [ <I>COMMAND</I> ] -[ <I>LANGUAGE</I> ] -<P> - -<A NAME="lbAD"> </A> -<H2>DESCRIPTION </H2> - -<P> - -<B>texshow</B> uses <B><A HREF="http://localhost/cgi-bin/man/man2html?1+perl">perl</A></B>(1) and PerlTk (see <B>Tk</B>(3pm)) to -show a overview of the commands and parameters of those. In the Tk -window you can search for a command and by switching the interface, -you can see the equivalent in another interface language. -<P> - -<I>COMMAND</I> is a <B>ConTeXt</B> command to search for in the -database. Note that Plain TeX commands are not (yet) in this database -although they can be used in <B>ConTeXt</B> source files. -<P> - -<I>LANGUAGE</I> can be one of <B>cz</B> (Czech), <B>de</B> -(German), <B>en</B> (US-English) or <B>nl</B> (Dutch). The default -language is English. -<P> - -You can also set the interface language with the -<B>--interface</B> switch. -<P> - -<A NAME="lbAE"> </A> -<H2>BINDINGS </H2> - -<P> - -Most keyboard events are bound to the search widget, with the -exception of <B><Page Up></B> (aka <B><Prior></B>) and <B><Page Down></B> -(aka <B><Next></B>). <B><Control-q></B>, <B><Control-x></B>, and -<B><Alt-F4></B> quit the application. -<P> - -<A NAME="lbAF"> </A> -<H2>OPTIONS </H2> - -<P> - -<DL COMPACT> -<DT><B>--help</B><DD> -Print a brief syntax summary -<DT><B>--interface=</B><I>LANGUAGE</I><DD> -Primary interface language. -See DESCRIPTION for <I>LANGUAGE</I> options. -</DL> -<P> - -<A NAME="lbAG"> </A> -<H2>FILES </H2> - -<P> - -<DL COMPACT> -<DT><I>TEXMF/tex/context/interface/cont-XX</I>.xml<DD> -The database -file. At this writing, <I>XX</I> can be one of <B>cz</B> (Czech), -<B>de</B> (German), <B>en</B> (English), <B>fr</B> (French), -<B>it</B> (Italian), <B>nl</B> (Dutch), or <B>ro</B> (Romanian). -Which file is used is determined by the -<I>LANGUAGE</I> specified on the command line. -</DL> -<P> - -<A NAME="lbAH"> </A> -<H2>SEE ALSO </H2> - -<P> - -<B><A HREF="http://localhost/cgi-bin/man/man2html?1+texexec">texexec</A></B>(1), <B><A HREF="http://localhost/cgi-bin/man/man2html?1+pdfetex">pdfetex</A></B>(1), <B><A HREF="http://localhost/cgi-bin/man/man2html?1+pdftex">pdftex</A></B>(1). -<P> - -Web page: <<A HREF="http://www.pragma-ade.com/">http://www.pragma-ade.com/</A>> -<P> - -<A NAME="lbAI"> </A> -<H2>AUTHOR </H2> - -<P> - -This version of <B>texshow</B> was written by Taco Hoekwater -<<A HREF="mailto:taco@elvenkind.com">taco@elvenkind.com</A>>. -<P> - -This manpage was written by Tobias Burnus -<<A HREF="mailto:burnus@gmx.de">burnus@gmx.de</A>> and C.M. Connelly -<c@eskimo.com>. -<P> - -<B>texshow</B> is part of <B>ConTeXt</B> and is available -from <<A HREF="http://www.pragma-ade.com/pragma-ade/">http://www.pragma-ade.com/pragma-ade/</A>>. -<P> - -<P> - -<HR> -<A NAME="index"> </A><H2>Index</H2> -<DL> -<DT><A HREF="#lbAB">NAME </A><DD> -<DT><A HREF="#lbAC">SYNOPSIS </A><DD> -<DT><A HREF="#lbAD">DESCRIPTION </A><DD> -<DT><A HREF="#lbAE">BINDINGS </A><DD> -<DT><A HREF="#lbAF">OPTIONS </A><DD> -<DT><A HREF="#lbAG">FILES </A><DD> -<DT><A HREF="#lbAH">SEE ALSO </A><DD> -<DT><A HREF="#lbAI">AUTHOR </A><DD> -</DL> -<HR> -This document was created by -<A HREF="http://localhost/cgi-bin/man/man2html">man2html</A>, -using the manual pages.<BR> -Time: 09:31:15 GMT, July 19, 2006 -</BODY> -</HTML> diff --git a/scripts/context/lua/mtx-context.lua b/scripts/context/lua/mtx-context.lua index b94b330bd..418387fce 100644 --- a/scripts/context/lua/mtx-context.lua +++ b/scripts/context/lua/mtx-context.lua @@ -299,7 +299,6 @@ found = usedname ~= "" xml.each(command,"ctx:new", function(r,d,k) d[k] = ctxrunner.substitute(newfile) end) - -- message: preprocessing #{oldfile} into #{newfile} using #{pp} ctxdata.variables['old'] = oldfile ctxdata.variables['new'] = newfile xml.each(command,"ctx:value", function(r,d,k) @@ -750,6 +749,10 @@ function scripts.context.run(ctxdata,filename) if environment.argument("batchmode") then flags[#flags+1] = "--interaction=batchmode" end + if environment.argument("synctex") then + logs.simple("warning: syntex is enabled") -- can add upto 5% runtime + flags[#flags+1] = "--synctex=1" + end flags[#flags+1] = "--fmt=" .. string.quote(formatfile) flags[#flags+1] = "--lua=" .. string.quote(scriptfile) flags[#flags+1] = "--backend=pdf" @@ -769,16 +772,16 @@ function scripts.context.run(ctxdata,filename) --~ scripts.context.make(formatname) --~ returncode, errorstring = os.spawn(command) --~ if returncode == 3 then - --~ logs.simple("fatal error, return code 3, message: %s",errorstring or "?") + --~ logs.simple("ks: return code 3, message: %s",errorstring or "?") --~ os.exit(1) --~ end --~ end if not returncode then - logs.simple("fatal error, no return code, message: %s",errorstring or "?") + logs.simple("fatal error: no return code, message: %s",errorstring or "?") os.exit(1) break elseif returncode > 0 then - logs.simple("fatal error, return code: %s",returncode or "?") + logs.simple("fatal error: return code: %s",returncode or "?") os.exit(returncode) break else @@ -799,10 +802,10 @@ function scripts.context.run(ctxdata,filename) logs.simple("arrange run: %s",command) local returncode, errorstring = os.spawn(command) if not returncode then - logs.simple("fatal error, no return code, message: %s",errorstring or "?") + logs.simple("fatal error: no return code, message: %s",errorstring or "?") os.exit(1) elseif returncode > 0 then - logs.simple("fatal error, return code: %s",returncode or "?") + logs.simple("fatal error: return code: %s",returncode or "?") os.exit(returncode) end end diff --git a/scripts/context/lua/mtx-fonts.lua b/scripts/context/lua/mtx-fonts.lua index dd3190475..42e5e3f2c 100644 --- a/scripts/context/lua/mtx-fonts.lua +++ b/scripts/context/lua/mtx-fonts.lua @@ -134,6 +134,8 @@ function scripts.fonts.save(name,sub) else save(fontinfo.fullname,fontloader.open(filename)) end + else + logs.simple("font: %s cannot be read",filename) end else logs.simple("font: %s not saved",filename) @@ -141,6 +143,8 @@ function scripts.fonts.save(name,sub) else logs.simple("font: %s not found",name) end + else + logs.simple("font: no name given") end end diff --git a/scripts/context/lua/mtx-profile.lua b/scripts/context/lua/mtx-profile.lua index d99f7e926..1db9682ac 100644 --- a/scripts/context/lua/mtx-profile.lua +++ b/scripts/context/lua/mtx-profile.lua @@ -95,7 +95,7 @@ function scripts.profiler.analyse(filename) end end -function scripts.profiler.analyse(filename) +function scripts.profiler.x_analyse(filename) local f = io.open(filename) local calls = { } local lines = 0 diff --git a/scripts/context/lua/mtx-update.lua b/scripts/context/lua/mtx-update.lua index bc6ca4026..ef05f087d 100644 --- a/scripts/context/lua/mtx-update.lua +++ b/scripts/context/lua/mtx-update.lua @@ -69,6 +69,7 @@ scripts.update.base = { { "context/img/", "texmf-context" }, { "misc/setuptex/", "." }, { "misc/web2c", "texmf" }, + { "bin/common/luatex/", "texmf-<platform>" }, { "bin/common/<platform>/", "texmf-<platform>" }, { "bin/context/<platform>/", "texmf-<platform>" }, { "bin/metapost/<platform>/", "texmf-<platform>" }, diff --git a/scripts/context/perl/texshow.pl b/scripts/context/perl/texshow.pl deleted file mode 100644 index 629c28f99..000000000 --- a/scripts/context/perl/texshow.pl +++ /dev/null @@ -1,956 +0,0 @@ -eval '(exit $?0)' && eval 'exec perl -w -S $0 ${1+"$@"}' && eval 'exec perl -w -S $0 $argv:q' - if 0; - -#D \module -#D [ file=texshow.pl, -#D version=2006.08.04, -#D title=TeXShow, -#D subtitle=showing \CONTEXT\ commands, -#D author=Taco Hoekwater, -#D date=\currentdate, -#D copyright={Taco Hoekwater}] - -#D Early 1999 \TEXSHOW\ showed up in the \CONTEXT\ distribution. At that time -#D the user interface was described in files named \type {setup*.tex}. The -#D program used a stripped down version of these definition files, generated -#D by \CONTEXT\ itself. \TEXSHOW\ shows you the commands, their (optional) -#D arguments, as well as the parameters and their values. For some five years -#D there was no need to change \TEXSHOW. However, when a few years ago we -#D started providing an \XML\ variant of the user interface definitions, Taco -#D came up with \TEXSHOW||\XML. Because Patricks \CONTEXT\ garden and tools -#D like \CTXTOOLS\ also use the \XML\ definitions, it's time to drop the old -#D \TEX\ based definitions and move forward. From now on Taco's version is the -#D one to be used. -#D -#D Hans Hagen - Januari 2005 -#D -#D ChangeLog: -#D \startitemize -#D \item Add keyboard bindings for quitting the app: Ctrl-q,Ctrl-x,Alt-F4 (2006/07/19) -#D \item Support for define --resolve (2006/08/04) -#D \stopitemize - -use strict; -use Getopt::Long ; -use XML::Parser; -use Data::Dumper; -use Tk; -use Tk::ROText ; -use Config; -use Time::HiRes; - -$Getopt::Long::passthrough = 1 ; # no error message -$Getopt::Long::autoabbrev = 1 ; # partial switch accepted - -my $ShowHelp = 0; -my $Debug = 0; -my $Editmode = 0; -my $Interface = 'cont-en'; -my $current_command; -my $current_interface; -my $current_part; -my @setup_files; - -my %setups; -my %commes; -my %descrs; -my %examps; -my %trees; -my %positions; -my %locations; -my %crosslinks; - - -&GetOptions - ( "help" => \$ShowHelp , - "interface=s" => \$Interface , - "debug" => \$Debug, - "edit" => \$Editmode) ; - -print "\n"; - -show('TeXShow-XML 0.3 beta','Taco Hoekwater 2006',"/"); - -print "\n"; - -if ($ShowHelp) { - show('--help','print this help'); - show('--interface=lg','primary interface'); - show('--debug','print debugging info'); - show('string','show info about command \'string\''); - show('string lg','show info about \'string\' in language \'lg\''); - print "\n"; - exit 0; -} - -my $command = $ARGV[0] || ''; -my $interface = $ARGV[1] || ''; -if ($interface =~ /^[a-z][a-z]$/i) { - $Interface = 'cont-' . lc($interface); -} elsif ($interface && $command =~ /^[a-z][a-z]$/i) { - show('debug',"switching '$interface' and '$command'"); - $Interface = 'cont-' . lc($command); - $command = $interface; -} - -if ($command =~ s/^\\//) { - show('debug','removed initial command backslash'); -} - -show('interface', $Interface); -if ($command){ - show ('command', "\\$command") ; -} - -print "\n"; - -show('status','searching for setup files'); - -my $setup_path; -my ($mainwindow,$interfaceframe,$partframe,$leftframe,$rightframe,$buttonframe); -my ($request,$listbox,$textwindow,%interfacebuttons,%partbuttons); - -my ($textfont,$userfont,$buttonfont); - -my $Part; - -if (setups_found($Interface)) { - $current_interface = ''; - $current_command = ''; - $current_part = 'Command'; - show('status','loading setups') ; - load_setups($Interface) ; - show ('status','initializing display') ; - initialize_display(); - change_setup(); - show_command ($command); - $mainwindow->deiconify(); - show ('status','entering main loop') ; - MainLoop () ; - show ('status','closing down') ; -} else { - show ('error','no setup files found') ; -} -print "\n"; - -sub initialize_display { - my $dosish = ($Config{'osname'} =~ /dos|win/i) ; - my $default_size = $dosish ? 9 : 12 ; - my $s_vertical = 30 ; - my $s_horizontal = 72 ; - my $c_horizontal = 24 ; - if (!$dosish) { - $textfont = "-adobe-courier-bold-r-normal--$default_size-120-75-75-m-70-iso8859-1" ; - $userfont = "-adobe-courier-bold-o-normal--$default_size-120-75-75-m-70-iso8859-1" ; - $buttonfont = "-adobe-helvetica-bold-r-normal--$default_size-120-75-75-p-69-iso8859-1"; - } else { - $textfont = "Courier $default_size " ; - $userfont = "Courier $default_size italic" ; - $buttonfont = "Helvetica $default_size bold " ; - } - $mainwindow = MainWindow -> new ( -title => 'ConTeXt commands' ) ; - $buttonframe = $mainwindow -> Frame () ; # buttons - $leftframe = $mainwindow -> Frame () ; # leftside - $rightframe = $mainwindow -> Frame(); - $request = $rightframe -> Entry (-font => $textfont, - -background => 'ivory1', - -width => $c_horizontal); - $listbox = $rightframe -> Scrolled ('Listbox', - -scrollbars => 'e', - -font => $textfont, - -width => $c_horizontal, - -selectbackground => 'gray', - -background => 'ivory1', - -selectmode => 'browse') ; - $textwindow = $leftframe -> Scrolled ('ROText', - -scrollbars => 'se', - -height => $s_vertical, - -width => $s_horizontal, - -wrap => 'none', - -background => 'ivory1', - -font => $textfont); - $interfaceframe = $leftframe -> Frame(); - $mainwindow -> withdraw() ; - $mainwindow -> resizable ('y', 'y') ; - foreach (@setup_files) { - $interfacebuttons{$_} = $buttonframe -> Radiobutton (-text => $_, - -value => $_, - -font => $buttonfont, - -selectcolor => 'ivory1', - -indicatoron => 0, - -command => \&change_setup, - -variable => \$Interface ); - - $interfacebuttons{$_} -> pack (-padx => '2p',-pady => '2p','-side' => 'left' ); - } - foreach (qw(Command Description Comments Examples)) { - $partbuttons{$_} = $interfaceframe -> Radiobutton (-text => $_, - -value => $_, - -font => $buttonfont, - -selectcolor => 'ivory1', - -indicatoron => 0, - -command => \&change_part, - -variable => \$Part ); - $partbuttons{$_} -> pack (-padx => '2p',-pady => '2p','-side' => 'left' ); - } - # global top - $buttonframe -> pack ( -side => 'top' , -fill => 'x' , -expand => 0 ) ; - # top in left - $interfaceframe -> pack ( -side => 'top' , -fill => 'x' , -expand => 0 ) ; - $textwindow -> pack ( -side => 'top' , -fill => 'both' , -expand => 1 ) ; - $leftframe -> pack ( -side => 'left' , -fill => 'both' , -expand => 1 ) ; - # right - $request -> pack ( -side => 'top' , -fill => 'x' ) ; - $listbox -> pack ( -side => 'bottom' , -fill => 'both' , -expand => 1 ) ; - $rightframe -> pack ( -side => 'right' , -fill => 'both' , -expand => 1 ) ; - $listbox -> bind ('<B1-Motion>', \&show_command ) ; - $listbox -> bind ('<1>' , \&show_command ) ; - $listbox -> bind ('<Key>' , \&show_command ) ; - $textwindow -> tag ('configure', 'user' , -font => $userfont ) ; - $textwindow -> tag ('configure', 'optional' , -font => $userfont ) ; - $textwindow -> tag ('configure', 'command' , -foreground => 'green3' ) ; - $textwindow -> tag ('configure', 'variable' , -font => $userfont ) ; - $textwindow -> tag ('configure', 'default' , -underline => 1 ) ; - $textwindow -> tag ('configure', 'symbol' , -foreground => 'blue3' ) ; - $textwindow -> tag ('configure', 'or' , -foreground => 'yellow3' ) ; - $textwindow -> tag ('configure', 'argument' , -foreground => 'red3' ) ; - $textwindow -> tag ('configure', 'par' , -lmargin1 => '4m' , - -wrap => 'word' , - -lmargin2 => '6m' ) ; - foreach my $chr ('a'..'z','A'..'Z') { - $mainwindow -> bind ( "<KeyPress-$chr>", sub { insert_request(shift, $chr) } ); - } - $request -> bind ('<Return>', sub { handle_request() } ) ; - $mainwindow -> bind ( "<backslash>", sub { insert_request(shift, "\\") } ) ; - $mainwindow -> bind ( "<space>", sub { new_request() } ) ; - $mainwindow -> bind ( "<BackSpace>", sub { delete_request() } ) ; - $mainwindow -> bind ( "<Prior>", sub { prev_command() } ) ; - $mainwindow -> bind ( "<Next>", sub { next_command() } ) ; - $mainwindow -> bind ( "<Control-x>", sub { exit(0) } ) ; - $mainwindow -> bind ( "<Control-X>", sub { exit(0) } ) ; - $mainwindow -> bind ( "<Control-q>", sub { exit(0) } ) ; - $mainwindow -> bind ( "<Control-Q>", sub { exit(0) } ) ; - $mainwindow -> bind ( "<Alt-F4>", sub { exit(0) } ) ; -} - -sub show { - my ($pre,$post,$sep) = @_; - unless ($pre eq 'debug' && !$Debug) { - $sep = ':' unless defined $sep; - print sprintf("%22s $sep %+s\n",$pre,$post); - } -} - -sub change_setup { - # switches to another setup file - if ($current_interface ne $Interface ) { - my $loc = 0; - if ($current_command) { - $loc = $positions{$Interface}{$current_command} || 0; - } - my @list = sort {lc $a cmp lc $b} keys %{$setups{$Interface}} ; - my $num = 0; - map { $locations{$Interface}{$_} = $num++; } @list; - $listbox -> delete ('0.0', 'end') ; - $listbox -> insert ('end', @list) ; - # try to switch to other command as well, here. - if ($current_command ne '') { - show_command($crosslinks{$Interface}[$loc] || ''); - } else { - $listbox -> selectionSet ('0.0', '0.0') ; - $listbox -> activate ('0.0') ; - } - } - $current_interface = $Interface; - $mainwindow -> focus ; -} - -sub change_part { - if ($Part ne $current_part) { - if($Part eq 'Command') { - show_command(); - } elsif ($Part eq 'Description') { - show_description(); - } elsif ($Part eq 'Comments') { - show_comments(); - } elsif ($Part eq 'Examples') { - show_examples(); - } - } - $current_part = $Part; -} - - -sub setups_found { - # find the setup files - my ($primary) = @_; - $setup_path = `kpsewhich --progname=context cont-en.xml` ; - chomp $setup_path; - show ('debug', "path = '$setup_path'"); - if ($setup_path) { - $setup_path =~ s/cont-en\.xml.*// ; - @setup_files = glob ("${setup_path}cont\-??.xml") ; # HH: pattern patched, too greedy - show ('debug', "globbed path into '@setup_files'"); - if (@setup_files) { - my $found = 0; - foreach (@setup_files) { - s/\.xml.*$//; - s/^.*?cont-/cont-/; - if ($_ eq $primary) { - $found = 1; - show ('debug', "found primary setup '$primary'"); - } else { - show ('debug', "found non-primary setup '$_'"); - } - } - if ($found) { - return 1; - } else { - show('error',"setup file for '$primary' not found, using 'cont-en'"); - $Interface = 'cont-en'; - return 1; - } - } else { - show('error',"setup file glob failed"); - } - } elsif ($!) { - show('error','kpsewhich not found'); - } else { - show('error','setup files not found'); - } - return 0; -} - -sub load_setup { - my ($path,$filename) = @_; - my $localdefs = {}; - unless (keys %{$setups{$filename}}) { - if (open(IN,"<${path}$filename.xml")) { - my $position = 0 ; - local $/ = '</cd:command>'; - while (my $data= <IN>) { - next if $data =~ /\<\/cd:interface/; - if ($data =~ /\<cd:interface/) { - $data =~ s/^(.*?)\<cd:command/\<cd:command/sm; - my $meta = $1; - while ($meta =~ s!<cd:define name=(['"])(.*?)\1>(.*?)</cd:define>!!sm) { - my $localdef = $2; - my $localval = $3; - $localdefs->{$localdef} = $localval; - } - } - # - if (keys %$localdefs) { - while ($data =~ /<cd:resolve/) { - $data =~ s/<cd:resolve name=(['"])(.*?)\1\/>/$localdefs->{$2}/ms; - } - } - $data =~ s/\s*\n\s*//g; - $data =~ /\<cd:command(.*?)\>/; - my $info = $1; - my ($name,$environment) = ('',''); - while ($info =~ s/^\s*(.*?)\s*=\s*(["'])(.*?)\2\s*//) { - my $a = $1; my $b = $3; - if ($a eq 'name') { - $name = $b; - } elsif ($a eq 'type') { - $environment = $b; - } - } - my $cmd = $name; - if ($environment) { - $cmd = "start" . $name; - } - $setups {$filename}{$cmd} = $data ; - $trees {$filename}{$cmd} = undef; - $positions {$filename}{$cmd} = ++$position ; - $crosslinks{$filename}[$position] = $cmd ; - } - close IN; - # now get explanations as well ... - my $explname = $filename; - $explname =~ s/cont-/expl-/; - my $extras = 0 ; - if (open(IN,"<${path}$explname.xml")) { - local $/ = '</cd:explanation>'; - while (my $data= <IN>) { - if ($data =~ /\<\/cd:explanations/) { - next; - } - if ($data =~ /\<cd:explanations/) { - $data =~ s/^(.*?)\<cd:explanation /\<cd:explanation /sm; - my $meta = $1; - } - # - $extras++; - $data =~ /\<cd:explanation(.*?)\>/; - my $info = $1; - my ($name,$environment) = ('',''); - while ($info =~ s/^\s*(.*?)\s*=\s*(["'])(.*?)\2\s*//) { - my $a = $1; my $b = $3; - if ($a eq 'name') { - $name = $b; - } elsif ($a eq 'type') { - $environment = $b; - } - } - my $cmd = $name; - if ($environment) { - $cmd = "start" . $name; - } - my $comment = ''; - my $description = ''; - my @examples = (); - $data =~ /\<cd:description\>(.*)\<\/cd:description\>/s and $description = $1; - $data =~ /\<cd:comment\>(.*)\<\/cd:comment\>/s and $comment = $1; - while ($data =~ s/\<cd:example\>(.*?)\<\/cd:example\>//s) { - push @examples, $1; - } - if (length($comment) && $comment =~ /\S/) { - $commes {$filename}{$cmd} = $comment; - } - if (length($description) && $description =~ /\S/) { - $descrs {$filename}{$cmd} = $description; - } - my $testex = "@examples"; - if (length($testex) && $testex =~ /\S/) { - $examps {$filename}{$cmd} = [@examples]; - } - } - } - if ($extras) { - show('debug',"interface '$filename', $position\&$extras commands"); - } else { - show('debug',"interface '$filename', $position commands"); - } - } else { - show ('debug',"open() of ${path}$filename.xml failed"); - } - } - $Interface = $filename ; -} - -sub load_setups { - my ($primary) = @_; - # load all setup files, but default to $primary - my $t0 = [Time::HiRes::gettimeofday()]; - foreach my $setup (@setup_files) { - if ($setup ne $primary) { - load_setup ($setup_path,$setup); - show('status',"loading '$setup' took " .Time::HiRes::tv_interval($t0) . " seconds"); - $t0 = [Time::HiRes::gettimeofday()]; - }; - }; - load_setup ($setup_path,$primary); - show('status',"loading '$primary' took " .Time::HiRes::tv_interval($t0) . " seconds"); -} - -my @history = (); -my $current_history = 0; - -sub show_command { - my ($command,$nofix) = @_; - if (keys %{$setups{$Interface}}) { - my $key = ''; - if (defined $command && $command && - (defined $setups{$Interface}{$command} || - defined $setups{$Interface}{"start" . $command})) { - $key = $command; - my $whence =$locations{$Interface}{$command}; - $listbox -> selectionClear ('0.0','end') ; - $listbox -> selectionSet($whence,$whence); - $listbox -> activate($whence); - $listbox -> see($whence); - } else { - $listbox -> selectionSet('0.0','0.0') unless $listbox->curselection(); - $key = $listbox -> get($listbox->curselection()) ; - } - show('debug',"current command: $current_command"); - show('debug'," new command: $key"); - $current_command = $key ; - $textwindow -> delete ('1.0', 'end' ) ; - $partbuttons{"Command"}->select(); - $partbuttons{"Command"}->configure('-state' => 'normal'); - $partbuttons{"Description"}->configure('-state' => 'disabled'); - $partbuttons{"Comments"}->configure('-state' => 'disabled'); - $partbuttons{"Examples"}->configure('-state' => 'disabled'); - if (defined $commes{$Interface}{$key}) { - $partbuttons{"Comments"}->configure('-state' => 'normal'); - } - if (defined $descrs{$Interface}{$key}) { - $partbuttons{"Description"}->configure('-state' => 'normal'); - } - if (defined $examps{$Interface}{$key}) { - $partbuttons{"Examples"}->configure('-state' => 'normal'); - } - unless (defined $nofix && $nofix) { - push @history, $key; - $current_history = $#history; - } - do_update_command ($key) ; - $mainwindow -> update(); - $mainwindow -> focus() ; - } -} - -sub prev_command { - if ($current_history > 0) { - $current_history--; - show_command($history[$current_history],1); - } -} - -sub next_command { - unless ($current_history == $#history) { - $current_history++; - show_command($history[$current_history],1); - } -} - -sub show_description { - $textwindow -> delete ('1.0', 'end' ) ; - if (defined $descrs{$current_interface}{$current_command}) { - $textwindow-> insert ('end',$descrs{$current_interface}{$current_command}); - } - $mainwindow -> update(); - $mainwindow -> focus() ; -} - -sub show_comments { - $textwindow -> delete ('1.0', 'end' ) ; - if (defined $commes{$current_interface}{$current_command}) { - $textwindow-> insert ('end',$commes{$current_interface}{$current_command}); - } - $mainwindow -> update(); - $mainwindow -> focus() ; -} - - -sub show_examples { - $textwindow -> delete ('1.0', 'end' ) ; - if (defined $examps{$current_interface}{$current_command}) { - $textwindow-> insert ('end',join("\n\n",@{$examps{$current_interface}{$current_command}})); - } - $mainwindow -> update(); - $mainwindow -> focus() ; -} - - - - -sub has_attr { - my ($elem,$att,$val) = @_; - return 1 if (attribute($elem,$att) eq $val); - return 0; -} - - -sub view_post { - my ($stuff,$extra) = @_; - $extra = '' unless defined $extra; - $stuff =~ /^(.)(.*?)(.)$/; - my ($l,$c,$r) = ($1,$2,$3); - if ($l eq '[' || $l eq '(') { - return ($l,['symbol','par',$extra],$c,['par',$extra],$r,['symbol','par',$extra],"\n",'par'); - } else { - return ($l,['argument','par',$extra],$c,['par',$extra],$r,['argument','par',$extra],"\n",'par'); - } -} - -sub view_pre { - my ($stuff) = @_; - $stuff =~ /^(.)(.*?)(.)$/; - my ($l,$c,$r) = ($1,$2,$3); - if ($l eq '[' || $l eq '(') { - return ($l,['symbol'],$c,'',$r,['symbol']); - } else { - return ($l,['argument'],$c,'',$r,['argument']); - } -} - -sub create_setup_arguments { - my $argx = shift; - my @predisp = (); - my @postdisp = (); - foreach my $arg (children($argx)) { - if (name($arg) eq 'cd:keywords') { - # children are Constant* & Inherit? & Variable* - my @children = children($arg); - my $optional = (attribute($arg,'optional') eq 'yes' ? 'optional' : ''); - if (@children){ - push @predisp,'[', ['symbol',$optional]; - if (has_attr($arg,'list', 'yes')) { - if (has_attr($arg,'interactive', 'exclusive')) { - push @predisp, '...', ''; - } else { - push @predisp, '..,...,..', ''; - } - } else { - push @predisp,'...', ''; - } - push @predisp,']', ['symbol',$optional]; - } - push @postdisp,'[', ['symbol','par',$optional]; - my $firsttrue = 1; - foreach my $kwd (@children) { - if ($firsttrue) { - $firsttrue = 0; - } else { - push @postdisp,', ', ['symbol','par']; - } - if (name($kwd) eq 'cd:constant' || - name($kwd) eq 'cd:variable') { - my $v = attribute($kwd,'type'); - my $def = ''; - my $var = ''; - $var = 'variable' if (name($kwd) eq 'cd:variable') ; - $def = 'default' if (has_attr($kwd,'default', 'yes')); - if ($v =~ /^cd:/) { - $v =~ s/^cd://; - $v .= "s" if (has_attr($arg,'list', 'yes')); - push @postdisp, $v, ['user',$def,'par',$var]; - } else { - push @postdisp, $v, [$def,'par',$var]; - } - } elsif (name($kwd) eq 'cd:inherit') { - my $v = attribute($kwd,'name'); - $textwindow -> tag ('configure', $v , -foreground => 'blue3',-underline => 1 ) ; - $textwindow -> tagBind($v,'<ButtonPress>',sub {show_command($v)} ); - push @postdisp,"see ","par", "$v", [$v,'par']; - } - } - push @postdisp,']', ['symbol','par',$optional]; - push @postdisp,"\n", 'par'; - } elsif (name($arg) eq 'cd:assignments') { - # children are Parameter* & Inherit? - my @children = children($arg); - my $optional = (attribute($arg,'optional') eq 'yes' ? 'optional' : ''); - if (@children) { - push @predisp,'[', ['symbol',$optional]; - if (has_attr($arg,'list', 'yes')) { - push @predisp, '..,..=..,..', ''; - } else { - push @predisp,'..=..', ''; - } - push @predisp,']', ['symbol',$optional]; - push @postdisp,'[', ['symbol','par',$optional]; - my $isfirst = 1; - foreach my $assn (@children) { - if ($isfirst) { - $isfirst = 0; - } else { - push @postdisp,",\n ", ['symbol','par']; - } - if (name($assn) eq 'cd:parameter') { - push @postdisp,attribute($assn,'name'), 'par'; - push @postdisp,'=', ['symbol','par']; - my $firstxtrue = 1; - foreach my $par (children($assn)) { - if ($firstxtrue) { - $firstxtrue = 0; - } else { - push @postdisp,'|', ['or','par']; - } - if (name($par) eq 'cd:constant' || name($par) eq 'cd:variable') { - my $var = ''; - $var = 'variable' if name($par) eq 'cd:variable'; - my $v = attribute($par,'type'); - if ($v =~ /^cd:/) { - $v =~ s/^cd://; - push @postdisp,$v, ['user','par',$var]; - } else { - push @postdisp,$v, ['par',$var]; - } - } - } - } elsif (name($assn) eq 'cd:inherit') { - my $v = attribute($assn,'name'); - $textwindow -> tag ('configure', $v , -foreground => 'blue3',-underline => 1 ) ; - $textwindow -> tagBind($v,'<ButtonPress>',sub {show_command($v)} ); - push @postdisp,"see ","par", "$v", [$v,'par']; - } - } - push @postdisp,"]", ['symbol','par',$optional], "\n", ''; - } - } elsif (name($arg) eq 'cd:content') { - push @predisp, view_pre('{...}'); - push @postdisp, view_post('{...}'); - } elsif (name($arg) eq 'cd:triplet') { - if (has_attr($arg,'list','yes')) { - push @predisp, view_pre('[x:y:z=,..]'); - push @postdisp,view_post('[x:y:z=,..]'); - } else { - push @predisp, view_pre('[x:y:z=]'); - push @postdisp,view_post('[x:y:z=]'); - } - } elsif (name($arg) eq 'cd:reference') { - my $optional = (attribute($arg,'optional') eq 'yes' ? 'optional' : ''); - if (has_attr($arg,'list','yes')) { - push @postdisp, view_post('[ref,..]',$optional); - push @predisp, view_pre('[ref,..]'); - } else { - push @postdisp, view_post('[ref]',$optional); - push @predisp, view_pre('[ref]');; - } - } elsif (name($arg) eq 'cd:word') { - if (has_attr($arg,'list','yes')) { - push @predisp, view_pre ('{...,...}'); - push @postdisp,view_post('{...,...}'); - } else { - push @predisp, view_pre('{...}'); - push @postdisp, view_post('{...}'); - } - } elsif (name($arg) eq 'cd:nothing') { - my $sep = attribute($arg,'separator'); - if ($sep) { - if($sep eq 'backslash') { -# push @postdisp,'\\\\','par'; - push @predisp,'\\\\',''; - } else { -# push @postdisp,$sep,'par'; - push @predisp,$sep,''; - } - } - push @predisp,'...',''; - push @postdisp,'text',['variable','par'],"\n",'par'; - } elsif (name($arg) eq 'cd:file') { - push @predisp,'...',['default']; - push @postdisp,'...',['default','par'],"\n",'par'; - } elsif (name($arg) eq 'cd:csname') { - push @predisp,'\command',['command']; - push @postdisp,'\command',['command','par'],"\n",'par'; - } elsif (name($arg) eq 'cd:index') { - if (has_attr($arg,'list','yes')) { - push @predisp,view_pre('{..+...+..}'); - push @postdisp,view_post('{..+...+..}'); - } else { - push @predisp, view_pre('{...}'); - push @postdisp,view_post('{...}'); - } - } elsif (name($arg) eq 'cd:position') { - if (has_attr($arg,'list','yes')) { - push @predisp,view_pre('(...,...)'); - push @postdisp,view_post('(...,...)'); - } else { - push @predisp,view_pre('(...)'); - push @postdisp,view_post('(...)'); - } - } elsif (name($arg) eq 'cd:displaymath') { - push @predisp, ('$$',['argument'],'...','','$$',['argument']); - push @postdisp, ('$$',['argument','par'],'...',['par'],'$$',['argument','par']); - } elsif (name($arg) eq 'cd:tex') { - my $sep = attribute($arg,'separator'); - if ($sep) { - if($sep eq 'backslash') { -# push @postdisp,'\\\\','par'; - push @predisp,'\\\\',''; - } else { -# push @postdisp,$sep,'par'; - push @predisp,$sep,''; - } - } - my $cmd = "\\" . attribute($arg,'command'); - push @predisp,$cmd,''; -# push @postdisp,$cmd,['command','par'],"\n",'par'; - } - } - return (\@predisp,\@postdisp); -} - - -# <foo><head id="a">Hello <em>there</em></head><bar>Howdy<ref/></bar>do</foo> -# -# would be: -# -# Tag Content -# ================================================================== -# [foo, [{}, head, [{id => "a"}, 0, "Hello ", em, [{}, 0, "there"]], -# bar, [ {}, 0, "Howdy", ref, [{}]], -# 0, "do" -# ] -# ] - -sub attribute { - my ($elem,$att) = @_; - if (defined $elem->[1] && defined $elem->[1]->[0] && defined $elem->[1]->[0]->{$att}) { - my $ret = $elem->[1]->[0]->{$att}; - show ('debug',"returning attribute $att=$ret"); - return $elem->[1]->[0]->{$att}; - } else { - return ''; - } -} - -sub name { - my ($elem) = @_; - if (defined $elem->[0] ) { - return $elem->[0]; - } else { - return ''; - } -} - -# return all children at a certain level -sub children { - my ($elem) = @_; - if (defined $elem->[1] && defined $elem->[1]->[1]) { - my @items = @{$elem->[1]}; - shift @items ; # deletes the attribute. - my @ret = (); - while (@items) { - push @ret, [shift @items, shift @items]; - } - return @ret; - } else { - return (); - } -} - -# return the first child with the right name -sub find { - my ($elem,$name) = @_; - if ($elem->[0] eq $name) { - return $elem; - } - if (ref($elem->[1]) eq 'ARRAY') { - my @contents = @{$elem->[1]}; - shift @contents; - while (my $ename = shift @contents) { - my $con = shift @contents; - if ($ename eq $name) { - return [$ename,$con]; - } - } - } - return []; -} - -sub do_update_command # type: 0=display, 1=compute only - { my ($command, $type) = @_ ; - $type = 0 unless defined $type; - my $setup; - if (!defined $trees{$Interface}{$command}) { - my $parser = XML::Parser->new('Style' => 'Tree'); - $trees{$Interface}{$command} = $parser->parse($setups{$Interface}{$command}); - } - $setup = $trees{$Interface}{$command} ; - my $predisp = undef; - my $postdisp = undef; - my @cmddisp = (); - my @cmddispafter = (); - my $pradisp = undef; - my $altdisp = undef; - if (attribute($setup,'file')) { - my $filename = attribute($setup,'file'); - my $fileline = attribute($setup,'line') || 0; - $textwindow->insert ('end',"$filename:${fileline}::\n\n", '' ); - } - # start with backslash - push @cmddisp, "\\", 'command' ; - my $env = 0; - if (has_attr($setup,'type','environment')) { - $env = 1; - } - if ($env) { push @cmddisp, "start", 'command' ; } - if ($env) { push @cmddispafter, " ... ", '', "\\stop", 'command' ; } - my $seq = find($setup,'cd:sequence'); - # display rest of command name - foreach my $seqpart (children($seq)) { - my $text = attribute($seqpart,'value'); - if (name($seqpart) eq 'cd:variable') { - push @cmddisp, $text, ['command','user']; - if ($env) { push @cmddispafter, $text, ['command','user']; } - } elsif (name($seqpart) eq 'cd:string') { - push @cmddisp, $text, 'command'; - if ($env) { push @cmddispafter, $text, 'command'; } - } - } - # - my $args = find($setup,'cd:arguments'); - # display commands - if ($args) { - my $curarg = 0; - foreach my $arg (children($args)) { - if (name($arg) eq 'cd:choice') { - my ($a,$b) = children($arg); - ($predisp,$postdisp) = create_setup_arguments(['cd:arguments',[{}, @$a]]); - ($pradisp,$altdisp) = create_setup_arguments(['cd:arguments',[{}, @$b]]); - } else { - ($predisp,$postdisp) = create_setup_arguments($args); - } - $curarg++; - } - } - return if $type; - if(defined $postdisp) { - if(defined $altdisp) { - $textwindow->insert('end',@cmddisp,@$predisp,@cmddispafter, "\n",'', - @cmddisp,@$pradisp,@cmddispafter, "\n\n",'', - @cmddisp, "\n",'', - @$postdisp, "\n",'', - @cmddisp, "\n",'', - @$altdisp); - } else { - $textwindow->insert('end',@cmddisp,@$predisp, @cmddispafter ,"\n\n",'', - @cmddisp,"\n",'', - @$postdisp); - } - } else { - $textwindow->insert('end',@cmddisp); - } -} - - -#D The next feature is dedicated to Tobias, who suggested -#D it, and Taco, who saw it as yet another proof of the -#D speed of \PERL. It's also dedicated to Ton, who needs it -#D for translating the big manual. - -sub handle_request { - my $index = $listbox -> index('end') ; - return unless $index; - my $req = $request -> get ; - return unless $req; - $req =~ s/\\//o ; - $req =~ s/\s//go ; - $request -> delete('0','end') ; - $request -> insert('0',$req) ; - return unless $req; - my ($l,$c) = split (/\./,$index) ; - for (my $i=0;$i<=$l;$i++) { - $index = "$i.0" ; - my $str = $listbox -> get ($index, $index) ; - if (defined $str && ref($str) eq 'ARRAY') { - $str = "@{$str}"; - } - if (defined $str && $str =~ /^$req/) { - show_command($str) ; - return ; - } - } -} - -sub insert_request { - my ($self, $chr) = @_ ; - # don't echo duplicate if $chr was keyed in in the (focussed) entrybox - $request -> insert ('end', $chr) unless $self eq $request; - handle_request(); -} - -sub delete_request { - my $self = shift ; - # delete last character, carefully - if ($self ne $request) { - my $to = $request -> index ('end') ; - my $from = $to - 1 ; - if ($from<0) { $from = 0 } - $request -> delete ($from,$to); - } - handle_request(); -} - -sub new_request { - $request -> delete (0,'end') ; - handle_request(); -} - diff --git a/tex/context/base/anch-pos.lua b/tex/context/base/anch-pos.lua index 05498212f..61ed73933 100644 --- a/tex/context/base/anch-pos.lua +++ b/tex/context/base/anch-pos.lua @@ -80,12 +80,13 @@ function jobpositions.MPd(id) local jpi = pcol[id] or ptbs[id] texprint(ctxcatco -- the following are only for MP so there we can leave out the pt --- can be writes +-- can be writes and no format needed any more function jobpositions.MPxy(id) local jpi = pcol[id] or ptbs[id] if jpi then texprint(ctxcatcodes,format('(%s-%s,%s-%s)',jpi[2],dx,jpi[3],dy)) +--~ texprint(ctxcatcodes,'(',jpi[2],'-',dx,',',jpi[3],'-',dy,')') else texprint(ctxcatcodes,'(0,0)') end @@ -94,6 +95,7 @@ function jobpositions.MPll(id) local jpi = pcol[id] or ptbs[id] if jpi then texprint(ctxcatcodes,format('(%s-%s,%s-%s-%s)',jpi[2],dx,jpi[3],jpi[6],dy)) +--~ texprint(ctxcatcodes,'(',jpi[2],'-',dx,',',jpi[3],'-',jpi[6],'-',dy,')') else texprint(ctxcatcodes,'(0,0)') end @@ -102,6 +104,7 @@ function jobpositions.MPlr(id) local jpi = pcol[id] or ptbs[id] if jpi then texprint(ctxcatcodes,format('(%s+%s-%s,%s-%s-%s)',jpi[2],jpi[4],dx,jpi[3],jpi[6],dy)) +--~ texprint(ctxcatcodes,'(',jpi[2],'+',jpi[4],'-',dx,',',jpi[3],'-',jpi[6],'-',dy,')') else texprint(ctxcatcodes,'(0,0)') end @@ -110,6 +113,7 @@ function jobpositions.MPur(id) local jpi = pcol[id] or ptbs[id] if jpi then texprint(ctxcatcodes,format('(%s+%s-%s,%s+%s-%s)',jpi[2],jpi[4],dx,jpi[3],jpi[5],dy)) +--~ texprint(ctxcatcodes,'(',jpi[2],'+',jpi[4],'-',dx,',',jpi[3],'+',jpi[5],'-',dy,')') else texprint(ctxcatcodes,'(0,0)') end @@ -118,6 +122,7 @@ function jobpositions.MPul(id) local jpi = pcol[id] or ptbs[id] if jpi then texprint(ctxcatcodes,format('(%s-%s,%s+%s-%s)',jpi[2],dx,jpi[3],jpi[5],dy)) +--~ texprint(ctxcatcodes,'(',jpi[2],'-',dx,',',jpi[3],'+',jpi[5],'-',dy,')') else texprint(ctxcatcodes,'(0,0)') end diff --git a/tex/context/base/catc-ctx.tex b/tex/context/base/catc-ctx.tex index 83e802e77..21e7d0136 100644 --- a/tex/context/base/catc-ctx.tex +++ b/tex/context/base/catc-ctx.tex @@ -14,13 +14,13 @@ %D We prefer to define relevant catcode tables in this file instead %D of everywhere around. -\ifx\ctxcatcodes \undefined \newcatcodetable \ctxcatcodes \fi -\ifx\mthcatcodes \undefined \newcatcodetable \mthcatcodes \fi % math, not used, too tricky -\ifx\xmlcatcodesn\undefined \newcatcodetable \xmlcatcodesn \fi % normal -\ifx\xmlcatcodese\undefined \newcatcodetable \xmlcatcodese \fi % entitle -\ifx\xmlcatcodesr\undefined \newcatcodetable \xmlcatcodesr \fi % reduce -\ifx\typcatcodesa\undefined \newcatcodetable \typcatcodesa \fi % { } -\ifx\typcatcodesb\undefined \newcatcodetable \typcatcodesb \fi % < > +\ifdefined \ctxcatcodes \else \newcatcodetable \ctxcatcodes \fi +\ifdefined \mthcatcodes \else \newcatcodetable \mthcatcodes \fi % math, not used, too tricky +\ifdefined \xmlcatcodesn \else \newcatcodetable \xmlcatcodesn \fi % normal +\ifdefined \xmlcatcodese \else \newcatcodetable \xmlcatcodese \fi % entitle +\ifdefined \xmlcatcodesr \else \newcatcodetable \xmlcatcodesr \fi % reduce +\ifdefined \typcatcodesa \else \newcatcodetable \typcatcodesa \fi % { } +\ifdefined \typcatcodesb \else \newcatcodetable \typcatcodesb \fi % < > \startcatcodetable \ctxcatcodes \catcode`\^^I = 10 @@ -202,7 +202,7 @@ \catcodetable \ctxcatcodes \let\defaultcatcodetable\ctxcatcodes -\let\xmlcatcodes \xmlcatcodesn +\let\xmlcatcodes \xmlcatcodesn % beware, in mkiv we use \notcatcodes \endinput diff --git a/tex/context/base/catc-def.tex b/tex/context/base/catc-def.tex index 0346f6dae..e80cfe125 100644 --- a/tex/context/base/catc-def.tex +++ b/tex/context/base/catc-def.tex @@ -13,12 +13,12 @@ %D The following catcode tables are rather \CONTEXT\ independent. -\ifx\nilcatcodes \undefined \newcatcodetable \nilcatcodes \fi -\ifx\texcatcodes \undefined \newcatcodetable \texcatcodes \fi -\ifx\luacatcodes \undefined \newcatcodetable \luacatcodes \fi -\ifx\notcatcodes \undefined \newcatcodetable \notcatcodes \fi -\ifx\vrbcatcodes \undefined \newcatcodetable \vrbcatcodes \fi -\ifx\prtcatcodes \undefined \newcatcodetable \prtcatcodes \fi +\ifdefined\nilcatcodes \else \newcatcodetable \nilcatcodes \fi +\ifdefined\texcatcodes \else \newcatcodetable \texcatcodes \fi +\ifdefined\luacatcodes \else \newcatcodetable \luacatcodes \fi +\ifdefined\notcatcodes \else \newcatcodetable \notcatcodes \fi +\ifdefined\vrbcatcodes \else \newcatcodetable \vrbcatcodes \fi +\ifdefined\prtcatcodes \else \newcatcodetable \prtcatcodes \fi \startcatcodetable \nilcatcodes \catcode`\^^I = 10 % ascii tab is a blank space diff --git a/tex/context/base/char-ini.mkiv b/tex/context/base/char-ini.mkiv index eb11fd83c..2f1fe23a1 100644 --- a/tex/context/base/char-ini.mkiv +++ b/tex/context/base/char-ini.mkiv @@ -67,9 +67,10 @@ \number\xmlcatcodesn, \number\xmlcatcodese, \number\xmlcatcodesr, + \number\xmlcatcodes, } ) - catcodes.register("xmlcatcodes",\number\xmlcatcodes) + catcodes.register("xmlcatcodes",\number\xmlcatcodes) } \protect \endinput diff --git a/tex/context/base/colo-ini.lua b/tex/context/base/colo-ini.lua index 256af4707..fc63d6b3a 100644 --- a/tex/context/base/colo-ini.lua +++ b/tex/context/base/colo-ini.lua @@ -23,6 +23,8 @@ local a_transparency = attributes.private('transparency') local a_colorspace = attributes.private('colormodel') local a_background = attributes.private('background') +-- no format needed any more or maybe use low level commands (less tokenization) + local a_l_c_template = "\\setevalue{(ca:%s)}{%s}" .. "\\setevalue{(cs:%s)}{\\dosetattribute{color}{%s}}" local a_g_c_template = "\\setxvalue{(ca:%s)}{%s}" .. @@ -350,7 +352,7 @@ function colors.formatgray(ca,separator) return format("%0.3f",(cv and cv[2]) or 0) end -function colors.colorcomponents(ca) +function colors.colorcomponents(ca) -- return list local cv = colors.value(ca) if cv then local model = cv[1] diff --git a/tex/context/base/cont-new.tex b/tex/context/base/cont-new.tex index f32c851dc..012a9c552 100644 --- a/tex/context/base/cont-new.tex +++ b/tex/context/base/cont-new.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2009.10.02 13:14} +\newcontextversion{2009.10.16 16:13} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/context.mkii b/tex/context/base/context.mkii index bbc05ae50..d5284cd25 100644 --- a/tex/context/base/context.mkii +++ b/tex/context/base/context.mkii @@ -34,6 +34,7 @@ \loadmarkfile{syst-ext} \loadmarkfile{syst-new} \loadmarkfile{syst-con} +\loadcorefile{syst-ltx} \loadmarkfile{thrd-trg} % based on: David Carlisle \loadmarkfile{syst-fnt} diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv index 378a00873..bd2bbe504 100644 --- a/tex/context/base/context.mkiv +++ b/tex/context/base/context.mkiv @@ -43,6 +43,7 @@ \loadmarkfile{syst-aux} \loadmarkfile{syst-lua} \loadmarkfile{syst-con} +\loadcorefile{syst-ltx} \loadmarkfile{syst-fnt} \loadmarkfile{syst-str} @@ -315,7 +316,7 @@ \loadmarkfile{bibl-bib} \loadmarkfile{bibl-tra} -\loadmarkfile{x-xtag} % at some point this will nto be preloaded +\loadmarkfile{x-xtag} % at some point this will not be preloaded \loadcorefile{meta-xml} diff --git a/tex/context/base/context.tex b/tex/context/base/context.tex index 8354ede04..3f87e7a12 100644 --- a/tex/context/base/context.tex +++ b/tex/context/base/context.tex @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2009.10.02 13:14} +\edef\contextversion{2009.10.16 16:13} %D For those who want to use this: diff --git a/tex/context/base/core-ctx.lua b/tex/context/base/core-ctx.lua index 4dbff3663..f9920d4c4 100644 --- a/tex/context/base/core-ctx.lua +++ b/tex/context/base/core-ctx.lua @@ -25,13 +25,12 @@ function commands.loadctxpreplist() commands.writestatus("systems","loading ctx log file (specified)") -- todo: m!systems end --~ end - for r, d, k in xml.elements(x,"ctx:prepfile") do - local dk = d[k] - local name = xml.content(dk) + for e in xml.collected(x,"ctx:prepfile") do + local name = xml.content(e) if islocal then name = file.basename(name) end - local done = dk.at['done'] or 'no' + local done = e.at['done'] or 'no' if trace_prepfiles then commands.writestatus("systems","registering %s -> %s",done) end diff --git a/tex/context/base/core-def.mkii b/tex/context/base/core-def.mkii index e51cd96d3..bcf8d6b9d 100644 --- a/tex/context/base/core-def.mkii +++ b/tex/context/base/core-def.mkii @@ -81,4 +81,16 @@ \ifx\writetolistfalse \undefined \else \appendtoks \writetolistfalse \to \everybeforeutilityread \fi \ifx\notesenabledfalse \undefined \else \appendtoks \notesenabledfalse \to \everybeforeutilityread \fi +\def\synctexwarning + {\ifdefined\synctex \ifnum\synctex=\zerocount \else + \writeline + \writestatus\m!systems{BEWARE: syntex functionality is enabled!}% + \writeline + \globallet\synctexwarning\relax + \fi \fi} + +\prependtoks \synctexwarning \to \everyjob +\prependtoks \synctexwarning \to \everystarttext +\appendtoks \synctexwarning \to \everystoptext + \protect \endinput diff --git a/tex/context/base/core-def.mkiv b/tex/context/base/core-def.mkiv index 0943a5cf9..d3d24acd7 100644 --- a/tex/context/base/core-def.mkiv +++ b/tex/context/base/core-def.mkiv @@ -78,4 +78,16 @@ \synchronizelocallinespecs \to \everyswitchtobodyfont +\def\synctexwarning + {\ifdefined\synctex \ifnum\synctex=\zerocount \else + \writeline + \writestatus\m!systems{BEWARE: syntex functionality is enabled!}% + \writeline + \globallet\synctexwarning\relax + \fi \fi} + +\prependtoks \synctexwarning \to \everyjob +\prependtoks \synctexwarning \to \everystarttext +\appendtoks \synctexwarning \to \everystoptext + \protect \endinput diff --git a/tex/context/base/core-job.lua b/tex/context/base/core-job.lua index af2c71551..8f5517df3 100644 --- a/tex/context/base/core-job.lua +++ b/tex/context/base/core-job.lua @@ -89,11 +89,10 @@ end local function convertexamodes(str) local x = xml.convert(str) - for e, d, k in xml.elements(x,"exa:variable") do - local dk = d[k] - local label = dk.at and dk.at.label + for e in xml.collected(x,"exa:variable") do + local label = e.at and e.at.label if label and label ~= "" then - local data = xml.content(dk) or "" + local data = xml.content(e) or "" local mode = label:match("^mode:(.+)$") if mode then texsprint(ctxcatcodes,format("\\enablemode[%s:%s]",mode,data)) diff --git a/tex/context/base/font-ctx.lua b/tex/context/base/font-ctx.lua index 54e724dd0..de1422454 100644 --- a/tex/context/base/font-ctx.lua +++ b/tex/context/base/font-ctx.lua @@ -297,26 +297,25 @@ function define.command_1(str) elseif name == "unknown" then texsprint(ctxcatcodes,"\\fcglet\\somefontname\\defaultfontfile") else - texsprint(ctxcatcodes,format("\\fcxdef\\somefontname{%s}",name)) + texsprint(ctxcatcodes,"\\fcxdef\\somefontname{",name,"}") end -- we can also use a count for the size if size and size ~= "" then local mode, size = sizepattern:match(size) if size and mode then count.scaledfontmode = mode - texsprint(ctxcatcodes,format("\\def\\somefontsize{%s}",size)) + texsprint(ctxcatcodes,"\\def\\somefontsize{",size,"}") else count.scaledfontmode = 0 - texsprint(ctxcatcodes,format("\\let\\somefontsize\\empty",size)) + texsprint(ctxcatcodes,"\\let\\somefontsize\\empty") end elseif true then -- so we don't need to check in tex count.scaledfontmode = 2 ---~ texsprint(ctxcatcodes,format("\\def\\somefontsize{*}",size)) - texsprint(ctxcatcodes,format("\\let\\somefontsize\\empty",size)) + texsprint(ctxcatcodes,"\\let\\somefontsize\\empty") else count.scaledfontmode = 0 - texsprint(ctxcatcodes,format("\\let\\somefontsize\\empty",size)) + texsprint(ctxcatcodes,"\\let\\somefontsize\\empty") end specification = define.makespecification(str,lookup,name,sub,method,detail,size) end diff --git a/tex/context/base/font-ini.lua b/tex/context/base/font-ini.lua index 6bebf6a86..5feb512b0 100644 --- a/tex/context/base/font-ini.lua +++ b/tex/context/base/font-ini.lua @@ -11,6 +11,8 @@ if not modules then modules = { } end modules ['font-ini'] = { --ldx]]-- local utf = unicode.utf8 +local format, serialize = string.format, table.serialize +local write_nl = texio.write_nl if not fontloader then fontloader = fontforge end @@ -91,7 +93,24 @@ function fonts.show_char_data(n) end local chr = tfmdata.characters[n] if chr then - texio.write_nl(table.serialize(chr,string.format("U_%04X",n))) + write_nl(format("%s @ %s => U%04X => %s => ",tfmdata.fullname,tfmdata.size,n,utf.char(n)) .. serialize(chr,false)) + end + end +end + +function fonts.show_font_parameters() + local tfmdata = fonts.ids[font.current()] + if tfmdata then + local parameters, mathconstants = tfmdata.parameters, tfmdata.MathConstants + local hasparameters, hasmathconstants = parameters and next(parameters), mathconstants and next(mathconstants) + if hasparameters then + write_nl(format("%s @ %s => parameters => ",tfmdata.fullname,tfmdata.size) .. serialize(parameters,false)) + end + if hasmathconstants then + write_nl(format("%s @ %s => math constants => ",tfmdata.fullname,tfmdata.size) .. serialize(mathconstants,false)) + end + if not hasparameters and not hasmathconstants then + write_nl(format("%s @ %s => no parameters and/or mathconstants",tfmdata.fullname,tfmdata.size)) end end end diff --git a/tex/context/base/font-ini.mkiv b/tex/context/base/font-ini.mkiv index 8c797652b..16ca08160 100644 --- a/tex/context/base/font-ini.mkiv +++ b/tex/context/base/font-ini.mkiv @@ -4011,6 +4011,11 @@ % \fi\fi\fi\fi % \endcsname} +%D goodies: + +\def\showchardata#1{\ctxlua{fonts.show_char_data("#1")}} +\def\showfontdata {\ctxlua{fonts.show_font_parameters()}} + \protect \endinput % \startluacode diff --git a/tex/context/base/font-mis.lua b/tex/context/base/font-mis.lua index cc20b9711..a1b717217 100644 --- a/tex/context/base/font-mis.lua +++ b/tex/context/base/font-mis.lua @@ -11,7 +11,7 @@ local lower, strip = string.lower, string.strip fonts.otf = fonts.otf or { } -fonts.otf.version = fonts.otf.version or 2.628 +fonts.otf.version = fonts.otf.version or 2.631 fonts.otf.pack = true fonts.otf.cache = containers.define("fonts", "otf", fonts.otf.version, true) diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua index cf3384126..59aff301b 100644 --- a/tex/context/base/font-otf.lua +++ b/tex/context/base/font-otf.lua @@ -82,7 +82,7 @@ otf.features.default = otf.features.default or { } otf.enhancers = otf.enhancers or { } otf.glists = { "gsub", "gpos" } -otf.version = 2.628 -- beware: also sync font-mis.lua +otf.version = 2.631 -- beware: also sync font-mis.lua otf.pack = true -- beware: also sync font-mis.lua otf.syncspace = true otf.notdef = false @@ -202,6 +202,7 @@ local enhancers = { "patch bugs", "merge cid fonts", "prepare unicode", "cleanup ttf tables", "compact glyphs", "reverse coverage", "cleanup aat", "enrich with features", "add some missing characters", +--~ "reorganize mark classes", "reorganize kerns", -- moved here "flatten glyph lookups", "flatten anchor tables", "flatten feature tables", "prepare luatex tables", @@ -354,6 +355,22 @@ end -- todo: normalize, design_size => designsize +otf.enhancers["reorganize mark classes"] = function(data,filename) + if data.mark_classes then + local unicodes = data.luatex.unicodes + local reverse = { } + for name, class in next, data.mark_classes do + local t = { } + for s in gmatch(class,"[^ ]+") do + t[unicodes[s]] = true + end + reverse[name] = t + end + data.luatex.markclasses = reverse + data.mark_classes = nil + end +end + otf.enhancers["prepare luatex tables"] = function(data,filename) data.luatex = data.luatex or { } local luatex = data.luatex @@ -657,12 +674,21 @@ otf.enhancers["analyse subtables"] = function(data,filename) end local flags = gk.flags if flags then +--~ gk.flags = { -- forcing false packs nicer +--~ (flags.ignorecombiningmarks and "mark") or false, +--~ (flags.ignoreligatures and "ligature") or false, +--~ (flags.ignorebaseglyphs and "base") or false, +--~ flags.r2l or false, +--~ } gk.flags = { -- forcing false packs nicer - (flags.ignorecombiningmarks and "mark") or false, - (flags.ignoreligatures and "ligature") or false, - (flags.ignorebaseglyphs and "base") or false, - flags.r2l or false + ((flags.ignorecombiningmarks or flags.mark_class) and "mark") or false, + ( flags.ignoreligatures and "ligature") or false, + ( flags.ignorebaseglyphs and "base") or false, + flags.r2l or false, } +--~ if flags.mark_class then +--~ gk.markclass = luatex.markclasses[flags.mark_class] +--~ end end end end diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua index 66e7f7a3f..14837d2e1 100644 --- a/tex/context/base/font-otn.lua +++ b/tex/context/base/font-otn.lua @@ -136,6 +136,7 @@ local trace_bugs = false trackers.register("otf.bugs", function local trace_details = false trackers.register("otf.details", function(v) trace_details = v end) local trace_applied = false trackers.register("otf.applied", function(v) trace_applied = v end) local trace_steps = false trackers.register("otf.steps", function(v) trace_steps = v end) +local trace_skips = false trackers.register("otf.skips", function(v) trace_skips = v end) trackers.register("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) trackers.register("otf.normal_chain", function(v) otf.setcontextchain(v and "normal") end) @@ -1506,16 +1507,26 @@ end -- we don't need to pass the currentcontext, saves a bit -- make a slow variant then can be activated but with more tracing +local function show_skip(kind,chainname,char,ck,class) + if ck[9] then + logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) + else + logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2]) + end +end + local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache) -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] local flags, done = sequence.flags, false local skipmark, skipligature, skipbase = flags[1], flags[2], flags[3] local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) + local markclass = sequence.markclass for k=1,#contexts do local match, current, last = true, start, start local ck = contexts[k] local sequence = ck[3] local s = #sequence + -- f..l = mid string if s == 1 then -- never happens match = current.id == glyph and current.subtype<256 and current.font == currentfont and sequence[1][current.char] @@ -1543,9 +1554,13 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local ccd = descriptions[char] if ccd then local class = ccd.class +--~ if class == skipmark or class == skipligature or class == skipbase or (markclass and not markclass[char]) then if class == skipmark or class == skipligature or class == skipbase then ---~ if someskip and class == skipmark or class == skipligature or class == skipbase then +--~ if someskip and (class == skipmark or class == skipligature or class == skipbase) then -- skip 'm + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end last = last.next elseif sequence[n][char] then if n < l then @@ -1573,6 +1588,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence -- end end if match and f > 1 then + -- before local prev = start.prev if prev then local n = f-1 @@ -1585,9 +1601,13 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local ccd = descriptions[char] if ccd then local class = ccd.class +--~ if class == skipmark or class == skipligature or class == skipbase or (markclass and not markclass[char]) then if class == skipmark or class == skipligature or class == skipbase then --~ if someskip and class == skipmark or class == skipligature or class == skipbase then -- skip 'm + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end elseif sequence[n][char] then n = n -1 else @@ -1624,9 +1644,10 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence end end if match and s > l then + -- after local current = last.next if current then - -- removed optimiziation for s-l == 1, we have to deal with marks anyway + -- removed optimization for s-l == 1, we have to deal with marks anyway local n = l + 1 while n <= s do if current then @@ -1637,9 +1658,13 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local ccd = descriptions[char] if ccd then local class = ccd.class +--~ if class == skipmark or class == skipligature or class == skipbase or (markclass and not markclass[char]) then if class == skipmark or class == skipligature or class == skipbase then --~ if someskip and class == skipmark or class == skipligature or class == skipbase then -- skip 'm + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end elseif sequence[n][char] then n = n + 1 else @@ -1800,6 +1825,8 @@ end local resolved = { } -- we only resolve a font,script,language pair once +-- todo: pass all these 'locals' in a table + function fonts.methods.node.otf.features(head,font,attr) if trace_steps then checkstep(head) @@ -1959,6 +1986,7 @@ function fonts.methods.node.otf.features(head,font,attr) -- sequence kan weg local ok start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) +--~ texio.write_nl(tostring(lookupname),tostring(lookupmatch),tostring(ok)) if ok then success = true end diff --git a/tex/context/base/font-tfm.lua b/tex/context/base/font-tfm.lua index aeaf4fc94..905da59ce 100644 --- a/tex/context/base/font-tfm.lua +++ b/tex/context/base/font-tfm.lua @@ -258,6 +258,7 @@ function tfm.do_scale(tfmtable, scaledpoints) t.unicodes = tfmtable.unicodes t.indices = tfmtable.indices t.marks = tfmtable.marks +t.colorscheme = tfmtable.colorscheme t.descriptions = descriptions if tfmtable.fonts then t.fonts = table.fastcopy(tfmtable.fonts) -- hm also at the end diff --git a/tex/context/base/grph-inc.lua b/tex/context/base/grph-inc.lua index df60b87ba..57ac0bda4 100644 --- a/tex/context/base/grph-inc.lua +++ b/tex/context/base/grph-inc.lua @@ -190,8 +190,8 @@ function figures.setpaths(locationset,pathlist) end figures.paths, last_pathlist = t, pathlist if trace_figures then - logs.report("figures","locations: %s",last_locationset) - logs.report("figures","path list: %s",table.concat(figures.paths)) + commands.writestatus("figures","locations: %s",last_locationset) + commands.writestatus("figures","path list: %s",table.concat(figures.paths, " ")) end end @@ -300,7 +300,6 @@ do end end function figures.tprint(category,tag,default) ---~ print("!!!!!!!!",category,tag,default) texsprint(ctxcatcodes,figures.get(category,tag,default)) end function figures.current() @@ -355,15 +354,15 @@ local function register(askedname,specification) if not found then specification.found = false if trace_figures then - logs.report("figures","format not supported: %s",format) + commands.writestatus("figures","format not supported: %s",format) end else specification.found = true if trace_figures then if validtypes[format] then - logs.report("figures","format natively supported by backend: %s",format) + commands.writestatus("figures","format natively supported by backend: %s",format) else - logs.report("figures","format supported by output file format: %s",format) + commands.writestatus("figures","format supported by output file format: %s",format) end end end @@ -380,7 +379,7 @@ local function locate(request) -- name, format, cache if figures.found[askedname] then return figures.found[askedname] end - local askedpath= file.dirname(askedname) + local askedpath= file.is_rootbased_path(askedname) local askedbase = file.basename(askedname) local askedformat = (request.format ~= "" and request.format ~= "unknown" and request.format) or file.extname(askedname) or "" local askedcache = request.cache @@ -407,7 +406,7 @@ local function locate(request) -- name, format, cache }) end end - if askedpath ~= "" then + if askedpath then -- path and type given, todo: strip pieces of path if figures.exists(askedname,askedformat) then return register(askedname, { @@ -442,7 +441,7 @@ local function locate(request) -- name, format, cache end end end - elseif askedpath ~= "" then + elseif askedpath then for _, format in ipairs(figures.order) do local list = figures.formats[format].list or { format } for _, suffix in ipairs(list) do @@ -592,17 +591,20 @@ end -- -- -- generic -- -- -- -function figures.existers.generic(askedname) ---~ local result = io.exists(askedname) ---~ result = (result==true and askedname) or result ---~ local result = resolvers.find_file(askedname) or "" - local result = resolvers.findbinfile(askedname) or "" - if result == "" then result = false end +function figures.existers.generic(askedname,resolve) + -- not findbinfile + local result + if lfs.isfile(askedname) then + result = askedname + elseif resolve then + result = resolvers.findbinfile(askedname) or "" + if result == "" then result = false end + end if trace_figures then if result then - logs.report("figures","found: %s -> %s",askedname,result) + commands.writestatus("figures","found: %s -> %s",askedname,result) else - logs.report("figures","not found: %s",askedname) + commands.writestatus("figures","not found: %s",askedname) end end return result @@ -821,13 +823,13 @@ function bases.find(basename,askedlabel) end t = false if base[2] and base[3] then -- rlx:library - for e, d, k in xml.elements(base[3],"/(*:library|figurelibrary)/*:figure/*:label") do + for e in xml.collected(base[3],"/(*:library|figurelibrary)/*:figure/*:label") do page = page + 1 - if xml.content(d[k]) == askedlabel then + if xml.content(e) == askedlabel then t = { base = file.replacesuffix(base[2],"pdf"), format = "pdf", - name = xml.filters.text(e,"*:file"), + name = xml.text(e,"../*:file"), -- to be checked page = page, } bases.found[askedlabel] = t diff --git a/tex/context/base/grph-inc.mkii b/tex/context/base/grph-inc.mkii index dde7ab2e6..1bd7544d8 100644 --- a/tex/context/base/grph-inc.mkii +++ b/tex/context/base/grph-inc.mkii @@ -476,7 +476,10 @@ \fi} \def\externalfigurestamp % needs \edef'd macros! - {\wantedfigurename + {\ifx\wantedfigurepath\empty\else + -\wantedfigurepath + \fi + \wantedfigurename \ifx\wantedfiguretype\empty\else \ifx\wantedfiguretype\s!unknown\else -\wantedfiguretype diff --git a/tex/context/base/l-aux.lua b/tex/context/base/l-aux.lua index 39b8f9546..8d74ec178 100644 --- a/tex/context/base/l-aux.lua +++ b/tex/context/base/l-aux.lua @@ -221,3 +221,21 @@ function aux.accesstable(target) end return t end + +-- as we use this a lot ... + +--~ function aux.cachefunction(action,weak) +--~ local cache = { } +--~ if weak then +--~ setmetatable(cache, { __mode = "kv" } ) +--~ end +--~ local function reminder(str) +--~ local found = cache[str] +--~ if not found then +--~ found = action(str) +--~ cache[str] = found +--~ end +--~ return found +--~ end +--~ return reminder, cache +--~ end diff --git a/tex/context/base/l-file.lua b/tex/context/base/l-file.lua index bdb4872d7..8cc50a6aa 100644 --- a/tex/context/base/l-file.lua +++ b/tex/context/base/l-file.lua @@ -230,11 +230,11 @@ local rootbased = lpeg.P("/") + letter*lpeg.P(":") -- ./name ../name /name c: :// name/name function file.is_qualified_path(filename) - return qualified:match(filename) + return qualified:match(filename) ~= nil end function file.is_rootbased_path(filename) - return rootbased:match(filename) + return rootbased:match(filename) ~= nil end local slash = lpeg.S("\\/") diff --git a/tex/context/base/l-lpeg.lua b/tex/context/base/l-lpeg.lua index 41dc2769b..2c95730c4 100644 --- a/tex/context/base/l-lpeg.lua +++ b/tex/context/base/l-lpeg.lua @@ -100,3 +100,15 @@ function string:split(separator) end return c:match(self) end + +--~ function lpeg.L(list,pp) +--~ local p = pp +--~ for l=1,#list do +--~ if p then +--~ p = p + lpeg.P(list[l]) +--~ else +--~ p = lpeg.P(list[l]) +--~ end +--~ end +--~ return p +--~ end diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua index e14514291..9e0b12f7c 100644 --- a/tex/context/base/l-table.lua +++ b/tex/context/base/l-table.lua @@ -26,6 +26,14 @@ function table.strip(tab) return lst end +function table.keys(t) + local k = { } + for key,_ in next, t do + k[#k+1] = key + end + return k +end + local function compare(a,b) return (tostring(a) < tostring(b)) end @@ -798,22 +806,6 @@ function table.reverse(t) return tt end ---~ function table.keys(t) ---~ local k = { } ---~ for k,_ in next, t do ---~ k[#k+1] = k ---~ end ---~ return k ---~ end - ---~ function table.keys_as_string(t) ---~ local k = { } ---~ for k,_ in next, t do ---~ k[#k+1] = k ---~ end ---~ return concat(k,"") ---~ end - function table.insert_before_value(t,value,extra) for i=1,#t do if t[i] == extra then diff --git a/tex/context/base/l-xml.lua b/tex/context/base/l-xml.lua index cd4a797b0..8d1284daa 100644 --- a/tex/context/base/l-xml.lua +++ b/tex/context/base/l-xml.lua @@ -8,10 +8,8 @@ if not modules then modules = { } end modules ['l-xml'] = { -- RJ: key=value ... lpeg.Ca(lpeg.Cc({}) * (pattern-producing-key-and-value / rawset)^0) --- this module needs a cleanup: check latest lpeg, passing args, (sub)grammar, etc etc --- some code may move to l-xmlext --- some day we will really compile the lpaths (just construct functions) --- todo: some things per xml file, like namespace remapping +-- this file has been replaced by the lxml-* files and in due time this will become +-- a stub (currently it's used in some workflows) --[[ldx-- <p>The parser used here is inspired by the variant discussed in the lua book, but diff --git a/tex/context/base/luat-lib.mkiv b/tex/context/base/luat-lib.mkiv index f3c383ae5..d1aea092c 100644 --- a/tex/context/base/luat-lib.mkiv +++ b/tex/context/base/luat-lib.mkiv @@ -42,7 +42,13 @@ \registerctxluafile{luat-ini} {1.001} \registerctxluafile{luat-env} {1.001} -\registerctxluafile{l-xml} {1.001} % we need to load lxml-tab earlier so this will change ! ! ! ! ! ! ! +%registerctxluafile{l-xml} {1.001} % we need to load lxml-tab earlier so this will change ! ! ! ! ! ! ! +\registerctxluafile{lxml-tab} {1.001} +\registerctxluafile{lxml-lpt} {1.001} +\registerctxluafile{lxml-xml} {1.001} +\registerctxluafile{lxml-aux} {1.001} +\registerctxluafile{lxml-mis} {1.001} +\registerctxluafile{lxml-ent} {1.001} \startruntimeluacode \edef\asciia{\ctxlua{tex.sprint(logs.mode)}} diff --git a/tex/context/base/lxml-aux.lua b/tex/context/base/lxml-aux.lua new file mode 100644 index 000000000..ccff8e90d --- /dev/null +++ b/tex/context/base/lxml-aux.lua @@ -0,0 +1,428 @@ +if not modules then modules = { } end modules ['lxml-aux'] = { + version = 1.001, + comment = "this module is the basis for the lxml-* ones", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- not all functions here make sense anymore vbut we keep them for +-- compatibility reasons + +local xmlparseapply, xmlconvert, xmlcopy = xml.parse_apply, xml.convert, xml.copy + +local type = type +local insert, remove = table.insert, table.remove +local gmatch, gsub = string.gmatch, string.gsub + +local function withelements(e,handle,depth) + if e and handle then + local edt = e.dt + if edt then + depth = depth or 0 + for i=1,#edt do + local e = edt[i] + if type(e) == "table" then + handle(e,depth) + withelements(e,handle,depth+1) + end + end + end + end +end + +xml.withelements = withelements + +function xml.withelement(e,n,handle) -- slow + if e and n ~= 0 and handle then + local edt = e.dt + if edt then + if n > 0 then + for i=1,#edt do + local ei = edt[i] + if type(ei) == "table" then + if n == 1 then + handle(ei) + return + else + n = n - 1 + end + end + end + elseif n < 0 then + for i=#edt,1,-1 do + local ei = edt[i] + if type(ei) == "table" then + if n == -1 then + handle(ei) + return + else + n = n + 1 + end + end + end + end + end + end +end + +xml.elements_only = xml.collected + +function xml.each_element(root, pattern, handle, reverse) + local collected = xmlparseapply({ root },pattern) + if collected then + if reverse then + for c=#collected,1,-1 do + handle(collected[c]) + end + else + for c=1,#collected do + handle(collected[c]) + end + end + return collected + end +end + +xml.process_elements = xml.each_element + +function xml.process_attributes(root, pattern, handle) + local collected = xmlparseapply({ root },pattern) + if collected and handle then + for c=1,#collected do + handle(collected[c].at) + end + end + return collected +end + +--[[ldx-- +<p>The following functions collect elements and texts.</p> +--ldx]]-- + +-- are these still needed -> lxml-cmp.lua + +function xml.collect_elements(root, pattern) + return xmlparseapply({ root },pattern) +end + +function xml.collect_texts(root, pattern, flatten) -- todo: variant with handle + local collected = xmlparseapply({ root },pattern) + if collected and flatten then + local xmltostring = xml.tostring + for c=1,#collected do + collected[c] = xmltostring(collected[c]) + end + end + return collected or { } +end + +function xml.collect_tags(root, pattern, nonamespace) + local collected = xmlparseapply({ root },pattern) + if collected then + local t = { } + for c=1,#collected do + local e = collected[c] + local ns, tg = e.ns, e.tg + if nonamespace then + t[#t+1] = tg + elseif ns == "" then + t[#t+1] = tg + else + t[#t+1] = ns .. ":" .. tg + end + end + return t + end +end + +--[[ldx-- +<p>We've now arrives at the functions that manipulate the tree.</p> +--ldx]]-- + +local no_root = { no_root = true } + +function xml.inject_element(root, pattern, element, prepend) + if root and element then + if type(element) == "string" then + element = xmlconvert(element,no_root) + end + if element then + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + local r = e.__p__ + local d = r.dt + local k = e.ni + if element.ri then + element = element.dt[element.ri].dt + else + element = element.dt + end + local edt + if r.ri then + edt = r.dt[r.ri].dt + else + edt = d and d[k] and d[k].dt + end + if edt then + local be, af + if prepend then + be, af = xmlcopy(element), edt + else + be, af = edt, xmlcopy(element) + end + for i=1,#af do + be[#be+1] = af[i] + end + if r.ri then + r.dt[r.ri].dt = be + else + d[k].dt = be + end + else + -- r.dt = element.dt -- todo + end + end + end + end + end +end + +-- todo: copy ! + +function xml.insert_element(root, pattern, element, before) -- todo: element als functie + if root and element then + if pattern == "/" then + xml.inject_element(root, pattern, element, before) + else + local matches, collect = { }, nil + if type(element) == "string" then + element = xmlconvert(element,true) + end + if element and element.ri then + element = element.dt[element.ri] + end + if element then + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + local r = e.__p__ + local d = r.dt + local k = e.ni + if not before then + k = k + 1 + end + if element.tg then + insert(d,k,element) -- untested + else + local edt = element.dt + if edt then + for i=1,#edt do + insert(d,k,edt[i]) + k = k + 1 + end + end + end + end + end + end + end + end +end + +xml.insert_element_after = xml.insert_element +xml.insert_element_before = function(r,p,e) xml.insert_element(r,p,e,true) end +xml.inject_element_after = xml.inject_element +xml.inject_element_before = function(r,p,e) xml.inject_element(r,p,e,true) end + +function xml.delete_element(root, pattern) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + remove(e.__p__.dt,e.ni) + e.ni = nil + end + end + return collection +end + +function xml.replace_element(root, pattern, element) + if type(element) == "string" then + element = xmlconvert(element,true) + end + if element and element.ri then + element = element.dt[element.ri] + end + if element then + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + e.__p__.dt[e.ni] = element.dt -- maybe not clever enough + end + end + end +end + +local function include(xmldata,pattern,attribute,recursive,loaddata) + -- parse="text" (default: xml), encoding="" (todo) + -- attribute = attribute or 'href' + pattern = pattern or 'include' + loaddata = loaddata or io.loaddata + local collected = xmlparseapply({ xmldata },pattern) + if collected then + for c=1,#collected do + local ek = collected[c] + local name = nil + local ekdt = ek.dt + local ekat = ek.at + local epdt = ek.__p__.dt + if not attribute or attribute == "" then + name = (type(ekdt) == "table" and ekdt[1]) or ekdt -- ckeck, probably always tab or str + end + if not name then + for a in gmatch(attribute or "href","([^|]+)") do + name = ekat[a] + if name then break end + end + end + local data = (name and name ~= "" and loaddata(name)) or "" + if data == "" then + epdt[ek.ni] = "" -- xml.empty(d,k) + elseif ekat["parse"] == "text" then + -- for the moment hard coded + epdt[ek.ni] = xml.escaped(data) -- d[k] = xml.escaped(data) + else + local settings = xmldata.settings + settings.parent_root = xmldata -- to be tested + local xi = xmlconvert(data,settings) + if not xi then + epdt[ek.ni] = "" -- xml.empty(d,k) + else + if recursive then + include(xi,pattern,attribute,recursive,loaddata) + end + epdt[ek.ni] = xml.body(xi) -- xml.assign(d,k,xi) + end + end + end + end +end + +xml.include = include + +function xml.strip_whitespace(root, pattern, nolines) -- strips all leading and trailing space ! + local collected = xmlparseapply({ root },pattern) + if collected then + for i=1,#collected do + local e = collected[i] + local edt = e.dt + if edt then + local t = { } + for i=1,#edt do + local str = edt[i] + if type(str) == "string" then + if str == "" then + -- stripped + else + if nolines then + str = gsub(str,"[ \n\r\t]+"," ") + end + if str == "" then + -- stripped + else + t[#t+1] = str + end + end + else +--~ str.ni = i + t[#t+1] = str + end + end + e.dt = t + end + end + end +end + +local function rename_space(root, oldspace, newspace) -- fast variant + local ndt = #root.dt + for i=1,ndt or 0 do + local e = root[i] + if type(e) == "table" then + if e.ns == oldspace then + e.ns = newspace + if e.rn then + e.rn = newspace + end + end + local edt = e.dt + if edt then + rename_space(edt, oldspace, newspace) + end + end + end +end + +xml.rename_space = rename_space + +function xml.remap_tag(root, pattern, newtg) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + collected[c].tg = newtg + end + end +end + +function xml.remap_namespace(root, pattern, newns) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + collected[c].ns = newns + end + end +end + +function xml.check_namespace(root, pattern, newns) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + if (not e.rn or e.rn == "") and e.ns == "" then + e.rn = newns + end + end + end +end + +function xml.remap_name(root, pattern, newtg, newns, newrn) + local collected = xmlparseapply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + e.tg, e.ns, e.rn = newtg, newns, newrn + end + end +end + +--[[ldx-- +<p>Here are a few synonyms.</p> +--ldx]]-- + +xml.each = xml.each_element +xml.process = xml.process_element +xml.strip = xml.strip_whitespace +xml.collect = xml.collect_elements +xml.all = xml.collect_elements + +xml.insert = xml.insert_element_after +xml.inject = xml.inject_element_after +xml.after = xml.insert_element_after +xml.before = xml.insert_element_before +xml.delete = xml.delete_element +xml.replace = xml.replace_element diff --git a/tex/context/base/lxml-dir.lua b/tex/context/base/lxml-dir.lua new file mode 100644 index 000000000..12d7680ed --- /dev/null +++ b/tex/context/base/lxml-dir.lua @@ -0,0 +1,107 @@ +if not modules then modules = { } end modules ['lxml-dir'] = { + version = 1.001, + comment = "this module is the basis for the lxml-* ones", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local format, gsub = string.format, string.gsub +local get_id = lxml.id +local texsprint, ctxcatcodes = tex.sprint, tex.ctxcatcodes +local xmlparseapply = xml.parse_apply + +--~ <?xml version="1.0" standalone="yes"?> +--~ <!-- demo.cdx --> +--~ <directives> +--~ <!-- +--~ <directive attribute='id' value="100" setup="cdx:100"/> +--~ <directive attribute='id' value="101" setup="cdx:101"/> +--~ --> +--~ <!-- +--~ <directive attribute='cdx' value="colors" element="cals:table" setup="cdx:cals:table:colors"/> +--~ <directive attribute='cdx' value="vertical" element="cals:table" setup="cdx:cals:table:vertical"/> +--~ <directive attribute='cdx' value="noframe" element="cals:table" setup="cdx:cals:table:noframe"/> +--~ --> +--~ <directive attribute='cdx' value="*" element="cals:table" setup="cdx:cals:table:*"/> +--~ </directives> + + + +lxml.directives = lxml.directives or { } + +local directives = lxml.directives + +local data = { + setup = { }, + before = { }, + after = { } +} + +local function load_setup(filename) + local fullname = resolvers.find_file(filename) or "" + if fullname ~= "" then + filename = fullname + end + local collection = xmlparseapply({ get_id(xml.load(filename)) },"directive") + if collection then + for i=1,#collection do + local at = collection[i].at + local attribute, value, element = at.attribute or "", at.value or "", at.element or '*' + local setup, before, after = at.setup or "", at.before or "", at.after or "" + if attribute ~= "" and value ~= "" then + local key = format("%s::%s::%s",element,attribute,value) + local t = data[key] or { } + if setup ~= "" then t.setup = setup end + if before ~= "" then t.before = before end + if after ~= "" then t.after = after end + data[key] = t + end + end + end +end + +local function handle_setup(category,root,attribute,element) + root = get_id(root) + if attribute then + local value = root.at[attribute] + if value then + if not element then + local ns, tg = root.rn or root.ns, root.tg + if ns == "" then + element = tg + else + element = ns .. ':' .. tg + end + end + local setup = data[format("%s::%s::%s",element,attribute,value)] + if setup then + setup = setup[category] + end + if setup then + texsprint(ctxcatcodes,"\\directsetup{",setup,"}") + else + setup = data[format("%s::%s::*",element,attribute)] + if setup then + setup = setup[category] + end + if setup then + texsprint(ctxcatcodes,"\\directsetup{",gsub(setup,'%*',value),"}") + end + end + end + end +end + +directives.load = load_setup +directives.handle = handle_setup + +function directives.setup(root,attribute,element) + handle_setup('setup',root,attribute,element) +end +function directives.before(root,attribute,element) + handle_setup('before',root,attribute,element) +end +function directives.after(root,attribute,element) + handle_setup('after',root,attribute,element) +end diff --git a/tex/context/base/lxml-ent.lua b/tex/context/base/lxml-ent.lua index c91d12706..69c37e8f9 100644 --- a/tex/context/base/lxml-ent.lua +++ b/tex/context/base/lxml-ent.lua @@ -6,102 +6,21 @@ if not modules then modules = { } end modules ['lxml-ent'] = { license = "see context related readme files" } -local type, next, tonumber, tostring, setmetatable, loadstring = type, next, tonumber, tostring, setmetatable, loadstring -local format, gsub, find = string.format, string.gsub, string.find -local utfchar = unicode.utf8.char +local type, next = type, next +local texsprint, ctxcatcodes = tex.sprint, tex.ctxcatcodes +local utfupper = utf.upper --[[ldx-- <p>We provide (at least here) two entity handlers. The more extensive resolver consults a hash first, tries to convert to <l n='utf'/> next, and finaly calls a handler when defines. When this all fails, the original entity is returned.</p> + +<p>We do things different now but it's still somewhat experimental</p> --ldx]]-- xml.entities = xml.entities or { } -- xml.entity_handler == function -function xml.entity_handler(e) - return format("[%s]",e) -end - -local function toutf(s) - return utfchar(tonumber(s,16)) -end - -local function utfize(root) - local d = root.dt - for k=1,#d do - local dk = d[k] - if type(dk) == "string" then - -- test prevents copying if no match - if find(dk,"&#x.-;") then - d[k] = gsub(dk,"&#x(.-);",toutf) - end - else - utfize(dk) - end - end -end - -xml.utfize = utfize - -local function resolve(e) -- hex encoded always first, just to avoid mkii fallbacks - if find(e,"^#x") then - return utfchar(tonumber(e:sub(3),16)) - elseif find(e,"^#") then - return utfchar(tonumber(e:sub(2))) - else - local ee = xml.entities[e] -- we cannot shortcut this one (is reloaded) - if ee then - return ee - else - local h = xml.entity_handler - return (h and h(e)) or "&" .. e .. ";" - end - end -end - -local function resolve_entities(root) - if not root.special or root.tg == "@rt@" then - local d = root.dt - for k=1,#d do - local dk = d[k] - if type(dk) == "string" then - if find(dk,"&.-;") then - d[k] = gsub(dk,"&(.-);",resolve) - end - else - resolve_entities(dk) - end - end - end -end - -xml.resolve_entities = resolve_entities - -function xml.utfize_text(str) - if find(str,"&#") then - return (gsub(str,"&#x(.-);",toutf)) - else - return str - end -end - -function xml.resolve_text_entities(str) -- maybe an lpeg. maybe resolve inline - if find(str,"&") then - return (gsub(str,"&(.-);",resolve)) - else - return str - end -end - -function xml.show_text_entities(str) - if find(str,"&") then - return (gsub(str,"&(.-);","[%1]")) - else - return str - end -end - -- experimental, this will be done differently function xml.merge_entities(root) @@ -113,3 +32,21 @@ function xml.merge_entities(root) end end end + +function xml.resolved_entity(str) + local e = xml.entities[str] + if e then + local te = type(e) + if te == "function" then + e(str) + else + texsprint(ctxcatcodes,e) + end + else + texsprint(ctxcatcodes,"\\xmle{",str,"}{",utfupper(str),"}") -- we need to use our own upper + end +end + +xml.entities.amp = function() tex.write("&") end +xml.entities.lt = function() tex.write("<") end +xml.entities.gt = function() tex.write(">") end diff --git a/tex/context/base/lxml-inf.lua b/tex/context/base/lxml-inf.lua new file mode 100644 index 000000000..629c869ec --- /dev/null +++ b/tex/context/base/lxml-inf.lua @@ -0,0 +1,53 @@ +if not modules then modules = { } end modules ['lxml-inf'] = { + version = 1.001, + comment = "this module is the basis for the lxml-* ones", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- This file will be loaded runtime by x-pending.tex. + +local status, stack + +local function get(e,d) + local ns, tg = e.ns, e.tg + local name = tg + if ns ~= "" then name = ns .. ":" .. tg end + stack[d] = name + local ec = e.command + if ec == true then + ec = "system: text" + elseif ec == false then + ec = "system: skip" + elseif ec == nil then + ec = "system: not set" + elseif type(ec) == "string" then + ec = "setup: " .. ec + else -- function + ec = tostring(ec) + end + local tag = concat(stack," => ",1,d) + local s = status[tag] + if not s then + s = { } + status[tag] = s + end + s[ec] = (s[ec] or 0) + 1 +end + +local function get_command_status(id) + status, stack = {}, {} + if id then + xmlwithelements(get_id(id),get) + return status + else + local t = { } + for id, _ in next, loaded do + t[id] = get_command_status(id) + end + return t + end +end + +lxml.get_command_status = get_command_status diff --git a/tex/context/base/lxml-ini.lua b/tex/context/base/lxml-ini.lua deleted file mode 100644 index cc4b953cd..000000000 --- a/tex/context/base/lxml-ini.lua +++ /dev/null @@ -1,1390 +0,0 @@ -if not modules then modules = { } end modules ['lxml-ini'] = { - version = 1.001, - comment = "companion to lxml-ini.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local utf = unicode.utf8 -local tex = tex or {} - -local texsprint, texprint, texwrite, utfchar = tex.sprint or print, tex.print or print, tex.write or print, utf.char -local concat, insert, remove, gsub, find = table.concat, table.insert, table.remove -local format, sub, gsub, find = string.format, string.sub, string.gsub, string.find -local type, next, tonumber, tostring = type, next, tonumber, tostring - -local ctxcatcodes = tex.ctxcatcodes -local texcatcodes = tex.texcatcodes -local vrbcatcodes = tex.vrbcatcodes - -local trace_setups = false trackers.register("lxml.setups", function(v) trace_setups = v end) -local trace_loading = false trackers.register("lxml.loading", function(v) trace_loading = v end) -local trace_access = false trackers.register("lxml.access", function(v) trace_access = v end) - --- todo: speed up: remember last index/match combination - -local traverse, lpath = xml.traverse, xml.lpath - -local xmlfilter, xmlfirst, xmllast, xmlall, xmlcount = xml.filter, xml.first, xml.last, xml.all, xml.count -local xmlcollect, xmlcontent, xmlcollect_texts, xmlcollect_tags, xmlcollect_elements = xml.collect, xml.content, xml.collect_texts, xml.collect_tags, xml.collect_elements -local xmlattribute, xmlindex, xmlchainattribute = xml.filters.attribute, xml.filters.index, xml.filters.chainattribute -local xmlelements, xmlsetproperty = xml.elements, xml.setproperty - -document = document or { } -document.xml = document.xml or { } - --- todo: loaded and myself per document so that we can garbage collect buffers - -lxml = lxml or { } -lxml.loaded = { } -lxml.noffiles = 0 -lxml.nofconverted = 0 -lxml.nofindices = 0 - -local loaded = lxml.loaded - --- experiment - -function lxml.store(id,root,filename) - loaded[id] = root - xmlsetproperty(root,"name",id) - if filename then - xmlsetproperty(root,"filename",filename) - end -end - ---~ local currentdocuments, currentloaded, currentdocument, defaultdocument = { }, { }, "", "" - ---~ function lxml.pushdocument(name) -- catches double names ---~ if #currentdocuments == 0 then ---~ defaultdocument = name ---~ end ---~ currentdocument = name ---~ insert(currentdocuments,currentdocument) ---~ insert(currentloaded,loaded[currentdocument]) ---~ if trace_access then ---~ logs.report("lxml","pushed: %s",currentdocument) ---~ end ---~ end - ---~ function lxml.popdocument() ---~ currentdocument = remove(currentdocuments) ---~ if not currentdocument or currentdocument == "" then ---~ currentdocument = defaultdocument ---~ end ---~ loaded[currentdocument] = remove(currentloaded) ---~ if trace_access then ---~ logs.report("lxml","popped: %s",currentdocument) ---~ end ---~ end - ---~ local splitter = ((lpeg.R("09")^1/tonumber) + lpeg.Cc(false)) * lpeg.P("@")^0 * (lpeg.C(lpeg.P(1)^1) + lpeg.Cc(false)) - -local splitter = lpeg.C((1-lpeg.P(":"))^1) * lpeg.P("::") * lpeg.C(lpeg.P(1)^1) - -lxml.idsplitter = splitter - -function lxml.splitid(id) - local d, i = splitter:match(id) - if d then - return d, i - else - return "", id - end -end - -local function get_id(id, qualified) - if type(id) == "table" then - return id - else - local lid = loaded[id] - if lid then - return lid - else - local d, i = splitter:match(id) - if d then - local ld = loaded[d] - if ld then - local ldi = ld.index - if ldi then - local root = ldi[tonumber(i)] - if root then - if qualified then -- we need this else two args that confuse others - return root, d - else - return root - end - elseif trace_access then - logs.report("lxml","'%s' has no index entry '%s'",d,i) - end - elseif trace_access then - logs.report("lxml","'%s' has no index",d) - end - elseif trace_access then - logs.report("lxml","'%s' is not loaded",d) - end - else ---~ local ld = loaded[currentdocument] ---~ if ld then ---~ local ldi = ld.index ---~ if ldi then ---~ local root = ldi[tonumber(id)] ---~ if root then ---~ if qualified then -- we need this else two args that confuse others ---~ return root, currentdocument ---~ else ---~ return root ---~ end ---~ elseif trace_access then ---~ logs.report("lxml","current document '%s' has no index entry '%s'",currentdocument,id) ---~ end ---~ elseif trace_access then ---~ logs.report("lxml","current document '%s' has no index",currentdocument) ---~ end ---~ elseif trace_access then ---~ logs.report("lxml","current document '%s' not loaded",currentdocument) ---~ end - end - end - end -end - -lxml.id = get_id - -function lxml.root(id) - return loaded[id] -end - -do - - xml.specialhandler = xml.specialhandler or { } - - local specialhandler = xml.specialhandler - local serialize = xml.serialize - - local crlf = lpeg.P("\r\n") - local cr = lpeg.P("\r") - local lf = lpeg.P("\n") - local space = lpeg.S(" \t\f\v") - local newline = crlf + cr + lf - local spacing = newline * space^0 - local content = lpeg.C((1-spacing)^1) - local verbose = lpeg.C((1-(space+newline))^1) - - -- local capture = ( - -- newline^2 * lpeg.Cc("") / texprint + - -- newline * lpeg.Cc(" ") / texsprint + - -- content / texsprint - -- )^0 - - local capture = ( - space^0 * newline^2 * lpeg.Cc("") / texprint + - space^0 * newline * space^0 * lpeg.Cc(" ") / texsprint + - content / texsprint - )^0 - - local forceraw, rawroot = false, nil - - function lxml.startraw() - forceraw = true - end - function lxml.stopraw() - forceraw = false - end - function lxml.rawroot() - return rawroot - end - function lxml.rawpath(rootid) - if rawroot and type(rawroot) == "table" then - local text, path, rp - if not rawroot.dt then - text, path, rp = "text", "", rawroot[0] - else - path, rp = "tree", "", rawroot.__p__ - end - while rp do - local rptg = rp.tg - if rptg then - path = rptg .. "/" .. path - end - rp = rp.__p__ - end - return { rootid, "/" .. path, text } - end - end - - local function sprint(root) - if not root then - --~ rawroot = false - -- quit - else - local tr = type(root) - if tr == "string" then -- can also be result of lpath - --~ rawroot = false - capture:match(root) - elseif tr == "table" then - rawroot = forceraw and root - serialize(root,sprint,nil,nil,specialhandler,forceraw) - end - end - end - - xml.sprint = sprint - - function xml.tprint(root) -- we can move sprint inline - local tr = type(root) - if tr == "table" then - local n = #root - if n == 0 then - sprint("") -- empty element, else no setup triggered (check this! ) - else - for i=1,n do - sprint(root[i]) - end - end - elseif tr == "string" then - sprint(root) - end - end - - function xml.cprint(root) -- content - if not root then ---~ rawroot = false - -- quit - elseif type(root) == 'string' then ---~ rawroot = false - capture:match(root) - else - local rootdt = root.dt - rawroot = forceraw and root - if rootdt then -- the main one - serialize(rootdt,sprint,nil,nil,specialhandler,forceraw) - else -- probably dt - serialize(root,sprint,nil,nil,specialhandler,forceraw) - end - end - end - - -- lines (untested) - - local buffer = { } - - local capture = ( - newline^2 / function() buffer[#buffer+1] = "" end + - newline / function() buffer[#buffer] = buffer[#buffer] .. " " end + - content / function(s) buffer[#buffer] = buffer[#buffer] .. s end - )^0 - - function lines(root) - if not root then ---~ rawroot = false - -- quit - elseif type(root) == 'string' then ---~ rawroot = false - capture:match(root) - elseif next(root) then -- tr == 'table' - rawroot = forceraw and root - serialize(root,lines,forceraw) - end - end - - function xml.lines(root) - buffer = { "" } - lines(root) - return result - end - - -- cdata - - local linecommand = "\\obeyedline" - local spacecommand = "\\obeyedspace" -- "\\strut\\obeyedspace" - local beforecommand = "" - local aftercommand = "" - - local capture = ( - newline / function( ) texsprint(texcatcodes,linecommand,"{}") end + - verbose / function(s) texsprint(vrbcatcodes,s) end + - space / function( ) texsprint(texcatcodes,spacecommand,"{}") end - )^0 - - local function toverbatim(str) - if beforecommand then texsprint(texcatcodes,beforecommand,"{}") end - capture:match(str) - if aftercommand then texsprint(texcatcodes,aftercommand,"{}") end - end - - function lxml.set_verbatim(before,after,obeyedline,obeyedspace) - beforecommand, aftercommand, linecommand, spacecommand = before, after, obeyedline, obeyedspace - end - - function lxml.set_cdata() - specialhandler['@cd@'] = toverbatim - end - - function lxml.reset_cdata() - specialhandler['@cd@'] = nil - end - - -- local capture = (space^0*newline)^0 * capture * (space+newline)^0 * -1 - - local function toverbatim(str) - if beforecommand then texsprint(texcatcodes,beforecommand,"{}") end - -- todo: add this to capture - str = gsub(str,"^[ \t]+[\n\r]+","") - str = gsub(str,"[ \t\n\r]+$","") - capture:match(str) - if aftercommand then texsprint(texcatcodes,aftercommand,"{}") end - end - - function lxml.verbatim(id,before,after) - local root = get_id(id) - if root then - if before then texsprint(ctxcatcodes,format("%s[%s]",before,root.tg)) end - -- serialize(root.dt,toverbatim,nil,nil,nil,true) -- was root - local t = { } - serialize(root.dt,function(s) t[#t+1] = s end,nil,nil,nil,true) -- was root - toverbatim(table.concat(t,"")) - if after then texsprint(ctxcatcodes,after) end - end - end - function lxml.inlineverbatim(id) - lxml.verbatim(id,"\\startxmlinlineverbatim","\\stopxmlinlineverbatim") - end - function lxml.displayverbatim(id) - lxml.verbatim(id,"\\startxmldisplayverbatim","\\stopxmldisplayverbatim") - end - - local pihandlers = { } - - specialhandler['@pi@'] = function(str) - for i=1,#pihandlers do - pihandlers[i](str) - end - end - - xml.pihandlers = pihandlers - - local kind = lpeg.P("context-") * lpeg.C((1-lpeg.P("-"))^1) * lpeg.P("-directive") - local space = lpeg.S(" \n\r") - local spaces = space^0 - local class = lpeg.C((1-space)^0) - local key = class - local value = lpeg.C(lpeg.P(1-(space * -1))^0) - - local parser = kind * spaces * class * spaces * key * spaces * value - - pihandlers[#pihandlers+1] = function(str) - -- local kind, class, key, value = parser:match(str) - if str then - local a, b, c, d = parser:match(str) - if d then - texsprint(ctxcatcodes,format("\\xmlcontextdirective{%s}{%s}{%s}{%s}",a,b,c,d)) - end - end - end - - -- print(contextdirective("context-mathml-directive function reduction yes ")) - -- print(contextdirective("context-mathml-directive function ")) - - function lxml.main(id) - serialize(get_id(id),sprint,nil,nil,specialhandler) -- the real root (@rt@) - end - - specialhandler['@dt@'] = function() - -- nothing - end - -end - -local xmlsprint = xml.sprint -local xmltprint = xml.tprint - --- redefine xml load - -xml.originalload = xml.originalload or xml.load - -local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming - -function xml.load(filename) - lxml.noffiles = lxml.noffiles + 1 - lxml.nofconverted = lxml.nofconverted + 1 - starttiming(xml) - local ok, data = resolvers.loadbinfile(filename) - local xmltable = xml.convert((ok and data) or "") - stoptiming(xml) - return xmltable -end - -function lxml.load(id,filename) - filename = commands.preparedfile(filename) - if trace_loading then - commands.writestatus("lxml","loading file '%s' as '%s'",filename,id) - end - local xmltable = xml.load(filename) - lxml.store(id,xmltable,filename) - return xmltable, filename -end - -function lxml.register(id,xmltable,filename) - lxml.store(id,xmltable,filename) - return xmltable -end - -function lxml.include(id,pattern,attribute,recurse) - starttiming(xml) - local root = get_id(id) - xml.include(root,pattern,attribute,recurse,function(filename) - if filename then - filename = commands.preparedfile(filename) - if file.dirname(filename) == "" and root.filename then - filename = file.join(file.dirname(root.filename),filename) - end - if trace_loading then - commands.writestatus("lxml","including file: %s",filename) - end - lxml.noffiles = lxml.noffiles + 1 - lxml.nofconverted = lxml.nofconverted + 1 - return resolvers.loadtexfile(filename) or "" - else - return "" - end - end) - stoptiming(xml) -end - -function lxml.utfize(id) - xml.utfize(get_id(id)) -end - -function lxml.filter(id,pattern) - xmlsprint(xmlfilter(get_id(id),pattern)) -end -function lxml.first(id,pattern) - xmlsprint(xmlfirst(get_id(id),pattern)) -end -function lxml.last(id,pattern) - xmlsprint(xmllast(get_id(id),pattern)) -end -function lxml.all(id,pattern) - traverse(get_id(id), lpath(pattern), function(r,d,k) - if d then - xmlsprint(d[k]) - end - return false - end) -end - -function lxml.nonspace(id,pattern) -- slow, todo loop - xmltprint(xmlcollect(get_id(id),pattern,true)) -end - -function lxml.strip(id,pattern,nolines) - xml.strip(get_id(id),pattern,nolines) -end - -function lxml.text(id,pattern) - -- we can avoid some steps by passing xmlsprint - xmltprint(xmlcollect_texts(get_id(id),pattern) or {}) -end - -function lxml.tags(id,pattern) - local tags = xmlcollect_tags(get_id(id),pattern) - if tags then - texsprint(concat(tags,",")) - end -end - -function lxml.raw(id,pattern) -- the content, untouched by commands - local c = xmlfilter(get_id(id),pattern) - if c then - xml.serialize(c.dt,texsprint,nil,nil,nil,true) - end -end - -function lxml.snippet(id,i) - local e = get_id(id) - if e then - local edt = e.dt - if edt then - xmlsprint(edt[i]) - end - end -end - -function xml.element(e,n) - if e then - local edt = e.dt - if edt then - if n > 0 then - for i=1,#edt do - local ei = edt[i] - if type(ei) == "table" then - if n == 1 then - xmlsprint(ei) - return - else - n = n - 1 - end - end - end - elseif n < 0 then - for i=#edt,1,-1 do - local ei = edt[i] - if type(ei) == "table" then - if n == -1 then - xmlsprint(ei) - return - else - n = n + 1 - end - end - end - end - end - end -end - -function lxml.element(id,n) - xml.element(get_id(id),n) -end - -function lxml.stripped(id,pattern,nolines) - local str = xmlcontent(get_id(id),pattern) or "" - str = gsub(str,"^%s*(.-)%s*$","%1") - if nolines then - str = gsub(str,"%s+"," ") - end - xmlsprint(str) -end - -function lxml.flush(id) - id = get_id(id) - local dt = id and id.dt - if dt then - xmlsprint(dt) - end -end - -function lxml.direct(id) - xmlsprint(get_id(id)) -end - -function lxml.index(id,pattern,i) - xmlsprint((xmlindex(get_id(id),pattern,i))) -end - -function lxml.attribute(id,pattern,a,default) --todo: snelle xmlatt - local str = xmlattribute(get_id(id),pattern,a) or "" - texsprint((str == "" and default) or str) -end -function lxml.chainattribute(id,pattern,a,default) --todo: snelle xmlatt - local str = xmlchainattribute(get_id(id),pattern,a) or "" - texsprint((str == "" and default) or str) -end - -function lxml.count(id,pattern) - texsprint(xmlcount(get_id(id),pattern) or 0) -end -function lxml.nofelements(id) - local e = get_id(id) - local edt = e.dt - if edt and type(edt) == "table" then - local n = 0 - for i=1,#edt do - if type(edt[i]) == "table" then - n = n + 1 - end - end - texsprint(n) - else - texsprint(0) - end -end -function lxml.name(id) -- or remapped name? -> lxml.info, combine - local r = get_id(id) - local ns = r.rn or r.ns or "" - if ns ~= "" then - texsprint(ns,":",r.tg) - else - texsprint(r.tg) - end -end -function lxml.tag(id) -- tag vs name -> also in l-xml tag->name - texsprint(get_id(id).tg or "") -end -function lxml.namespace(id) -- or remapped name? - local root = get_id(id) - texsprint(root.rn or root.ns or "") -end - ---~ function lxml.concat(id,what,separator,lastseparator) ---~ texsprint(concat(xml.collect_texts(get_id(id),what,true),separator or "")) ---~ end - -function lxml.concatrange(id,what,start,stop,separator,lastseparator) -- test this on mml - local t = xmlcollect_elements(get_id(id),what,true) -- ignorespaces - local separator = separator or "" - local lastseparator = lastseparator or separator or "" - start, stop = (start == "" and 1) or tonumber(start) or 1, (stop == "" and #t) or tonumber(stop) or #t - if stop < 0 then stop = #t + stop end -- -1 == last-1 - for i=start,stop do - xmlsprint(t[i]) - if i == #t then - -- nothing - elseif i == #t-1 and lastseparator ~= "" then - texsprint(ctxcatcodes,lastseparator) - elseif separator ~= "" then - texsprint(ctxcatcodes,separator) - end - end -end - -function lxml.concat(id,what,separator,lastseparator) - lxml.concatrange(id,what,false,false,separator,lastseparator) -end - --- string : setup --- true : text (no <self></self>) --- false : ignore --- function : call - --- todo: free self after usage, i.e. after the setup, which --- means a call to lua; we can also choose a proper maximum --- and cycle or maybe free on demand - --- problems with empty elements --- we use a real tex.sprint, else spaces go wrong --- maybe just a .. because this happens often - -function lxml.serialize(root, command) - local tc = type(command) - if tc == "string" then - -- setup - local ix = root.ix - local rootname = root.name - if rootname then - if not ix then - lxml.addindex(rootname,false,true) - ix = root.ix - end ---~ print(rootname,ix,command) - texsprint(ctxcatcodes,format("\\xmls{%s::%s}{%s}",rootname,ix,command)) - else ---~ print(ix,command) - texsprint(ctxcatcodes,format("\\xmls{%s}{%s}",ix,command)) - end - elseif tc == "function" then - -- function - command(root) - elseif command == true then - -- text (no <self></self>) / so, no mkii fallback then - xmltprint(root.dt) - elseif command == false then - -- ignore - else - -- texsprint("?") - -- fuzzy, so ignore too - end -end - -xml.setserializer(lxml.serialize) - -function lxml.setaction(id,pattern,action) - for rt, dt, dk in xmlelements(get_id(id),pattern) do - dt[dk].command = action - end -end - -function lxml.setsetup(id,pattern,setup) - if not setup or setup == "" or setup == "*" or setup == "-" or setup == "+" then - for rt, dt, dk in xmlelements(get_id(id),pattern) do - local dtdk = dt and dt[dk] or rt - local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg - if tg then -- to be sure - local command = (ns == "" and tg) or (ns .. ":" .. tg) - if setup == "-" then - dtdk.command = false - if trace then - logs.report("lxml","lpath matched -> %s -> skipped", setup) - end - elseif setup == "+" then - dtdk.command = true - if trace_setups then - logs.report("lxml","lpath matched -> %s -> text", setup) - end - else - dtdk.command = tg -- command -- setup - if trace_setups then - if ns == "" then - logs.report("lxml","lpath matched -> %s -> %s", tg, tg) - else - logs.report("lxml","lpath matched -> %s:%s -> %s", ns, tg, tg) - end - end - end - end - end - else - local a, b = setup:match("^(.+:)([%*%-])$") - if a and b then - for rt, dt, dk in xmlelements(get_id(id),pattern) do - local dtdk = (dt and dt[dk]) or rt - local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg - if b == "-" then - dtdk.command = false - if trace_setups then - if ns == "" then - logs.report("lxml","lpath matched -> %s -> skipped", tg) - else - logs.report("lxml","lpath matched -> %s:%s -> skipped", ns, tg) - end - end - elseif b == "+" then - dtdk.command = true - if trace_setups then - if ns == "" then - logs.report("lxml","lpath matched -> %s -> text", tg) - else - logs.report("lxml","lpath matched -> %s:%s -> text", ns, tg) - end - end - else - dtdk.command = a .. tg - if trace_setups then - if ns == "" then - logs.report("lxml","lpath matched -> %s -> %s", tg, dtdk.command) - else - logs.report("lxml","lpath matched -> %s:%s -> %s", ns, tg, dtdk.command) - end - end - end - end - else - if trace_setups then - logs.report("lxml","lpath pattern -> %s -> %s", pattern, setup) - end - for rt, dt, dk in xmlelements(get_id(id),pattern) do - local dtdk = (dt and dt[dk]) or rt - dtdk.command = setup - if trace_setups then - local ns, tg = dtdk.rn or dtdk.ns, dtdk.tg - if ns == "" then - logs.report("lxml","lpath matched -> %s -> %s", tg, setup) - else - logs.report("lxml","lpath matched -> %s:%s -> %s", ns, tg, setup) - end - end - end - end - end -end - -function lxml.idx(id,pattern,i) -- hm, hashed, needed? - local r = get_id(id) - if r then - local rp = r.patterns - if not rp then - rp = { } - r.patterns = rp - end - if not rp[pattern] then - rp[pattern] = xmlcollect_elements(r,pattern) -- dd, rr - end - local rpi = rp[pattern] and rp[pattern][i] - if rpi then - xmlsprint(rpi) - end - end -end - -function lxml.info(id) - id = get_id(id) - local ns, tg = id.ns, id.tg - if ns and ns ~= "" then -- best make a function - tg = ns .. ":" .. tg - else - tg = tg or "?" - end - texsprint(tg) -end - --- can be simplified ... we can use the outer name - ---~ local function command(root,pattern,cmd) -- met zonder '' ---~ if find(cmd,"^[\'\"]") then ---~ cmd = sub(cmd,2,-2) ---~ end ---~ local rootname = root.name ---~ traverse(root, lpath(pattern), function(r,d,k) ---~ -- this can become pretty large ---~ local m = (d and d[k]) or r -- brrr this r, maybe away ---~ local ix = m.ix ---~ if not ix then ---~ lxml.addindex(rootname,false,true) ---~ ix = m.ix ---~ end ---~ texsprint(ctxcatcodes,format("\\xmls{%s::%s}{%s}",rootname,ix,cmd)) ---~ end) ---~ end ---~ local function qualifiedcommand(parent,root,pattern,cmd) -- met zonder '' ---~ if find(cmd,"^[\'\"]") then ---~ cmd = sub(cmd,2,-2) ---~ end ---~ traverse(root, lpath(pattern), function(r,d,k) ---~ local m = (d and d[k]) or r ---~ local ix = m.ix ---~ if not ix then ---~ lxml.addindex(parent,false,true) ---~ ix = m.ix ---~ end ---~ texsprint(ctxcatcodes,format("\\xmls{%s::%s}{%s}",parent,ix,cmd)) ---~ end) ---~ end - -local rootname, thecmd -local function ndoit(r,d,k) - local m = (d and d[k]) or r - local ix = m.ix - if not ix then - lxml.addindex(rootname,false,true) - ix = m.ix - end - texsprint(ctxcatcodes,format("\\xmls{%s::%s}{%s}",rootname,ix,thecmd)) -end -local function qdoit(r,d,k) - local m = (d and d[k]) or r - local ix = m.ix - if not ix then - lxml.addindex(parent,false,true) - ix = m.ix - end - texsprint(ctxcatcodes,format("\\xmls{%s::%s}{%s}",rootname,ix,thecmd)) -end -local function command(root,pattern,cmd) -- met zonder '' - if find(cmd,"^[\'\"]") then - thecmd = sub(cmd,2,-2) - else - thecmd = cmd - end - rootname = root.name - traverse(root, lpath(pattern), ndoit) -end -local function qualifiedcommand(parent,root,pattern,cmd) -- met zonder '' - if find(cmd,"^[\'\"]") then - thecmd = sub(cmd,2,-2) - else - thecmd = cmd - end - rootname = parent - traverse(root, lpath(pattern), qdoit) -end - -function lxml.command(id,pattern,cmd) - if find(cmd,"^[\'\"]") then - thecmd = sub(cmd,2,-2) - else - thecmd = cmd - end - local i, p = get_id(id,true) - if p then - rootname = p - traverse(i, lpath(pattern), qdoit) - else - rootname = i.name - traverse(i, lpath(pattern), ndoit) - end -end - -xml.filters.command = command - -local function dofunction(root,pattern,fnc) - traverse(root, lpath(pattern), xml.functions[fnc]) -- r, d, t -end - -xml.filters["function"] = dofunction - ---~ <?xml version="1.0" standalone="yes"?> ---~ <!-- demo.cdx --> ---~ <directives> ---~ <!-- ---~ <directive attribute='id' value="100" setup="cdx:100"/> ---~ <directive attribute='id' value="101" setup="cdx:101"/> ---~ --> ---~ <!-- ---~ <directive attribute='cdx' value="colors" element="cals:table" setup="cdx:cals:table:colors"/> ---~ <directive attribute='cdx' value="vertical" element="cals:table" setup="cdx:cals:table:vertical"/> ---~ <directive attribute='cdx' value="noframe" element="cals:table" setup="cdx:cals:table:noframe"/> ---~ --> ---~ <directive attribute='cdx' value="*" element="cals:table" setup="cdx:cals:table:*"/> ---~ </directives> - -lxml.directives = { } - -local data = { - setup = { }, - before = { }, - after = { } -} - -function lxml.directives.load(filename) - local fullname = resolvers.find_file(filename) or "" - if fullname ~= "" then - filename = fullname - end - local root = xml.load(filename) - for r, d, k in xmlelements(root,"directive") do - local dk = d[k] - local at = dk.at - local attribute, value, element = at.attribute or "", at.value or "", at.element or '*' - local setup, before, after = at.setup or "", at.before or "", at.after or "" - if attribute ~= "" and value ~= "" then - local key = format("%s::%s::%s",element,attribute,value) - local t = data[key] or { } - if setup ~= "" then t.setup = setup end - if before ~= "" then t.before = before end - if after ~= "" then t.after = after end - data[key] = t - end - end -end - -function lxml.directives.setup(root,attribute,element) - lxml.directives.handle_setup('setup',root,attribute,element) -end -function lxml.directives.before(root,attribute,element) - lxml.directives.handle_setup('before',root,attribute,element) -end -function lxml.directives.after(root,attribute,element) - lxml.directives.handle_setup('after',root,attribute,element) -end - -function lxml.directives.handle_setup(category,root,attribute,element) - root = get_id(root) - attribute = attribute - if attribute then - local value = root.at[attribute] - if value then - if not element then - local ns, tg = root.rn or root.ns, root.tg - if ns == "" then - element = tg - else - element = ns .. ':' .. tg - end - end - local setup = data[format("%s::%s::%s",element,attribute,value)] - if setup then - setup = setup[category] - end - if setup then - texsprint(ctxcatcodes,format("\\directsetup{%s}",setup)) - else - setup = data[format("%s::%s::*",element,attribute)] - if setup then - setup = setup[category] - end - if setup then - texsprint(ctxcatcodes,format("\\directsetup{%s}",gsub(setup,'%*',value))) - end - end - end - end -end - -function xml.getbuffer(name) -- we need to make sure that commands are processed - if not name or name == "" then - name = tex.jobname - end - lxml.nofconverted = lxml.nofconverted + 1 - xml.tostring(xml.convert(concat(buffers.data[name] or {},""))) -end - -function lxml.loadbuffer(id,name) - if not name or name == "" then - name = tex.jobname - end - starttiming(xml) - lxml.nofconverted = lxml.nofconverted + 1 - local xmltable = xml.convert(buffers.collect(name or id,"\n")) - lxml.store(id,xmltable) - stoptiming(xml) - return xmltable, name or id -end - -function lxml.loaddata(id,str) - starttiming(xml) - lxml.nofconverted = lxml.nofconverted + 1 - local xmltable = xml.convert(str or "") - lxml.store(id,xmltable) - stoptiming(xml) - return xmltable, id -end - -function lxml.loadregistered(id) - return loaded[id], id -end - --- for the moment here: - -lxml.set_verbatim("\\xmlcdatabefore", "\\xmlcdataafter", "\\xmlcdataobeyedline", "\\xmlcdataobeyedspace") -lxml.set_cdata() - -local traced = { } - -function lxml.trace_text_entities(str) - return gsub(str,"&(.-);",function(s) - traced[s] = (traced[s] or 0) + 1 - return "["..s.."]" - end) -end - -function lxml.show_text_entities() - for k,v in ipairs(table.sortedkeys(traced)) do - local h = v:match("^#x(.-)$") - if h then - local d = tonumber(h,16) - local u = utfchar(d) - logs.report("lxml","entity: %s / %s / %s / n=%s",h,d,u,traced[v]) - else - logs.report("lxml","entity: %s / n=%s",v,traced[v]) - end - end -end - -local error_entity_handler = function(s) return format("[%s]",s) end -local element_entity_handler = function(s) return format("<ctx:e n='%s'/>",s) end - -function lxml.set_mkii_entityhandler() - xml.entity_handler = error_entity_handler - xml.set_text_cleanup() -end -function lxml.set_mkiv_entityhandler() - xml.entity_handler = element_entity_handler - xml.set_text_cleanup(xml.resolve_text_entities) -end -function lxml.reset_entityhandler() - xml.entity_handler = error_entity_handler - xml.set_text_cleanup() -end - -local function with_elements_only(e,handle) - if e and handle then - local etg = e.tg - if etg then - if e.special and etg ~= "@rt@" then - if resthandle then - resthandle(e) - end - else - local edt = e.dt - if edt then - for i=1,#edt do - local e = edt[i] - if type(e) == "table" then - handle(e) - with_elements_only(e,handle) - end - end - end - end - end - end -end - - local function with_elements_only(e,handle,depth) - if e and handle then - local edt = e.dt - if edt then - depth = depth or 0 - for i=1,#edt do - local e = edt[i] - if type(e) == "table" then - handle(e,depth) - with_elements_only(e,handle,depth+1) - end - end - end - end -end - -xml.with_elements_only = with_elements_only - -local function to_text(e) - if e.command == nil then - local etg = e.tg - if etg and e.special and etg ~= "@rt@" then - e.command = false -- i.e. skip - else - e.command = true -- i.e. no <self></self> - end - end -end -local function to_none(e) - if e.command == nil then - e.command = false -- i.e. skip - end -end - --- can be made faster: just recurse over whole table, todo - -function lxml.set_command_to_text(id) - xml.with_elements_only(get_id(id),to_text) -end - -function lxml.set_command_to_none(id) - xml.with_elements_only(get_id(id),to_none) -end - -function lxml.get_command_status(id) - local status, stack = {}, {} - local function get(e,d) - local ns, tg = e.ns, e.tg - local name = tg - if ns ~= "" then name = ns .. ":" .. tg end - stack[d] = name - local ec = e.command - if ec == true then - ec = "system: text" - elseif ec == false then - ec = "system: skip" - elseif ec == nil then - ec = "system: not set" - elseif type(ec) == "string" then - ec = "setup: " .. ec - else -- function - ec = tostring(ec) - end - local tag = table.concat(stack," => ",1,d) - local s = status[tag] - if not s then - s = { } - status[tag] = s - end - s[ec] = (s[ec] or 0) + 1 - end - if id then - xml.with_elements_only(get_id(id),get) - return status - else - local t = { } - for id, _ in pairs(loaded) do - t[id] = lxml.get_command_status(id) - end - return t - end -end - -local setups = { } - -function lxml.installsetup(what,document,setup,where) - document = document or "*" - local sd = setups[document] - if not sd then sd = { } setups[document] = sd end - for k=1,#sd do - if sd[k] == setup then sd[k] = nil break end - end - if what == 1 then - if trace_loading then - commands.writestatus("lxml","prepending setup %s for %s",setup,document) - end - insert(sd,1,setup) - elseif what == 2 then - if trace_loading then - commands.writestatus("lxml","appending setup %s for %s",setup,document) - end - insert(sd,setup) - elseif what == 3 then - if trace_loading then - commands.writestatus("lxml","inserting setup %s for %s before %s",setup,document,where) - end - table.insert_before_value(sd,setup,where) - elseif what == 4 then - if trace_loading then - commands.writestatus("lxml","inserting setup %s for %s after %s",setup,document,where) - end - table.insert_after_value(sd,setup,where) - end -end - -function lxml.flushsetups(...) - local done = { } - for _, document in ipairs({...}) do - local sd = setups[document] - if sd then - for k=1,#sd do - local v= sd[k] - if not done[v] then - if trace_loading then - commands.writestatus("lxml","applying setup %02i = %s to %s",k,v,document) - end - texsprint(ctxcatcodes,format("\\directsetup{%s}",v)) - done[v] = true - end - end - elseif trace_loading then - commands.writestatus("lxml","no setups for %s",document) - end - end -end - -function lxml.resetsetups(document) - if trace_loading then - commands.writestatus("lxml","resetting all setups for %s",document) - end - setups[document] = { } -end - -function lxml.removesetup(document,setup) - local s = setups[document] - if s then - for i=1,#s do - if s[i] == setup then - if trace_loading then - commands.writestatus("lxml","removing setup %s for %s",setup,document) - end - remove(t,i) - break - end - end - end -end - --- rather new, indexed storage (backward refs), maybe i will merge this - -function lxml.addindex(name,check_sum,force) - local root = get_id(name) - if root and (not root.index or force) then -- weird, only called once - local n, index, maxindex, check = 0, root.index or { }, root.maxindex or 0, root.check or { } - local function nest(root) - local dt = root.dt - if not root.ix then - maxindex = maxindex + 1 - root.ix = maxindex - check[maxindex] = root.tg - index[maxindex] = root - n = n + 1 - end - if dt then - for k=1,#dt do - local dk = dt[k] - if type(dk) == "table" then - nest(dk) - end - end - end - end - nest(root) - lxml.nofindices = lxml.nofindices + n - -- - if type(name) ~= "string" then - name = "unknown" - end - -- todo: checksum at the end, when tuo saved ---~ if root.checksum then ---~ -- extension mode ---~ root.index = index ---~ root.maxindex = maxindex ---~ commands.writestatus("lxml",format("checksum adapted for %s",tostring(name))) ---~ elseif check_sum then ---~ local tag = format("lxml:%s:checksum",name) ---~ local oldchecksum = jobvariables.collected[tag] ---~ local newchecksum = md5.HEX(concat(check,".")) -- maybe no "." needed ---~ jobvariables.tobesaved[tag] = newchecksum ---~ -- ---~ if oldchecksum and oldchecksum ~= "" and oldchecksum ~= newchecksum then ---~ root.index = { } ---~ root.maxindex = 0 ---~ root.checksum = newchecksum ---~ commands.writestatus("lxml",format("checksum mismatch for %s (extra run needed)",tostring(name))) ---~ else ---~ root.index = index ---~ root.maxindex = maxindex ---~ root.checksum = newchecksum ---~ commands.writestatus("lxml",format("checksum match for %s: %s",tostring(name),newchecksum)) ---~ end ---~ else - root.index = index - root.maxindex = maxindex ---~ end - if trace_access then - logs.report("lxml","%s indexed, %s nodes",tostring(name),maxindex) - end - end -end - --- we can share the index - -function lxml.checkindex(name) - local root = get_id(name) - return (root and root.index) or 0 -end - -function lxml.withindex(name,n,command) -- will change as name is always there now - local i, p = splitter:match(n) - if p then - texsprint(ctxcatcodes,format("\\xmls{%s}{%s}",n,command)) - else - texsprint(ctxcatcodes,format("\\xmls{%s::%s}{%s}",name,n,command)) - end -end - -function lxml.getindex(name,n) -- will change as name is always there now - local i, p = splitter:match(n) - if p then - texsprint(ctxcatcodes,n) - else - texsprint(ctxcatcodes,format("%s::%s",name,n)) - end -end - --- - -local found, isempty = xml.found, xml.isempty - -local doif, doifnot, doifelse = commands.doif, commands.doifnot, commands.doifelse - -function lxml.doif (id,pattern) doif (found(get_id(id),pattern,false)) end -function lxml.doifnot (id,pattern) doifnot (found(get_id(id),pattern,false)) end -function lxml.doifelse (id,pattern) doifelse(found(get_id(id),pattern,false)) end - --- todo: if no second arg or second arg == "" then quick test - -function lxml.doiftext (id,pattern) doif (found (get_id(id),pattern,true)) end -function lxml.doifnottext (id,pattern) doifnot (found (get_id(id),pattern,true)) end -function lxml.doifelsetext (id,pattern) doifelse(found (get_id(id),pattern,true)) end - --- special case: "*" and "" -> self else lpath lookup - -function lxml.doifelseempty(id,pattern) doifelse(isempty(get_id(id),pattern ~= "" and pattern ~= nil)) end -- not yet done, pattern - --- status info - -statistics.register("xml load time", function() - local noffiles, nofconverted = lxml.noffiles, lxml.nofconverted - if noffiles > 0 or nofconverted > 0 then - return format("%s seconds, %s files, %s converted", statistics.elapsedtime(xml), noffiles, nofconverted) - else - return nil - end -end) - ---~ statistics.register("lxml preparation time", function() ---~ local n = #lxml.self ---~ if n > 0 then ---~ local stats = xml.statistics() ---~ return format("%s seconds, %s backreferences, %s lpath calls, %s cached calls", statistics.elapsedtime(xml), n, stats.lpathcalls, stats.lpathcached) ---~ else ---~ return nil ---~ end ---~ end) - -statistics.register("lxml preparation time", function() - local noffiles, nofconverted = lxml.noffiles, lxml.nofconverted - if noffiles > 0 or nofconverted > 0 then - local stats = xml.statistics() - return format("%s seconds, %s nodes, %s lpath calls, %s cached calls", statistics.elapsedtime(lxml), lxml.nofindices, stats.lpathcalls, stats.lpathcached) - else - return nil - end -end) diff --git a/tex/context/base/lxml-ini.mkiv b/tex/context/base/lxml-ini.mkiv index 05f89652c..d3a56afbe 100644 --- a/tex/context/base/lxml-ini.mkiv +++ b/tex/context/base/lxml-ini.mkiv @@ -15,18 +15,23 @@ \writestatus{loading}{ConTeXt XML Support / Initialization} -\registerctxluafile{lxml-tab}{1.001} -\registerctxluafile{lxml-pth}{1.001} -\registerctxluafile{lxml-ent}{1.001} -\registerctxluafile{lxml-mis}{1.001} -\registerctxluafile{lxml-ini}{1.001} +%registerctxluafile{lxml-tab}{1.001} % loader +%registerctxluafile{lxml-lpt}{1.001} % parser +%registerctxluafile{lxml-xml}{1.001} % xml finalizers +%registerctxluafile{lxml-aux}{1.001} % extras using parser +%registerctxluafile{lxml-mis}{1.001} % extras independent of parser +%registerctxluafile{lxml-ent}{1.001} % entity hacks +\registerctxluafile{lxml-tex}{1.001} % tex finalizers +\registerctxluafile{lxml-dir}{1.001} % ctx hacks \unprotect +\def\c!entities{entities} % to be internationalized + \def\xmlmain #1{\ctxlua{lxml.main("#1")}} \def\xmlall #1#2{\ctxlua{lxml.all("#1","#2")}} -\def\xmlatt #1#2{\ctxlua{lxml.attribute("#1","/","#2")}} -\def\xmlattdef #1#2#3{\ctxlua{lxml.attribute("#1","/","#2","#3")}} +\def\xmlatt #1#2{\ctxlua{lxml.att("#1","#2")}} +\def\xmlattdef #1#2#3{\ctxlua{lxml.att("#1","#2","#3")}} \def\xmlchainatt #1#2{\ctxlua{lxml.chainattribute("#1","/","#2")}} \def\xmlchainattdef #1#2#3{\ctxlua{lxml.chainattribute("#1","/","#2","#3")}} \def\xmlattribute #1#2#3{\ctxlua{lxml.attribute("#1","#2","#3")}} @@ -40,6 +45,7 @@ \def\xmldirectivesbefore #1{\ctxlua{lxml.directives.before("#1")}} \def\xmldirectivesafter #1{\ctxlua{lxml.directives.after("#1")}} \def\xmlfilter #1#2{\ctxlua{lxml.filter("#1",\!!bs#2\!!es)}} +\def\xmlfunction #1#2{\ctxlua{lxml["function"]("#1",\!!bs#2\!!es)}} \def\xmlfirst #1#2{\ctxlua{lxml.first("#1","#2")}} \def\xmlflush #1{\ctxlua{lxml.flush("#1")}} %def\xmlcontent #1{\ctxlua{lxml.content("#1")}} @@ -51,18 +57,12 @@ \def\xmlinfo #1{\hbox{\ttxx[\ctxlua{lxml.info("#1")}]}} \def\xmlshow #1{\startpacked\ttx\xmlverbatim{#1}\stoppacked} \def\xmllast #1#2{\ctxlua{lxml.last("#1","#2")}} -\def\xmlload #1#2{\ctxlua{lxml.load("#1","#2")}} -\def\xmlloadbuffer #1#2{\ctxlua{lxml.loadbuffer("#1","#2")}} -\def\xmlloaddata #1#2{\ctxlua{lxml.loaddata("#1",\!!bs#2\!!es)}} -\def\xmlloadregistered #1#2{\ctxlua{lxml.loadregistered("#1")}} -\def\xmlloaddirectives #1{\ctxlua{lxml.directives.load("#1")}} \def\xmlname #1{\ctxlua{lxml.name("#1")}} \def\xmlnamespace #1{\ctxlua{lxml.namespace("#1")}} \def\xmlnonspace #1#2{\ctxlua{lxml.nonspace("#1","#2")}} \def\xmlraw #1#2{\ctxlua{lxml.raw("#1","#2")}} \def\xmlsnippet #1#2{\ctxlua{lxml.snippet("#1",#2)}} \def\xmlelement #1#2{\ctxlua{lxml.element("#1",#2)}} -\def\xmlnofelements #1{\ctxlua{lxml.nofelements("#1")}} \def\xmlregisterns #1#2{\ctxlua{xml.registerns("#1","#2")}} % document \def\xmlremapname #1#2#3#4{\ctxlua{xml.remapname(lxml.id("#1"),"#2","#3","#4")}} % element \def\xmlremapnamespace #1#2#3{\ctxlua{xml.rename_space(lxml.id("#1"),"#2","#3")}} % document @@ -75,12 +75,17 @@ \def\xmlstrippednolines #1#2{\ctxlua{lxml.stripped("#1","#2",true)}} \def\xmltag #1{\ctxlua{lxml.tag("#1")}} \def\xmltext #1#2{\ctxlua{lxml.text("#1","#2")}} -\def\xmltags #1#2{\ctxlua{lxml.tags("#1","#2")}} -\def\xmlutfize #1{\ctxlua{lxml.utfize("#1")}} \def\xmlverbatim #1{\ctxlua{lxml.verbatim("#1")}} \def\xmldisplayverbatim #1{\ctxlua{lxml.displayverbatim("#1")}} \def\xmlinlineverbatim #1{\ctxlua{lxml.inlineverbatim("#1")}} +\def\xmlload #1#2{\ctxlua{lxml.load("#1","#2","\@@xmentities","\@@xmcompress")}} +\def\xmlloadbuffer #1#2{\ctxlua{lxml.loadbuffer("#1","#2","\@@xmentities","\@@xmcompress")}} +\def\xmlloaddata #1#2{\ctxlua{lxml.loaddata("#1",\!!bs#2\!!es,"\@@xmentities","\@@xmcompress")}} +\def\xmlloadregistered #1#2{\ctxlua{lxml.loadregistered("#1","\@@xmentities","\@@xmcompress")}} +\def\xmlloaddirectives #1{\ctxlua{lxml.directives.load("#1")}} +\def\xmlpos #1{\ctxlua{lxml.pos("#1")}} + %def\xmldoifelse #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",false))}} %def\xmldoifelsetext #1#2{\ctxlua{cs.testcase(xml.found(lxml.id("#1"),"#2",true ))}} @@ -97,11 +102,9 @@ \def\xmldoiftext #1#2{\ctxlua{lxml.doiftext("#1","#2")}} \def\xmldoifnottext #1#2{\ctxlua{lxml.doifnottext("#1","#2")}} \def\xmldoifelsetext #1#2{\ctxlua{lxml.doifelsetext("#1","#2")}} -\def\xmldoifelseempty #1#2{\ctxlua{lxml.doifelseempty("#1","#2")}} % #2, "*" or "" == self not yet implemented -\def\xmldoifelseselfempty #1{\ctxlua{lxml.doifelseempty("#1")}} -\def\xmldefaulttotext #1{\ifcase\xmlprocessingmode\or\or \ctxlua{lxml.set_command_to_text("#1")}\fi} -\def\xmldefaulttonone #1{\ifcase\xmlprocessingmode\or\or\or\ctxlua{lxml.set_command_to_none("#1")}\fi} +%def\xmldoifelseempty #1#2{\ctxlua{lxml.doifelseempty("#1","#2")}} % #2, "*" or "" == self not yet implemented +%def\xmldoifelseselfempty #1{\ctxlua{lxml.doifelseempty("#1")}} % \startxmlsetups xml:include % \xmlinclude{main}{include}{filename|href} @@ -112,11 +115,10 @@ \let\xmlgrab\xmlsetsetup % obsolete \let\xmlself\s!unknown % obsolete -\def\xmlsetup#1#2% - {%\def\xmlself{#1}% - \setupwithargument{#2}{#1}} +\def\xmlsetup#1#2{\setupwithargument{#2}{#1}} \let\xmls\xmlsetup +\let\xmlw\setupwithargument \newtoks \registeredxmlsetups @@ -164,11 +166,7 @@ \edef\xmldocument{#3}% #2 can be \xmldocument and set as such %xmlpushdocument{#3}% #2{#3}{#4}% - \ifcase\xmlprocessingmode - \enableXML - \else - \setcatcodetable\notcatcodes - \fi + \setcatcodetable\notcatcodes \doifelsenothing{#5} {\xmlsetup{#3}{xml:process}} {\xmlsetup{#3}{#5}}% @@ -198,10 +196,6 @@ % instructions preceding the root element; well, in some % sense that is the root -\long\def\xmlloop#1#2#3% - {\def\xmli##1##2{\xmlidx{#1}{#2/##1}{##2}}% - \dorecurse{\xmlcount{#1}{#2}}{#3}} - \long\def\xmlconnect#1#2#3% inefficient {\scratchcounter\xmlcount{#1}{#2}\relax \ifcase\scratchcounter \or @@ -278,98 +272,43 @@ % setting up xml: % -% \setupxml[\c!method=mkii] % mixed mkiv and mkii -% \setupxml[\c!method=mkiv,\c!default=] % mkiv only -% \setupxml[\c!method=mkiv,\c!default=\v!none] % mkiv only, undefined -> hidden -% \setupxml[\c!method=mkiv,\c!default=\v!text] % mkiv only, undefined -> text +% \setupxml[\c!default=] % mkiv only == text +% \setupxml[\c!default=\v!none] % mkiv only, undefined -> hidden +% \setupxml[\c!default=\v!text] % mkiv only, undefined -> text % \def\xmlctxdirective#1#2#3{\doif{#1}{clue}{\doif{#2}{page}}{\page[#3]}} -\chardef\xmlprocessingmode=0 % 0=mixed, 1=mkivonly, 2=mkivonly-default-text, 3=mkivonly-default-none - -% \setupxml[method=mkiv,strip=yes,entities=yes,default=text] +\chardef\xmlprocessingmode=0 % 0=unset, 1=text, 2=hidden \newtoks\everysetupxml \def\setupxml[#1]{\getparameters[\??xm][#1]\the\everysetupxml} -\def\c!entities{entities} % to be internationalized -\def\s!mkiv {mkiv} -\def\s!mkii {mkii} - -% entities -\newif\ifautoXMLentities +\letvalue{\??xm:\s!default:\v!normal}\zerocount +\letvalue{\??xm:\s!default:\v!none }\zerocount +\letvalue{\??xm:\s!default:\v!text }\plusone +\letvalue{\??xm:\s!default:\v!hidden}\plustwo -\def\xmlkeepentities{\ctxlua{lxml.reset_entityhandler()}} -\def\xmlmkiientities{\ctxlua{lxml.set_mkii_entityhandler()}\autoXMLentitiestrue} -\def\xmlmkiventities{\ctxlua{lxml.set_mkiv_entityhandler()}} - -\let\xmlresolveentities\xmlmkiventities % will become \xmlmkiventities - -\letvalue{\??xm:1:\s!mkii }\zerocount -\letvalue{\??xm:1:\s!mkiv }\plusone - -\letvalue{\??xm:2:\v!none }\plusone -\letvalue{\??xm:2:\v!text }\plustwo -\letvalue{\??xm:2:\v!hidden}\plusthree - -\letvalue{\??xm:ii:\v!yes }\xmlresolveentities -\letvalue{\??xm:ii:\v!no }\xmlkeepentities -\letvalue{\??xm:ii:\s!mkii}\xmlmkiientities -\letvalue{\??xm:ii:\s!mkiv}\xmlmkiventities - -\letvalue{\??xm:iv:\v!yes }\xmlresolveentities -\letvalue{\??xm:iv:\v!no }\xmlkeepentities -\letvalue{\??xm:iv:\s!mkii}\xmlmkiventities -\letvalue{\??xm:iv:\s!mkiv}\xmlmkiventities +\def\xmldefaulttotext#1% + {\ifcase\xmlprocessingmode + % unset + \or + \ctxlua{lxml.set_command_to_text("#1")}% 1 + \or + \ctxlua{lxml.set_command_to_none("#1")}% 2 + \else + % unset + \fi} \appendtoks - \chardef\xmlprocessingmode\executeifdefined{\??xm:1:\@@xmmethod}\zerocount - \ifcase\xmlprocessingmode - % mkii, permits both methods - \executeifdefined{\??xm:ii:\@@xmentities}\xmlkeepentities - \or - % mkiv, mkiv exclusively - \chardef\xmlprocessingmode\executeifdefined{\??xm:2:\@@xmdefault}\plusone - \executeifdefined{\??xm:iv:\@@xmentities}\xmlresolveentities - \else - % unset - \fi - \ifcase\xmlprocessingmode - \ctxlua{characters.setmkiientities()}% - \else - \ctxlua{characters.setmkiventities()}% - \fi - \doifelse\@@xmcompress\v!yes % this key may change, maybe compress=yes|no - {\ctxlua{xml.strip_cm_and_dt=true}} - {\ctxlua{xml.strip_cm_and_dt=false}}% + \chardef\xmlprocessingmode\executeifdefined{\??xm:\s!default:\@@xmdefault}\plusone \to \everysetupxml \def\xmlinitialize{\the\everysetupxml} -\newcount\charactersactiveoffset \charactersactiveoffset="10000 - -\startextendcatcodetable\ctxcatcodes - \catcode\numexpr\charactersactiveoffset+`<\relax=13 - \catcode\numexpr\charactersactiveoffset+`&\relax=13 - \catcode\numexpr\charactersactiveoffset+`>\relax=13 -\stopextendcatcodetable -\startextendcatcodetable\xmlcatcodes - \catcode\numexpr\charactersactiveoffset+`<\relax=13 - \catcode\numexpr\charactersactiveoffset+`&\relax=13 - \catcode\numexpr\charactersactiveoffset+`>\relax=13 -\stopextendcatcodetable - -\ctxlua { % entities are remembered in the format - characters.remapentity("<",characters.active_offset + utf.byte("<")) - characters.remapentity("&",characters.active_offset + utf.byte("&")) - characters.remapentity(">",characters.active_offset + utf.byte(">")) -} - \setupxml - [\c!method=mkii, % mixed mode - \c!default=\v!hidden, % ignore elements that are not defined + [\c!default=, % flush all \c!compress=\v!no, % strip comment \c!entities=\v!yes] % replace entities @@ -391,44 +330,75 @@ % % mkii: [\processXMLbuffer]\quad mkiv: [\xmlprocessbuffer{main}{}{}] -% some mkii commands, but mkiv-ified - \def\xmlmapvalue#1#2#3{\setvalue{\??xm:v:#1:#2}{#3}} % keep #3 to grab spaces \def\xmlvalue #1#2#3{\executeifdefined{\??xm:v:#1:#2}{#3}} \let\xmlmapval\xmlmapvalue \let\xmlval \xmlvalue -% brrrr, give this at the top of a style that needs to stub mkiv loading +%D Experimental: + +\def\xmlgetindex #1{\ctxlua{lxml.getindex("\xmldocument","#1")}} +\def\xmlrawindex #1{\ctxlua{lxml.rawindex("#1")}} +\def\xmlwithindex #1#2{\ctxlua{lxml.withindex("\xmldocument","#1","#2")}} +\def\xmlreference #1#2{\string\xmlwithindex{#1}{#2}} -\def\remapXMLtoMKIV - {\ifx\xmldocument\undefined\def\xmldocument{main}\fi - \def\processXMLfile ##1{\xmlprocessfile \xmldocument{##1}{}}% - \def\processXMLfilegrouped ##1{\xmlprocessfile \xmldocument{##1}{}}% maybe still grouped? - \def\processXMLbuffer {\dosingleempty\doprocessXMLbuffer}% - \def\doprocessXMLbuffer [##1]{\xmlprocessbuffer\xmldocument{##1}{}}% - \def\XMLdata ##1{\xmlprocessdata \xmldocument{##1}{}}% - \def\startXMLdata##1\stopXMLdata{\xmlprocessdata \xmldocument{##1}{}}} +%D Entities: -\let\normalprocessXMLfilegrouped\processXMLfilegrouped +\chardef\xmlautoentities=1 % 0=off, 1=upper, 2=upper,lower -\def\processXMLfileMKIV % one-shot, will become obsolete, personal hack - {\dosingleempty\doprocessXMLfileMKIV} +\def\xmlsetentity#1#2{\ctxlua{xml.entities['#1'] = \!!bs\detokenize{#2}\!!es}} -\def\doprocessXMLfileMKIV[#1]% - {\def\processXMLfilegrouped##1% - {\let\processXMLfilegrouped\normalprocessXMLfilegrouped - \doifelsenothing{#1}{\xmlprocess{main}{##1}{}}{\xmlprocess{#1}{##1}{}}}} +% \xmlsetentity{tex}{\TEX{}} % {} needed -%D Experimental: +\unexpanded\def\xmle + {\ifcase\xmlautoentities + \expandafter\xmle@none + \or + \expandafter\xmle@upper + \or + \expandafter\xmle@upperlower + \else + \expandafter\xmle@none + \fi} -%def\xmlpushdocument#1{\ctxlua{lxml.pushdocument("#1")}} -%def\xmlpopdocument {\ctxlua{lxml.popdocument()}} -%def\xmladdindex #1{\ctxlua{lxml.addindex("#1")}} +\def\xmle@none#1#2% safe + {[#1]} -\def\xmlgetindex #1{\ctxlua{lxml.getindex("\xmldocument","#1")}} -\def\xmlrawindex #1{\ctxlua{lxml.rawindex("#1")}} -\def\xmlwithindex #1#2{\ctxlua{lxml.withindex("\xmldocument","#1","#2")}} -\def\xmlreference #1#2{\string\xmlwithindex{#1}{#2}} +\def\xmle@upper#1#2% can be abbreviation + {\ifcsname#2\endcsname + \csname#2\expandafter\endcsname + \else + [#1]% + \fi} + +\def\xmle@upperlower#1#2% can be anything, so unsafe + {\ifcsname#2\endcsname + \csname#2\expandafter\endcsname + \else\ifcsname#1\endcsname + \csname#1\expandafter\expandafter\expandafter\endcsname + \else + [#1]% + \fi\fi} \protect \endinput + +% \newcount\charactersactiveoffset \charactersactiveoffset="10000 +% +% \startextendcatcodetable\ctxcatcodes +% \catcode\numexpr\charactersactiveoffset+`<\relax=13 +% \catcode\numexpr\charactersactiveoffset+`&\relax=13 +% \catcode\numexpr\charactersactiveoffset+`>\relax=13 +% \stopextendcatcodetable +% +% \startextendcatcodetable\xmlcatcodes % not needed +% \catcode\numexpr\charactersactiveoffset+`<\relax=13 +% \catcode\numexpr\charactersactiveoffset+`&\relax=13 +% \catcode\numexpr\charactersactiveoffset+`>\relax=13 +% \stopextendcatcodetable +% +% \ctxlua { % entities are remembered in the format +% characters.remapentity("<",characters.active_offset + utf.byte("<")) +% characters.remapentity("&",characters.active_offset + utf.byte("&")) +% characters.remapentity(">",characters.active_offset + utf.byte(">")) +% } diff --git a/tex/context/base/lxml-lpt.lua b/tex/context/base/lxml-lpt.lua new file mode 100644 index 000000000..184d2f1ae --- /dev/null +++ b/tex/context/base/lxml-lpt.lua @@ -0,0 +1,1017 @@ +if not modules then modules = { } end modules ['lxml-pth'] = { + version = 1.001, + comment = "this module is the basis for the lxml-* ones", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- e.ni is only valid after a filter run + +local concat, remove, insert = table.concat, table.remove, table.insert +local type, next, tonumber, tostring, setmetatable, loadstring = type, next, tonumber, tostring, setmetatable, loadstring +local format, upper, lower, gmatch, gsub, find, rep = string.format, string.upper, string.lower, string.gmatch, string.gsub, string.find, string.rep + +--[[ldx-- +<p>This module can be used stand alone but also inside <l n='mkiv'/> in +which case it hooks into the tracker code. Therefore we provide a few +functions that set the tracers. Here we overload a previously defined +function.</p> +<p>If I can get in the mood I will make a variant that is XSLT compliant +but I wonder if it makes sense.</P> +--ldx]]-- + +--[[ldx-- +<p>Expecially the lpath code is experimental, we will support some of xpath, but +only things that make sense for us; as compensation it is possible to hook in your +own functions. Apart from preprocessing content for <l n='context'/> we also need +this module for process management, like handling <l n='ctx'/> and <l n='rlx'/> +files.</p> + +<typing> +a/b/c /*/c +a/b/c/first() a/b/c/last() a/b/c/index(n) a/b/c/index(-n) +a/b/c/text() a/b/c/text(1) a/b/c/text(-1) a/b/c/text(n) +</typing> +--ldx]]-- + +local trace_lpath = false if trackers then trackers.register("xml.lpath", function(v) trace_lpath = v end) end +local trace_lparse = false if trackers then trackers.register("xml.lparse", function(v) trace_lparse = v end) end + +--[[ldx-- +<p>We've now arrived at an interesting part: accessing the tree using a subset +of <l n='xpath'/> and since we're not compatible we call it <l n='lpath'/>. We +will explain more about its usage in other documents.</p> +--ldx]]-- + +local lpathcalls = 0 function xml.lpathcalls () return lpathcalls end +local lpathcached = 0 function xml.lpathcached() return lpathcached end + +xml.functions = xml.functions or { } -- internal +xml.expressions = xml.expressions or { } -- in expressions +xml.finalizers = xml.finalizers or { } -- fast do-with ... (with return value other than collection) +xml.specialhandler = xml.specialhandler or { } + +local functions = xml.functions +local expressions = xml.expressions +local finalizers = xml.finalizers + +finalizers.xml = finalizers.xml or { } +finalizers.tex = finalizers.tex or { } + +local function fallback (t, name) + local fn = finalizers[name] + if fn then + t[name] = fn + else + logs.report("xml","unknown sub finalizer '%s'",tostring(name)) + fn = function() end + end + return fn +end + +setmetatable(finalizers.xml, { __index = fallback }) +setmetatable(finalizers.tex, { __index = fallback }) + +xml.defaultprotocol = "xml" + +-- as xsl does not follow xpath completely here we will also +-- be more liberal especially with regards to the use of | and +-- the rootpath: +-- +-- test : all 'test' under current +-- /test : 'test' relative to current +-- a|b|c : set of names +-- (a|b|c) : idem +-- ! : not +-- +-- after all, we're not doing transformations but filtering. in +-- addition we provide filter functions (last bit) +-- +-- todo: optimizer +-- +-- .. : parent +-- * : all kids +-- / : anchor here +-- // : /**/ +-- ** : all in between +-- +-- so far we had (more practical as we don't transform) +-- +-- {/test} : kids 'test' under current node +-- {test} : any kid with tag 'test' +-- {//test} : same as above + +-- evaluator (needs to be redone, for the moment copied) + +-- todo: apply_axis(list,notable) and collection vs single + +local apply_axis = { } + +apply_axis['root'] = function(list) + local collected = { } + for l=1,#list do + local ll = list[l] + local rt = ll + while ll do + ll = ll.__p__ + if ll then + rt = ll + end + end + collected[#collected+1] = rt + end + return collected +end + +apply_axis['self'] = function(list) +--~ local collected = { } +--~ for l=1,#list do +--~ collected[#collected+1] = list[l] +--~ end +--~ return collected + return list +end + +apply_axis['child'] = function(list) + local collected = { } + for l=1,#list do + local dt = list[l].dt + for k=1,#dt do + local dk = dt[k] + if dk.tg then + collected[#collected+1] = dk + dk.ni = k -- refresh + end + end + end + return collected +end + +local function collect(list,collected) + local dt = list.dt + if dt then + for k=1,#dt do + local dk = dt[k] + if dk.tg then + collected[#collected+1] = dk + dk.ni = k -- refresh + collect(dk,collected) + end + end + end +end +apply_axis['descendant'] = function(list) + local collected = { } + for l=1,#list do + collect(list[l],collected) + end + return collected +end + +local function collect(list,collected) + local dt = list.dt + if dt then + for k=1,#dt do + local dk = dt[k] + if dk.tg then + collected[#collected+1] = dk + dk.ni = k -- refresh + collect(dk,collected) + end + end + end +end +apply_axis['descendant-or-self'] = function(list) + local collected = { } + for l=1,#list do + local ll = list[l] +if ll.special ~= true then -- catch double root + collected[#collected+1] = ll +end + collect(ll,collected) + end + return collected +end + +apply_axis['ancestor'] = function(list) + local collected = { } + for l=1,#list do + local ll = list[l] + while ll do + ll = ll.__p__ + if ll then + collected[#collected+1] = ll + end + end + end + return collected +end + +apply_axis['ancestor-or-self'] = function(list) + local collected = { } + for l=1,#list do + local ll = list[l] + collected[#collected+1] = ll + while ll do + ll = ll.__p__ + if ll then + collected[#collected+1] = ll + end + end + end + return collected +end + +apply_axis['parent'] = function(list) + local collected = { } + for l=1,#list do + local pl = list[l].__p__ + if pl then + collected[#collected+1] = pl + end + end + return collected +end + +apply_axis['attribute'] = function(list) + return { } +end + +apply_axis['following'] = function(list) + return { } +end + +apply_axis['following-sibling'] = function(list) + return { } +end + +apply_axis['namespace'] = function(list) + return { } +end + +apply_axis['preceding'] = function(list) + return { } +end + +apply_axis['preceding-sibling'] = function(list) + return { } +end + +apply_axis['auto-descendant-or-self'] = apply_axis['descendant-or-self'] +apply_axis['auto-descendant'] = apply_axis['descendant'] +apply_axis['auto-child'] = apply_axis['child'] +apply_axis['auto-self'] = apply_axis['self'] +apply_axis['initial-child'] = apply_axis['child'] + +local function apply_nodes(list,directive,nodes) + -- todo: nodes[1] etc ... negated node name in set ... when needed + -- ... currently ignored + local maxn = #nodes + if maxn == 3 then --optimized loop + local nns, ntg = nodes[2], nodes[3] + if not nns and not ntg then -- wildcard + if directive then + return list + else + return { } + end + else + local collected = { } + if not nns then -- only check tag + for l=1,#list do + local ll = list[l] + local ltg = ll.tg + if ltg then + if directive then + if ntg == ltg then + collected[#collected+1] = ll + end + elseif ntg ~= ltg then + collected[#collected+1] = ll + end + end + end + elseif not ntg then -- only check namespace + for l=1,#list do + local ll = list[l] + local lns = ll.rn or ll.ns + if lns then + if directive then + if lns == nns then + collected[#collected+1] = ll + end + elseif lns ~= nns then + collected[#collected+1] = ll + end + end + end + else -- check both + for l=1,#list do + local ll = list[l] + local ltg = ll.tg + if ltg then + local lns = ll.rn or ll.ns + local ok = ltg == ntg and lns == nns + if directive then + if ok then + collected[#collected+1] = ll + end + elseif not ok then + collected[#collected+1] = ll + end + end + end + end + return collected + end + else + local collected = { } + for l=1,#list do + local ll = list[l] + local ltg = ll.tg + if ltg then + local lns = ll.rn or ll.ns + local ok = false + for n=1,maxn,3 do + local nns, ntg = nodes[n+1], nodes[n+2] + ok = (not ntg or ltg == ntg) and (not nns or lns == nns) + if ok then + break + end + end + if directive then + if ok then + collected[#collected+1] = ll + end + elseif not ok then + collected[#collected+1] = ll + end + end + end + return collected + end +end + +local function apply_expression(list,expression,order) + local collected = { } + for l=1,#list do + local ll = list[l] + if expression(list,ll,l,order) then -- nasty, alleen valid als n=1 + collected[#collected+1] = ll + end + end + return collected +end + +local P, V, C, Cs, Cc, Ct, R, S, Cg, Cb = lpeg.P, lpeg.V, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.R, lpeg.S, lpeg.Cg, lpeg.Cb + +local spaces = S(" \n\r\t\f")^0 + +local lp_space = S(" \n\r\t\f") +local lp_any = P(1) + +local lp_noequal = P("!=") / "~=" + P("<=") + P(">=") + P("==") +local lp_doequal = P("=") / "==" +local lp_or = P("|") / " or " +local lp_and = P("&") / " and " + +local lp_builtin = P ( + P("first") / "1" + + P("last") / "#list" + + P("position") / "l" + + P("rootposition") / "order" + + P("index") / "ll.ni" + + P("text") / "(ll.dt[1] or '')" + + P("name") / "(ll.ns~='' and ll.ns..':'..ll.tg)" + + P("tag") / "ll.tg" + + P("ns") / "ll.ns" + ) * ((spaces * P("(") * spaces * P(")"))/"") + +local lp_attribute = (P("@") + P("attribute::")) / "" * Cc("ll.at['") * R("az","AZ","--","__")^1 * Cc("']") +local lp_fastpos = ((R("09","--","++")^1 * P(-1)) / function(s) return "l==" .. s end) + +local lp_reserved = C("and") + C("or") + C("not") + C("div") + C("mod") + C("true") + C("false") + +local lp_lua_function = C(R("az","AZ","__")^1 * (P(".") * R("az","AZ","__")^1)^1) * ("(") / function(t) -- todo: better . handling + return t .. "(" +end + +local lp_function = C(R("az","AZ","__")^1) * P("(") / function(t) -- todo: better . handling + if expressions[t] then + return "expr." .. t .. "(" + else + return "expr.error(" + end +end + +local lparent = lpeg.P("(") +local rparent = lpeg.P(")") +local noparent = 1 - (lparent+rparent) +local nested = lpeg.P{lparent * (noparent + lpeg.V(1))^0 * rparent} +local value = lpeg.P(lparent * lpeg.C((noparent + nested)^0) * rparent) -- lpeg.P{"("*C(((1-S("()"))+V(1))^0)*")"} + +local lp_child = Cc("expr.child(e,'") * R("az","AZ","--","__")^1 * Cc("')") +local lp_string = Cc("'") * R("az","AZ","--","__")^1 * Cc("'") +local lp_content= (P("'") * (1-P("'"))^0 * P("'") + P('"') * (1-P('"'))^0 * P('"')) + +local cleaner + +local lp_special = (C(P("name")+P("text")+P("tag")+P("count")+P("child"))) * value / function(t,s) + if expressions[t] then + s = s and s ~= "" and cleaner:match(s) + if s and s ~= "" then + return "expr." .. t .. "(e," .. s ..")" + else + return "expr." .. t .. "(e)" + end + else + return "expr.error(" .. t .. ")" + end +end + +local content = + lp_builtin + + lp_attribute + + lp_special + + lp_noequal + lp_doequal + + lp_or + lp_and + + lp_reserved + + lp_lua_function + lp_function + + lp_content + -- too fragile + lp_child + + lp_any + +local converter = lpeg.Cs ( + lp_fastpos + (lpeg.P { lparent * (lpeg.V(1))^0 * rparent + content } )^0 +) + +cleaner = lpeg.Cs ( ( +--~ lp_fastpos + + lp_reserved + + lp_string + +1 )^1 ) + +--~ expr + +local template_e = [[ + local expr = xml.expressions + return function(list,ll,l,root) + return %s + end +]] + +local template_f_y = [[ + local finalizer = xml.finalizers['%s']['%s'] + return function(collection) + return finalizer(collection,%s) + end +]] + +local template_f_n = [[ + return xml.finalizers['%s']['%s'] +]] + +-- + +local function errorrunner_e(str,cnv) + logs.report("lpath","error in expression: %s => %s",str,cnv) + return false +end +local function errorrunner_f(str,arg) + logs.report("lpath","error in finalizer: %s(%s)",str,arg or "") + return false +end + +local function register_nodes(nodetest,nodes) + return { kind = "nodes", nodetest = nodetest, nodes = nodes } +end + +local function register_expression(expression) + local converted = converter:match(expression) + local runner = loadstring(format(template_e,converted)) + runner = (runner and runner()) or function() errorrunner_e(expression,converted) end + return { kind = "expression", expression = expression, converted = converted, evaluator = runner } +end + +local function register_finalizer(protocol,name,arguments) + local runner + if arguments and arguments ~= "" then + runner = loadstring(format(template_f_y,protocol or xml.defaultprotocol,name,arguments)) + else + runner = loadstring(format(template_f_n,protocol or xml.defaultprotocol,name)) + end + runner = (runner and runner()) or function() errorrunner_f(name,arguments) end + return { kind = "finalizer", name = name, arguments = arguments, finalizer = runner } +end + +local expression = P { "ex", + ex = "[" * C((V("sq") + V("dq") + (1 - S("[]")) + V("ex"))^0) * "]", + sq = "'" * (1 - S("'"))^0 * "'", + dq = '"' * (1 - S('"'))^0 * '"', +} + +local arguments = P { "ar", + ar = "(" * Cs((V("sq") + V("dq") + V("nq") + P(1-P(")")))^0) * ")", + nq = ((1 - S("),'\""))^1) / function(s) return format("%q",s) end, + sq = P("'") * (1 - P("'"))^0 * P("'"), + dq = P('"') * (1 - P('"'))^0 * P('"'), +} + +-- todo: better arg parser + +local register_self = { kind = "axis", axis = "self" } -- , apply = apply_axis["self"] } +local register_parent = { kind = "axis", axis = "parent" } -- , apply = apply_axis["parent"] } +local register_descendant = { kind = "axis", axis = "descendant" } -- , apply = apply_axis["descendant"] } +local register_child = { kind = "axis", axis = "child" } -- , apply = apply_axis["child"] } +local register_descendant_or_self = { kind = "axis", axis = "descendant-or-self" } -- , apply = apply_axis["descendant-or-self"] } +local register_root = { kind = "axis", axis = "root" } -- , apply = apply_axis["root"] } +local register_ancestor = { kind = "axis", axis = "ancestor" } -- , apply = apply_axis["ancestor"] } +local register_ancestor_or_self = { kind = "axis", axis = "ancestor-or-self" } -- , apply = apply_axis["ancestor-or-self"] } +local register_attribute = { kind = "axis", axis = "attribute" } -- , apply = apply_axis["attribute"] } +local register_namespace = { kind = "axis", axis = "namespace" } -- , apply = apply_axis["namespace"] } +local register_following = { kind = "axis", axis = "following" } -- , apply = apply_axis["following"] } +local register_following_sibling = { kind = "axis", axis = "following-sibling" } -- , apply = apply_axis["following-sibling"] } +local register_preceding = { kind = "axis", axis = "preceding" } -- , apply = apply_axis["preceding"] } +local register_preceding_sibling = { kind = "axis", axis = "preceding-sibling" } -- , apply = apply_axis["preceding-sibling"] } + +local register_auto_descendant_or_self = { kind = "axis", axis = "auto-descendant-or-self" } -- , apply = apply_axis["auto-descendant-or-self"] } +local register_auto_descendant = { kind = "axis", axis = "auto-descendant" } -- , apply = apply_axis["auto-descendant"] } +local register_auto_self = { kind = "axis", axis = "auto-self" } -- , apply = apply_axis["auto-self"] } +local register_auto_child = { kind = "axis", axis = "auto-child" } -- , apply = apply_axis["auto-child"] } + +local register_initial_child = { kind = "axis", axis = "initial-child" } -- , apply = apply_axis["initial-child"] } + +local register_all_nodes = { kind = "nodes", nodetest = true, nodes = { true, false, false } } + +local function register_error(str) + return { kind = "error", comment = format("unparsed: %s",str) } +end + +local parser = Ct { "patterns", -- can be made a bit faster by moving pattern outside + + patterns = spaces * V("protocol") * spaces * V("initial") * spaces * V("step") * spaces * + (P("/") * spaces * V("step") * spaces)^0, + + protocol = Cg(V("letters"),"protocol") * P("://") + Cg(Cc(nil),"protocol"), + + step = (V("shortcuts") + V("axis") * spaces * V("nodes")^0 + V("error")) * spaces * V("expressions")^0 * spaces * V("finalizer")^0, + + axis = V("descendant") + V("child") + V("parent") + V("self") + V("root") + V("ancestor") + + V("descendant_or_self") + V("following") + V("following_sibling") + + V("preceding") + V("preceding_sibling") + V("ancestor_or_self") + + #(1-P(-1)) * Cc(register_auto_child), + + initial = (P("/") * spaces * Cc(register_initial_child))^-1, + + error = (P(1)^1) / register_error, + + shortcuts_a = V("s_descendant_or_self") + V("s_descendant") + V("s_child") + V("s_parent") + V("s_self") + V("s_root") + V("s_ancestor"), + + shortcuts = V("shortcuts_a") * (spaces * "/" * spaces * V("shortcuts_a"))^0, + + s_descendant_or_self = P("/") * Cc(register_descendant_or_self), + s_descendant = P("**") * Cc(register_descendant), + s_child = P("*") * Cc(register_child ), + s_parent = P("..") * Cc(register_parent ), + s_self = P("." ) * Cc(register_self ), + s_root = P("^^") * Cc(register_root ), + s_ancestor = P("^") * Cc(register_ancestor ), + + descendant = P("descendant::") * Cc(register_descendant ), + child = P("child::") * Cc(register_child ), + parent = P("parent::") * Cc(register_parent ), + self = P("self::") * Cc(register_self ), + root = P('root::') * Cc(register_root ), + ancestor = P('ancestor::') * Cc(register_ancestor ), + descendant_or_self = P('descendant-or-self::') * Cc(register_descendant_or_self ), + ancestor_or_self = P('ancestor-or-self::') * Cc(register_ancestor_or_self ), + -- attribute = P('attribute::') * Cc(register_attribute ), + -- namespace = P('namespace::') * Cc(register_namespace ), + following = P('following::') * Cc(register_following ), + following_sibling = P('following-sibling::') * Cc(register_following_sibling ), + preceding = P('preceding::') * Cc(register_preceding ), + preceding_sibling = P('preceding-sibling::') * Cc(register_preceding_sibling ), + + nodes = (V("nodefunction") * spaces * P("(") * V("nodeset") * P(")") + V("nodetest") * V("nodeset")) / register_nodes, + + expressions = expression / register_expression, + + letters = R("az")^1, + name = (1-lpeg.S("/[]()|:*!"))^1, + negate = P("!") * Cc(false), + + nodefunction = V("negate") + P("not") * Cc(false) + Cc(true), + nodetest = V("negate") + Cc(true), + nodename = (V("negate") + Cc(true)) * spaces * ((V("wildnodename") * P(":") * V("wildnodename")) + (Cc(false) * V("wildnodename"))), + wildnodename = (C(V("name")) + P("*") * Cc(false)) * #(1-P("(")), + nodeset = spaces * Ct(V("nodename") * (spaces * P("|") * spaces * V("nodename"))^0) * spaces, + + finalizer = (Cb("protocol") * P("/")^-1 * C(V("name")) * arguments * P(-1)) / register_finalizer, + +} + +local cache = { } + +local function nodesettostring(set,nodetest) + local t = { } + for i=1,#set,3 do + local directive, ns, tg = set[i], set[i+1], set[i+2] + if not ns or ns == "" then ns = "*" end + if not tg or tg == "" then tg = "*" end + tg = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) + t[#t+1] = (directive and tg) or format("not(%s)",tg) + end + if nodetest == false then + return format("not(%s)",concat(t,"|")) + else + return concat(t,"|") + end +end + +local function tagstostring(list) + if #list == 0 then + return "no elements" + else + local t = { } + for i=1, #list do + local li = list[i] + local ns, tg = li.ns, li.tg + if not ns or ns == "" then ns = "*" end + if not tg or tg == "" then tg = "*" end + t[#t+1] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) + end + return concat(t," ") + end +end + +xml.nodesettostring = nodesettostring + +local function lshow(parsed) + if type(parsed) == "string" then + parsed = parse_pattern(parsed) + end + local s = table.serialize_functions -- ugly + table.serialize_functions = false -- ugly + logs.report("lpath","%s://%s => %s",parsed.protocol or xml.defaultprotocol,parsed.pattern,table.serialize(parsed,false)) + table.serialize_functions = s -- ugly +end + +xml.lshow = lshow + +local function parse_pattern(pattern) -- the gain of caching is rather minimal + lpathcalls = lpathcalls + 1 + if type(pattern) == "table" then + return pattern + else + local parsed = cache[pattern] + if parsed then + lpathcached = lpathcached + 1 + else + parsed = parser:match(pattern) + if parsed then + parsed.pattern = pattern + local np = #parsed + if np == 0 then + parsed = { pattern = pattern, register_self, state = "parsing error" } + logs.report("lpath","parsing error in '%s'",pattern) + lshow(parsed) + else + -- we could have done this with a more complex parsed but this + -- is cleaner + local pi = parsed[1] + if pi.axis == "auto-child" then + parsed.comment = "auto-child replaced by auto-descendant-or-self" + parsed[1] = register_auto_descendant_or_self + --~ parsed.comment = "auto-child replaced by auto-descendant" + --~ parsed[1] = register_auto_descendant + elseif pi.axis == "initial-child" and np > 1 and parsed[2].axis then + parsed.comment = "initial-child removed" -- we could also make it a auto-self + remove(parsed,1) + end + end + else + parsed = { pattern = pattern } + end + cache[pattern] = parsed + if trace_lparse then + lshow(parsed) + end + end + return parsed + end +end + +-- we can move all calls inline and then merge the trace back +-- technically we can combine axis and the next nodes which is +-- what we did before but this a bit cleaner (but slower too) +-- but interesting is that it's not that much faster when we +-- go inline +-- +-- beware: we need to return a collection even when we filter +-- else the (simple) cache gets messed up + +-- caching found lookups saves not that much (max .1 sec on a 8 sec run) +-- and it also messes up finalizers + +local function parse_apply(list,pattern) + -- we avoid an extra call + local parsed = cache[pattern] + if parsed then + lpathcalls = lpathcalls + 1 + lpathcached = lpathcached + 1 + elseif type(pattern) == "table" then + lpathcalls = lpathcalls + 1 + parsed = pattern + else + parsed = parse_pattern(pattern) or pattern + end + if not parsed then + return + end + local nofparsed = #parsed + if nofparsed > 0 then + if trace_lpath then + if trace_lparse then + lshow(parsed) + end + logs.report("lpath", "collecting : %s",pattern) + logs.report("lpath", " root tags : %s",tagstostring(list)) + local collected = list + for i=1,nofparsed do + local pi = parsed[i] + local kind = pi.kind + if kind == "axis" then + collected = apply_axis[pi.axis](collected) + -- collected = pi.apply(collected) + logs.report("lpath", "% 10i : ax : %s",(collected and #collected) or 0,pi.axis) + elseif kind == "nodes" then + collected = apply_nodes(collected,pi.nodetest,pi.nodes) + logs.report("lpath", "% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) + elseif kind == "expression" then + collected = apply_expression(collected,pi.evaluator,i) + logs.report("lpath", "% 10i : ex : %s",(collected and #collected) or 0,pi.expression) + elseif kind == "finalizer" then + collected = pi.finalizer(collected) + logs.report("lpath", "% 10i : fi : %s : %s(%s)",(collected and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") + return collected + end + if not collected or #collected == 0 then + return nil + end + end + return collected + else + local collected = list + for i=1,nofparsed do + local pi = parsed[i] + local kind = pi.kind + if kind == "axis" then + local axis = pi.axis + if axis ~= "self" then + collected = apply_axis[axis](collected) + end + elseif kind == "nodes" then + collected = apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind == "expression" then + collected = apply_expression(collected,pi.evaluator,i) + elseif kind == "finalizer" then + return pi.finalizer(collected) + end + if not collected or #collected == 0 then + return nil + end + end + return collected + end + end +end + +-- internal (parsed) + +expressions.child = function(e,pattern) + return parse_apply({ e },pattern) -- todo: cache +end +expressions.count = function(e,pattern) + local collected = parse_apply({ e },pattern) -- todo: cache + return (collected and #collected) or 0 +end + +-- external + +expressions.oneof = function(s,...) -- slow + local t = {...} for i=1,#t do if s == t[i] then return true end end return false +end +expressions.error = function(str) + xml.error_handler("unknown function in lpath expression",tostring(str or "?")) + return false +end +expressions.undefined = function(s) + return s == nil +end + +expressions.contains = find +expressions.find = find +expressions.upper = upper +expressions.lower = lower +expressions.number = tonumber +expressions.boolean = toboolean + +-- user interface + +local function traverse(root,pattern,handle) + logs.report("xml","use 'xml.selection' instead for '%s'",pattern) + local collected = parse_apply({ root },pattern) + if collected then + for c=1,#collected do + local e = collected[c] + local r = e.__p__ + handle(r,r.dt,e.ni) + end + end +end + +local function selection(root,pattern,handle) + local collected = parse_apply({ root },pattern) + if collected then + if handle then + for c=1,#collected do + handle(collected[c]) + end + else + return collected + end + end +end + +xml.parse_parser = parser +xml.parse_pattern = parse_pattern +xml.parse_apply = parse_apply +xml.traverse = traverse -- old method, r, d, k +xml.selection = selection -- new method, simple handle + +local lpath = parse_pattern + +xml.lpath = lpath + +function xml.cached_patterns() + return cache +end + +-- generic function finalizer (independant namespace) + +local function dofunction(collected,fnc) + if collected then + local f = functions[fnc] + if f then + for c=1,#collected do + f(collected[c]) + end + else + logs.report("xml","unknown function '%s'",fnc) + end + end +end + +xml.finalizers.xml["function"] = dofunction +xml.finalizers.tex["function"] = dofunction + +-- functions + +expressions.text = function(e,n) + local rdt = e.__p__.dt + return (rdt and rdt[n]) or "" +end + +expressions.name = function(e,n) -- ns + tg + local found = false + n = tonumber(n) or 0 + if n == 0 then + found = type(e) == "table" and e + elseif n < 0 then + local d, k = e.__p__.dt, e.ni + for i=k-1,1,-1 do + local di = d[i] + if type(di) == "table" then + if n == -1 then + found = di + break + else + n = n + 1 + end + end + end + else + local d, k = e.__p__.dt, e.ni + for i=k+1,#d,1 do + local di = d[i] + if type(di) == "table" then + if n == 1 then + found = di + break + else + n = n - 1 + end + end + end + end + if found then + local ns, tg = found.rn or found.ns or "", found.tg + if ns ~= "" then + return ns .. ":" .. tg + else + return tg + end + else + return "" + end +end + +expressions.tag = function(e,n) -- only tg + local found = false + n = tonumber(n) or 0 + if n == 0 then + found = (type(e) == "table") and e -- seems to fail + elseif n < 0 then + local d, k = e.__p__.dt, e.ni + for i=k-1,1,-1 do + local di = d[i] + if type(di) == "table" then + if n == -1 then + found = di + break + else + n = n + 1 + end + end + end + else + local d, k = e.__p__.dt, e.ni + for i=k+1,#d,1 do + local di = d[i] + if type(di) == "table" then + if n == 1 then + found = di + break + else + n = n - 1 + end + end + end + end + return (found and found.tg) or "" +end + +--[[ldx-- +<p>This is the main filter function. It returns whatever is asked for.</p> +--ldx]]-- + +function xml.filter(root,pattern) -- no longer funny attribute handling here + return parse_apply({ root },pattern) +end + +--[[ldx-- +<p>Often using an iterators looks nicer in the code than passing handler +functions. The <l n='lua'/> book describes how to use coroutines for that +purpose (<url href='http://www.lua.org/pil/9.3.html'/>). This permits +code like:</p> + +<typing> +for r, d, k in xml.elements(xml.load('text.xml'),"title") do + print(d[k]) -- old method +end +for e in xml.collected(xml.load('text.xml'),"title") do + print(e) -- new one +end +</typing> +--ldx]]-- + +local wrap, yield = coroutine.wrap, coroutine.yield + +function xml.elements(root,pattern,reverse) -- r, d, k + local collected = parse_apply({ root },pattern) + if collected then + if reverse then + return wrap(function() for c=#collected,1,-1 do + local e = collected[c] local r = e.__p__ yield(r,r.dt,e.ni) + end end) + else + return wrap(function() for c=1,#collected do + local e = collected[c] local r = e.__p__ yield(r,r.dt,e.ni) + end end) + end + end + return wrap(function() end) +end + +function xml.collected(root,pattern,reverse) -- e + local collected = parse_apply({ root },pattern) + if collected then + if reverse then + return wrap(function() for c=#collected,1,-1 do yield(collected[c]) end end) + else + return wrap(function() for c=1,#collected do yield(collected[c]) end end) + end + end + return wrap(function() end) +end diff --git a/tex/context/base/lxml-mis.lua b/tex/context/base/lxml-mis.lua index a117b1af9..eff012013 100644 --- a/tex/context/base/lxml-mis.lua +++ b/tex/context/base/lxml-mis.lua @@ -18,7 +18,8 @@ this module. Since this module is also used in <l n='mtxrun'/> we've put them here instead of loading mode modules there then needed.</p> --ldx]]-- -function xml.gsub(t,old,new) + +local function xmlgsub(t,old,new) local dt = t.dt if dt then for k=1,#dt do @@ -26,26 +27,24 @@ function xml.gsub(t,old,new) if type(v) == "string" then dt[k] = gsub(v,old,new) else - xml.gsub(v,old,new) + xmlgsub(v,old,new) end end end end +xmlgsub = xmlgsub + function xml.strip_leading_spaces(dk,d,k) -- cosmetic, for manual - if d and k and d[k-1] and type(d[k-1]) == "string" then - local s = d[k-1]:match("\n(%s+)") - xml.gsub(dk,"\n"..string.rep(" ",#s),"\n") + if d and k then + local dkm = d[k-1] + if dkm and type(dkm) == "string" then + local s = match(dkm,"\n(%s+)") + xmlgsub(dk,"\n"..rep(" ",#s),"\n") + end end end -function xml.serialize_path(root,lpath,handle) - local dk, r, d, k = xml.first(root,lpath) - dk = xml.copy(dk) - xml.strip_leading_spaces(dk,d,k) - xml.serialize(dk,handle) -end - --~ xml.escapes = { ['&'] = '&', ['<'] = '<', ['>'] = '>', ['"'] = '"' } --~ xml.unescapes = { } for k,v in pairs(xml.escapes) do xml.unescapes[v] = k end @@ -71,8 +70,6 @@ local escaped = Cs(normal * (special * normal)^0) -- 100 * 1000 * "oeps< oeps> oeps&" : gsub:lpeg == 0153:0280:0151:0080 (last one by roberto) --- unescaped = Cs((S("<")/"<" + S(">")/">" + S("&")/"&" + 1)^0) --- unescaped = Cs((((P("&")/"") * (P("lt")/"<" + P("gt")/">" + P("amp")/"&") * (P(";")/"")) + 1)^0) local normal = (1 - S"&")^0 local special = P("<")/"<" + P(">")/">" + P("&")/"&" local unescaped = Cs(normal * (special * normal)^0) @@ -88,19 +85,3 @@ xml.cleansed_pattern = cleansed function xml.escaped (str) return escaped :match(str) end function xml.unescaped(str) return unescaped:match(str) end function xml.cleansed (str) return cleansed :match(str) end - -function xml.join(t,separator,lastseparator) - if #t > 0 then - local result = { } - for k,v in pairs(t) do - result[k] = xml.tostring(v) - end - if lastseparator then - return concat(result,separator or "",1,#result-1) .. (lastseparator or "") .. result[#result] - else - return concat(result,separator) - end - else - return "" - end -end diff --git a/tex/context/base/lxml-pth.lua b/tex/context/base/lxml-pth.lua deleted file mode 100644 index a1bebe017..000000000 --- a/tex/context/base/lxml-pth.lua +++ /dev/null @@ -1,1735 +0,0 @@ -if not modules then modules = { } end modules ['lxml-pth'] = { - version = 1.001, - comment = "this module is the basis for the lxml-* ones", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - -local concat, remove, insert = table.concat, table.remove, table.insert -local type, next, tonumber, tostring, setmetatable, loadstring = type, next, tonumber, tostring, setmetatable, loadstring -local format, lower, gmatch, gsub, find, rep = string.format, string.lower, string.gmatch, string.gsub, string.find, string.rep - ---[[ldx-- -<p>This module can be used stand alone but also inside <l n='mkiv'/> in -which case it hooks into the tracker code. Therefore we provide a few -functions that set the tracers. Here we overload a previously defined -function.</p> -<p>If I can get in the mood I will make a variant that is XSLT compliant -but I wonder if it makes sense.</P> ---ldx]]-- - -local trace_lpath = false if trackers then trackers.register("xml.lpath", function(v) trace_lpath = v end) end - -local settrace = xml.settrace -- lxml-tab - -function xml.settrace(str,value) - if str == "lpath" then - trace_lpath = value or false - else - settrace(str,value) -- lxml-tab - end -end - ---[[ldx-- -<p>We've now arrived at an intersting part: accessing the tree using a subset -of <l n='xpath'/> and since we're not compatible we call it <l n='lpath'/>. We -will explain more about its usage in other documents.</p> ---ldx]]-- - -local lpathcalls = 0 -- statistics -local lpathcached = 0 -- statistics - -xml.functions = xml.functions or { } -xml.expressions = xml.expressions or { } - -local functions = xml.functions -local expressions = xml.expressions - --- although we could remap all to expressions we prefer to have a few speed ups --- like simple expressions as they happen to occur a lot and then we want to --- avoid too many loops - -local actions = { - [10] = "stay", - [11] = "parent", - [12] = "subtree root", - [13] = "document root", - [14] = "any", - [15] = "many", - [16] = "initial", - [20] = "match", - [21] = "match one of", - [22] = "match and attribute eq", - [23] = "match and attribute ne", - [24] = "match one of and attribute eq", - [25] = "match one of and attribute ne", - [26] = "has name", - [27] = "has attribute", - [28] = "has value", - [29] = "fast match", - [30] = "select", - [31] = "expression", - [40] = "processing instruction", -} - --- a rather dumb lpeg - -local P, S, R, C, V, Cc = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc - --- instead of using functions we just parse a few names which saves a call --- later on --- --- we can use a metatable - -local lp_space = S(" \n\r\t") -local lp_any = P(1) -local lp_position = P("position()") / "ps" -local lp_index = P("index()") / "id" -local lp_text = P("text()") / "tx" -local lp_name = P("name()") / "(ns~='' and ns..':'..tg)" -- "((rt.ns~='' and rt.ns..':'..rt.tg) or '')" -local lp_tag = P("tag()") / "tg" -- (rt.tg or '') -local lp_ns = P("ns()") / "ns" -- (rt.ns or '') -local lp_noequal = P("!=") / "~=" + P("<=") + P(">=") + P("==") -local lp_doequal = P("=") / "==" ---~ local lp_attribute = P("@") / "" * Cc("(at['") * R("az","AZ","--","__")^1 * Cc("'] or '')") -local lp_attribute = P("@") / "" * Cc("at['") * R("az","AZ","--","__")^1 * Cc("']") -local lp_or = P("|") / " or " -local lp_and = P("&") / " and " - -local lp_reserved = C("and") + C("or") + C("not") + C("div") + C("mod") + C("true") + C("false") - -local lp_lua_function = C(R("az","AZ","--","__")^1 * (P(".") * R("az","AZ","--","__")^1)^1) * P("(") / function(t) -- todo: better . handling - return t .. "(" -end - -local lp_function = C(R("az","AZ","--","__")^1) * P("(") / function(t) -- todo: better . handling - if expressions[t] then - return "expressions." .. t .. "(" - else - return "expressions.error(" - end -end - -local lparent = lpeg.P("(") -local rparent = lpeg.P(")") -local noparent = 1 - (lparent+rparent) -local nested = lpeg.P{lparent * (noparent + lpeg.V(1))^0 * rparent} -local value = lpeg.P(lparent * lpeg.C((noparent + nested)^0) * rparent) -- lpeg.P{"("*C(((1-S("()"))+V(1))^0)*")"} - -local lp_child = Cc("expressions.child(r,k,'") * R("az","AZ","--","__")^1 * Cc("')") -local lp_string = Cc("'") * R("az","AZ","--","__")^1 * Cc("'") -local lp_content= Cc("tx==") * (P("'") * (1-P("'"))^0 * P("'") + P('"') * (1-P('"'))^0 * P('"')) - - --- if we use a dedicated namespace then we don't need to pass rt and k - -local converter, cleaner - -local lp_special = (C(P("name")+P("text")+P("tag")+P("count")+P("child"))) * value / function(t,s) - if expressions[t] then - if s then - return "expressions." .. t .. "(r,k," .. cleaner:match(s) ..")" - else - return "expressions." .. t .. "(r,k)" - end - else - return "expressions.error(" .. t .. ")" - end -end - -local content = - lp_position + - lp_index + - lp_text + lp_name + -- fast one - lp_special + - lp_noequal + lp_doequal + - lp_attribute + - lp_or + lp_and + - lp_lua_function + - lp_function + - lp_reserved + - lp_content + - lp_child + - lp_any - -converter = lpeg.Cs ( - (lpeg.P { lparent * (lpeg.V(1))^0 * rparent + content } )^0 -) - -cleaner = lpeg.Cs ( ( - lp_reserved + - lp_string + -1 )^1 ) - --- expressions,root,rootdt,k,e,edt,ns,tg,idx,hsh[tg] or 1 - -local template = [[ - -- todo: locals for xml.functions - return function(expressions,r,d,k,e,dt,ns,tg,id,ps) - local at, tx = e.at or { }, dt[1] or "" - return %s - end -]] - -local function make_expression(str) ---~ print(">>>",str) - str = converter:match(str) ---~ print("<<<",str) - local s = loadstring(format(template,str)) - if s then - return str, s() - else - return str, "" - end -end - -local space = S(' \r\n\t') -local squote = S("'") -local dquote = S('"') -local lparent = P('(') -local rparent = P(')') -local atsign = P('@') -local lbracket = P('[') -local rbracket = P(']') -local exclam = P('!') -local period = P('.') -local eq = P('==') + P('=') -local ne = P('<>') + P('!=') -local star = P('*') -local slash = P('/') -local colon = P(':') -local bar = P('|') -local hat = P('^') -local valid = R('az', 'AZ', '09') + S('_-') -local name_yes = C(valid^1 + star) * colon * C(valid^1 + star) -- permits ns:* *:tg *:* -local name_nop = Cc("*") * C(valid^1) -local name = name_yes + name_nop -local number = C((S('+-')^0 * R('09')^1)) / tonumber -local names = (bar^0 * name)^1 -local morenames = name * (bar^0 * name)^1 -local instructiontag = P('pi::') -local spacing = C(space^0) -local somespace = space^1 -local optionalspace = space^0 -local text = C(valid^0) -local value = (squote * C((1 - squote)^0) * squote) + (dquote * C((1 - dquote)^0) * dquote) -local empty = 1-slash -local nobracket = 1-(lbracket+rbracket) - --- this has to become a proper grammar instead of a substitution (which we started --- with when we moved to lpeg) - -local is_eq = lbracket * atsign * name * eq * value * rbracket * #(1-lbracket) -local is_ne = lbracket * atsign * name * ne * value * rbracket * #(1-lbracket) -local is_attribute = lbracket * atsign * name * rbracket * #(1-lbracket) -local is_value = lbracket * value * rbracket * #(1-lbracket) -local is_number = lbracket * number * rbracket * #(1-lbracket) -local is_name = lbracket * name * rbracket * #(1-lbracket) - ---~ local is_expression = lbracket * (C(nobracket^1))/make_expression * rbracket ---~ local is_expression = is_expression * (Cc(" and ") * is_expression)^0 - ---~ local is_expression = (lbracket/"(") * C(nobracket^1) * (rbracket/")") ---~ local is_expression = lpeg.Cs(is_expression * (Cc(" and ") * is_expression)^0) / make_expression - -local is_position = function(s) return " position()==" .. s .. " " end - -local is_expression = (is_number/is_position) + (lbracket/"(") * (nobracket^1) * (rbracket/")") -local is_expression = lpeg.Cs(is_expression * (Cc(" and ") * is_expression)^0) / make_expression - -local is_one = name -local is_none = exclam * name -local is_one_of = ((lparent * names * rparent) + morenames) -local is_none_of = exclam * ((lparent * names * rparent) + morenames) - ---~ local stay_action = { 11 }, beware, sometimes we adapt ! - -local stay = (period ) -local parent = (period * period ) / function( ) return { 11 } end -local subtreeroot = (slash + hat ) / function( ) return { 12 } end -local documentroot = (hat * hat ) / function( ) return { 13 } end -local any = (star ) / function( ) return { 14 } end -local many = (star * star ) / function( ) return { 15 } end -local initial = (hat * hat * hat ) / function( ) return { 16 } end - -local match = (is_one ) / function(...) return { 20, true , ... } end -local match_one_of = (is_one_of ) / function(...) return { 21, true , ... } end -local dont_match = (is_none ) / function(...) return { 20, false, ... } end -local dont_match_one_of = (is_none_of ) / function(...) return { 21, false, ... } end - -local match_and_eq = (is_one * is_eq ) / function(...) return { 22, true , ... } end -local match_and_ne = (is_one * is_ne ) / function(...) return { 23, true , ... } end -local dont_match_and_eq = (is_none * is_eq ) / function(...) return { 22, false, ... } end -local dont_match_and_ne = (is_none * is_ne ) / function(...) return { 23, false, ... } end - -local match_one_of_and_eq = (is_one_of * is_eq ) / function(...) return { 24, true , ... } end -local match_one_of_and_ne = (is_one_of * is_ne ) / function(...) return { 25, true , ... } end -local dont_match_one_of_and_eq = (is_none_of * is_eq ) / function(...) return { 24, false, ... } end -local dont_match_one_of_and_ne = (is_none_of * is_ne ) / function(...) return { 25, false, ... } end - -local has_name = (is_one * is_name) / function(...) return { 26, true , ... } end -local dont_has_name = (is_none * is_name) / function(...) return { 26, false, ... } end -local has_attribute = (is_one * is_attribute) / function(...) return { 27, true , ... } end -local dont_has_attribute = (is_none * is_attribute) / function(...) return { 27, false, ... } end -local has_value = (is_one * is_value ) / function(...) return { 28, true , ... } end -local dont_has_value = (is_none * is_value ) / function(...) return { 28, false, ... } end -local position = (is_one * is_number ) / function(...) return { 30, true, ... } end -local dont_position = (is_none * is_number ) / function(...) return { 30, false, ... } end - -local expression = (is_one * is_expression)/ function(...) return { 31, true, ... } end -local dont_expression = (is_none * is_expression)/ function(...) return { 31, false, ... } end - -local self_expression = ( is_expression) / function(...) return { 31, true, "*", "*", ... } end -local dont_self_expression = (exclam * is_expression) / function(...) return { 31, false, "*", "*", ... } end - -local instruction = (instructiontag * text ) / function(...) return { 40, ... } end -local nothing = (empty ) / function( ) return { 15 } end -- 15 ? -local crap = (1-slash)^1 - --- a few ugly goodies: - -local docroottag = P('^^') / function() return { 12 } end -local subroottag = P('^') / function() return { 13 } end -local roottag = P('root::') / function() return { 12 } end -local parenttag = P('parent::') / function() return { 11 } end -local childtag = P('child::') -local selftag = P('self::') - --- there will be more and order will be optimized - -local selector = ( - instruction + --- many + any + -- brrr, not here ! - parent + stay + - dont_position + position + -- fast one - dont_has_attribute + has_attribute + -- fast ones - dont_has_name + has_name + -- fast ones - dont_has_value + has_value + -- fast ones - dont_match_one_of_and_eq + dont_match_one_of_and_ne + - match_one_of_and_eq + match_one_of_and_ne + - dont_match_and_eq + dont_match_and_ne + - match_and_eq + match_and_ne + - dont_expression + expression + - dont_self_expression + self_expression + - dont_match_one_of + match_one_of + - dont_match + match + - many + any + - crap + empty -) - -local grammar = lpeg.Ct { "startup", - startup = (initial + documentroot + subtreeroot + roottag + docroottag + subroottag)^0 * V("followup"), - followup = ((slash + parenttag + childtag + selftag)^0 * selector)^1, -} - -local function compose(str) - if not str or str == "" then - -- wildcard - return true - elseif str == '/' then - -- root - return false - else - local map = grammar:match(str) - if #map == 0 then - return true - else - if map[1][1] == 32 then - -- lone expression - insert(map, 1, { 11 }) - end - local m = map[1][1] - if #map == 1 then - if m == 14 or m == 15 then - -- wildcard - return true - elseif m == 12 then - -- root - return false - end - elseif #map == 2 and m == 12 and map[2][1] == 20 then - map[2][1] = 29 - return { map[2] } - end - if m ~= 11 and m ~= 12 and m ~= 13 and m ~= 14 and m ~= 15 and m ~= 16 then - insert(map, 1, { 16 }) - end - return map - end - end -end - -local cache = { } - -local function lpath(pattern,trace) - lpathcalls = lpathcalls + 1 - if type(pattern) == "string" then - local result = cache[pattern] - if result == nil then -- can be false which is valid -) - result = compose(pattern) - cache[pattern] = result - lpathcached = lpathcached + 1 - end - if trace or trace_lpath then - xml.lshow(result) - end - return result - else - return pattern - end -end - -xml.lpath = lpath - -function xml.cached_patterns() - return cache -end - --- we run out of locals (limited to 200) --- --- local fallbackreport = (texio and texio.write) or io.write - -function xml.lshow(pattern,report) --- report = report or fallbackreport - report = report or (texio and texio.write) or io.write - local lp = lpath(pattern) - if lp == false then - report(" -: root\n") - elseif lp == true then - report(" -: wildcard\n") - else - if type(pattern) == "string" then - report(format("pattern: %s\n",pattern)) - end - for k=1,#lp do - local v = lp[k] - if #v > 1 then - local t = { } - for i=2,#v do - local vv = v[i] - if type(vv) == "string" then - t[#t+1] = (vv ~= "" and vv) or "#" - elseif type(vv) == "boolean" then - t[#t+1] = (vv and "==") or "<>" - end - end - report(format("%2i: %s %s -> %s\n", k,v[1],actions[v[1]],concat(t," "))) - else - report(format("%2i: %s %s\n", k,v[1],actions[v[1]])) - end - end - end -end - -function xml.xshow(e,...) -- also handy when report is given, use () to isolate first e - local t = { ... } --- local report = (type(t[#t]) == "function" and t[#t]) or fallbackreport - local report = (type(t[#t]) == "function" and t[#t]) or (texio and texio.write) or io.write - if e == nil then - report("<!-- no element -->\n") - elseif type(e) ~= "table" then - report(tostring(e)) - elseif e.tg then - report(tostring(e) .. "\n") - else - for i=1,#e do - report(tostring(e[i]) .. "\n") - end - end -end - ---[[ldx-- -<p>An <l n='lpath'/> is converted to a table with instructions for traversing the -tree. Hoever, simple cases are signaled by booleans. Because we don't know in -advance what we want to do with the found element the handle gets three arguments:</p> - -<lines> -<t>r</t> : the root element of the data table -<t>d</t> : the data table of the result -<t>t</t> : the index in the data table of the result -</lines> - -<p> Access to the root and data table makes it possible to construct insert and delete -functions.</p> ---ldx]]-- - -local functions = xml.functions -local expressions = xml.expressions - -expressions.contains = string.find -expressions.find = string.find -expressions.upper = string.upper -expressions.lower = string.lower -expressions.number = tonumber -expressions.boolean = toboolean - -expressions.oneof = function(s,...) -- slow - local t = {...} for i=1,#t do if s == t[i] then return true end end return false -end - -expressions.error = function(str) - xml.error_handler("unknown function in lpath expression",str or "?") - return false -end - -functions.text = function(root,k,n) -- unchecked, maybe one deeper ---~ local t = type(t) -- ? ---~ if t == "string" then ---~ return t ---~ else -- todo n - local rdt = root.dt - return (rdt and rdt[k]) or root[k] or "" ---~ end -end - -functions.name = function(d,k,n) -- ns + tg - local found = false - n = n or 0 - if not k then - -- not found - elseif n == 0 then - local dk = d[k] - found = dk and (type(dk) == "table") and dk - elseif n < 0 then - for i=k-1,1,-1 do - local di = d[i] - if type(di) == "table" then - if n == -1 then - found = di - break - else - n = n + 1 - end - end - end - else - for i=k+1,#d,1 do - local di = d[i] - if type(di) == "table" then - if n == 1 then - found = di - break - else - n = n - 1 - end - end - end - end - if found then - local ns, tg = found.rn or found.ns or "", found.tg - if ns ~= "" then - return ns .. ":" .. tg - else - return tg - end - else - return "" - end -end - -functions.tag = function(d,k,n) -- only tg - local found = false - n = n or 0 - if not k then - -- not found - elseif n == 0 then - local dk = d[k] - found = dk and (type(dk) == "table") and dk - elseif n < 0 then - for i=k-1,1,-1 do - local di = d[i] - if type(di) == "table" then - if n == -1 then - found = di - break - else - n = n + 1 - end - end - end - else - for i=k+1,#d,1 do - local di = d[i] - if type(di) == "table" then - if n == 1 then - found = di - break - else - n = n - 1 - end - end - end - end - return (found and found.tg) or "" -end - -expressions.text = functions.text -expressions.name = functions.name -expressions.tag = functions.tag - -local function traverse(root,pattern,handle,reverse,index,parent,wildcard) -- multiple only for tags, not for namespaces - if not root then -- error - return false - elseif pattern == false then -- root - handle(root,root.dt,root.ri) - return false - elseif pattern == true then -- wildcard - local rootdt = root.dt - if rootdt then - local start, stop, step = 1, #rootdt, 1 - if reverse then - start, stop, step = stop, start, -1 - end - for k=start,stop,step do - if handle(root,rootdt,root.ri or k) then return false end - if not traverse(rootdt[k],true,handle,reverse) then return false end - end - end - return false - elseif root.dt then - index = index or 1 - local action = pattern[index] - local command = action[1] - if command == 29 then -- fast case /oeps - local rootdt = root.dt - for k=1,#rootdt do - local e = rootdt[k] - local tg = e.tg - if e.tg then - local ns = e.rn or e.ns - local ns_a, tg_a = action[3], action[4] - local matched = (ns_a == "*" or ns == ns_a) and (tg_a == "*" or tg == tg_a) - if not action[2] then matched = not matched end - if matched then - if handle(root,rootdt,k) then return false end - end - end - end - elseif command == 11 then -- parent - local ep = root.__p__ or parent - if index < #pattern then - if not traverse(ep,pattern,handle,reverse,index+1,root) then return false end - elseif handle(ep) then -- wrong (others also) - return false - else - -- ? - end - else - if (command == 16 or command == 12) and index == 1 then -- initial - -- wildcard = true - wildcard = command == 16 -- ok? - index = index + 1 - action = pattern[index] - command = action and action[1] or 0 -- something is wrong - end - if command == 11 then -- parent - local ep = root.__p__ or parent - if index < #pattern then - if not traverse(ep,pattern,handle,reverse,index+1,root) then return false end - elseif handle(ep) then - return false - else - -- ? - end - else - local rootdt = root.dt - local start, stop, step, n, dn = 1, #rootdt, 1, 0, 1 - if command == 30 then - if action[5] < 0 then - start, stop, step = stop, start, -1 - dn = -1 - end - elseif reverse and index == #pattern then - start, stop, step = stop, start, -1 - end - local idx, hsh = 0, { } -- this hsh will slooow down the lot - for k=start,stop,step do -- we used to have functions for all but a case is faster - local e = rootdt[k] - local ns, tg = e.rn or e.ns, e.tg - if tg then - -- we can optimize this for simple searches, but it probably does not pay off - hsh[tg] = (hsh[tg] or 0) + 1 - idx = idx + 1 - if command == 30 then - local ns_a, tg_a = action[3], action[4] - if tg == tg_a then - matched = ns_a == "*" or ns == ns_a - elseif tg_a == '*' then - matched, multiple = ns_a == "*" or ns == ns_a, true - else - matched = false - end - if not action[2] then matched = not matched end - if matched then - n = n + dn - if n == action[5] then - if index == #pattern then - if handle(root,rootdt,root.ri or k) then return false end - else - if not traverse(e,pattern,handle,reverse,index+1,root) then return false end - end - break - end - elseif wildcard then - if not traverse(e,pattern,handle,reverse,index,root,true) then return false end - end - else - local matched, multiple = false, false - if command == 20 then -- match - local ns_a, tg_a = action[3], action[4] - if tg == tg_a then - matched = ns_a == "*" or ns == ns_a - elseif tg_a == '*' then - matched, multiple = ns_a == "*" or ns == ns_a, true - else - matched = false - end - if not action[2] then matched = not matched end - elseif command == 21 then -- match one of - multiple = true - for i=3,#action,2 do - local ns_a, tg_a = action[i], action[i+1] - if (ns_a == "*" or ns == ns_a) and (tg == "*" or tg == tg_a) then - matched = true - break - end - end - if not action[2] then matched = not matched end - elseif command == 22 then -- eq - local ns_a, tg_a = action[3], action[4] - if tg == tg_a then - matched = ns_a == "*" or ns == ns_a - elseif tg_a == '*' then - matched, multiple = ns_a == "*" or ns == ns_a, true - else - matched = false - end - matched = matched and e.at[action[6]] == action[7] - elseif command == 23 then -- ne - local ns_a, tg_a = action[3], action[4] - if tg == tg_a then - matched = ns_a == "*" or ns == ns_a - elseif tg_a == '*' then - matched, multiple = ns_a == "*" or ns == ns_a, true - else - matched = false - end - if not action[2] then matched = not matched end - matched = mached and e.at[action[6]] ~= action[7] - elseif command == 24 then -- one of eq - multiple = true - for i=3,#action-2,2 do - local ns_a, tg_a = action[i], action[i+1] - if (ns_a == "*" or ns == ns_a) and (tg == "*" or tg == tg_a) then - matched = true - break - end - end - if not action[2] then matched = not matched end - matched = matched and e.at[action[#action-1]] == action[#action] - elseif command == 25 then -- one of ne - multiple = true - for i=3,#action-2,2 do - local ns_a, tg_a = action[i], action[i+1] - if (ns_a == "*" or ns == ns_a) and (tg == "*" or tg == tg_a) then - matched = true - break - end - end - if not action[2] then matched = not matched end - matched = matched and e.at[action[#action-1]] ~= action[#action] - elseif command == 26 then -- has child - local ns_a, tg_a = action[3], action[4] - if tg == tg_a then - matched = ns_a == "*" or ns == ns_a - elseif tg_a == '*' then - matched, multiple = ns_a == "*" or ns == ns_a, true - else - matched = false - end - if not action[2] then matched = not matched end - if matched then - -- ok, we could have the whole sequence here ... but > 1 has become an expression - local ns_a, tg_a, ok, edt = action[5], action[6], false, e.dt - for k=1,#edt do - local edk = edt[k] - if type(edk) == "table" then - if (ns_a == "*" or edk.ns == ns_a) and (edk.tg == tg_a) then - ok = true - break - end - end - end - matched = matched and ok - end - elseif command == 27 then -- has attribute - local ns_a, tg_a = action[3], action[4] - if tg == tg_a then - matched = ns_a == "*" or ns == ns_a - elseif tg_a == '*' then - matched, multiple = ns_a == "*" or ns == ns_a, true - else - matched = false - end - if not action[2] then matched = not matched end - matched = matched and e.at[action[6]] - elseif command == 28 then -- has value (text match) - local edt, ns_a, tg_a = e.dt, action[3], action[4] - if tg == tg_a then - matched = ns_a == "*" or ns == ns_a - elseif tg_a == '*' then - matched, multiple = ns_a == "*" or ns == ns_a, true - else - matched = false - end - if not action[2] then matched = not matched end - matched = matched and edt and edt[1] == action[5] - elseif command == 31 then - local edt, ns_a, tg_a = e.dt, action[3], action[4] - if tg == tg_a then - matched = ns_a == "*" or ns == ns_a - elseif tg_a == '*' then - matched, multiple = ns_a == "*" or ns == ns_a, true - else - matched = false - end - if not action[2] then matched = not matched end - if matched then - matched = action[6](expressions,root,rootdt,k,e,edt,ns,tg,idx,hsh[tg] or 1) - end - end - if matched then -- combine tg test and at test - if index == #pattern then - if handle(root,rootdt,root.ri or k) then return false end - if wildcard then - if multiple then - if not traverse(e,pattern,handle,reverse,index,root,true) then return false end - else - -- maybe or multiple; anyhow, check on (section|title) vs just section and title in example in lxml - if not traverse(e,pattern,handle,reverse,index,root) then return false end - end - end - else - -- todo: [expr][expr] - if not traverse(e,pattern,handle,reverse,index+1,root) then return false end - end - elseif command == 14 then -- any - if index == #pattern then - if handle(root,rootdt,root.ri or k) then return false end - else - if not traverse(e,pattern,handle,reverse,index+1,root) then return false end - end - elseif command == 15 then -- many - if index == #pattern then - if handle(root,rootdt,root.ri or k) then return false end - else - if not traverse(e,pattern,handle,reverse,index+1,root,true) then return false end - end - -- not here : 11 - elseif command == 11 then -- parent - local ep = e.__p__ or parent - if index < #pattern then - if not traverse(ep,pattern,handle,reverse,root,index+1) then return false end - elseif handle(root,rootdt,k) then - return false - end - elseif command == 40 and e.special and tg == "@pi@" then -- pi - local pi = action[2] - if pi ~= "" then - local pt = e.dt[1] - if pt and pt:find(pi) then - if handle(root,rootdt,k) then - return false - end - end - elseif handle(root,rootdt,k) then - return false - end - elseif wildcard then - if not traverse(e,pattern,handle,reverse,index,root,true) then return false end - end - end - else - -- not here : 11 - if command == 11 then -- parent - local ep = e.__p__ or parent - if index < #pattern then - if not traverse(ep,pattern,handle,reverse,index+1,root) then return false end - elseif handle(ep) then - return false - else - -- - end - break -- else loop - end - end - end - end - end - end - return true -end - -xml.traverse = traverse - -expressions.child = function(root,k,what) -- we could move the lpath converter to the scanner - local ok = false - traverse(root.dt[k],lpath(what),function(r,d,t) ok = true return true end) - return ok -end - -expressions.count = function(root,k,what) -- we could move the lpath converter to the scanner - local n = 0 - traverse(root.dt[k],lpath(what),function(r,d,t) n = n + 1 return false end) - return n -end - ---[[ldx-- -<p>Next come all kind of locators and manipulators. The most generic function here -is <t>xml.filter(root,pattern)</t>. All registers functions in the filters namespace -can be path of a search path, as in:</p> - -<typing> -local r, d, k = xml.filter(root,"/a/b/c/position(4)" -</typing> ---ldx]]-- - -xml.filters = { } - -local traverse, lpath, convert = xml.traverse, xml.lpath, xml.convert - -local filters = xml.filters - -function filters.default(root,pattern) - local rt, dt, dk - traverse(root, lpath(pattern), function(r,d,k) rt,dt,dk = r,d,k return true end) - return dt and dt[dk], rt, dt, dk -end - -function filters.attributes(root,pattern,arguments) - local rt, dt, dk - traverse(root, lpath(pattern), function(r,d,k) rt, dt, dk = r, d, k return true end) - local ekat = (dt and dt[dk] and dt[dk].at) or (rt and rt.at) - if ekat then - if arguments then - return ekat[arguments] or "", rt, dt, dk - else - return ekat, rt, dt, dk - end - else - return { }, rt, dt, dk - end -end - --- new - -local rt, dt, dk - -local function action(r,d,k) rt, dt, dk = r, d, k return true end - -function filters.chainattribute(root,pattern,arguments) -- todo: optional levels - rt, dt, dk = nil, nil, nil - traverse(root, lpath(pattern), action) - local dtk = dt and dt[dk] - local ekat = (dtk and dtk.at) or (rt and rt.at) - local rp = rt - while true do - if ekat then - local a = ekat[arguments] - if a then - return a, rt, dt, dk - end - end - rp = rp.__p__ - if rp then - ekat = rp.at - else - return "", rt, dt, dk - end - end -end - --- - -function filters.reverse(root,pattern) - local rt, dt, dk - traverse(root, lpath(pattern), function(r,d,k) rt,dt,dk = r,d,k return true end, 'reverse') - return dt and dt[dk], rt, dt, dk -end - -function filters.count(root,pattern,everything) - local n = 0 - traverse(root, lpath(pattern), function(r,d,t) - if everything or type(d[t]) == "table" then - n = n + 1 - end - end) - return n -end - ---~ local n = 0 ---~ local function doit(r,d,t) ---~ if everything or type(d[t]) == "table" then ---~ n = n + 1 ---~ end ---~ end ---~ function filters.count(root,pattern,everything) ---~ n = 0 ---~ traverse(root, lpath(pattern), doit) ---~ return n ---~ end - -function filters.elements(root, pattern) -- == all - local t = { } - traverse(root, lpath(pattern), function(r,d,k) - local e = d[k] - if e then - t[#t+1] = e - end - end) - return t -end - -function filters.texts(root, pattern) - local t = { } - traverse(root, lpath(pattern), function(r,d,k) - local e = d[k] - if e and e.dt then - t[#t+1] = e.dt - end - end) - return t -end - -function filters.first(root,pattern) - local rt, dt, dk - traverse(root, lpath(pattern), function(r,d,k) rt,dt,dk = r,d,k return true end) - return dt and dt[dk], rt, dt, dk -end - -function filters.last(root,pattern) - local rt, dt, dk - traverse(root, lpath(pattern), function(r,d,k) rt,dt,dk = r,d,k return true end, 'reverse') - return dt and dt[dk], rt, dt, dk -end - -function filters.index(root,pattern,arguments) - local rt, dt, dk, reverse, i = nil, nil, nil, false, tonumber(arguments or '1') or 1 - if i and i ~= 0 then - if i < 0 then - reverse, i = true, -i - end - traverse(root, lpath(pattern), function(r,d,k) rt, dt, dk, i = r, d, k, i-1 return i == 0 end, reverse) - if i == 0 then - return dt and dt[dk], rt, dt, dk - end - end - return nil, nil, nil, nil -end - ---~ function filters.attribute(root,pattern,arguments) ---~ local rt, dt, dk ---~ traverse(root, lpath(pattern), function(r,d,k) rt, dt, dk = r, d, k return true end) ---~ local ekat = (dt and dt[dk] and dt[dk].at) or (rt and rt.at) ---~ return (ekat and (ekat[arguments] or (find(arguments,"^[\'\"]") and ekat[sub(arguments,2,-2)]))) or "" ---~ end - -local rt, dt, dk -local function doit(r,d,k) rt, dt, dk = r, d, k return true end -function filters.attribute(root,pattern,arguments) - rt, dt, dk = nil, nil, nil - traverse(root, lpath(pattern), doit) - local dtk = dt and dt[k] - local ekat = (dtk and dtk.at) or (rt and rt.at) - return (ekat and (ekat[arguments] or (find(arguments,"^[\'\"]") and ekat[sub(arguments,2,-2)]))) or "" -end - -function filters.text(root,pattern,arguments) -- ?? why index, tostring slow - local dtk, rt, dt, dk = filters.index(root,pattern,arguments) - if dtk then -- n - local dtkdt = dtk.dt - if not dtkdt then - return "", rt, dt, dk - elseif #dtkdt == 1 and type(dtkdt[1]) == "string" then - return dtkdt[1], rt, dt, dk - else - return xml.tostring(dtkdt), rt, dt, dk - end - else - return "", rt, dt, dk - end -end - -function filters.tag(root,pattern,n) - local tag = "" - traverse(root, lpath(pattern), function(r,d,k) - tag = xml.functions.tag(d,k,n and tonumber(n)) - return true - end) - return tag -end - -function filters.name(root,pattern,n) - local tag = "" - traverse(root, lpath(pattern), function(r,d,k) - tag = xml.functions.name(d,k,n and tonumber(n)) - return true - end) - return tag -end - ---[[ldx-- -<p>For splitting the filter function from the path specification, we can -use string matching or lpeg matching. Here the difference in speed is -neglectable but the lpeg variant is more robust.</p> ---ldx]]-- - --- not faster but hipper ... although ... i can't get rid of the trailing / in the path - -local P, S, R, C, V, Cc = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc - -local slash = P('/') -local name = (R("az","AZ","--","__"))^1 -local path = C(((1-slash)^0 * slash)^1) -local argument = P { "(" * C(((1 - S("()")) + V(1))^0) * ")" } -local action = Cc(1) * path * C(name) * argument -local attribute = Cc(2) * path * P('@') * C(name) -local direct = Cc(3) * Cc("../*") * slash^0 * C(name) * argument - -local parser = direct + action + attribute - -local filters = xml.filters -local attribute_filter = xml.filters.attributes -local default_filter = xml.filters.default - --- todo: also hash, could be gc'd - -function xml.filter(root,pattern) - local kind, a, b, c = parser:match(pattern) - if kind == 1 or kind == 3 then - return (filters[b] or default_filter)(root,a,c) - elseif kind == 2 then - return attribute_filter(root,a,b) - else - return default_filter(root,pattern) - end -end - ---~ slightly faster, but first we need a proper test file ---~ ---~ local hash = { } ---~ ---~ function xml.filter(root,pattern) ---~ local h = hash[pattern] ---~ if not h then ---~ local kind, a, b, c = parser:match(pattern) ---~ if kind == 1 then ---~ h = { kind, filters[b] or default_filter, a, b, c } ---~ elseif kind == 2 then ---~ h = { kind, attribute_filter, a, b, c } ---~ else ---~ h = { kind, default_filter, a, b, c } ---~ end ---~ hash[pattern] = h ---~ end ---~ local kind = h[1] ---~ if kind == 1 then ---~ return h[2](root,h[2],h[4]) ---~ elseif kind == 2 then ---~ return h[2](root,h[2],h[3]) ---~ else ---~ return h[2](root,pattern) ---~ end ---~ end - ---[[ldx-- -<p>The following functions collect elements and texts.</p> ---ldx]]-- - --- still somewhat bugged - -function xml.collect_elements(root, pattern, ignorespaces) - local rr, dd = { }, { } - traverse(root, lpath(pattern), function(r,d,k) - local dk = d and d[k] - if dk then - if ignorespaces and type(dk) == "string" and dk:find("[^%S]") then - -- ignore - else - local n = #rr+1 - rr[n], dd[n] = r, dk - end - end - end) - return dd, rr -end - -function xml.collect_texts(root, pattern, flatten) - local t = { } -- no r collector - traverse(root, lpath(pattern), function(r,d,k) - if d then - local ek = d[k] - local tx = ek and ek.dt - if flatten then - if tx then - t[#t+1] = xml.tostring(tx) or "" - else - t[#t+1] = "" -- hm - end - else - t[#t+1] = tx or "" - end - else - t[#t+1] = "" -- hm - end - end) - return t -end - -function xml.collect_tags(root, pattern, nonamespace) - local t = { } - xml.traverse(root, lpath(pattern), function(r,d,k) - local dk = d and d[k] - if dk and type(dk) == "table" then - local ns, tg = e.ns, e.tg - if nonamespace then - t[#t+1] = tg -- if needed we can return an extra table - elseif ns == "" then - t[#t+1] = tg - else - t[#t+1] = ns .. ":" .. tg - end - end - end) - return #t > 0 and {} -end - ---[[ldx-- -<p>Often using an iterators looks nicer in the code than passing handler -functions. The <l n='lua'/> book describes how to use coroutines for that -purpose (<url href='http://www.lua.org/pil/9.3.html'/>). This permits -code like:</p> - -<typing> -for r, d, k in xml.elements(xml.load('text.xml'),"title") do - print(d[k]) -end -</typing> - -<p>Which will print all the titles in the document. The iterator variant takes -1.5 times the runtime of the function variant which is due to the overhead in -creating the wrapper. So, instead of:</p> - -<typing> -function xml.filters.first(root,pattern) - for rt,dt,dk in xml.elements(root,pattern) - return dt and dt[dk], rt, dt, dk - end - return nil, nil, nil, nil -end -</typing> - -<p>We use the function variants in the filters.</p> ---ldx]]-- - -local wrap, yield = coroutine.wrap, coroutine.yield - -function xml.elements(root,pattern,reverse) - return wrap(function() traverse(root, lpath(pattern), yield, reverse) end) -end - -function xml.elements_only(root,pattern,reverse) - return wrap(function() traverse(root, lpath(pattern), function(r,d,k) yield(d[k]) end, reverse) end) -end - -function xml.each_element(root, pattern, handle, reverse) - local ok - traverse(root, lpath(pattern), function(r,d,k) ok = true handle(r,d,k) end, reverse) - return ok -end - ---~ todo: ---~ ---~ function xml.process_elements(root, pattern, handle) ---~ traverse(root, lpath(pattern), fnc, nil, nil, nil, handle) -> fnc gets r, d, k and handle (...) passed - -function xml.process_elements(root, pattern, handle) - traverse(root, lpath(pattern), function(r,d,k) - local dkdt = d[k].dt - if dkdt then - for i=1,#dkdt do - local v = dkdt[i] - if v.tg then handle(v) end - end - end - end) -end - -function xml.process_attributes(root, pattern, handle) - traverse(root, lpath(pattern), function(r,d,k) - local ek = d[k] - local a = ek.at or { } - handle(a) - if next(a) then -- next is faster than type (and >0 test) - ek.at = a - else - ek.at = nil - end - end) -end - ---[[ldx-- -<p>We've now arrives at the functions that manipulate the tree.</p> ---ldx]]-- - -function xml.inject_element(root, pattern, element, prepend) - if root and element then - local matches, collect = { }, nil - if type(element) == "string" then - element = convert(element,true) - end - if element then - collect = function(r,d,k) matches[#matches+1] = { r, d, k, element } end - traverse(root, lpath(pattern), collect) - for i=1,#matches do - local m = matches[i] - local r, d, k, element, edt = m[1], m[2], m[3], m[4], nil - if element.ri then - element = element.dt[element.ri].dt - else - element = element.dt - end - if r.ri then - edt = r.dt[r.ri].dt - else - edt = d and d[k] and d[k].dt - end - if edt then - local be, af - if prepend then - be, af = xml.copy(element), edt - else - be, af = edt, xml.copy(element) - end - for i=1,#af do - be[#be+1] = af[i] - end - if r.ri then - r.dt[r.ri].dt = be - else - d[k].dt = be - end - else - -- r.dt = element.dt -- todo - end - end - end - end -end - --- todo: copy ! - -function xml.insert_element(root, pattern, element, before) -- todo: element als functie - if root and element then - if pattern == "/" then - xml.inject_element(root, pattern, element, before) - else - local matches, collect = { }, nil - if type(element) == "string" then - element = convert(element,true) - end - if element and element.ri then - element = element.dt[element.ri] - end - if element then - collect = function(r,d,k) matches[#matches+1] = { r, d, k, element } end - traverse(root, lpath(pattern), collect) - for i=#matches,1,-1 do - local m = matches[i] - local r, d, k, element = m[1], m[2], m[3], m[4] - if not before then k = k + 1 end - if element.tg then - insert(d,k,element) -- untested ---~ elseif element.dt then ---~ for _,v in ipairs(element.dt) do -- i added ---~ insert(d,k,v) ---~ k = k + 1 ---~ end ---~ end - else - local edt = element.dt - if edt then - for i=1,#edt do - insert(d,k,edt[i]) - k = k + 1 - end - end - end - end - end - end - end -end - -xml.insert_element_after = xml.insert_element -xml.insert_element_before = function(r,p,e) xml.insert_element(r,p,e,true) end -xml.inject_element_after = xml.inject_element -xml.inject_element_before = function(r,p,e) xml.inject_element(r,p,e,true) end - -function xml.delete_element(root, pattern) - local matches, deleted = { }, { } - local collect = function(r,d,k) matches[#matches+1] = { r, d, k } end - traverse(root, lpath(pattern), collect) - for i=#matches,1,-1 do - local m = matches[i] - deleted[#deleted+1] = remove(m[2],m[3]) - end - return deleted -end - -function xml.replace_element(root, pattern, element) - if type(element) == "string" then - element = convert(element,true) - end - if element and element.ri then - element = element.dt[element.ri] - end - if element then - traverse(root, lpath(pattern), function(rm, d, k) - d[k] = element.dt -- maybe not clever enough - end) - end -end - -local function load_data(name) -- == io.loaddata - local f, data = io.open(name), "" - if f then - data = f:read("*all",'b') -- 'b' ? - f:close() - end - return data -end - -function xml.include(xmldata,pattern,attribute,recursive,loaddata) - -- parse="text" (default: xml), encoding="" (todo) - -- attribute = attribute or 'href' - pattern = pattern or 'include' - loaddata = loaddata or load_data - local function include(r,d,k) - local ek, name = d[k], nil - if not attribute or attribute == "" then - local ekdt = ek.dt - name = (type(ekdt) == "table" and ekdt[1]) or ekdt - end - if not name then - if ek.at then - for a in gmatch(attribute or "href","([^|]+)") do - name = ek.at[a] - if name then break end - end - end - end - local data = (name and name ~= "" and loaddata(name)) or "" - if data == "" then - xml.empty(d,k) - elseif ek.at["parse"] == "text" then -- for the moment hard coded - d[k] = xml.escaped(data) - else - -- data, no_root, strip_cm_and_dt, given_entities, parent_root (todo: entities) - local xi = xml.convert(data,nil,nil,xmldata.entities,xmldata) - if not xi then - xml.empty(d,k) - else - if recursive then - xml.include(xi,pattern,attribute,recursive,loaddata) - end - xml.assign(d,k,xi) - end - end - end - xml.each_element(xmldata, pattern, include) -end - -function xml.strip_whitespace(root, pattern, nolines) -- strips all leading and trailing space ! - traverse(root, lpath(pattern), function(r,d,k) - local dkdt = d[k].dt - if dkdt then -- can be optimized - local t = { } - for i=1,#dkdt do - local str = dkdt[i] - if type(str) == "string" then - if str == "" then - -- stripped - else - if nolines then - str = gsub(str,"[ \n\r\t]+"," ") - end - if str == "" then - -- stripped - else - t[#t+1] = str - end - end - else - t[#t+1] = str - end - end - d[k].dt = t - end - end) -end - -local function rename_space(root, oldspace, newspace) -- fast variant - local ndt = #root.dt - for i=1,ndt or 0 do - local e = root[i] - if type(e) == "table" then - if e.ns == oldspace then - e.ns = newspace - if e.rn then - e.rn = newspace - end - end - local edt = e.dt - if edt then - rename_space(edt, oldspace, newspace) - end - end - end -end - -xml.rename_space = rename_space - -function xml.remap_tag(root, pattern, newtg) - traverse(root, lpath(pattern), function(r,d,k) - d[k].tg = newtg - end) -end -function xml.remap_namespace(root, pattern, newns) - traverse(root, lpath(pattern), function(r,d,k) - d[k].ns = newns - end) -end -function xml.check_namespace(root, pattern, newns) - traverse(root, lpath(pattern), function(r,d,k) - local dk = d[k] - if (not dk.rn or dk.rn == "") and dk.ns == "" then - dk.rn = newns - end - end) -end -function xml.remap_name(root, pattern, newtg, newns, newrn) - traverse(root, lpath(pattern), function(r,d,k) - local dk = d[k] - dk.tg = newtg - dk.ns = newns - dk.rn = newrn - end) -end - -function xml.filters.found(root,pattern,check_content) - local found = false - traverse(root, lpath(pattern), function(r,d,k) - if check_content then - local dk = d and d[k] - found = dk and dk.dt and next(dk.dt) and true - else - found = true - end - return true - end) - return found -end - ---[[ldx-- -<p>Here are a few synonyms.</p> ---ldx]]-- - -xml.filters.position = xml.filters.index - -xml.count = xml.filters.count -xml.index = xml.filters.index -xml.position = xml.filters.index -xml.first = xml.filters.first -xml.last = xml.filters.last -xml.found = xml.filters.found - -xml.each = xml.each_element -xml.process = xml.process_element -xml.strip = xml.strip_whitespace -xml.collect = xml.collect_elements -xml.all = xml.collect_elements - -xml.insert = xml.insert_element_after -xml.inject = xml.inject_element_after -xml.after = xml.insert_element_after -xml.before = xml.insert_element_before -xml.delete = xml.delete_element -xml.replace = xml.replace_element - ---[[ldx-- -<p>The following helper functions best belong to the <t>lmxl-ini</t> -module. Some are here because we need then in the <t>mk</t> -document and other manuals, others came up when playing with -this module. Since this module is also used in <l n='mtxrun'/> we've -put them here instead of loading mode modules there then needed.</p> ---ldx]]-- - -function xml.gsub(t,old,new) - local dt = t.dt - if dt then - for k=1,#dt do - local v = dt[k] - if type(v) == "string" then - dt[k] = gsub(v,old,new) - else - xml.gsub(v,old,new) - end - end - end -end - -function xml.strip_leading_spaces(dk,d,k) -- cosmetic, for manual - if d and k and d[k-1] and type(d[k-1]) == "string" then - local s = d[k-1]:match("\n(%s+)") - xml.gsub(dk,"\n"..rep(" ",#s),"\n") - end -end - -function xml.serialize_path(root,lpath,handle) - local dk, r, d, k = xml.first(root,lpath) - dk = xml.copy(dk) - xml.strip_leading_spaces(dk,d,k) - xml.serialize(dk,handle) -end - ---~ xml.escapes = { ['&'] = '&', ['<'] = '<', ['>'] = '>', ['"'] = '"' } ---~ xml.unescapes = { } for k,v in pairs(xml.escapes) do xml.unescapes[v] = k end - ---~ function xml.escaped (str) return (gsub(str,"(.)" , xml.escapes )) end ---~ function xml.unescaped(str) return (gsub(str,"(&.-;)", xml.unescapes)) end ---~ function xml.cleansed (str) return (gsub(str,"<.->" , '' )) end -- "%b<>" - -local P, S, R, C, V, Cc, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cc, lpeg.Cs - --- 100 * 2500 * "oeps< oeps> oeps&" : gsub:lpeg|lpeg|lpeg --- --- 1021:0335:0287:0247 - --- 10 * 1000 * "oeps< oeps> oeps& asfjhalskfjh alskfjh alskfjh alskfjh ;al J;LSFDJ" --- --- 1559:0257:0288:0190 (last one suggested by roberto) - --- escaped = Cs((S("<&>") / xml.escapes + 1)^0) --- escaped = Cs((S("<")/"<" + S(">")/">" + S("&")/"&" + 1)^0) -local normal = (1 - S("<&>"))^0 -local special = P("<")/"<" + P(">")/">" + P("&")/"&" -local escaped = Cs(normal * (special * normal)^0) - --- 100 * 1000 * "oeps< oeps> oeps&" : gsub:lpeg == 0153:0280:0151:0080 (last one by roberto) - --- unescaped = Cs((S("<")/"<" + S(">")/">" + S("&")/"&" + 1)^0) --- unescaped = Cs((((P("&")/"") * (P("lt")/"<" + P("gt")/">" + P("amp")/"&") * (P(";")/"")) + 1)^0) -local normal = (1 - S"&")^0 -local special = P("<")/"<" + P(">")/">" + P("&")/"&" -local unescaped = Cs(normal * (special * normal)^0) - --- 100 * 5000 * "oeps <oeps bla='oeps' foo='bar'> oeps </oeps> oeps " : gsub:lpeg == 623:501 msec (short tags, less difference) - -local cleansed = Cs(((P("<") * (1-P(">"))^0 * P(">"))/"" + 1)^0) - -function xml.escaped (str) return escaped :match(str) end -function xml.unescaped(str) return unescaped:match(str) end -function xml.cleansed (str) return cleansed :match(str) end - -function xml.join(t,separator,lastseparator) - if #t > 0 then - local result = { } - for k,v in pairs(t) do - result[k] = xml.tostring(v) - end - if lastseparator then - return concat(result,separator or "",1,#result-1) .. (lastseparator or "") .. result[#result] - else - return concat(result,separator) - end - else - return "" - end -end - -function xml.statistics() - return { - lpathcalls = lpathcalls, - lpathcached = lpathcached, - } -end - --- xml.set_text_cleanup(xml.show_text_entities) --- xml.set_text_cleanup(xml.resolve_text_entities) - ---~ xml.lshow("/../../../a/(b|c)[@d='e']/f") ---~ xml.lshow("/../../../a/!(b|c)[@d='e']/f") ---~ xml.lshow("/../../../a/!b[@d!='e']/f") ---~ xml.lshow("a[text()=='b']") ---~ xml.lshow("a['b']") ---~ xml.lshow("(x|y)[b]") ---~ xml.lshow("/aaa[bbb]") ---~ xml.lshow("aaa[bbb][ccc][ddd]") ---~ xml.lshow("aaa['xxx']") ---~ xml.lshow("a[b|c]") ---~ xml.lshow("whatever['crap']") ---~ xml.lshow("whatever[count(whocares) > 1 and oeps and 'oeps']") ---~ xml.lshow("whatever[whocares]") ---~ xml.lshow("whatever[@whocares]") ---~ xml.lshow("whatever[count(whocares) > 1]") ---~ xml.lshow("a(@b)") ---~ xml.lshow("a[b|c|d]") ---~ xml.lshow("a[b and @b]") ---~ xml.lshow("a[b]") ---~ xml.lshow("a[b and b]") ---~ xml.lshow("a[b][c]") ---~ xml.lshow("a[b][1]") ---~ xml.lshow("a[1]") ---~ xml.lshow("a[count(b)!=0][count(b)!=0]") ---~ xml.lshow("a[not(count(b))][not(count(b))]") ---~ xml.lshow("a[count(b)!=0][count(b)!=0]") - ---~ x = xml.convert([[ ---~ <a> ---~ <b n='01'>01</b> ---~ <b n='02'>02</b> ---~ <b n='03'>03</b> ---~ <b n='04'>OK</b> ---~ <b n='05'>05</b> ---~ <b n='06'>06</b> ---~ <b n='07'>ALSO OK</b> ---~ </a> ---~ ]]) - ---~ xml.settrace("lpath",true) - ---~ xml.xshow(xml.first(x,"b[position() > 2 and position() < 5 and text() == 'ok']")) ---~ xml.xshow(xml.first(x,"b[position() > 2 and position() < 5 and text() == upper('ok')]")) ---~ xml.xshow(xml.first(x,"b[@n=='03' or @n=='08']")) ---~ xml.xshow(xml.all (x,"b[number(@n)>2 and number(@n)<6]")) ---~ xml.xshow(xml.first(x,"b[find(text(),'ALSO')]")) - ---~ str = [[ ---~ <?xml version="1.0" encoding="utf-8"?> ---~ <story line='mojca'> ---~ <windows>my secret</mouse> ---~ </story> ---~ ]] - ---~ x = xml.convert([[ ---~ <a><b n='01'>01</b><b n='02'>02</b><x>xx</x><b n='03'>03</b><b n='04'>OK</b></a> ---~ ]]) ---~ xml.xshow(xml.first(x,"b[tag(2) == 'x']")) ---~ xml.xshow(xml.first(x,"b[tag(1) == 'x']")) ---~ xml.xshow(xml.first(x,"b[tag(-1) == 'x']")) ---~ xml.xshow(xml.first(x,"b[tag(-2) == 'x']")) - ---~ print(xml.filter(x,"b/tag(2)")) ---~ print(xml.filter(x,"b/tag(1)")) diff --git a/tex/context/base/lxml-sor.lua b/tex/context/base/lxml-sor.lua index 5ef94cbf2..f2bb756f2 100644 --- a/tex/context/base/lxml-sor.lua +++ b/tex/context/base/lxml-sor.lua @@ -122,18 +122,16 @@ function lxml.sorters.flush(name,setup) if results and next(results) then for key, result in next, results do local tag, data = result.tag, result.data ---~ tex.sprint(ctxcatcodes,format("key=%s\\quad tag=%s\\blank",key,tag)) for d=1,#data do local dr = data[d] - texsprint(ctxcatcodes,format("\\xmls{%s}{%s}",dr.entry,setup)) + texsprint(ctxcatcodes,"\\xmlw{",setup,"}{",dr.entry,"}") end ---~ tex.sprint(ctxcatcodes,format("\\blank")) end else local entries = list and list.entries if entries then for i=1,#entries do - texsprint(ctxcatcodes,format("\\xmls{%s}{%s}",entries[i][1],setup)) + texsprint(ctxcatcodes,"\\xmlw{",setup,"}{",entries[i][1],"}") end end end diff --git a/tex/context/base/lxml-tab.lua b/tex/context/base/lxml-tab.lua index b49bf0ecb..faafa4462 100644 --- a/tex/context/base/lxml-tab.lua +++ b/tex/context/base/lxml-tab.lua @@ -10,6 +10,8 @@ if not modules then modules = { } end modules ['lxml-tab'] = { -- stripping spaces from e.g. cont-en.xml saves .2 sec runtime so it's not worth the -- trouble +local trace_entities = false trackers.register("xml.entities", function(v) trace_entities = v end) + --[[ldx-- <p>The parser used here is inspired by the variant discussed in the lua book, but handles comment and processing instructions, has a different structure, provides @@ -17,18 +19,6 @@ parent access; a first version used different trickery but was less optimized to went this route. First we had a find based parser, now we have an <l n='lpeg'/> based one. The find based parser can be found in l-xml-edu.lua along with other older code.</p> -<p>Expecially the lpath code is experimental, we will support some of xpath, but -only things that make sense for us; as compensation it is possible to hook in your -own functions. Apart from preprocessing content for <l n='context'/> we also need -this module for process management, like handling <l n='ctx'/> and <l n='rlx'/> -files.</p> - -<typing> -a/b/c /*/c -a/b/c/first() a/b/c/last() a/b/c/index(n) a/b/c/index(-n) -a/b/c/text() a/b/c/text(1) a/b/c/text(-1) a/b/c/text(n) -</typing> - <p>Beware, the interface may change. For instance at, ns, tg, dt may get more verbose names. Once the code is stable we will also remove some tracing and optimize the code.</p> @@ -39,26 +29,9 @@ xml = xml or { } --~ local xml = xml local concat, remove, insert = table.concat, table.remove, table.insert -local type, next, setmetatable, getmetatable = type, next, setmetatable, getmetatable +local type, next, setmetatable, getmetatable, tonumber = type, next, setmetatable, getmetatable, tonumber local format, lower, find = string.format, string.lower, string.find - ---[[ldx-- -<p>This module can be used stand alone but also inside <l n='mkiv'/> in -which case it hooks into the tracker code. Therefore we provide a few -functions that set the tracers.</p> ---ldx]]-- - -local trace_remap = false - -if trackers then - trackers.register("xml.remap", function(v) trace_remap = v end) -end - -function xml.settrace(str,value) - if str == "remap" then - trace_remap = value or false - end -end +local utfchar = unicode.utf8.char --[[ldx-- <p>First a hack to enable namespace resolving. A namespace is characterized by @@ -165,17 +138,16 @@ element.</p> </typing> --ldx]]-- -xml.strip_cm_and_dt = false -- an extra global flag, in case we have many includes - -- not just one big nested table capture (lpeg overflow) local nsremap, resolvens = xml.xmlns, xml.resolvens local stack, top, dt, at, xmlns, errorstr, entities = {}, {}, {}, {}, {}, nil, {} +local strip, cleanup, utfize, resolve = false, false, false, false -local mt = { __tostring = xml.text } +local mt = { } -function initialize_mt(root) +function initialize_mt(root) -- we will make a xml.new that then sets the mt as field mt = { __tostring = xml.text, __index = root } end @@ -187,13 +159,6 @@ function xml.check_error(top,toclose) return "" end -local strip = false -local cleanup = false - -function xml.set_text_cleanup(fnc) - cleanup = fnc -end - local function add_attribute(namespace,tag,value) if cleanup and #value > 0 then value = cleanup(value) -- new @@ -209,6 +174,22 @@ local function add_attribute(namespace,tag,value) end end +local function add_empty(spacing, namespace, tag) + if #spacing > 0 then + dt[#dt+1] = spacing + end + local resolved = (namespace == "" and xmlns[#xmlns]) or nsremap[namespace] or namespace + top = stack[#stack] + dt = top.dt + local t = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = top } + dt[#dt+1] = t + setmetatable(t, mt) + if at.xmlns then + remove(xmlns) + end + at = { } +end + local function add_begin(spacing, namespace, tag) if #spacing > 0 then dt[#dt+1] = spacing @@ -234,28 +215,12 @@ local function add_end(spacing, namespace, tag) end dt = top.dt dt[#dt+1] = toclose - dt[0] = top + -- dt[0] = top -- nasty circular reference when serializing table if toclose.at.xmlns then remove(xmlns) end end -local function add_empty(spacing, namespace, tag) - if #spacing > 0 then - dt[#dt+1] = spacing - end - local resolved = (namespace == "" and xmlns[#xmlns]) or nsremap[namespace] or namespace - top = stack[#stack] - dt = top.dt - local t = { ns=namespace or "", rn=resolved, tg=tag, at=at, dt={}, __p__ = top } - dt[#dt+1] = t - setmetatable(t, mt) - if at.xmlns then - remove(xmlns) - end - at = { } -end - local function add_text(text) if cleanup and #text > 0 then dt[#dt+1] = cleanup(text) @@ -279,7 +244,109 @@ local function set_message(txt) errorstr = "garbage at the end of the file: " .. gsub(txt,"([ \n\r\t]*)","") end -local P, S, R, C, V = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V +local reported_attribute_errors = { } + +local function attribute_value_error(str) + if not reported_attribute_errors[str] then + logs.report("xml","invalid attribute value: %q",str) + reported_attribute_errors[str] = true + at._error_ = str + end + return str +end +local function attribute_specification_error(str) + if not reported_attribute_errors[str] then + logs.report("xml","invalid attribute specification: %q",str) + reported_attribute_errors[str] = true + at._error_ = str + end + return str +end + +local dcache, hcache, acache = { }, { }, { } + +function xml.unknown_dec_entity_format(str) return format("&%s;", str) end +function xml.unknown_hex_entity_format(str) return format("&#x%s;",str) end +function xml.unknown_any_entity_format(str) return format("&%s;", str) end + +local function handle_hex_entity(str) + local h = hcache[str] + if not h then + if utfize then + local n = tonumber(str,16) + h = (n and utfchar(n)) or xml.unknown_hex_entity_format(str) or "" + if not n then + logs.report("xml","utfize, ignoring hex entity &#x%s;",str) + elseif trace_entities then + logs.report("xml","utfize, converting hex entity &#x%s; into %s",str,c) + end + else + if trace_entities then + logs.report("xml","found entity &#x%s;",str) + end + h = "&#" .. str .. ";" + end + hcache[str] = h + end + return h +end +local function handle_dec_entity(str) + local d = dcache[str] + if not d then + if utfize then + local n = tonumber(str) + d = (n and utfchar(n)) or xml.unknown_dec_entity_format(str) or "" + if not n then + logs.report("xml","utfize, ignoring dec entity &#%s;",str) + elseif trace_entities then + logs.report("xml","utfize, converting dec entity &#%s; into %s",str,c) + end + else + if trace_entities then + logs.report("xml","found entity &#%s;",str) + end + d = "&" .. str .. ";" + end + dcache[str] = d + end + return d +end +local function handle_any_entity(str) + if resolve then + local a = entities[str] -- per instance ! + if not a then + a = acache[str] + if not a then + if trace_entities then + logs.report("xml","ignoring entity &%s;",str) + else + -- can be defined in a global mapper and intercepted elsewhere + -- as happens in lxml-tex.lua + end + a = xml.unknown_any_entity_format(str) or "" + acache[str] = a + end + elseif trace_entities then + if not acache[str] then + logs.report("xml","converting entity &%s; into %s",str,r) + acache[str] = a + end + end + return a + else + local a = acache[str] + if not a then + if trace_entities then + logs.report("xml","found entity &%s;",str) + end + a = "&" .. str .. ";" + acache[str] = a + end + return a + end +end + +local P, S, R, C, V, Cs = lpeg.P, lpeg.S, lpeg.R, lpeg.C, lpeg.V, lpeg.Cs local space = S(' \r\n\t') local open = P('<') @@ -289,6 +356,7 @@ local dquote = S('"') local equal = P('=') local slash = P('/') local colon = P(':') +local semicolon = P(';') local ampersand = P('&') local valid = R('az', 'AZ', '09') + S('_-.') local name_yes = C(valid^1) * colon * C(valid^1) @@ -299,15 +367,36 @@ local utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') -- no capture local spacing = C(space^0) -local justtext = C((1-open)^1) + +local entitycontent = (1-open-semicolon)^0 +local entity = ampersand/"" * ( + P("#")/"" * ( + P("x")/"" * (entitycontent/handle_hex_entity) + + (entitycontent/handle_dec_entity) + ) + (entitycontent/handle_any_entity) + ) * (semicolon/"") + +local text_unparsed = C((1-open)^1) +local text_parsed = Cs(((1-open-ampersand)^1 + entity)^1) + local somespace = space^1 local optionalspace = space^0 local value = (squote * C((1 - squote)^0) * squote) + (dquote * C((1 - dquote)^0) * dquote) -- ampersand and < also invalid in value -local attribute = (somespace * name * optionalspace * equal * optionalspace * value) / add_attribute -local attributes = attribute^0 -local text = justtext / add_text +local whatever = space * name * optionalspace * equal +local wrongvalue = C(P(1-whatever-close)^1 + P(1-close)^1) / attribute_value_error + +local attributevalue = value + wrongvalue + +local attribute = (somespace * name * optionalspace * equal * optionalspace * attributevalue) / add_attribute +----- attributes = (attribute)^0 + +local endofattributes = slash * close + close -- recovery of flacky html +local attributes = (attribute + somespace^-1 * (((1-endofattributes)^1)/attribute_specification_error))^0 + +local parsedtext = text_parsed / add_text +local unparsedtext = text_unparsed / add_text local balanced = P { "[" * ((1 - S"[]") + V(1))^0 * "]" } -- taken from lpeg manual, () example local emptyelement = (spacing * open * name * attributes * optionalspace * slash * close) / add_empty @@ -360,25 +449,34 @@ local doctype = (spacing * begindoctype * somedoctype * enddoct -- local cdata = (lpeg.Cc("@cd@") * spacing * begincdata * somecdata * endcdata ) / add_special -- local doctype = (lpeg.Cc("@dt@") * spacing * begindoctype * somedoctype * enddoctype ) / add_special -local trailer = space^0 * (justtext/set_message)^0 +local trailer = space^0 * (text_unparsed/set_message)^0 -- comment + emptyelement + text + cdata + instruction + V("parent"), -- 6.5 seconds on 40 MB database file -- text + comment + emptyelement + cdata + instruction + V("parent"), -- 5.8 -- text + V("parent") + emptyelement + comment + cdata + instruction, -- 5.5 -local grammar = P { "preamble", +local grammar_parsed_text = P { "preamble", preamble = utfbom^0 * instruction^0 * (doctype + comment + instruction)^0 * V("parent") * trailer, parent = beginelement * V("children")^0 * endelement, - children = text + V("parent") + emptyelement + comment + cdata + instruction, + children = parsedtext + V("parent") + emptyelement + comment + cdata + instruction, } --- todo: xml.new + properties like entities and strip and such (store in root) +local grammar_unparsed_text = P { "preamble", + preamble = utfbom^0 * instruction^0 * (doctype + comment + instruction)^0 * V("parent") * trailer, + parent = beginelement * V("children")^0 * endelement, + children = unparsedtext + V("parent") + emptyelement + comment + cdata + instruction, +} -function xml.convert(data, no_root, strip_cm_and_dt, given_entities, parent_root) -- maybe use table met k/v (given_entities may disapear) - strip = strip_cm_and_dt or xml.strip_cm_and_dt - stack, top, at, xmlns, errorstr, result, entities = {}, {}, {}, {}, nil, nil, given_entities or {} - if parent_root then - mt = getmetatable(parent_root) +local function xmlconvert(data, settings) + settings = settings or { } -- no_root strip_cm_and_dt given_entities parent_root error_handler + strip = settings.strip_cm_and_dt + utfize = settings.utfize_entities + resolve = settings.resolve_entities + cleanup = settings.text_cleanup + stack, top, at, xmlns, errorstr, result, entities = {}, {}, {}, {}, nil, nil, settings.entities or {} + reported_attribute_errors = { } + if settings.parent_root then + mt = getmetatable(settings.parent_root) else initialize_mt(top) end @@ -387,20 +485,36 @@ function xml.convert(data, no_root, strip_cm_and_dt, given_entities, parent_root dt = top.dt if not data or data == "" then errorstr = "empty xml file" - elseif not grammar:match(data) then - errorstr = "invalid xml file" + elseif utfize or resolve then + if grammar_parsed_text:match(data) then + errorstr = "" + else + errorstr = "invalid xml file - parsed text" + end else - errorstr = "" + if grammar_unparsed_text:match(data) then + errorstr = "" + else + errorstr = "invalid xml file - unparsed text" + end end if errorstr and errorstr ~= "" then - result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={}, er = true } }, error = true } + result = { dt = { { ns = "", tg = "error", dt = { errorstr }, at={}, er = true } } } setmetatable(stack, mt) - if xml.error_handler then xml.error_handler("load",errorstr) end + local error_handler = settings.error_handler + if error_handler == false then + -- no error message + else + error_handler = error_handler or xml.error_handler + if error_handler then + xml.error_handler("load",errorstr) + end + end else result = stack[1] end - if not no_root then - result = { special = true, ns = "", tg = '@rt@', dt = result.dt, at={}, entities = entities } + if not settings.no_root then + result = { special = true, ns = "", tg = '@rt@', dt = result.dt, at={}, entities = entities, settings = settings } setmetatable(result, mt) local rdt = result.dt for k=1,#rdt do @@ -411,9 +525,14 @@ function xml.convert(data, no_root, strip_cm_and_dt, given_entities, parent_root end end end + if errorstr and errorstr ~= "" then + result.error = true + end return result end +xml.convert = xmlconvert + --[[ldx-- <p>Packaging data in an xml like table is done with the following function. Maybe it will go away (when not used).</p> @@ -446,16 +565,16 @@ function xml.load(filename) if type(filename) == "string" then local f = io.open(filename,'r') if f then - local root = xml.convert(f:read("*all")) + local root = xmlconvert(f:read("*all")) f:close() return root else - return xml.convert("") + return xmlconvert("") end elseif filename then -- filehandle - return xml.convert(filename:read("*all")) + return xmlconvert(filename:read("*all")) else - return xml.convert("") + return xmlconvert("") end end @@ -464,9 +583,11 @@ end valid trees, which is what the next function does.</p> --ldx]]-- +local no_root = { no_root = true } + function xml.toxml(data) if type(data) == "string" then - local root = { xml.convert(data,true) } + local root = { xmlconvert(data,no_root) } return (#root > 1 and root) or root[1] else return data @@ -511,222 +632,305 @@ alternative.</p> -- todo: add <?xml version='1.0' standalone='yes'?> when not present -local fallbackhandle = (tex and tex.sprint) or io.write - -local serializer - -function xml.setserializer(f) - serializer = f -end - -local function serialize(e, handle, textconverter, attributeconverter, specialconverter, nocommands) - if not e then - return - elseif not nocommands then - local ec = e.command - if ec ~= nil then -- we can have all kind of types - if e.special then - local etg, edt = e.tg, e.dt - local spc = specialconverter and specialconverter[etg] - if spc then - local result = spc(edt[1]) - if result then - handle(result) - return - else - -- no need to handle any further - end - end - end - if serializer then - serializer(e,ec) - return +function xml.checkbom(root) -- can be made faster + if root.ri then + local dt, found = root.dt, false + for k=1,#dt do + local v = dt[k] + if type(v) == "table" and v.special and v.tg == "@pi" and find(v.dt,"xml.*version=") then + found = true + break end end + if not found then + insert(dt, 1, { special=true, ns="", tg="@pi@", dt = { "xml version='1.0' standalone='yes'"} } ) + insert(dt, 2, "\n" ) + end end - handle = handle or fallbackhandle - local etg = e.tg - if etg then - if e.special then - local edt = e.dt - local spc = specialconverter and specialconverter[etg] - if spc then - local result = spc(edt[1]) - if result then - handle(result) +end + +--[[ldx-- +<p>At the cost of some 25% runtime overhead you can first convert the tree to a string +and then handle the lot.</p> +--ldx]]-- + +-- new experimental reorganized serialize + +local function verbose_element(e,handlers) + local handle = handlers.handle + local serialize = handlers.serialize + local ens, etg, eat, edt, ern = e.ns, e.tg, e.at, e.dt, e.rn + local ats = eat and next(eat) and { } + if ats then + for k,v in next, eat do + ats[#ats+1] = format('%s=%q',k,v) + end + end + if ern and trace_remap and ern ~= ens then + ens = ern + end + if ens ~= "" then + if edt and #edt > 0 then + if ats then + handle("<",ens,":",etg," ",concat(ats," "),">") + else + handle("<",ens,":",etg,">") + end + for i=1,#edt do + local e = edt[i] + if type(e) == "string" then + handle(e) else - -- no need to handle any further + serialize(e,handlers) end - elseif etg == "@pi@" then - -- handle(format("<?%s?>",edt[1])) - handle("<?" .. edt[1] .. "?>") - elseif etg == "@cm@" then - -- handle(format("<!--%s-->",edt[1])) - handle("<!--" .. edt[1] .. "-->") - elseif etg == "@cd@" then - -- handle(format("<![CDATA[%s]]>",edt[1])) - handle("<![CDATA[" .. edt[1] .. "]]>") - elseif etg == "@dt@" then - -- handle(format("<!DOCTYPE %s>",edt[1])) - handle("<!DOCTYPE " .. edt[1] .. ">") - elseif etg == "@rt@" then - serialize(edt,handle,textconverter,attributeconverter,specialconverter,nocommands) end + handle("</",ens,":",etg,">") else - local ens, eat, edt, ern = e.ns, e.at, e.dt, e.rn - local ats = eat and next(eat) and { } -- type test maybe faster if ats then - if attributeconverter then - for k,v in next, eat do - ats[#ats+1] = format('%s=%q',k,attributeconverter(v)) - end - else - for k,v in next, eat do - ats[#ats+1] = format('%s=%q',k,v) - end - end + handle("<",ens,":",etg," ",concat(ats," "),"/>") + else + handle("<",ens,":",etg,"/>") end - if ern and trace_remap and ern ~= ens then - ens = ern + end + else + if edt and #edt > 0 then + if ats then + handle("<",etg," ",concat(ats," "),">") + else + handle("<",etg,">") end - if ens ~= "" then - if edt and #edt > 0 then - if ats then - -- handle(format("<%s:%s %s>",ens,etg,concat(ats," "))) - handle("<" .. ens .. ":" .. etg .. " " .. concat(ats," ") .. ">") - else - -- handle(format("<%s:%s>",ens,etg)) - handle("<" .. ens .. ":" .. etg .. ">") - end - for i=1,#edt do - local e = edt[i] - if type(e) == "string" then - if textconverter then - handle(textconverter(e)) - else - handle(e) - end - else - serialize(e,handle,textconverter,attributeconverter,specialconverter,nocommands) - end - end - -- handle(format("</%s:%s>",ens,etg)) - handle("</" .. ens .. ":" .. etg .. ">") + for i=1,#edt do + local ei = edt[i] + if type(ei) == "string" then + handle(ei) else - if ats then - -- handle(format("<%s:%s %s/>",ens,etg,concat(ats," "))) - handle("<" .. ens .. ":" .. etg .. " " .. concat(ats," ") .. "/>") - else - -- handle(format("<%s:%s/>",ens,etg)) - handle("<" .. ens .. ":" .. etg .. "/>") - end + serialize(ei,handlers) end + end + handle("</",etg,">") + else + if ats then + handle("<",etg," ",concat(ats," "),"/>") else - if edt and #edt > 0 then - if ats then - -- handle(format("<%s %s>",etg,concat(ats," "))) - handle("<" .. etg .. " " .. concat(ats," ") .. ">") - else - -- handle(format("<%s>",etg)) - handle("<" .. etg .. ">") - end - for i=1,#edt do - local ei = edt[i] - if type(ei) == "string" then - if textconverter then - handle(textconverter(ei)) - else - handle(ei) - end - else - serialize(ei,handle,textconverter,attributeconverter,specialconverter,nocommands) - end - end - -- handle(format("</%s>",etg)) - handle("</" .. etg .. ">") - else - if ats then - -- handle(format("<%s %s/>",etg,concat(ats," "))) - handle("<" .. etg .. " " .. concat(ats," ") .. "/>") - else - -- handle(format("<%s/>",etg)) - handle("<" .. etg .. "/>") - end - end + handle("<",etg,"/>") end end - elseif type(e) == "string" then - if textconverter then - handle(textconverter(e)) + end +end + +local function verbose_pi(e,handlers) + handlers.handle("<?",e.dt[1],"?>") +end + +local function verbose_comment(e,handlers) + handlers.handle("<!--",e.dt[1],"-->") +end + +local function verbose_cdata(e,handlers) + handlers.handle("<![CDATA[", e.dt[1],"]]>") +end + +local function verbose_doctype(e,handlers) + handlers.handle("<!DOCTYPE ",e.dt[1],">") +end + +local function verbose_root(e,handlers) + handlers.serialize(e.dt,handlers) +end + +local function verbose_text(e,handlers) + handlers.handle(e) +end + +local function verbose_document(e,handlers) + local serialize = handlers.serialize + local functions = handlers.functions + for i=1,#e do + local ei = e[i] + if type(ei) == "string" then + functions["@tx@"](ei,handlers) else - handle(e) + serialize(ei,handlers) end - else - for i=1,#e do - local ei = e[i] - if type(ei) == "string" then - if textconverter then - handle(textconverter(ei)) - else - handle(ei) - end - else - serialize(ei,handle,textconverter,attributeconverter,specialconverter,nocommands) - end + end +end + +local function serialize(e,handlers,...) + local initialize = handlers.initialize + local finalize = handlers.finalize + local functions = handlers.functions + if initialize then + local state = initialize(...) + if not state == true then + return state end end + local etg = e.tg + if etg then + (functions[etg] or functions["@el@"])(e,handlers) + -- elseif type(e) == "string" then + -- functions["@tx@"](e,handlers) + else + functions["@dc@"](e,handlers) + end + if finalize then + return finalize() + end end -xml.serialize = serialize +local function xserialize(e,handlers) + local functions = handlers.functions + local etg = e.tg + if etg then + (functions[etg] or functions["@el@"])(e,handlers) + -- elseif type(e) == "string" then + -- functions["@tx@"](e,handlers) + else + functions["@dc@"](e,handlers) + end +end -function xml.checkbom(root) -- can be made faster - if root.ri then - local dt, found = root.dt, false - for k=1,#dt do - local v = dt[k] - if type(v) == "table" and v.special and v.tg == "@pi" and find(v.dt,"xml.*version=") then - found = true - break +local handlers = { } + +local function newhandlers(settings) + local t = table.copy(handlers.verbose or { }) -- merge + if settings then + for k,v in next, settings do + if type(v) == "table" then + tk = t[k] if not tk then tk = { } t[k] = tk end + for kk,vv in next, v do + tk[kk] = vv + end + else + t[k] = v end end - if not found then - insert(dt, 1, { special=true, ns="", tg="@pi@", dt = { "xml version='1.0' standalone='yes'"} } ) - insert(dt, 2, "\n" ) + if settings.name then + handlers[settings.name] = t end end + return t +end + +local nofunction = function() end + +function xml.sethandlersfunction(handler,name,fnc) + handler.functions[name] = fnc or nofunction +end + +function xml.gethandlersfunction(handler,name) + return handler.functions[name] end +function xml.gethandlers(name) + return handlers[name] +end + +newhandlers { + name = "verbose", + initialize = false, -- faster than nil and mt lookup + finalize = false, -- faster than nil and mt lookup + serialize = xserialize, + handle = print, + functions = { + ["@dc@"] = verbose_document, + ["@dt@"] = verbose_doctype, + ["@rt@"] = verbose_root, + ["@el@"] = verbose_element, + ["@pi@"] = verbose_pi, + ["@cm@"] = verbose_comment, + ["@cd@"] = verbose_cdata, + ["@tx@"] = verbose_text, + } +} + --[[ldx-- -<p>At the cost of some 25% runtime overhead you can first convert the tree to a string -and then handle the lot.</p> +<p>How you deal with saving data depends on your preferences. For a 40 MB database +file the timing on a 2.3 Core Duo are as follows (time in seconds):</p> + +<lines> +1.3 : load data from file to string +6.1 : convert string into tree +5.3 : saving in file using xmlsave +6.8 : converting to string using xml.tostring +3.6 : saving converted string in file +</lines> + +<p>Beware, these were timing with the old routine but measurements will not be that +much different I guess.</p> --ldx]]-- -function xml.tostring(root) -- 25% overhead due to collecting +-- maybe this will move to lxml-xml + +local result + +local xmlfilehandler = newhandlers { + name = "file", + initialize = function(name) result = io.open(name,"wb") return result end, + finalize = function() result:close() return true end, + handle = function(...) result:write(...) end, +} + +-- no checking on writeability here but not faster either +-- +-- local xmlfilehandler = newhandlers { +-- initialize = function(name) io.output(name,"wb") return true end, +-- finalize = function() io.close() return true end, +-- handle = io.write, +-- } + + +function xml.save(root,name) + serialize(root,xmlfilehandler,name) +end + +local result + +local xmlstringhandler = newhandlers { + name = "string", + initialize = function() result = { } return result end, + finalize = function() return concat(result) end, + handle = function(...) result[#result+1] = concat { ... } end +} + +local function xmltostring(root) -- 25% overhead due to collecting if root then if type(root) == 'string' then return root - elseif next(root) then -- next is faster than type (and >0 test) - local result = { } - serialize(root,function(s) result[#result+1] = s end) -- brrr, slow (direct printing is faster) - return concat(result,"") + else -- if next(root) then -- next is faster than type (and >0 test) + return serialize(root,xmlstringhandler) or "" end end return "" end +local function xmltext(root) -- inline + return (root and xmltostring(root)) or "" +end + +function initialize_mt(root) + mt = { __tostring = xmltext, __index = root } +end + +xml.defaulthandlers = handlers +xml.newhandlers = newhandlers +xml.serialize = serialize +xml.tostring = xmltostring +xml.text = xmltext + --[[ldx-- <p>The next function operated on the content only and needs a handle function that accepts a string.</p> --ldx]]-- -function xml.string(e,handle) +local function xmlstring(e,handle) if not handle or (e.special and e.tg ~= "@rt@") then -- nothing elseif e.tg then local edt = e.dt if edt then for i=1,#edt do - xml.string(edt[i],handle) + xmlstring(edt[i],handle) end end else @@ -734,33 +938,16 @@ function xml.string(e,handle) end end ---[[ldx-- -<p>How you deal with saving data depends on your preferences. For a 40 MB database -file the timing on a 2.3 Core Duo are as follows (time in seconds):</p> - -<lines> -1.3 : load data from file to string -6.1 : convert string into tree -5.3 : saving in file using xmlsave -6.8 : converting to string using xml.tostring -3.6 : saving converted string in file -</lines> - -<p>The save function is given below.</p> ---ldx]]-- - -function xml.save(root,name) - local f = io.open(name,"w") - if f then - xml.serialize(root,function(s) f:write(s) end) - f:close() - end -end +xml.string = xmlstring --[[ldx-- <p>A few helpers:</p> --ldx]]-- +function xml.parent(root) + return root.__p__ +end + function xml.body(root) return (root.ri and root.dt[root.ri]) or root end @@ -773,34 +960,19 @@ function xml.content(root) -- bugged return (root and root.dt and xml.tostring(root.dt)) or "" end -function xml.isempty(root, pattern) - if pattern == "" or pattern == "*" then - pattern = nil - end - if pattern then - -- todo - return false - else - return not root or not root.dt or #root.dt == 0 or root.dt == "" - end -end - --[[ldx-- <p>The next helper erases an element but keeps the table as it is, and since empty strings are not serialized (effectively) it does not harm. Copying the table would take more time. Usage:</p> - -<typing> -dt[k] = xml.empty() or xml.empty(dt,k) -</typing> --ldx]]-- -function xml.empty(dt,k) - if dt and k then - dt[k] = "" - return dt[k] - else - return "" +function xml.erase(dt,k) + if dt then + if k then + dt[k] = "" + else for k=1,#dt do + dt[1] = { "" } + end end end end diff --git a/tex/context/base/lxml-tex.lua b/tex/context/base/lxml-tex.lua new file mode 100644 index 000000000..69f5b5116 --- /dev/null +++ b/tex/context/base/lxml-tex.lua @@ -0,0 +1,1309 @@ +if not modules then modules = { } end modules ['lxml-tst'] = { + version = 1.001, + comment = "companion to lxml-ini.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local utf = unicode.utf8 + +local utfchar = utf.char +local concat, insert, remove, gsub, find = table.concat, table.insert, table.remove +local format, sub, gsub, find = string.format, string.sub, string.gsub, string.find +local type, next, tonumber, tostring = type, next, tonumber, tostring + +if not tex and not tex.sprint then + tex = { + sprint = function(catcodes,...) texio.write(table.concat{...}) end, + print = function(catcodes,...) texio.write(table.concat{...}) end, + write = function( ...) texio.write(table.concat{...}) end, + } + commands = { + writestatus = logs.report + } + resolvers.loadbinfile = function(filename) return true, io.loaddata(filename) end +end + +local texsprint, texprint, texwrite = tex.sprint, tex.print, tex.write +local texcatcodes, ctxcatcodes, vrbcatcodes = tex.texcatcodes, tex.ctxcatcodes, tex.vrbcatcodes + +local xmlelements, xmlcollected, xmlsetproperty = xml.elements, xml.collected, xml.setproperty +local xmlparseapply, xmlwithelements = xml.parse_apply, xml.withelements +local xmlserialize, xmlcollect, xmlcontent = xml.serialize, xml.collect, xml.content +local xmltostring = xml.tostring + +local variables = (interfaces and interfaces.variables) or { } + +local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming + +local trace_setups = false trackers.register("lxml.setups", function(v) trace_setups = v end) +local trace_loading = false trackers.register("lxml.loading", function(v) trace_loading = v end) +local trace_access = false trackers.register("lxml.access", function(v) trace_access = v end) +local trace_comments = false trackers.register("lxml.comments", function(v) trace_comments = v end) + +lxml = lxml or { } +lxml.loaded = lxml.loaded or { } +lxml.noffiles = 0 +lxml.nofconverted = 0 + +local loaded = lxml.loaded + +-- print(contextdirective("context-mathml-directive function reduction yes ")) +-- print(contextdirective("context-mathml-directive function ")) + +xml.defaultprotocol = "tex" + +local finalizers = xml.finalizers + +finalizers.xml = finalizers.xml or { } +finalizers.tex = finalizers.tex or { } + +-- this might look inefficient but it's actually rather efficient +-- because we avoid tokenization of leading spaces and xml can be +-- rather verbose (indented) + +local crlf = lpeg.P("\r\n") +local cr = lpeg.P("\r") +local lf = lpeg.P("\n") +local newline = crlf + cr + lf +local space = lpeg.S(" \t\f\v") +local ampersand = lpeg.P("&") +local semicolon = lpeg.P(";") +local spacing = newline * space^0 +local content = lpeg.C((1-spacing-ampersand)^1) +local verbose = lpeg.C((1-(space+newline))^1) +local entity = ampersand * lpeg.C((1-semicolon)^1) * semicolon + +local xmltextcapture = ( + space^0 * newline^2 * lpeg.Cc("") / texprint + -- better ^-2 ? + space^0 * newline * space^0 * lpeg.Cc(" ") / texsprint + + content / texsprint + -- current catcodes regime is notcatcodes + entity / xml.resolved_entity +)^0 + +local forceraw, rawroot = false, nil + +function lxml.startraw() + forceraw = true +end + +function lxml.stopraw() + forceraw = false +end + +function lxml.rawroot() + return rawroot +end + +--~ function lxml.rawpath(rootid) +--~ if rawroot and type(rawroot) == "table" then +--~ local text, path, rp +--~ if not rawroot.dt then +--~ text, path, rp = "text", "", rawroot[0] +--~ else +--~ path, rp = "tree", "", rawroot.__p__ +--~ end +--~ while rp do +--~ local rptg = rp.tg +--~ if rptg then +--~ path = rptg .. "/" .. path +--~ end +--~ rp = rp.__p__ +--~ end +--~ return { rootid, "/" .. path, text } +--~ end +--~ end + +-- cdata + +local linecommand = "\\obeyedline" +local spacecommand = "\\obeyedspace" -- "\\strut\\obeyedspace" +local beforecommand = "" +local aftercommand = "" + +local xmlverbosecapture = ( + newline / function( ) texsprint(texcatcodes,linecommand,"{}") end + + verbose / function(s) texsprint(vrbcatcodes,s) end + + space / function( ) texsprint(texcatcodes,spacecommand,"{}") end +)^0 + +local function toverbatim(str) + if beforecommand then texsprint(texcatcodes,beforecommand,"{}") end + xmlverbosecapture:match(str) + if aftercommand then texsprint(texcatcodes,aftercommand,"{}") end +end + +function lxml.set_verbatim(before,after,obeyedline,obeyedspace) + beforecommand, aftercommand, linecommand, spacecommand = before, after, obeyedline, obeyedspace +end + +local obeycdata = true + +function lxml.set_cdata() + obeycdata = true +end + +function lxml.reset_cdata() + obeycdata = false +end + +-- cdata and verbatim + +lxml.set_verbatim("\\xmlcdatabefore", "\\xmlcdataafter", "\\xmlcdataobeyedline", "\\xmlcdataobeyedspace") + +-- local capture = (space^0*newline)^0 * capture * (space+newline)^0 * -1 + +function lxml.toverbatim(str) + if beforecommand then texsprint(texcatcodes,beforecommand,"{}") end + -- todo: add this to capture + str = gsub(str,"^[ \t]+[\n\r]+","") + str = gsub(str,"[ \t\n\r]+$","") + xmlverbosecapture:match(str) + if aftercommand then texsprint(texcatcodes,aftercommand,"{}") end +end + +-- storage + +function lxml.store(id,root,filename) + loaded[id] = root + xmlsetproperty(root,"name",id) + if filename then + xmlsetproperty(root,"filename",filename) + end +end + +local splitter = lpeg.C((1-lpeg.P(":"))^1) * lpeg.P("::") * lpeg.C(lpeg.P(1)^1) + +lxml.idsplitter = splitter + +function lxml.splitid(id) + local d, i = splitter:match(id) + if d then + return d, i + else + return "", id + end +end + +local function get_id(id, qualified) + if id then + local lid = loaded[id] + if lid then + return lid + elseif type(id) == "table" then + return id + else + local d, i = splitter:match(id) + if d then + local ld = loaded[d] + if ld then + local ldi = ld.index + if ldi then + local root = ldi[tonumber(i)] + if root then + if qualified then -- we need this else two args that confuse others + return root, d + else + return root + end + elseif trace_access then + logs.report("lxml","'%s' has no index entry '%s'",d,i) + end + elseif trace_access then + logs.report("lxml","'%s' has no index",d) + end + elseif trace_access then + logs.report("lxml","'%s' is not loaded",d) + end + elseif trace_access then + logs.report("lxml","'%s' is not loaded",i) + end + end + elseif trace_access then + logs.report("lxml","invalid id (nil)") + end +end + +lxml.id = get_id +lxml.get_id = get_id + +function lxml.root(id) + return loaded[id] +end + +-- index + +local nofindices = 0 + +local function addindex(name,check_sum,force) + local root = get_id(name) + if root and (not root.index or force) then -- weird, only called once + local n, index, maxindex, check = 0, root.index or { }, root.maxindex or 0, root.check or { } + local function nest(root) + local dt = root.dt + if not root.ix then + maxindex = maxindex + 1 + root.ix = maxindex + check[maxindex] = root.tg -- still needed ? + index[maxindex] = root + n = n + 1 + end + if dt then + for k=1,#dt do + local dk = dt[k] + if type(dk) == "table" then + nest(dk) + end + end + end + end + nest(root) + nofindices = nofindices + n + -- + if type(name) ~= "string" then + name = "unknown" + end + root.index = index + root.maxindex = maxindex + if trace_access then + logs.report("lxml","%s indexed, %s nodes",tostring(name),maxindex) + end + end +end + +lxml.addindex = addindex + +-- another cache + +local function lxmlparseapply(id,pattern) -- better inline, saves call + return xmlparseapply({ get_id(id) }, pattern) +end + +lxml.filter = lxmlparseapply + +lxml["function"] = function(id,name) + local f = xml.functions[name] + return f and f(get_id(id)) +end + +-- rather new, indexed storage (backward refs), maybe i will merge this + +function lxml.checkindex(name) + local root = get_id(name) + return (root and root.index) or 0 +end + +function lxml.withindex(name,n,command) -- will change as name is always there now + local i, p = splitter:match(n) + if p then + texsprint(ctxcatcodes,"\\xmlw{",command,"}{",n,"}") + else + texsprint(ctxcatcodes,"\\xmlw{",command,"}{",name,"::",n,"}") + end +end + +function lxml.getindex(name,n) -- will change as name is always there now + local i, p = splitter:match(n) + if p then + texsprint(ctxcatcodes,n) + else + texsprint(ctxcatcodes,name,"::",n) + end +end + +-- loading (to be redone, no overload) + +xml.originalload = xml.originalload or xml.load + +local noffiles, nofconverted = 0, 0 + +function xml.load(filename) + noffiles = noffiles + 1 + nofconverted = nofconverted + 1 + starttiming(xml) + local ok, data = resolvers.loadbinfile(filename) + local xmltable = xml.convert((ok and data) or "") + stoptiming(xml) + return xmltable +end + +function lxml.convert(data,entities,compress) + local settings = { } + if compress and compress == variables.yes then + settings.strip_cm_and_dt = true + end + if entities and entities == variables.yes then + settings.utfize_entities = true + settings.resolve_entities = true + end + return xml.convert(data,settings) +end + +function lxml.load(id,filename,compress,entities) + filename = commands.preparedfile(filename) + if trace_loading then + commands.writestatus("lxml","loading file '%s' as '%s'",filename,id) + end + -- local xmltable = xml.load(filename) + local ok, data = resolvers.loadbinfile(filename) + local xmltable = lxml.convert((ok and data) or "",compress,entities) + lxml.store(id,xmltable,filename) + return xmltable, filename +end + +function lxml.register(id,xmltable,filename) + lxml.store(id,xmltable,filename) + return xmltable +end + +function lxml.include(id,pattern,attribute,recurse) + starttiming(xml) + local root = get_id(id) + xml.include(root,pattern,attribute,recurse,function(filename) + if filename then + filename = commands.preparedfile(filename) + if file.dirname(filename) == "" and root.filename then + filename = file.join(file.dirname(root.filename),filename) + end + if trace_loading then + commands.writestatus("lxml","including file: %s",filename) + end + noffiles = noffiles + 1 + nofconverted = nofconverted + 1 + return resolvers.loadtexfile(filename) or "" + else + return "" + end + end) + stoptiming(xml) +end + +function xml.getbuffer(name,compress,entities) -- we need to make sure that commands are processed + if not name or name == "" then + name = tex.jobname + end + nofconverted = nofconverted + 1 + xmltostring(lxml.convert(concat(buffers.data[name] or {},""),compress,entities)) +end + +function lxml.loadbuffer(id,name,compress,entities) + if not name or name == "" then + name = tex.jobname + end + starttiming(xml) + nofconverted = nofconverted + 1 + local xmltable = lxml.convert(buffers.collect(name or id,"\n"),compress,entities) + lxml.store(id,xmltable) + stoptiming(xml) + return xmltable, name or id +end + +function lxml.loaddata(id,str,compress,entities) + starttiming(xml) + nofconverted = nofconverted + 1 + local xmltable = lxml.convert(str or "",compress,entities) + lxml.store(id,xmltable) + stoptiming(xml) + return xmltable, id +end + +function lxml.loadregistered(id) + return loaded[id], id +end + +-- e.command: +-- +-- string : setup +-- true : text (no <self></self>) +-- false : ignore +-- function : call + +local function tex_doctype(e,handlers) + -- ignore +end + +local function tex_comment(e,handlers) + if trace_comments then + logs.report("lxml","comment: %s",e.dt[1]) + end +end + +local default_element_handler = xml.gethandlers("verbose").functions["@el@"] + +local function tex_element(e,handlers) + local command = e.command + if command == nil then + default_element_handler(e,handlers) + elseif command == true then + -- text (no <self></self>) / so, no mkii fallback then + handlers.serialize(e.dt,handlers) + elseif command == false then + -- ignore + else + local tc = type(command) + if tc == "string" then + local rootname, ix = e.name, e.ix + if rootname then + if not ix then + addindex(rootname,false,true) + ix = e.ix + end + texsprint(ctxcatcodes,"\\xmlw{",command,"}{",rootname,"::",ix,"}") + else + logs.report("lxml", "fatal error: no index for '%s'",command) + texsprint(ctxcatcodes,"\\xmlw{",command,"}{",ix or 0,"}") + end + elseif tc == "function" then + command(e) + end + end +end + +local pihandlers = { } xml.pihandlers = pihandlers + +local kind = lpeg.P("context-") * lpeg.C((1-lpeg.P("-"))^1) * lpeg.P("-directive") +local space = lpeg.S(" \n\r") +local spaces = space^0 +local class = lpeg.C((1-space)^0) +local key = class +local value = lpeg.C(lpeg.P(1-(space * -1))^0) + +local parser = kind * spaces * class * spaces * key * spaces * value + +pihandlers[#pihandlers+1] = function(str) +-- local kind, class, key, value = parser:match(str) + if str then + local a, b, c, d = parser:match(str) + if d then + texsprint(ctxcatcodes,"\\xmlcontextdirective{",a",}{",b,"}{",c,"}{",d,"}") + end + end +end + +local function tex_pi(e,handlers) + local str = e.dt[1] + for i=1,#pihandlers do + pihandlers[i](str) + end +end + +local function tex_cdata(e,handlers) + if obeycdata then + toverbatim(e.dt[1]) + end +end + +local function tex_text(e,handlers) + xmltextcapture:match(e) +end + +local function tex_handle(...) +-- logs.report("lxml", "error while flushing: %s", concat { ... }) + texsprint(...) -- notcatcodes is active anyway +end + +local xmltexhandler = xml.newhandlers { + name = "tex", + handle = tex_handle, + functions = { + -- ["@dc@"] = tex_document, + ["@dt@"] = tex_doctype, + -- ["@rt@"] = tex_root, + ["@el@"] = tex_element, + ["@pi@"] = tex_pi, + ["@cm@"] = tex_comment, + ["@cd@"] = tex_cdata, + ["@tx@"] = tex_text, + } +} + +function lxml.serialize(root) + xmlserialize(root,xmltexhandler) +end + +function lxml.setaction(id,pattern,action) + local collected = lxmlparseapply(id,pattern) + if collected then + for c=1,#collected do + collected[c].command = action + end + end +end + +local function sprint(root) + if root then + local tr = type(root) + if tr == "string" then -- can also be result of lpath + -- rawroot = false + xmltextcapture:match(root) + elseif tr == "table" then + if forceraw then + rawroot = root + texwrite(xmltostring(root)) + else + xmlserialize(root,xmltexhandler) + end + end + end +end + +local function tprint(root) -- we can move sprint inline + local tr = type(root) + if tr == "table" then + local n = #root + if n == 0 then + -- skip + else + for i=1,n do + sprint(root[i]) + end + end + elseif tr == "string" then + xmltextcapture:match(root) + end +end + +local function cprint(root) -- content + if not root then + -- rawroot = false + -- quit + elseif type(root) == 'string' then + -- rawroot = false + xmltextcapture:match(root) + else + local rootdt = root.dt + if forceraw then + rawroot = root + texwrite(xmltostring(rootdt or root)) + else + xmlserialize(rootdt or root,xmltexhandler) + end + end +end + +xml.sprint = sprint local xmlsprint = sprint -- redo these names +xml.tprint = tprint local xmltprint = tprint +xml.cprint = cprint local xmlcprint = cprint + +-- now we can flush + +function lxml.main(id) + xmlserialize(get_id(id),xmltexhandler) -- the real root (@rt@) +end + +--~ -- lines (untested) +--~ +--~ local buffer = { } +--~ +--~ local xmllinescapture = ( +--~ newline^2 / function() buffer[#buffer+1] = "" end + +--~ newline / function() buffer[#buffer] = buffer[#buffer] .. " " end + +--~ content / function(s) buffer[#buffer] = buffer[#buffer] .. s end +--~ )^0 +--~ +--~ local xmllineshandler = table.copy(xmltexhandler) +--~ +--~ xmllineshandler.handle = function(...) xmllinescapture:match(concat{ ... }) end +--~ +--~ function lines(root) +--~ if not root then +--~ -- rawroot = false +--~ -- quit +--~ elseif type(root) == 'string' then +--~ -- rawroot = false +--~ xmllinescapture:match(root) +--~ elseif next(root) then -- tr == 'table' +--~ xmlserialize(root,xmllineshandler) +--~ end +--~ end +--~ +--~ function xml.lines(root) -- used at all? +--~ buffer = { "" } +--~ lines(root) +--~ return result +--~ end + +local function to_text(e) + if e.command == nil then + local etg = e.tg + if etg and e.special and etg ~= "@rt@" then + e.command = false -- i.e. skip + else + e.command = true -- i.e. no <self></self> + end + end +end + +local function to_none(e) + if e.command == nil then + e.command = false -- i.e. skip + end +end + +-- setups + +local setups = { } + +function lxml.set_command_to_text(id) + xmlwithelements(get_id(id),to_text) +end + +function lxml.set_command_to_none(id) + xmlwithelements(get_id(id),to_none) +end + +function lxml.installsetup(what,document,setup,where) + document = document or "*" + local sd = setups[document] + if not sd then sd = { } setups[document] = sd end + for k=1,#sd do + if sd[k] == setup then sd[k] = nil break end + end + if what == 1 then + if trace_loading then + commands.writestatus("lxml","prepending setup %s for %s",setup,document) + end + insert(sd,1,setup) + elseif what == 2 then + if trace_loading then + commands.writestatus("lxml","appending setup %s for %s",setup,document) + end + insert(sd,setup) + elseif what == 3 then + if trace_loading then + commands.writestatus("lxml","inserting setup %s for %s before %s",setup,document,where) + end + table.insert_before_value(sd,setup,where) + elseif what == 4 then + if trace_loading then + commands.writestatus("lxml","inserting setup %s for %s after %s",setup,document,where) + end + table.insert_after_value(sd,setup,where) + end +end + +function lxml.flushsetups(...) + local done = { } + for _, document in ipairs({...}) do + local sd = setups[document] + if sd then + for k=1,#sd do + local v= sd[k] + if not done[v] then + if trace_loading then + commands.writestatus("lxml","applying setup %02i = %s to %s",k,v,document) + end + texsprint(ctxcatcodes,"\\directsetup{",v,"}") + done[v] = true + end + end + elseif trace_loading then + commands.writestatus("lxml","no setups for %s",document) + end + end +end + +function lxml.resetsetups(document) + if trace_loading then + commands.writestatus("lxml","resetting all setups for %s",document) + end + setups[document] = { } +end + +function lxml.removesetup(document,setup) + local s = setups[document] + if s then + for i=1,#s do + if s[i] == setup then + if trace_loading then + commands.writestatus("lxml","removing setup %s for %s",setup,document) + end + remove(t,i) + break + end + end + end +end + +function lxml.setsetup(id,pattern,setup) + if not setup or setup == "" or setup == "*" or setup == "-" or setup == "+" then + local collected = lxmlparseapply(id,pattern) + if collected then + if trace_setups then + for c=1, #collected do + local e = collected[c] + local ix = e.ix or 0 + if setup == "-" then + e.command = false + logs.report("lxml","lpath matched (a) %5i: %s = %s -> skipped",c,ix,setup) + elseif setup == "+" then + e.command = true + logs.report("lxml","lpath matched (b) %5i: %s = %s -> text",c,ix,setup) + else + local tg = e.tg + if tg then -- to be sure + e.command = tg + local ns = e.rn or e.ns + if ns == "" then + logs.report("lxml","lpath matched (c) %5i: %s = %s -> %s",c,ix,tg,tg) + else + logs.report("lxml","lpath matched (d) %5i: %s = %s:%s -> %s",c,ix,ns,tg,tg) + end + end + end + end + else + for c=1, #collected do + local e = collected[c] + if setup == "-" then + e.command = false + elseif setup == "+" then + e.command = true + else + e.command = e.tg + end + end + end + elseif trace_setups then + logs.report("lxml","no lpath matches for %s",pattern) + end + else + local a, b = setup:match("^(.+:)([%*%-])$") + if a and b then + local collected = lxmlparseapply(id,pattern) + if collected then + if trace_setups then + for c=1, #collected do + local e = collected[c] + local ns, tg, ix = e.rn or e.ns, e.tg, e.ix or 0 + if b == "-" then + e.command = false + if ns == "" then + logs.report("lxml","lpath matched (e) %5i: %s = %s -> skipped",c,ix,tg) + else + logs.report("lxml","lpath matched (f) %5i: %s = %s:%s -> skipped",c,ix,ns,tg) + end + elseif b == "+" then + e.command = true + if ns == "" then + logs.report("lxml","lpath matched (g) %5i: %s = %s -> text",c,ix,tg) + else + logs.report("lxml","lpath matched (h) %5i: %s = %s:%s -> text",c,ix,ns,tg) + end + else + e.command = a .. tg + if ns == "" then + logs.report("lxml","lpath matched (i) %5i: %s = %s -> %s",c,ix,tg,e.command) + else + logs.report("lxml","lpath matched (j) %5i: %s = %s:%s -> %s",c,ix,ns,tg,e.command) + end + end + end + else + for c=1, #collected do + local e = collected[c] + if b == "-" then + e.command = false + elseif b == "+" then + e.command = true + else + e.command = a .. e.tg + end + end + end + elseif trace_setups then + logs.report("lxml","no lpath matches for %s",pattern) + end + else + local collected = lxmlparseapply(id,pattern) + if collected then + if trace_setups then + for c=1, #collected do + local e = collected[c] + e.command = setup + local ns, tg, ix = e.rn or e.ns, e.tg, e.ix or 0 + if ns == "" then + logs.report("lxml","lpath matched (k) %5i: %s = %s -> %s",c,ix,tg,setup) + else + logs.report("lxml","lpath matched (l) %5i: %s = %s:%s -> %s",c,ix,ns,tg,setup) + end + end + else + for c=1, #collected do + collected[c].command = setup + end + end + elseif trace_setups then + logs.report("lxml","no lpath matches for %s",pattern) + end + end + end +end + +-- finalizers + +local finalizers = xml.finalizers.tex + +local function first(collected) + if collected then + xmlsprint(collected[1]) + end +end + +local function last(collected) + if collected then + xmlsprint(collected[#collected]) + end +end + +local function all(collected) + if collected then + for c=1,#collected do + xmlsprint(collected[c]) + end + end +end + +local function reverse(collected) + if collected then + for c=#collected,1,-1 do + xmlsprint(collected[c]) + end + end +end + +local function count(collected) + texwrite((collected and #collected) or 0) +end + +local function position(collected,n) + if collected then + n = tonumber(n) or 0 + if n < 0 then + n = #collected + n + 1 + end + if n > 0 then + xmlsprint(collected[n]) + end + end +end + +local function index(collected,n) + if collected then + n = tonumber(n) or 0 + if n < 0 then + n = #collected + n + 1 + end + if n > 0 then + texwrite(collected[n].ni or 0) + end + end +end + +local function command(collected,cmd) + if collected then + for c=1,#collected do + local e = collected[c] + local ix = e.ix + if not ix then + lxml.addindex(e.name,false,true) + ix = e.ix + end + texsprint(ctxcatcodes,"\\xmlw{",cmd,"}{",e.name,"::",ix,"}") + end + end +end + +local function attribute(collected,a,default) + if collected then + local at = collected[1].at + local str = (at and at[a]) or default + if str and str ~= "" then + texsprint(ctxcatcodes,str) + end + elseif default then + texsprint(ctxcatcodes,default) + end +end + +local function chainattribute(collected,arguments) -- todo: optional levels + if collected then + local e = collected[1] + while e do + local at = e.at + if at then + local a = at[arguments] + if a then + texsprint(ctxcatcodes,a) + end + else + break -- error + end + e = e.__p__ + end + end +end + +local function text(collected) + if collected then + local nc = #collected + if nc == 1 then -- hardly any gain so this will go + cprint(collected[1]) + else for c=1,nc do + cprint(collected[c]) + end end + end +end + +local function number(collected) + if collected then + local n = 0 + for c=1,#collected do + n = n + tonumber(collected[c].dt[1] or 0) + end + texwrite(n) + end +end + +local function concatrange(collected,start,stop,separator,lastseparator) -- test this on mml + if collected then + local nofcollected = #collected + local separator = separator or "" + local lastseparator = lastseparator or separator or "" + start, stop = (start == "" and 1) or tonumber(start) or 1, (stop == "" and nofcollected) or tonumber(stop) or nofcollected + if stop < 0 then stop = nofcollected + stop end -- -1 == last-1 + for i=start,stop do + xmlsprint(collected[i]) + if i == nofcollected then + -- nothing + elseif i == nofcollected-1 and lastseparator ~= "" then + texsprint(ctxcatcodes,lastseparator) + elseif separator ~= "" then + texsprint(ctxcatcodes,separator) + end + end + end +end + +local function concat(collected,separator,lastseparator) -- test this on mml + concatrange(collected,false,false,separator,lastseparator) +end + +finalizers.first = first +finalizers.last = last +finalizers.all = all +finalizers.reverse = reverse +finalizers.count = count +finalizers.command = command +finalizers.attribute = attribute +finalizers.text = text +finalizers.position = position +finalizers.index = index +finalizers.concat = concat +finalizers.concatrange = concatrange +finalizers.chainattribute = chainattribute +finalizers.default = all -- !! + +local concat = table.concat + +function finalizers.tag(root,pattern,n) + if collected then + local c + if n == 0 or not n then + c = collected[1] + elseif n > 1 then + c = collected[n] + else + c = collected[#collected-n+1] + end + if c then + texsprint(c.tg) + end + end +end + +function finalizers.name(root,pattern,n) + if collected then + local c + if n == 0 or not n then + c = collected[1] + elseif n > 1 then + c = collected[n] + else + c = collected[#collected-n+1] + end + if c then + if c.ns ~= "" then + texsprint(c.tg) + else + texsprint(c.ns,":",c.tg) + end + end + end +end + +function finalizers.tags(root,pattern,nonamespace) + if collected then + for c=1,#collected do + local e = collected[c] + local ns, tg = e.ns, e.tg + if nonamespace or ns == "" then + texsprint(tg) + else + texsprint(ns,":",tg) + end + end + end +end + +-- + +local function verbatim(id,before,after) + local root = get_id(id) + if root then + if before then texsprint(ctxcatcodes,before,"[",root.tg or "?","]") end + lxml.toverbatim(xmltostring(root.dt)) + if after then texsprint(ctxcatcodes,after) end + end +end +function lxml.inlineverbatim(id) + verbatim(id,"\\startxmlinlineverbatim","\\stopxmlinlineverbatim") +end +function lxml.displayverbatim(id) + verbatim(id,"\\startxmldisplayverbatim","\\stopxmldisplayverbatim") +end + +lxml.verbatim = verbatim + +-- helpers + +function lxml.first(id,pattern) + local collected = lxmlparseapply(id,pattern) + if collected then + first(collected) + end +end + +function lxml.last(id,pattern) + local collected = lxmlparseapply(id,pattern) + if collected then + last(collected) + end +end + +function lxml.all(id,pattern) + local collected = lxmlparseapply(id,pattern) + if collected then + all(collected) + end +end + +function lxml.count(id,pattern) + -- always needs to produce a result so no test here + count(lxmlparseapply(id,pattern)) +end + +function lxml.attribute(id,pattern,a,default) + local collected = lxmlparseapply(id,pattern) + if collected then + attribute(collected,a,default) + end +end + +function lxml.text(id,pattern) + local collected = lxmlparseapply(id,pattern) + if collected then + text(collected) + end +end + +function lxml.raw(id,pattern) -- the content, untouched by commands + local collected = lxmlparseapply(id,pattern) + if collected then + texsprint(xmltostring(collected[1].dt)) + end +end + +function lxml.position(id,pattern,n) + local collected = lxmlparseapply(id,pattern) + if collected then + position(collected,n) + end +end + +function lxml.chainattribute(id,pattern,a,default) + local collected = lxmlparseapply(id,pattern) + if collected then + chainattribute(collected,a,default) + end +end + +function lxml.concatrange(id,pattern,start,stop,separator,lastseparator) -- test this on mml + concatrange(lxmlparseapply(id,pattern),start,stop,separator,lastseparator) +end + +function lxml.concat(id,pattern,separator,lastseparator) + concatrange(lxmlparseapply(id,pattern),false,false,separator,lastseparator) +end + +function lxml.element(id,n) + position(lxmlparseapply(id,"/*"),n) +end + +lxml.index = lxml.position + +-- fast direct ones + +function lxml.content(root) -- bugged, does not print + local root = get_id(id) + local content = root and root.dt and xmltostring(root.dt) + if content then + texsprint(content) + end +end + +function lxml.pos(id) + local root = get_id(id) + texwrite((root and root.ni) or 0) +end + +function lxml.att(id,a,default) + local root = get_id(id) + if root then + local at = root.at + local str = (at and at[a]) or default + if str and str ~= "" then + texsprint(ctxcatcodes,str) + end + elseif default then + texsprint(ctxcatcodes,default) + end +end + +function lxml.name(id) -- or remapped name? -> lxml.info, combine + local r = get_id(id) + local ns = r.rn or r.ns or "" + if ns ~= "" then + texsprint(ns,":",r.tg) + else + texsprint(r.tg) + end +end + +function lxml.tag(id) -- tag vs name -> also in l-xml tag->name + texsprint(get_id(id).tg or "") +end + +function lxml.namespace(id) -- or remapped name? + local root = get_id(id) + texsprint(root.rn or root.ns or "") +end + +function lxml.flush(id) + id = get_id(id) + local dt = id and id.dt + if dt then + xmlsprint(dt) + end +end + +function lxml.snippet(id,i) + local e = get_id(id) + if e then + local edt = e.dt + if edt then + xmlsprint(edt[i]) + end + end +end + +function lxml.direct(id) + xmlsprint(get_id(id)) +end + +function lxml.command(id,pattern,cmd) + local i, p = get_id(id,true) + local collected = lxmlparseapply(i,pattern) + if collected then + local rootname = p or i.name + for c=1,#collected do + local e = collected[c] + local ix = e.ix + if not ix then + addindex(rootname,false,true) + ix = e.ix + end + texsprint(ctxcatcodes,"\\xmlw{",cmd,"}{",rootname,"::",ix,"}") + end + end +end + +-- loops + +function lxml.collected(id,pattern,reverse) + return xmlcollected(get_id(id),pattern,reverse) +end + +function lxml.elements(id,pattern,reverse) + return xmlelements(get_id(id),pattern,reverse) +end + +-- obscure ones + +lxml.info = lxml.name + +-- testers + +local found, empty = xml.found, xml.empty + +local doif, doifnot, doifelse = commands.doif, commands.doifnot, commands.doifelse + +function lxml.doif (id,pattern) doif (found(get_id(id),pattern)) end +function lxml.doifnot (id,pattern) doifnot (found(get_id(id),pattern)) end +function lxml.doifelse (id,pattern) doifelse(found(get_id(id),pattern)) end +function lxml.doiftext (id,pattern) doif (not empty(get_id(id),pattern)) end +function lxml.doifnottext (id,pattern) doifnot (not empty(get_id(id),pattern)) end +function lxml.doifelsetext (id,pattern) doifelse(not empty(get_id(id),pattern)) end + +-- special case: "*" and "" -> self else lpath lookup + +--~ function lxml.doifelseempty(id,pattern) doifelse(isempty(get_id(id),pattern ~= "" and pattern ~= nil)) end -- not yet done, pattern + +-- status info + +statistics.register("xml load time", function() + if noffiles > 0 or nofconverted > 0 then + return format("%s seconds, %s files, %s converted", statistics.elapsedtime(xml), noffiles, nofconverted) + else + return nil + end +end) + +statistics.register("lxml preparation time", function() + if noffiles > 0 or nofconverted > 0 then + return format("%s seconds, %s nodes, %s lpath calls, %s cached calls", + statistics.elapsedtime(lxml), nofindices, xml.lpathcalls(), xml.lpathcached()) + else + return nil + end +end) + +-- misc + +function lxml.nonspace(id,pattern) -- slow, todo loop + xmltprint(xmlcollect(get_id(id),pattern,true)) +end + +function lxml.strip(id,pattern,nolines) + xml.strip(get_id(id),pattern,nolines) +end + +function lxml.stripped(id,pattern,nolines) + local str = xmlcontent(get_id(id),pattern) or "" + str = gsub(str,"^%s*(.-)%s*$","%1") + if nolines then + str = gsub(str,"%s+"," ") + end + xmlsprint(str) +end diff --git a/tex/context/base/lxml-xml.lua b/tex/context/base/lxml-xml.lua new file mode 100644 index 000000000..7ff3f5955 --- /dev/null +++ b/tex/context/base/lxml-xml.lua @@ -0,0 +1,242 @@ +if not modules then modules = { } end modules ['lxml-xml'] = { + version = 1.001, + comment = "this module is the basis for the lxml-* ones", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +local finalizers = xml.finalizers.xml +local xmlfilter = xml.filter -- we could inline this one for speed +local xmltostring = xml.tostring +local xmlserialize = xml.serialize + +local function first(collected) + return collected and collected[1] +end + +local function last(collected) + return collected and collected[#collected] +end + +local function all(collected) + return collected +end + +local function reverse(collected) + if collected then + local reversed = { } + for c=#collected,1,-1 do + reversed[#reversed+1] = collected[c] + end + return reversed + end +end + +local function attribute(collected,name) + local at = collected and collected[1].at + return at and at[name] +end + +local function att(id,name) + local at = id.at + return at and at[name] +end + +local function count(collected) + return (collected and #collected) or 0 +end + +local function position(collected,n) + if collected then + n = tonumber(n) or 0 + if n < 0 then + return collected[#collected + n + 1] + else + return collected[n] + end + end +end + +local function index(collected) + if collected then + return collected[1].ni + end +end + +local function attributes(collected,arguments) + if collected then + local at = collected[1].at + if arguments then + return at[arguments] + elseif next(at) then + return at -- all of them + end + end +end + +local function chainattribute(collected,arguments) -- todo: optional levels + if collected then + local e = collected[1] + while e do + local at = e.at + if at then + local a = at[arguments] + if a then + return a + end + else + break -- error + end + e = e.__p__ + end + end + return "" +end + +local function text(collected) + if collected then + return xmltostring(collected[1]) -- only first as we cannot concat function + else + return "" + end +end + +local function texts(collected) + if collected then + local t = { } + for c=1,#collected do + local e = collection[c] + if e and e.dt then + t[#t+1] = e.dt + end + end + return t + end +end + +local function tag(collected,n) + if collected then + local c + if n == 0 or not n then + c = collected[1] + elseif n > 1 then + c = collected[n] + else + c = collected[#collected-n+1] + end + return c and c.tg + end +end + +local function name(collected,n) + if collected then + local c + if n == 0 or not n then + c = collected[1] + elseif n > 1 then + c = collected[n] + else + c = collected[#collected-n+1] + end + if c then + if c.ns == "" then + return c.tg + else + return c.ns .. ":" .. c.tg + end + end + end +end + +local function tags(collected,nonamespace) + if collected then + local t = { } + for c=1,#collected do + local e = collected[c] + local ns, tg = e.ns, e.tg + if nonamespace or ns == "" then + t[#t+1] = tg + else + t[#t+1] = ns .. ":" .. tg + end + end + return t + end +end + +local function empty(collected) + if collected then + for c=1,#collected do + local e = collected[c] + if e then + local edt = e.dt + if edt then + local n = #edt + if (n > 2) or (n > 0 and edt[1] == "") then + return false + end + end + end + end + end + return true +end + +finalizers.first = first +finalizers.last = last +finalizers.all = all +finalizers.reverse = reverse +finalizers.elements = all +finalizers.default = all +finalizers.attribute = attribute +finalizers.att = att +finalizers.count = count +finalizers.position = position +finalizers.index = index +finalizers.attributes = attributes +finalizers.chainattribute = chainattribute +finalizers.text = text +finalizers.texts = texts +finalizers.tag = tag +finalizers.name = name +finalizers.tags = tags +finalizers.empty = empty + +-- shortcuts -- we could support xmlfilter(id,pattern,first) + +function xml.first(id,pattern) + return first(xmlfilter(id,pattern)) +end + +function xml.last(id,pattern) + return last(xmlfilter(id,pattern)) +end + +function xml.count(id,pattern) + return count(xmlfilter(id,pattern)) +end + +function xml.attribute(id,pattern,a,default) + return attribute(xmlfilter(id,pattern),a,default) +end + +function xml.text(id,pattern) + return text(xmlfilter(id,pattern)) +end + +function xml.raw(id,pattern) + return xmlserialize(xmlfilter(id,pattern)) +end + +function xml.position(id,pattern,n) + return position(xmlfilter(id,pattern),n) +end + +function xml.empty(id,pattern) + return empty(xmlfilter(id,pattern)) +end + +xml.all = xml.filter +xml.index = xml.position +xml.found = xml.filter diff --git a/tex/context/base/math-vfu.lua b/tex/context/base/math-vfu.lua index 42b7ef05d..e54d68fbe 100644 --- a/tex/context/base/math-vfu.lua +++ b/tex/context/base/math-vfu.lua @@ -223,7 +223,7 @@ function fonts.vf.math.alas(main,id,size) arrow(main,0x2192,0xFE192,0xFF501,false) -- right end -local reverse -- index -> unicode +local unique = 0 -- testcase: \startTEXpage \math{!\text{-}\text{-}\text{-}} \stopTEXpage function fonts.basecopy(tfmtable) local t, c, p = { }, { }, { } @@ -237,10 +237,13 @@ function fonts.basecopy(tfmtable) p[k] = v end t.characters, t.parameters = c, p + unique = unique + 1 + t.fullname = t.fullname .. "-" .. unique return t end local reported = { } +local reverse -- index -> unicode function fonts.vf.math.define(specification,set) if not reverse then diff --git a/tex/context/base/meta-pdf.lua b/tex/context/base/meta-pdf.lua index ed3d1ac18..b33d65bd9 100644 --- a/tex/context/base/meta-pdf.lua +++ b/tex/context/base/meta-pdf.lua @@ -47,8 +47,8 @@ resetall() -- todo: collect and flush packed using pdfliteral node injection but we're -- in no hurry as this kind of conversion does not happen that often in mkiv -local function pdfcode(str) - texsprint(ctxcatcodes,"\\pdfliteral{" .. str .. "}") +local function pdfcode(str) -- could be a node.write instead + texsprint(ctxcatcodes,"\\pdfliteral{",str,"}") end local function texcode(str) diff --git a/tex/context/base/mult-sys.tex b/tex/context/base/mult-sys.tex index c16ebee18..88f7132e4 100644 --- a/tex/context/base/mult-sys.tex +++ b/tex/context/base/mult-sys.tex @@ -160,6 +160,10 @@ \definesystemconstant {hascaption} \definesystemconstant {haslevel} +\definesystemconstant {mkiv} +\definesystemconstant {mkii} +\definesystemconstant {entities} + %D A more experienced \TEX\ user will recognize the next four %D constants. We need these because font-definitions are %D partially english. @@ -612,6 +616,7 @@ \definesystemvariable {wr} % WitRuimte \definesystemvariable {wl} % WordList \definesystemvariable {xf} % XML File +\definesystemvariable {xl} % lxml (mkiv) \definesystemvariable {xm} % xml (mkiv) \definesystemvariable {xp} % XML Processing \definesystemvariable {xy} % schaal diff --git a/tex/context/base/node-bck.lua b/tex/context/base/node-bck.lua index 24d0a1592..94fbac85f 100644 --- a/tex/context/base/node-bck.lua +++ b/tex/context/base/node-bck.lua @@ -6,6 +6,9 @@ if not modules then modules = { } end modules ['node-bck'] = { license = "see context related readme files" } +-- beware, this one takes quite some runtime, so we need a status flag +-- maybe some page related state + local hlist = node.id("hlist") local vlist = node.id("vlist") diff --git a/tex/context/base/node-fnt.lua b/tex/context/base/node-fnt.lua index 3ca6de0bd..72bb140ca 100644 --- a/tex/context/base/node-fnt.lua +++ b/tex/context/base/node-fnt.lua @@ -178,7 +178,6 @@ else do -- X000 1100 = 12 = 0x1C = leftghost -- X001 0100 = 20 = 0x14 = rightghost - function nodes.protect_glyphs(head) local done = false for g in traverse_id(glyph,head) do diff --git a/tex/context/base/node-inj.lua b/tex/context/base/node-inj.lua index 2a7ea9263..2befb0167 100644 --- a/tex/context/base/node-inj.lua +++ b/tex/context/base/node-inj.lua @@ -309,11 +309,12 @@ function nodes.inject_kerns(head,where,keep) -- if rlmode and rlmode < 0 then -- n.xoffset = p.xoffset + d[1] -- else +local k = wx[p] +if k then + n.xoffset = p.xoffset - d[1] - k[2] +else n.xoffset = p.xoffset - d[1] ---~ local k = wx[p] ---~ if k then ---~ wx[n] = k ---~ end +end -- end if mk[p] then n.yoffset = p.yoffset + d[2] diff --git a/tex/context/base/node-ref.lua b/tex/context/base/node-ref.lua index d7ead7163..02f6ca82d 100644 --- a/tex/context/base/node-ref.lua +++ b/tex/context/base/node-ref.lua @@ -112,6 +112,9 @@ local function inject_list(id,current,reference,make,stack,pardir,txtdir) if id == hlist then -- can be either an explicit hbox or a line and there is no way -- to recognize this; anyway only if ht/dp (then inline) + -- + -- to be tested: 0=unknown, 1=linebreak, 2=hbox +--~ if id.subtype == 1 then local sr = stack[reference] if first then if sr and sr[2] then @@ -133,6 +136,10 @@ local function inject_list(id,current,reference,make,stack,pardir,txtdir) else -- also weird end +--~ else +--~ print("!!!!!!!!!!!!!!!!!") + -- simple +--~ end else -- ok end @@ -185,26 +192,27 @@ local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,tx txtdir = current.dir end elseif id == hlist or id == vlist then ---~ if r and (not skip or r > skip) then if not reference and r and (not skip or r > skip) then inject_list(id,current,r,make,stack,pardir,txtdir) ---~ done[r] = true end -if r then done[r] = (done[r] or 0) + 1 end + if r then + done[r] = (done[r] or 0) + 1 + end local list = current.list if list then local _ current.list, _, pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir) end -if r then done[r] = done[r] - 1 end + if r then + done[r] = done[r] - 1 + end elseif not r then -- just go on, can be kerns elseif not reference then reference, first, last, firstdir = r, current, current, txtdir elseif r == reference then last = current ---~ elseif not done[reference] then -elseif (done[reference] or 0) == 0 then + elseif (done[reference] or 0) == 0 then if not skip or r > skip then head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) reference, first, last, firstdir = nil, nil, nil, nil @@ -214,7 +222,6 @@ elseif (done[reference] or 0) == 0 then end current = current.next end ---~ if reference and not done[reference] then if reference and (done[reference] or 0) == 0 then head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) end diff --git a/tex/context/base/pack-rul.mkiv b/tex/context/base/pack-rul.mkiv index 807532665..ecf69d3d6 100644 --- a/tex/context/base/pack-rul.mkiv +++ b/tex/context/base/pack-rul.mkiv @@ -2892,15 +2892,13 @@ \setbox\scratchbox\hbox to \hsize {\dimen4\dimexpr .5ex+.5\linewidth\relax \dimen6\dimexpr-.5ex+.5\linewidth\relax - \doifnothing{#1}\firstargumentfalse - \iffirstargument - \doifelse\@@tllocation\v!inmargin - {\llap{\doattributes\??tl\c!style\c!color{#1}\hskip\leftmargindistance}} - {\color[\@@tlrulecolor] - {\vrule\!!height\dimen4\!!depth\dimen6\!!width\@@tlwidth}% - \hbox spread 2\dimexpr\@@tldistance\relax - {\hss\doattributes\??tl\c!style\c!color{\strut#1}\hss}}% - \fi + \doifsomething{#1} + {\doifelse\@@tllocation\v!inmargin + {\llap{\doattributes\??tl\c!style\c!color{#1}\hskip\leftmargindistance}} + {\color[\@@tlrulecolor] + {\vrule\!!height\dimen4\!!depth\dimen6\!!width\@@tlwidth}% + \hbox spread 2\dimexpr\@@tldistance\relax + {\hss\doattributes\??tl\c!style\c!color{\strut#1}\hss}}}% \color[\@@tlrulecolor] {\leaders\hrule\!!height\dimen4\!!depth\dimen6\hfill}}% \ht\scratchbox\strutht diff --git a/tex/context/base/page-lin.lua b/tex/context/base/page-lin.lua index 0efb6314e..c8f5161d8 100644 --- a/tex/context/base/page-lin.lua +++ b/tex/context/base/page-lin.lua @@ -42,15 +42,16 @@ nodes.lines.scratchbox = nodes.lines.scratchbox or 0 -- cross referencing function nodes.lines.number(n) + n = tonumber(n) local cr = cross_references[n] or 0 cross_references[n] = nil return cr end -local function resolve(n,m) +local function resolve(n,m) -- we can now check the 'line' flag (todo) while n do local id = n.id - if id == whatsit then + if id == whatsit then -- why whatsit local a = has_attribute(n,line_reference) if a then cross_references[a] = m @@ -62,6 +63,33 @@ local function resolve(n,m) end end +function nodes.lines.finalize(t) + local getnumber = nodes.lines.number + for _,p in next, t do + for _,r in next, p do + if r.metadata.kind == "line" then + local e = r.entries + e.linenumber = getnumber(e.text or 0) + end + end + end +end + +local filters = jobreferences.filters +local helpers = structure.helpers + +jobreferences.registerfinalizer(nodes.lines.finalize) + +filters.line = filters.line or { } + +function filters.line.default(data) + helpers.title(data.entries.linenumber or "?",data.metadata) +end + +function filters.line.page(data,prefixspec,pagespec) -- redundant + helpers.prefixpage(data,prefixspec,pagespec) +end + -- boxed variant nodes.lines.boxed = { } diff --git a/tex/context/base/page-lin.mkiv b/tex/context/base/page-lin.mkiv index c82d4d520..c2dbfb774 100644 --- a/tex/context/base/page-lin.mkiv +++ b/tex/context/base/page-lin.mkiv @@ -95,12 +95,17 @@ \def\mkstoptextlinenumbering {\doresetattribute{line-number}} +% we could make this a bit more efficient by putting the end reference +% in the same table as the start one but why make thinsg complex ... + +\let\dofinishlinereference\dofinishfullreference + \def\mksomelinereference#1#2#3% {\dontleavehmode\begingroup \global\advance\linerefcounter\plusone \dosetattribute{line-reference}\linerefcounter - % this will change and is troublesome anyway - #3\textreference[#2]{\noexpand\ctxlua{tex.sprint(nodes.lines.number(\the\linerefcounter))}}% + #3% + \expanded{\dodosetreference{line}{#2}{}{\the\linerefcounter}}% kind labels userdata text \endgroup} \def\mkstartlinereference#1{\mksomelinereference{#1}{lr:b:#1}{}\ignorespaces} @@ -388,9 +393,9 @@ \def\doifelsesamelinereference#1#2#3% {\doifreferencefoundelse{lr:b:#1} - {\let\fline\currenttextreference + {\edef\fline{\currentreferencetext}% \doifreferencefoundelse{lr:e:#1} - {\let\tline\currenttextreference + {\edef\tline{\currentreferencetext}% \ifx\fline\tline#2\else#3\fi} {\unknownreference{#1}#2}} {\unknownreference{#1}#2}} diff --git a/tex/context/base/scrp-ini.lua b/tex/context/base/scrp-ini.lua index f0224d311..b28c297d0 100644 --- a/tex/context/base/scrp-ini.lua +++ b/tex/context/base/scrp-ini.lua @@ -265,6 +265,8 @@ end -- eventually we might end up with more extensive parsing -- todo: pass t[start..stop] == original +-- +-- one of the time consuming functiions: function scripts.preprocess(head) local start = first_character(head) diff --git a/tex/context/base/sort-lan.mkii b/tex/context/base/sort-lan.mkii index ad5232b02..26ee80405 100644 --- a/tex/context/base/sort-lan.mkii +++ b/tex/context/base/sort-lan.mkii @@ -200,4 +200,18 @@ \exportsortdivision {z+1} {zcaron} \stopmode +% Polish: + +\startmode[sortorder-pl] + \exportsortrule {aogonek} {a+1} + \exportsortrule {cacute} {c+1} + \exportsortrule {eogonek} {e+1} + \exportsortrule {lstroke} {l+1} + \exportsortrule {nacute} {n+1} + \exportsortrule {oacute} {o+1} + \exportsortrule {sacute} {s+1} + \exportsortrule {zacute} {z+1} + \exportsortrule {zdotaccent} {z+2} +\stopmode + \endinput diff --git a/tex/context/base/spac-ver.lua b/tex/context/base/spac-ver.lua index 2ef7afbf9..dbb5b0f39 100644 --- a/tex/context/base/spac-ver.lua +++ b/tex/context/base/spac-ver.lua @@ -309,7 +309,7 @@ do -- todo: interface.variables elseif keyword == k_category then local category = tonumber(detail) if category then - texsprint(ctxcatcodes,format("\\setblankcategory{%s}",category)) + texsprint(ctxcatcodes,"\\setblankcategory{",category,"}") if category ~= oldcategory then texsprint(ctxcatcodes,"\\flushblankhandling") oldcategory = category @@ -318,25 +318,20 @@ do -- todo: interface.variables elseif keyword == k_order and detail then local order = tonumber(detail) if order then - texsprint(ctxcatcodes,format("\\setblankorder{%s}",order)) + texsprint(ctxcatcodes,"\\setblankorder{",order,"}") end elseif keyword == k_penalty and detail then local penalty = tonumber(detail) if penalty then - texsprint(ctxcatcodes,format("\\setblankpenalty{%s}",penalty)) + texsprint(ctxcatcodes,"\\setblankpenalty{",penalty,"}") end else amount = tonumber(amount) or 1 local sk = skip[keyword] ---~ if sk then ---~ texsprint(ctxcatcodes,format("\\addblankskip{%s}{%s}{%s}",amount,sk[1],sk[2] or sk[1])) ---~ else -- no check ---~ texsprint(ctxcatcodes,format("\\addblankskip{%s}{%s}{%s}",amount,keyword,keyword)) ---~ end if sk then - texsprint(ctxcatcodes,format("\\addpredefinedblankskip{%s}{%s}",amount,keyword)) + texsprint(ctxcatcodes,"\\addpredefinedblankskip{",amount,"}{",keyword,"}") else -- no check - texsprint(ctxcatcodes,format("\\addaskedblankskip{%s}{%s}",amount,keyword)) + texsprint(ctxcatcodes,"\\addaskedblankskip{",amount,"}{",keyword,"}") end end end diff --git a/tex/context/base/spac-ver.mkiv b/tex/context/base/spac-ver.mkiv index 229a49193..7cf33169b 100644 --- a/tex/context/base/spac-ver.mkiv +++ b/tex/context/base/spac-ver.mkiv @@ -1594,6 +1594,8 @@ \unexpanded\def\vspacing {\dosingleempty\dovspacing} +% todo: when packed blocks blank, we need to enable forced + \def\dovspacing % blank also has a \flushnotes {\ifinpagebody \expandafter\dovspacingyes diff --git a/tex/context/base/strc-ini.lua b/tex/context/base/strc-ini.lua index a46827902..8fa304ed4 100644 --- a/tex/context/base/strc-ini.lua +++ b/tex/context/base/strc-ini.lua @@ -24,7 +24,7 @@ local format, concat, match = string.format, table.concat, string.match local count, texwrite, texprint, texsprint = tex.count, tex.write, tex.print, tex.sprint local type, next, tonumber, tostring = type, next, tonumber, tostring -local ctxcatcodes, xmlcatcodes = tex.ctxcatcodes, 11 -- tex.xmlcatcodes +local ctxcatcodes, xmlcatcodes = tex.ctxcatcodes, 11 -- tex.xmlcatcodes -- tricky as we're in notcatcodes local trace_processors = false trackers.register("structure.processors", function(v) trace_processors = v end) @@ -157,7 +157,7 @@ local tags = { entry = "ctx:registerentry", } -function helpers.title(title,metadata) +function helpers.title(title,metadata) -- brrr if title and title ~= "" then if metadata then if metadata.coding == "xml" then diff --git a/tex/context/base/strc-ref.lua b/tex/context/base/strc-ref.lua index a145098df..7698f203b 100644 --- a/tex/context/base/strc-ref.lua +++ b/tex/context/base/strc-ref.lua @@ -40,21 +40,31 @@ local exporters, runners = jobreferences.exporters, jobreferences.runners local currentreference = nil -jobreferences.initializers = jobreferences.initializers or { } +local initializers = { } +local finalizers = { } function jobreferences.registerinitializer(func) -- we could use a token register instead - jobreferences.initializers[#jobreferences.initializers+1] = func + initializers[#initializers+1] = func +end +function jobreferences.registerfinalizer(func) -- we could use a token register instead + finalizers[#finalizers+1] = func end local function initializer() tobesaved, collected = jobreferences.tobesaved, jobreferences.collected - for k,v in ipairs(jobreferences.initializers) do - v(tobesaved,collected) + for i=1,#initializers do + initializers[i](tobesaved,collected) + end +end +local function finalizer() + tobesaved = jobreferences.tobesaved + for i=1,#finalizers do + finalizers[i](tobesaved) end end if job then - job.register('jobreferences.collected', jobreferences.tobesaved, initializer) + job.register('jobreferences.collected', jobreferences.tobesaved, initializer, finalizer) end -- todo: delay split till later as in destinations we split anyway diff --git a/tex/context/base/strc-sec.mkiv b/tex/context/base/strc-sec.mkiv index 7510934fd..87e38274b 100644 --- a/tex/context/base/strc-sec.mkiv +++ b/tex/context/base/strc-sec.mkiv @@ -369,7 +369,7 @@ \c!saveinlist=\ifconditional\structureheadtolist\v!yes\else\v!no\fi, \c!level=\currentstructureheadlevel, \c!name=#1, - \c!number=\ifconditional\structureheadshownumber\v!yes\else\v!no\fi, + \c!number=\ifconditional\structureheaddoincrement\ifconditional\structureheadshownumber\v!yes\else\v!no\fi\else\v!no\fi, \c!bookmark=, \c!marking=, \c!list=, diff --git a/tex/context/base/supp-fil.mkii b/tex/context/base/supp-fil.mkii index 1e86498e4..fc0492442 100644 --- a/tex/context/base/supp-fil.mkii +++ b/tex/context/base/supp-fil.mkii @@ -392,6 +392,8 @@ \readfile{#1}{#2}{#3}% \popcatcodetable} +\ifdefined\xmlcatcodes \else \let\xmlcatcodes\xmlcatcodesn \fi + \def\readxmlfile#1#2#3% {\pushcatcodetable \catcodetable \xmlcatcodes \readfile{#1}{#2}{#3}% diff --git a/tex/context/base/syst-aux.mkiv b/tex/context/base/syst-aux.mkiv index a7c83f246..fe2fbefbe 100644 --- a/tex/context/base/syst-aux.mkiv +++ b/tex/context/base/syst-aux.mkiv @@ -3403,7 +3403,7 @@ %D for conditional errors. %D Krzysztof Leszczynski suggested to provide access to the level by -%D means of a \type {#1}. I decided to pass the more frquently used +%D means of a \type {#1}. I decided to pass the more frequently used %D level as \type {#1} and the less favoured depth as \type {#2}. The %D intended usage is: %D @@ -3431,6 +3431,15 @@ \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel \@EA\dodorecurse\@EA1\@EA{\number#1}} +\long\def\ydorecurse#1#2% + {\global\advance\outerrecurse \plusone + \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel + \let\recurselevel\!!plusone + \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname##1##2{#2}% + \expandrecursecontent + \@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname + \global\advance\outerrecurse \minusone} + \long\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 {\global\advance\outerrecurse \plusone \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname##1##2{#4}% diff --git a/tex/context/base/syst-ext.mkii b/tex/context/base/syst-ext.mkii index 649ab0530..f9827db8b 100644 --- a/tex/context/base/syst-ext.mkii +++ b/tex/context/base/syst-ext.mkii @@ -969,7 +969,7 @@ %D for conditional errors. %D Krzysztof Leszczynski suggested to provide access to the level by -%D means of a \type {#1}. I decided to pass the more frquently used +%D means of a \type {#1}. I decided to pass the more frequently used %D level as \type {#1} and the less favoured depth as \type {#2}. The %D intended usage is: %D @@ -997,6 +997,15 @@ \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel \@EA\dodorecurse\@EA1\@EA{\number#1}} +\long\def\ydorecurse#1#2% + {\global\advance\outerrecurse \plusone + \global\@EA\let\csname\@@irecurse\recursedepth\endcsname\recurselevel + \let\recurselevel\!!plusone + \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname##1##2{#2}% + \expandrecursecontent + \@EA\let\@EA\recurselevel\csname\@@irecurse\recursedepth\endcsname + \global\advance\outerrecurse \minusone} + \long\def\dostepwiserecurse#1#2#3#4% can be made faster by postponing #4 {\global\advance\outerrecurse \plusone \long\global\@EA\def\csname\@@arecurse\recursedepth\endcsname##1##2{#4}% diff --git a/tex/context/base/syst-ltx.tex b/tex/context/base/syst-ltx.tex new file mode 100644 index 000000000..ae89ff25b --- /dev/null +++ b/tex/context/base/syst-ltx.tex @@ -0,0 +1,56 @@ +%D \module +%D [ file=syst-ltx, +%D version=2009.10.13, +%D title=\CONTEXT\ System Macros, +%D subtitle=General, +%D author=Hans Hagen, +%D date=\currentdate, +%D copyright=PRAGMA] +%C +%C This module is part of the \CONTEXT\ macro||package and is +%C therefore copyrighted by \PRAGMA. See mreadme.pdf for +%C details. + +%D \macros +%D {newcommand} +%D +%D This module replaces \type {t-bibltx} and implements the +%D \LATEX\ newcommand (sort of). This command is not to be +%D used directly but only is there for the occasional +%D \BIBTEX\ file that uses it. +%D +%D \starttyping +%D \newcommand\oeps {oeps} +%D \newcommand\oeps[6] {oeps[#1]#6} +%D \newcommand\oeps[6][whatever]{oeps[#1]#6} +%D \stoptyping + +\unprotect + +\def\newcommand#1% + {\dotripleempty\donewcommand[#1]} + +\def\donewcommand + {\ifthirdargument + \expandafter\donewcommandtwo + \else\ifsecondargument + \expandafter\expandafter\expandafter\donewcommandone + \else + \expandafter\expandafter\expandafter\donewcommandzero + \fi\fi} + +\def\donewcommandzero[#1][#2][#3]% + {\long\def#1} + +\def\donewcommandone[#1][#2][#3]% + {\scratchtoks{\long\def#1}% + \dorecurse{#2}{\scratchtoks\expandafter{\the\scratchtoks######1}}% + \the\scratchtoks} + +\def\donewcommandtwo[#1][#2][#3]% + {\long\def#1{\doifnextcharelse[{\getvalue{>>\string#1>>}}{\getvalue{>>\string#1>>}[#3]}}% + \scratchtoks{\long\setvalue{>>\string#1>>}[##1]}% + \dostepwiserecurse{2}{#2}{1}{\scratchtoks\expandafter{\the\scratchtoks######1}}% + \the\scratchtoks} + +\protect \endinput diff --git a/tex/context/base/x-ct.mkiv b/tex/context/base/x-ct.mkiv index 17ea25408..03a0e4b69 100644 --- a/tex/context/base/x-ct.mkiv +++ b/tex/context/base/x-ct.mkiv @@ -45,8 +45,8 @@ do local function specifiedtemplate(root,templatespec) local template = { } - for r, d, k in xml.elements(root,templatespec) do - local at = d[k].at + for e in xml.collected(root,templatespec) do + local at = e.at local tm = halignments[at.align] or "" if toboolean(at.paragraph) then tm = tm .. "p" @@ -54,7 +54,7 @@ do template[#template+1] = tm end if #template > 0 then - return "|" .. table.join(template,"|") .. "|" + return "|" .. table.concat(template,"|") .. "|" else return nil end @@ -62,8 +62,8 @@ do local function autotemplate(root,rowspec,cellspec) local max = 0 - for r, d, k in xml.elements(root,rowspec) do - local n = xml.count(d[k],cellspec) + for e in xml.collected(root,rowspec) do + local n = xml.count(e,cellspec) if n > max then max = n end end if max == 2 then @@ -101,10 +101,10 @@ do lxml.directives.setup(root,'cdx') -- todo: head and foot texsprint(tex.ctxcatcodes, string.format("\\starttabulate[%s]",template)) - for r, d, k in xml.elements(root,bodyrowspec) do + for e in xml.collected(root,bodyrowspec) do texsprint(tex.ctxcatcodes, "\\NC ") - for r, d, k in xml.elements(d[k],cellspec) do - texsprint(xml.content(d[k])) + for e in xml.collected(e,cellspec) do + texsprint(xml.content(e)) texsprint(tex.ctxcatcodes, "\\NC") end texsprint(tex.ctxcatcodes, "\\NR") @@ -141,13 +141,12 @@ do lxml.directives.setup(root,'cdx') -- todo: alignments texsprint(tex.ctxcatcodes, string.format("\\startcombination[%s]",template)) - for r, d, k in xml.elements(root,pairspec) do - local dk = d[k] + for e in xml.collected(root,pairspec) do texsprint(tex.ctxcatcodes,"{") - xmlsprint(xml.filter(dk,contentspec) or "") + xmlsprint(xml.filter(e,contentspec) or "") texsprint(tex.ctxcatcodes,"}") texsprint(tex.ctxcatcodes,"{") - xmlsprint(xml.filter(dk,captionspec) or "") + xmlsprint(xml.filter(e,captionspec) or "") texsprint(tex.ctxcatcodes,"}") end texsprint(tex.ctxcatcodes, "\\stopcombination") @@ -169,34 +168,3 @@ end \xmlregisterns{context}{context} \endinput - -% this replaces: - -% \startxmlsetups tabulate -% \starttabulate[||p|] -% \xmlall{#1}{/body/row} -% \stoptabulate -% \stopxmlsetups -% \startxmlsetups tabulate:row -% \NC \xmlall{#1}{/cell} \NR -% \stopxmlsetups -% \startxmlsetups tabulate:cell -% \xmlflush{#1} \NC -% \stopxmlsetups - -% \startxmlsetups combination -% \startcollecting -% \startcollect -% \startcombination[\xmlatt{#1}{nx}*\xmlatt{#1}{ny}] -% \stopcollect -% \xmlloop {#1} {combiblock} { -% \startcollect -% {\xmli{/content}{##1}} -% {\xmli{/caption}{##1}} -% \stopcollect -% } -% \startcollect -% \stopcombination -% \stopcollect -% \stopcollecting -% \stopxmlsetups diff --git a/tex/context/base/x-ldx.tex b/tex/context/base/x-ldx.tex index 750529649..eb242245c 100644 --- a/tex/context/base/x-ldx.tex +++ b/tex/context/base/x-ldx.tex @@ -133,8 +133,6 @@ \def\xmldocument{ldx} -% \processXMLfileMKIV[ldx] - \usemodule[abr-02] % key -> kw diff --git a/tex/context/base/x-mathml.lua b/tex/context/base/x-mathml.lua index 46f7248c6..d1ac4f62d 100644 --- a/tex/context/base/x-mathml.lua +++ b/tex/context/base/x-mathml.lua @@ -6,22 +6,17 @@ if not modules then modules = { } end modules ['x-mathml'] = { license = "see context related readme files" } +local type, pairs = type, pairs local utf = unicode.utf8 +local texsprint, ctxcatcodes = tex.sprint, tex.ctxcatcodes +local format, lower = string.format, string.lower +local utfchar, utffind, utfgmatch = utf.char, utf.find, utf.gmatch +local xmlsprint, xmlcprint = xml.sprint, xml.cprint +local utfcharacters, utfvalues = string.utfcharacters, string.utfvalues -lxml = lxml or { } lxml.mml = lxml.mml or { } -local texsprint = tex.sprint -local format = string.format -local lower = string.lower -local utfchar = utf.char -local utffind = utf.find -local xmlsprint = xml.sprint -local xmlcprint = xml.cprint - -local ctxcatcodes = tex.ctxcatcodes - -local utfcharacters, utfvalues = string.utfcharacters, string.utfvalues +local get_id = lxml.get_id -- an alternative is to remap to private codes, where we can have -- different properties .. to be done; this will move and become @@ -389,30 +384,29 @@ local csymbols = { }, } -function xml.functions.remapmmlcsymbol(r,d,k) - local dk = d[k] - local at = dk.at +function xml.functions.remapmmlcsymbol(e) + local at = e.at local cd = at.cd if cd then cd = csymbols[cd] if cd then - local tx = dk.dt[1] + local tx = e.dt[1] if tx and tx ~= "" then local tg = cd[tx] if tg then at.cd = nil at.cdbase = nil - dk.dt = { } + e.dt = { } if type(tg) == "table" then for k, v in pairs(tg) do if k == "tag" then - dk.tg = v + e.tg = v else at[k] = v end end else - dk.tg = tg + e.tg = tg end end end @@ -420,34 +414,32 @@ function xml.functions.remapmmlcsymbol(r,d,k) end end -function xml.functions.remapmmlbind(r,d,k) - d[k].tg = "apply" +function xml.functions.remapmmlbind(e) + e.tg = "apply" end -function xml.functions.remapopenmath(r,d,k) - local dk = d[k] - local tg = dk.tg +function xml.functions.remapopenmath(e) + local tg = e.tg if tg == "OMOBJ" then - dk.tg = "math" + e.tg = "math" elseif tg == "OMA" then - dk.tg = "apply" + e.tg = "apply" elseif tg == "OMB" then - dk.tg = "apply" + e.tg = "apply" elseif tg == "OMS" then - -- xml.functions.remapmmlcsymbol(r,d,k) - local at = dk.at - dk.tg = "csymbol" - dk.dt = { at.name or "unknown" } + local at = e.at + e.tg = "csymbol" + e.dt = { at.name or "unknown" } at.name = nil elseif tg == "OMV" then - local at = dk.at - dk.tg = "ci" - dk.dt = { at.name or "unknown" } + local at = e.at + e.tg = "ci" + e.dt = { at.name or "unknown" } at.name = nil elseif tg == "OMI" then - dk.tg = "ci" + e.tg = "ci" end - dk.rn = "mml" + e.rn = "mml" end function lxml.mml.checked_operator(str) @@ -461,8 +453,7 @@ end function lxml.mml.mn(id,pattern) -- maybe at some point we need to interpret the number, but -- currently we assume an upright font - local str = xml.content(lxml.id(id),pattern) or "" - -- str = str:gsub("^%s*(.-)%s*$","%1") + local str = xml.content(get_id(id),pattern) or "" str = str:gsub("(%s+)",utfchar(0x205F)) -- medspace e.g.: twenty one (nbsp is not seen) texsprint(ctxcatcodes,(str:gsub(".",n_replacements))) end @@ -472,12 +463,12 @@ function characters.remapentity(chr,slot) end function lxml.mml.mo(id,pattern) - local str = xml.content(lxml.id(id),pattern) or "" + local str = xml.content(get_id(id),pattern) or "" texsprint(ctxcatcodes,(utf.gsub(str,".",o_replacements))) end function lxml.mml.mi(id,pattern) - local str = xml.content(lxml.id(id),pattern) or "" + local str = xml.content(get_id(id),pattern) or "" -- str = str:gsub("^%s*(.-)%s*$","%1") local rep = i_replacements[str] if rep then @@ -499,7 +490,7 @@ end --~ local rightdelimiters = "[" .. table.keys_as_string(r_replacements) .. "]" function lxml.mml.mfenced(id,pattern) -- multiple separators - id = lxml.id(id) + id = get_id(id) local left, right, separators = id.at.open or "(", id.at.close or ")", id.at.separators or "," local l, r = l_replacements[left], r_replacements[right] texsprint(ctxcatcodes,"\\enabledelimiter") @@ -510,30 +501,33 @@ function lxml.mml.mfenced(id,pattern) -- multiple separators texsprint(ctxcatcodes,left) end texsprint(ctxcatcodes,"\\disabledelimiter") - local n = xml.count(id,pattern) - if n == 0 then - -- skip - elseif n == 1 then - lxml.all(id,pattern) - else - local t = { } - for s in utf.gmatch(separators,"[^%s]") do - t[#t+1] = s - end - for i=1,n do - lxml.idx(id,pattern,i) -- kind of slow, some day ... - if i < n then - local m = t[i] or t[#t] or "" - if m == "|" then - m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter" - elseif m == doublebar then - m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter" - elseif m == "{" then - m = "\\{" - elseif m == "}" then - m = "\\}" + local collected = lxml.filter(id,pattern) + if collected then + local n = #collected + if n == 0 then + -- skip + elseif n == 1 then + lxml.all(id,pattern) + else + local t = { } + for s in utfgmatch(separators,"[^%s]") do + t[#t+1] = s + end + for i=1,n do + xmlsprint(collected[i]) -- to be checked + if i < n then + local m = t[i] or t[#t] or "" + if m == "|" then + m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter" + elseif m == doublebar then + m = "\\enabledelimiter\\middle|\\relax\\disabledelimiter" + elseif m == "{" then + m = "\\{" + elseif m == "}" then + m = "\\}" + end + texsprint(ctxcatcodes,m) end - texsprint(ctxcatcodes,m) end end end @@ -569,10 +563,7 @@ end function lxml.mml.mmultiscripts(id) local done, toggle = false, false - id = lxml.id(id) - -- for i=1,#id.dt do local e = id.dt[i] if type(e) == table then ... - for r, d, k in xml.elements(id,"/*") do - local e = d[k] + for e in lxml.collected(id,"/*") do local tag = e.tg if tag == "mprescripts" then texsprint(ctxcatcodes,"{}") @@ -582,8 +573,7 @@ function lxml.mml.mmultiscripts(id) end end local done, toggle = false, false - for r, d, k in xml.elements(id,"/*") do - local e = d[k] + for e in lxml.collected(id,"/*") do local tag = e.tg if tag == "mprescripts" then break @@ -621,10 +611,10 @@ local frametypes = { function lxml.mml.mcolumn(root) root = lxml.id(root) local matrix, numbers = { }, 0 - local function collect(m,dk) - local tag = dk.tg + local function collect(m,e) + local tag = e.tg if tag == "mi" or tag == "mn" or tag == "mo" or tag == "mtext" then - local str = xml.content(dk) + local str = xml.content(e) for s in utfcharacters(str) do -- utf.gmatch(str,".") btw, the gmatch was bugged m[#m+1] = { tag, s } end @@ -635,25 +625,24 @@ function lxml.mml.mcolumn(root) end end elseif tag == "mspace" or tag == "mline" then - local str = dk.at.spacing or "" + local str = e.at.spacing or "" for s in utfcharacters(str) do -- utf.gmatch(str,".") btw, the gmatch was bugged m[#m+1] = { tag, s } end elseif tag == "mline" then - m[#m+1] = { tag, dk } + m[#m+1] = { tag, e } end end - for r, d, k in xml.elements(root,"/*") do + for e in lxml.collected(root,"/*") do local m = { } matrix[#matrix+1] = m - local dk = d[k] - if dk.tg == "mrow" then + if e.tg == "mrow" then -- only one level - for r, d, k in xml.elements(dk,"/*") do - collect(m,d[k]) + for e in lxml.collected(e,"/*") do + collect(m,e) end else - collect(m,dk) + collect(m,e) end end tex.sprint(ctxcatcodes,"\\halign\\bgroup\\hss$#$&$#$\\cr") @@ -716,10 +705,8 @@ end local spacesplitter = lpeg.Ct(lpeg.splitat(" ")) function lxml.mml.mtable(root) - root = lxml.id(root) - -- todo: align, rowspacing, columnspacing, rowlines, columnlines - + root = get_id(root) local at = root.at local rowalign = at.rowalign local columnalign = at.columnalign @@ -732,23 +719,21 @@ function lxml.mml.mtable(root) texsprint(ctxcatcodes, format("\\bTABLE[frame=%s,offset=%s]",frametypes[frame or "none"] or "off",framespacing)) --~ context.bTABLE { frame = frametypes[frame or "none"] or "off", offset = framespacing } - for r, d, k in xml.elements(root,"/(mml:mtr|mml:mlabeledtr)") do + for e in lxml.collected(root,"/(mml:mtr|mml:mlabeledtr)") do texsprint(ctxcatcodes,"\\bTR") --~ context.bTR() - local dk = d[k] - local at = dk.at + local at = e.at local col = 0 local rfr = at.frame or (frames and frames [#frames]) local rra = at.rowalign or (rowaligns and rowaligns [#rowaligns]) local rca = at.columnalign or (columnaligns and columnaligns[#columnaligns]) - local ignorelabel = dk.tg == "mlabeledtr" - for rr, dd, kk in xml.elements(dk,"/mml:mtd") do + local ignorelabel = e.tg == "mlabeledtr" + for e in lxml.collected(e,"/mml:mtd") do -- nested we can use xml.collected col = col + 1 if ignorelabel and col == 1 then -- get rid of label, should happen at the document level else - local dk = dd[kk] - local at = dk.at + local at = e.at local rowspan, columnspan = at.rowspan or 1, at.columnspan or 1 local cra = rowalignments [at.rowalign or (rowaligns and rowaligns [col]) or rra or "center"] or "lohi" local cca = columnalignments[at.columnalign or (columnaligns and columnaligns[col]) or rca or "center"] or "middle" @@ -758,16 +743,16 @@ function lxml.mml.mtable(root) --~ context.bTD { align = format("{%s,%s}",cra,cca), frame = cfr, nx = columnspan, ny = rowspan } --~ context.bmath() --~ context.ignorespaces() - xmlcprint(dk) + xmlcprint(e) texsprint(ctxcatcodes,"\\removeunwantedspaces$\\eTD") -- $ --~ context.emath() --~ context.removeunwantedspaces() --~ context.eTD() end end ---~ if dk.tg == "mlabeledtr" then +--~ if e.tg == "mlabeledtr" then --~ texsprint(ctxcatcodes,"\\bTD") ---~ xmlcprint(xml.first(dk,"/!mml:mtd")) +--~ xmlcprint(xml.first(e,"/!mml:mtd")) --~ texsprint(ctxcatcodes,"\\eTD") --~ end texsprint(ctxcatcodes,"\\eTR") @@ -778,19 +763,21 @@ function lxml.mml.mtable(root) end function lxml.mml.csymbol(root) - root = lxml.id(root) - local encoding = root.at.encoding or "" - local hash = url.hashed(lower(root.at.definitionUrl or "")) + root = get_id(root) + local at = root.at + local encoding = at.encoding or "" + local hash = url.hashed(lower(at.definitionUrl or "")) local full = hash.original or "" local base = hash.path or "" - local text = string.strip(xml.content(root) or "") - texsprint(ctxcatcodes,format("\\mmlapplycsymbol{%s}{%s}{%s}{%s}",full,base,encoding,text)) + local text = string.strip(lxml.content(root)) +--~ texsprint(ctxcatcodes,format("\\mmlapplycsymbol{%s}{%s}{%s}{%s}",full,base,encoding,text)) + texsprint(ctxcatcodes,"\\mmlapplycsymbol{",full,"}{",base,"}{",encoding,"}{",text,"}") end function lxml.mml.menclosepattern(root) - root = lxml.id(root) + root = get_id(root) local a = root.at.notation if a and a ~= "" then - texsprint("mml:enclose:"..a:gsub(" +",",mml:enclose:")) + texsprint("mml:enclose:",a:gsub(" +",",mml:enclose:")) end end diff --git a/tex/context/base/x-mathml.mkiv b/tex/context/base/x-mathml.mkiv index 73958e129..e58dee765 100644 --- a/tex/context/base/x-mathml.mkiv +++ b/tex/context/base/x-mathml.mkiv @@ -28,7 +28,7 @@ \xmlfilter {\xmldocument} {omt:*/function(remapopenmath)} \xmlfilter {\xmldocument} {mml:bind/function(remapmmlbind)} \xmlfilter {\xmldocument} {mml:csymbol/function(remapmmlcsymbol)} - \xmlsetsetup{\xmldocument} {mml:*} {*} + \xmlsetsetup{\xmldocument} {mml:*} {mml:*} \xmlsetsetup{\xmldocument} {mml:apply/mml:apply/mml:inverse/../../..} {mml:apply:inverse} \xmlstrip {\xmldocument} {(mml:mi|mml:mo|mml:mn|mml:mtext|mml:csymbol)} \stopxmlsetups @@ -334,7 +334,7 @@ \MMLcreset \MMLdoL \mmlfirst{#1} - \ifnum\xmlnofelements{#1}>\plusone + \ifnum\xmlcount{#1}>\plusone \negthinspace % not enough \left(\MMLcreset\xmlconcatrange{#1}{/*}{2}{}{\MMLseparator,}\right) \fi @@ -354,7 +354,7 @@ \startxmlsetups mml:apply:mml:ci \xmlfirst{#1}{/mml:ci} - \ifnum\xmlnofelements{#1}>\plusone + \ifnum\xmlcount{#1}>\plusone \left(\MMLcreset\xmlconcatrange{#1}{/*}{2}{}{\MMLseparator,}\right) \fi \stopxmlsetups @@ -368,7 +368,7 @@ % fn \startxmlsetups mmc:fn:\utfchar{"00B1} % plusminus ± - \ifnum\xmlnofelements{#1}>\plustwo + \ifnum\xmlcount{#1}>\plustwo \MMLcreset \left(\xmlconcat{#1}{/*}{2}{}{\mmlfirst{#1}}\right) \else @@ -382,7 +382,7 @@ \startxmlsetups mmc:fn \begingroup - \edef\mmlnoffn{\xmlnofelements{#1}} + \edef\mmlnoffn{\xmlcount{#1}} \ifnum\mmlnoffn>\plustwo \def\MMCfnleft {\left(} \def\MMCfnright{\right)} @@ -428,7 +428,7 @@ } { \MMLcreset \mmlfirst{#1} - \ifnum\xmlnofelements{#1}>\plusone + \ifnum\xmlcount{#1}>\plusone \negthinspace \left(\MMLcreset\xmlconcat{#1}{2}{}{\MMLseparator,}\right) \fi @@ -759,7 +759,7 @@ \doif \MMLdeclarestate \v!start { \mathopnolimits{declare} \mmlfirst{#1} - \ifnum\xmlnofelements{#1}>\plusone + \ifnum\xmlcount{#1}>\plusone \thickspace \mathopnolimits{as} \thickspace @@ -1589,7 +1589,7 @@ \startxmlsetups mml:vector \begingroup - \ifnum\xmlnofelements{#1}>\plusone + \ifnum\xmlcount{#1}>\plusone \doifelse\MMLvectordirection\v!horizontal { \left(\xmlconcat{#1}{/*}{\MMLseparator\MMLvectorseparator}\right) } { @@ -1846,12 +1846,12 @@ % setups \startxmlsetups mml:mi % todo: mathvariant mathsize mathcolor mathbackground - \ctxlua{lxml.mml.mi("#1","*")} + \ctxlua{lxml.mml.mi("#1","/*")} \stopxmlsetups \startxmlsetups mml:mn % todo: mathvariant mathsize mathcolor mathbackground \begingroup - \mr \ctxlua{lxml.mml.mn("#1","*")}% no \hbox, would be ok for . , but spoils rest + \mr \ctxlua{lxml.mml.mn("#1","/*")}% no \hbox, would be ok for . , but spoils rest \endgroup \stopxmlsetups @@ -1862,7 +1862,7 @@ \startxmlsetups mml:mo \doif {\xmlatt{#1}{maxsize}} {1} {\settrue\mmlignoredelimiter} \doif {\xmlatt{#1}{stretchy}} {false} {\settrue\mmlignoredelimiter} - \ctxlua{lxml.mml.mo("#1","*")} + \ctxlua{lxml.mml.mo("#1","/*")} \setfalse\mmlignoredelimiter \stopxmlsetups @@ -2074,8 +2074,9 @@ \startxmlsetups mml:mrow \begingroup - \ifnum\xmlcount{#1}{/mml:mo}=\plustwo - \xmldoifelse {#1} {/mml:mo[position()==1 or position()==\xmlnofelements{#1}]} {% we need a {} + \edef\nofmmlrows{\xmlcount{#1}{/mml:mo}}% + \ifnum\nofmmlrows=\plustwo + \xmldoifelse {#1} {/mml:mo[position()==1 or position()==\nofmmlrows]} {% we need a {} \def\MMLleft {\left } \def\MMLright {\right} \def\MMLmiddle{\middle} @@ -2140,12 +2141,12 @@ \startxmlsetups mml:mover % \mathop { - \edef\mmlovertoken{\xmlraw{#1}{/mml:*[position()==2]}} + \edef\mmlovertoken{\xmlraw{#1}{/mml:*[2]}} \doifelse{\utfmathclass\mmlovertoken}{accent} { \edef\mmlovercommand{\utfmathcommand\mmlovertoken} \mmlexecuteifdefined\mmlovercommand\mathematics{\mmlfirst{#1}} } { - \edef\mmlbasetoken{\xmlraw{#1}{/mml:*[position()==1]}} + \edef\mmlbasetoken{\xmlraw{#1}{/mml:*[1]}} \edef\mmlbasecommand{\utfmathfiller\mmlbasetoken} \edef\mmlovercommand{\utfmathfiller\mmlovertoken} \vbox { @@ -2164,12 +2165,12 @@ \startxmlsetups mml:munder % \mathop { - \edef\mmlundertoken{\xmlraw{#1}{/mml:*[position()==2]}} + \edef\mmlundertoken{\xmlraw{#1}{/mml:*[2]}} \doifelse{\utfmathclass\mmlundertoken}{accent} { \edef\mmlundercommand{\utfmathcommand\mmlundertoken} \mmlexecuteifdefined\mmlundercommand\mathematics{\mmlfirst{#1}} } { - \edef\mmlbasetoken {\xmlraw{#1}{/mml:*[position()==1]}} + \edef\mmlbasetoken {\xmlraw{#1}{/mml:*[1]}} \edef\mmlbasecommand {\utfmathfiller\mmlbasetoken} \edef\mmlundercommand{\utfmathfiller\mmlundertoken} \vtop { @@ -2187,7 +2188,7 @@ \stopxmlsetups \startxmlsetups mml:munderover - \edef\mmlbasetoken{\xmlraw{#1}{/mml:*[position()==1]}} + \edef\mmlbasetoken{\xmlraw{#1}{/mml:*[1]}} \edef\mmlbasecommand{\utfmathcommand\mmlbasetoken} \mmlexecuteifdefined\mmlbasecommand{\mathematics{\mmlfirst{#1}}}_{\mmlsecond{#1}}^{\mmlthird{#1}} \stopxmlsetups diff --git a/tex/context/base/x-pending.mkiv b/tex/context/base/x-pending.mkiv index 114b81890..20fe5fb6a 100644 --- a/tex/context/base/x-pending.mkiv +++ b/tex/context/base/x-pending.mkiv @@ -17,6 +17,7 @@ %D \MKII\ code, when enabled. \startluacode + dofile(resolvers.find_file("lxml-inf.lua")) local list = { } function document.check_pending_xml_element(str) list[str] = (list[str] and (list[str]+1)) or 1 diff --git a/tex/context/base/x-set-11.mkiv b/tex/context/base/x-set-11.mkiv index 02d75fc4c..639cac03e 100644 --- a/tex/context/base/x-set-11.mkiv +++ b/tex/context/base/x-set-11.mkiv @@ -12,6 +12,11 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. +% \startluacode +% collectgarbage("stop") +% function collectgarbage() return 0 end +% \stopluacode + % todo: for fun: pure lua interface, but as this style evolved over 15 years % it's a waste of time % @@ -141,7 +146,8 @@ {\doifsomething{#1} {\doonlyonce{setups:#1} {\xmlloadonly{setups}{#1}{setups}% - \xmlcommand{setups}{/interface/command}{xml:setups:register}}}} % qualified path saves > 50% runtime +% \xmlcommand{setups}{/interface/command}{xml:setups:register}}}} % qualified path saves > 50% runtime + \xmlfilter{setups}{/interface/command/command(xml:setups:register)}}}} % qualified path saves > 50% runtime \newif\ifshortsetup @@ -170,7 +176,8 @@ \showsetupindeed{#1}} \def\showsetupindeed#1% - {\xmlcommand{setups}{/interface/command[@name='#1']}{xml:setups:typeset}} +% {\xmlcommand{setups}{/interface/command[@name='#1']}{xml:setups:typeset}} + {\xmlfilter{setups}{/interface/command[@name='#1']/command(xml:setups:typeset)}} \def\placesetup {\placelistofsorts[texcommand][\c!criterium=\v!used]} \def\placeallsetups{\placelistofsorts[texcommand][\c!criterium=\v!all ]} @@ -206,7 +213,8 @@ } { \tex{} } - \xmlfirst{#1}{/sequence} +% \xmlfirst{#1}{/sequence} + \xmlfilter{#1}{/sequence/first()} \ignorespaces \egroup \xmldoif{#1}{/arguments} { @@ -214,7 +222,8 @@ \enablemode[setups-pass-one] \doglobal\newcounter\currentSETUPargument \ignorespaces - \xmltext{#1}{/arguments} +% \xmltext{#1}{/arguments} + \xmlfilter{#1}{/arguments/text()} \egroup } \doif {\xmlatt{#1}{type}} {environment} { @@ -224,7 +233,8 @@ \ttsl } \tex{\e!stop} - \xmlfirst{#1}{/sequence/variable} +% \xmlfirst{#1}{/sequence/variable} + \xmlfilter{#1}{/sequence/variable/first()} \ignorespaces \egroup } @@ -235,7 +245,8 @@ \doglobal\newcounter\currentSETUPargument \blank[\v!line] \switchtobodyfont[small] - \ignorespaces\xmltext{#1}{/arguments}\endgraf +% \ignorespaces\xmltext{#1}{/arguments}\endgraf + \ignorespaces\xmlfilter{#1}{/arguments/text()}\endgraf \egroup } \getvalue{\e!stop setuptext} @@ -249,7 +260,8 @@ \startxmlsetups xml:setups:resolve \ignorespaces - \xmlfirst{setups}{define[@name='\xmlatt{#1}{name}']} +% \xmlfirst{setups}{define[@name='\xmlatt{#1}{name}']} + \xmlfilter{setups}{/interface/define[@name='\xmlatt{#1}{name}']/first()} \stopxmlsetups %D This is the first pass; here we generate the top line. @@ -320,7 +332,8 @@ } { \xdef\currentSETUPwidth{0pt}% \bgroup - \xmlcommand{#1}{/parameter}{xml:setups:parameter:measure} +% \xmlcommand{#1}{/parameter}{xml:setups:parameter:measure} + \xmlfilter{#1}{/parameter/command(xml:setups:parameter:measure)} \egroup \startfirstSETUPcolumn{\showSETUPnumber}% % \xmldoifelseempty {#1} {} { diff --git a/tex/context/bib/t-bib.mkii b/tex/context/bib/t-bib.mkii deleted file mode 100644 index ac3494725..000000000 --- a/tex/context/bib/t-bib.mkii +++ /dev/null @@ -1,5 +0,0 @@ -% some code will move here - -\unprotect - -\protect \endinput diff --git a/tex/context/bib/t-bib.mkiv b/tex/context/bib/t-bib.mkiv deleted file mode 100644 index 9a01bc7cd..000000000 --- a/tex/context/bib/t-bib.mkiv +++ /dev/null @@ -1,62 +0,0 @@ -%D Note by HH: -%D -%D We use a still somewhat experimental extension to the list -%D mechanism. Eventually the bibtex module will use the bibl loader -%D and access the data by means of lpath expressions. In that case we -%D don't need to process the bibliography but still need to track -%D usage as done here. - -\unprotect - -\startluacode -local list = { } - -bibtexhacks = { - reset = function() list = { } end, - add = function(str) list[#list+1] = str end, - flush = function() tex.sprint(table.concat(list,",")) end, -} -\stopluacode - -% HACK WILL GO: - -\def\namedlistparameter#1#2{\csname\dolistparameter{\??li#1}#2\endcsname} - -% TILL HERE - -\let\bibrefprefixcounter\!!plusone -\def\bibrefprefix {\bibrefprefixcounter:} -\let\preparebibrefprefix\relax -\let\preparebibreflist \gobbleoneargument -\let\bibreflist \empty - -\setuplist[pubs][\c!state=\s!start] - -\installstructurelistprocessor{pubs:userdata} - {\ctxlua{bibtexhacks.add(structure.lists.uservalue("\currentlist",\currentlistindex,"bibref"))}} - -\def\docitation#1% - {\expanded{\writedatatolist[pubs][bibref=#1]}} - -\def\filllocalpublist - {\edef\currentlist{pubs}% - \doif{\listparameter\c!criterium}{cite}{\setuplist[pubs][\c!criterium=\v!here]}% - \ctxlua{bibtexhacks.reset()}% - \placestructurelist{pubs}{\listparameter\c!criterium}{\listparameter\c!number}% - \edef\localpublist{\ctxlua{bibtexhacks.flush()}}} - -\def\gotobiblink#1[#2]{\doifreferencefoundelse{\bibrefprefix#2}{\goto{#1}[\bibrefprefix#2]}{\unknownreference{#2}}} -\def\atbiblink [#1]{\doifreferencefoundelse{\bibrefprefix#1}{\at [\bibrefprefix#1]}{\unknownreference{#1}}} -\def\inbiblink [#1]{\doifreferencefoundelse{\bibrefprefix#1}{\at [\bibrefprefix#1]}{\unknownreference{#1}}} - -\ifdefined\normaldodoplacepublications \else % just in case we load twice - - \let\normaldodoplacepublications\dodoplacepublications - - \def\dodoplacepublications - {\normaldodoplacepublications - \doglobal\increment\bibrefprefixcounter} - -\fi - -\protect \endinput diff --git a/tex/context/bib/t-bib.tex b/tex/context/bib/t-bib.tex deleted file mode 100644 index 11ed1309c..000000000 --- a/tex/context/bib/t-bib.tex +++ /dev/null @@ -1,1929 +0,0 @@ -%D \module -%D [ file=t-bib, -%D version=2009.08.13, -%D title=\CONTEXT\ Publication Module, -%D subtitle=Publications, -%D author=Taco Hoekwater, -%D date=\currentdate, -%D copyright=Public Domain] -%C -%C Donated to the public domain. - - -%D The original was developed independantly by Taco Hoekwater while still working for Kluwer -%D Academic publishers (it still used the dutch interface then). Development continued after -%D he left Kluwer, and in Januari 2005, the then already internationalized file was merged -%D with the core distribution by Hans Hagen. The current version is once again by Taco. -%D -%D More documentation and additional resources can be found on the contextgarden: -%D \hyphenatedurl{http://wiki.contextgarden.net//Bibliography}. - -%D \subject{DONE (dd/mm/yyyy)} -%D -%D \startitemize -%D \item add author definition (and associated system variable) (26/05/2005) -%D \item add finalnamesep support for Oxford comma (17/09/2005) -%D \item add \type{\insert...} for: doi, eprint, howpublished (19/09/2005) -%D \item allow a defaulted \type{\setupcite} (19/11/2005) -%D \item renamed citation type 'number' to 'serial' (19/11/2005) -%D \item better definition of \type{\inverted...author} (19/11/2005) -%D \item don't reset [numbercommand] in \type {\setuppublication} by default (20/11/2005) -%D \item don't disable other \type {\setuppublication} keys if alternative is present (20/11/2005) -%D \item drop \type{\sanitizeaccents} (20/11/2005) -%D \item added \type{\nocite} and \type{\cite[none]} (21/11/2005) -%D \item added headtext for it (23/11/2005) -%D \item make \type{\cite[url]} and \type{\cite[doi]} interactive (23/11/2005) -%D \item make right-aligned labels in the list work even when autohang=no -%D \item use 'et al.' instead of 'et.al.'. Pointed out by Peter M�nster (30/12/2005) -%D \item added headtext for cz (31/12/2005) -%D \item Keep whitespace after \type{\cite} with single argument (31/12/2005) -%D \item Fix broken \type{\cite{}} support (31/12/2005) -%D \item Use \type{\readfile} inside \type{\usepublications} instead of \type{\readsysfile} (12/01/2006) -%D \item Use \type{\currentbibyear} and \type{\currentbibauthor} instead of \type{\YR} and \type{\AU} (05/02/2006) -%D \item Fix compressed version of authoryear style (05/02/2006) -%D \item Rename the clashing data fields \type{\url} and \type{\type} to \type{\biburl} and \type{\bibtype} (05/02/2006) -%D \item Added two french bibl files from Renaud Aubin (06/02/2006) -%D \item Five new bib class and eight extra bib fields, for IEEEtran (07/02/2006) -%D \item French keyword translation, provided by Renaud (08/02/2006) -%D \item fix underscores in undefined keys (22/02/2006) -%D \item Destroy interactivity in labels of the publication list (13/03/2006) -%D \item fix multi-cite list compression (11/4/2006) -%D \item fix \type{\getcitedata} (11/4/2006) -%D \item magic for chapter bibs (18-25/4/2006) -%D \item language setting (25/4/2006) -%D \item use \type{\hyphenatedurl} for \type{\inserturl} (25/4/2006) -%D \item Add \type{\docitation} to \type{\nocite}(26/4/2006) -%D \item patents can have numbers, added to bst files (26/4/2006) -%D \item \type{\docitation} needs a \type{\iftrialtypesetting} (27/4/2006) -%D \item \type{\filllocalpublist}'s loop is bound by definedness, not resolvedness (27/4/2006) -%D \item \type{\setuppublications[monthconversion=]} added (15/5/2006) -%D \item use \type{\undefinedreference} instead of bare question marks (15/5/2006) -%D \item add grouping around \type{\placepublications} commands (16/5/2006) -%D \item fix a bug in \type{\cite{<item>}} (17/5/2006) -%D \item support \type{\cite[authornum]} (18/5/2006) -%D \item make \type{\cite} unexpandable (20/6/2006) -%D \item allow hyperlinks in author\&year combo's -%D (cite list compression has to be off) (20/6/2006) -%D \item fix duplicate labels for per-chapter style (20/6/2006) -%D \item allow \type{\setupcite[interaction=(start|stop)]} -%D \item fix the item number in the publication list with 'numbering=yes' (22/6/2006) -%D \item make the default criterium for \type{\placepublications} be \type{previous} (23/6/2006) -%D \item fix \type{\normalauthor} and \type{\normalshortauthor} spacing (29/6/2006) -%D \item do not typeset empty arguments to \type{\typesetapublication} (29/6/2006) -%D \item add \type{symbol=none} to \type{\setuplist} in unnumbered -%D mode to prevent typesetting of bare numbers (29/6/2006) -%D \item remove two incorrect spaces from bibl-num.tex (1/7/2006) -%D \item reset font styles within \type{\cite}, so that font switches -%D in \type{left} stay in effect (12/7/2006) -%D \item guard added against loading bbl files multiple times (13/7/2006) -%D \item fix \type{\cite[num]} with compression is on. (14/7/2006) -%D \item test \type{\iflocation} before deciding to use the -%D interactive version of cite (18/7/2006) -%D \item support \type{\setupcite[authoretallimit=1]} (18/7/2006) -%D \item support use of \type{\cite} within titles and captions by -%D saveguarding the list item extraction and reference placement -%D code (19/7/2006) -%D \item support \type{\setuppublicationlist[title=\chapter]} (4/8/2006) -%D \item use the expansion of \type{\headtext{pubs}} (4/8/2006) -%D \item hook added for repeated authors in publication list -%D \type{\setuppublicationlist[artauthorcommand=\mythreeargscommand]} -%D (4/8/2006) -%D \item make the bracketed arguments of \type{\artauthor}, \type{\author} -%D and \type{\editor} (bbl commands) optional (4/8/2006) -%D \item the constants \type{sorttype}, \type{compress} and -%D \type{autohang} have moved to the core (8/8/2006) -%D \item bibtex is now registered as a program to be run by texexec (8/8/2006) -%D \item fix a bug in \type{\setupcite[authoretallimit=1]} (9/8/2006) -%D \item fix a bug inside citations that prevented lastpubsep from ever being -%D used due to a volatile \type{\commalistsize} (25/8/2006). -%D \item added the possibility of \type{\placepublications[option=continue]} -%D (6/9/2006) -%D \item Mojca translated Master's Thesis to Masterarbeit (bibl-apa-de.tex) -%D (12/9/2006) -%D \item Added \type{\setuppublicationlist[maybeyear=off]} by request from -%D Thomas Schmitz (15/9/2006) -%D \item Removed some spurious spaces pointed out by willi egger (19/9/2006) -%D \item Add configuration of bibtex executable name (4/11/2006) -%D \item Fix numbering=short and numbering=bib (spotted by Matthias W�chter) (4/11/2006) -%D \item third attempt to get a correct release (5/11/2006) -%D \item fix a few missing dots in bibl-num.tex (7/12/2006) -%D \item Patch for DOI's by Tobias Burnus (17/4/2007) -%D \item Patch for \type{\insertbiburl} and \type{\insertdoi} for Tobias Burnus (18/4/2007) -%D \item Added a missing \type{\relax} in \type{\dospecialbibinsert}, -%D that made the space before the {\it et al.} text disappear. (18/4/2007) -%D \item Attempt to fix percent signs in bbl files. As a side-effect, -%D this prohibits comments in \tex{startpublication} blocks! (17/4/2008) -%D \item Patch from Matthias W\"achter that allows arbitrary .bst -%D files to be used with \tex{setupbibtex} (25/9/2008) -%D \item Extended for the new multilingual setups for the Oct 2008 current of ConTeXt (23/10/2008) -%D \item Multilingual setups needed another fix (27/10/2008) -%D \item Two fixes for bibl-apa by Michael Green (27/10/2008) -%D \item Catalan translation of 'References' (10/11/2008) -%D \item 'chapter' -> 'chapitre' in bibl-apa-fr (27/11/2008) -%D \item Run bibtex via os.execute in mkiv modee (01/12/2008) -%D \item Small correction in bibl-apa's placement of volume -%D information in articles (05/01/2009) -%D \item Handle multi-author (more than two) cases in \type{\cite} -%D (02/03/2009) -%D \item Suppress a syntax error in \type{cont-xp} mode. The output is -%D probably not right, though (02/03/2009) -%D \item Added a \tex{loadmarkfile} at the end, and two new files -%D from Hans. The \type{t-bib.mkiv} is needed to make the module -%D work with the new structure code (17/04/2009) -%D \item Added a patch to \type{t-bib.mkiv} from Hans to make the -%D cross referencing between multiple citations an -%D bibliographies work (27/04/2009) -%D \item Remove a superfluous \type{\unprotect} in t-bib.mkiv (11/05/2009). -%D \item Patch of incollection in bibl-ams.tex from Xan (08/06/2009). -%D \item Patch of unpublished in bibl-ams.tex from Xan (22/07/2009). -%D \item Modified \type{\bibdogetupsometextprefix} so it works for undefined -%D language labels, from Hans (13/08/2009). -%D \stopitemize -%D -%D \subject{WISHLIST} -%D -%D \startitemize -%D \item link back from publication list to citation -%D \item export \type {\citation{<cited item>}} -%D \item support mlbibtex -%D \item don't load the whole lot, but filter entries instead -%D \stopitemize - -\unprotect - -%D start with a temp hack the file will still work with pre-Oct 2008 -%D versions of ConTeXt: - - -\def\startinterface #1 - {\doifnot{#1}{all}{\doifnotinset\currentinterface{#1}{\gobbleuntil\stopinterface}}} - -\let\checksetvalue\gobbletwoarguments - - -%\defineinterfacevariable {title} {title} -%\defineinterfacevariable {short} {short} -%\defineinterfacevariable {cite} {cite} -%\defineinterfacevariable {bbl} {bbl} -%\defineinterfacevariable {bib} {bib} -%\defineinterfacevariable {author} {author} - -%D A few new shortcuts: - -\definesystemvariable {pv} % PublicationVariable -\definesystemvariable {pb} % PuBlication - -\definemessageconstant {bib} -\definefileconstant {bibextension} {bbl} - -%D Some user information messages. - -\startmessages all library: bib - title: publications - 1: file -- not found, unknown style ignored - 2: file -- not found, waiting for bibtex - 3: wrote a new auxiliary file \jobname.aux - 4: loading database from -- - 5: warning: cite argument -- on \the\inputlineno - 6: loading formatting style from -- -\stopmessages - -%D Some constants for the multi-lingual interface - -\startinterface all - \setinterfaceconstant {database} {database} - \setinterfaceconstant {artauthor} {artauthor} - \setinterfaceconstant {editor} {editor} - \setinterfaceconstant {authoretallimit} {authoretallimit} - \setinterfaceconstant {artauthoretallimit} {artauthoretallimit} - \setinterfaceconstant {editoretallimit} {editoretallimit} - \setinterfaceconstant {authoretaldisplay} {authoretaldisplay} - \setinterfaceconstant {artauthoretaldisplay} {artauthoretaldisplay} - \setinterfaceconstant {editoretaldisplay} {editoretaldisplay} - \setinterfaceconstant {authoretaltext} {authoretaltext} - \setinterfaceconstant {artauthoretaltext} {artauthoretaltext} - \setinterfaceconstant {editoretaltext} {editoretaltext} - \setinterfaceconstant {otherstext} {otherstext} - \setinterfaceconstant {andtext} {andtext} - \setinterfaceconstant {totalnumber} {totalnumber} - \setinterfaceconstant {firstnamesep} {firstnamesep} - \setinterfaceconstant {vonsep} {vonsep} - \setinterfaceconstant {juniorsep} {juniorsep} - \setinterfaceconstant {surnamesep} {surnamesep} - \setinterfaceconstant {lastnamesep} {lastnamesep} - \setinterfaceconstant {finalnamesep} {finalnamesep} - \setinterfaceconstant {namesep} {namesep} - \setinterfaceconstant {pubsep} {pubsep} - \setinterfaceconstant {lastpubsep} {lastpubsep} - \setinterfaceconstant {refcommand} {refcommand} - \setinterfaceconstant {samplesize} {samplesize} -\stopinterface - -\startinterface dutch - \setinterfacevariable {title} {titel} - \setinterfacevariable {short} {kort} - \setinterfacevariable {cite} {cite} - \setinterfacevariable {bbl} {bbl} - \setinterfacevariable {bib} {bib} - \setinterfacevariable {author} {auteur} -\stopinterface - -\startinterface english - \setinterfacevariable {title} {title} - \setinterfacevariable {short} {short} - \setinterfacevariable {cite} {cite} - \setinterfacevariable {bbl} {bbl} - \setinterfacevariable {bib} {bib} - \setinterfacevariable {author} {author} -\stopinterface - -\startinterface german - \setinterfacevariable {title} {titel} - \setinterfacevariable {short} {kurz} - \setinterfacevariable {cite} {cite} - \setinterfacevariable {bbl} {bbl} - \setinterfacevariable {bib} {bib} - \setinterfacevariable {author} {autor} -\stopinterface - -\startinterface czech - \setinterfacevariable {title} {titul} - \setinterfacevariable {short} {short} - \setinterfacevariable {cite} {cite} - \setinterfacevariable {bbl} {bbl} - \setinterfacevariable {bib} {bib} - \setinterfacevariable {author} {autor} -\stopinterface - -\startinterface italian - \setinterfacevariable {title} {titolo} - \setinterfacevariable {short} {short} - \setinterfacevariable {cite} {cite} - \setinterfacevariable {bbl} {bbl} - \setinterfacevariable {bib} {bib} - \setinterfacevariable {author} {autore} -\stopinterface - -\startinterface romanian - \setinterfacevariable {title} {titlu} - \setinterfacevariable {short} {short} - \setinterfacevariable {cite} {cite} - \setinterfacevariable {bbl} {bbl} - \setinterfacevariable {bib} {bib} - \setinterfacevariable {author} {autor} -\stopinterface - -\startinterface french - \setinterfacevariable {title} {titre} - \setinterfacevariable {short} {short} - \setinterfacevariable {cite} {cite} - \setinterfacevariable {bbl} {bbl} - \setinterfacevariable {bib} {bib} - \setinterfacevariable {author} {auteur} -\stopinterface - -\def\biblistname{pubs} % for compatibility - -%D how to load the references. There is some new stuff here -%D to support Idris' (incorrect :-)) use of projects - -\let\preloadbiblist\relax - -\ifx\currentcomponent\v!text - % single file - \edef\temp{\the\everystarttext}% - \ifx\temp\empty - % post-starttext - \def\preloadbiblist{\dousepublications\jobname }% - \else - % pre-starttext - \appendtoks \dousepublications\jobname \to \everystarttext - \fi - % -\else \ifx\currentcomponent\v!project - % a project file, have to set up the partial products! - \def\startproduct #1 % - {\doateverystarttext - \dousepublications{#1}% - \donextlevel\v!product\currentproduct - \doexecutefileonce\doexecutefileonce - \donotexecutefile\doexecutefile#1\\}% - % -\else \ifx\currentcomponent\v!product - % a product file - \def\preloadbiblist{\dousepublications\jobname }% - % -\else - % a component? not sure what to do - \def\preloadbiblist{\dousepublications\jobname }% - % -\fi \fi \fi - -\definelist[pubs] -\setuplist[pubs][\c!width=] - - -%D The text string for the publication list header - -\setupheadtext[en][pubs=References] -\setupheadtext[nl][pubs=Literatuur] -\setupheadtext[de][pubs=Literatur] -\setupheadtext[it][pubs=Bibliografia] -\setupheadtext[sl][pubs=Literatura] -\setupheadtext[fr][pubs=Bibliographie] -\setupheadtext[ca][pubs=Referències] - -%D \macros{bibdoif,bibdoifnot,bibdoifelse} -%D -%D Here are a few small helpers that are used a lot -%D in all the typesetting commands -%D (\type{\insert...}) we will encounter later. - -\long\def\bibdoifelse#1% - {\@EA\def\@EA\!!stringa\@EA{#1}% - \ifx\!!stringa\empty - \expandafter\secondoftwoarguments - \else - \expandafter\firstoftwoarguments - \fi} - -\long\def\bibdoifnot#1% - {\@EA\def\@EA\!!stringa\@EA{#1}% - \ifx\!!stringa\empty - \expandafter\firstofoneargument - \else - \expandafter\gobbleoneargument - \fi} - -\long\def\bibdoif#1% - {\@EA\def\@EA\!!stringa\@EA{#1}% - \ifx\!!stringa\empty - \expandafter\gobbleoneargument - \else - \expandafter\firstofoneargument - \fi} - - -%D Bibtex settings separated out - -%D No point in writing the aux file if there is no database... - -\def\setupbibtex{\dosingleempty\dosetupbibtex} - -\def\dosetupbibtex[#1]% - {\let\@@pbdatabase\empty - \getparameters[\??pb][sort=\s!default,#1]% - \expanded{\processaction[\@@pbsort]} - [ \v!no=>\def\bibstyle{cont-no}, - \v!author=>\def\bibstyle{cont-au}, - \v!title=>\def\bibstyle{cont-ti}, - \v!short=>\def\bibstyle{cont-ab}, - \s!default=>\def\bibstyle{cont-no}, - \s!unknown=>\def\bibstyle{\@@pbsort}]% - \ifx\@@pbdatabase\empty\else \writeauxfile \fi} - -\dosetupbibtex[bibtex=bibtex] - -%D \macros{writeauxfile} -%D -%D Unfortunately, \BIBTEX\ is not the best configurable program -%D around. The names of the commands it parses as well as the \type{.aux} -%D extension to the file name are both hardwired. -%D -%D This means \CONTEXT\ has to write a \LATEX-style auxiliary file, yuk! -%D The good news is that it can be rather short. We'll just ask -%D \BIBTEX\ to output the entire database(s) into the \type{bbl} file. -%D -%D The \type{\bibstyle} command controls how the \type{bbl} file will -%D be sorted. The possibilities are: -%D -%D \startitemize[packed] -%D \item by author (+year, title): cont-au.bst -%D \item by title (+author, year): cont-ti.bst -%D \item by short key as in abbrev.bst: cont-ab.bst -%D \item not sorted at all: cont-no.bst -%D \stopitemize - -\def\writeauxfile - {\doifmode{*first} - {\openout \scratchwrite \jobname.aux - \write \scratchwrite {\string\citation{*}}% - \write \scratchwrite {\string\bibstyle{\bibstyle}}% - \write \scratchwrite {\string\bibdata{\@@pbdatabase}}% - \closeout\scratchwrite - \showmessage\m!bib{3}{}% - \doifmodeelse{*mkiv} - {\ctxlua{os.execute('\@@pbbibtex\space\jobname')}} - {\expanded{\installprogram{\@@pbbibtex\space\jobname}}}}} - -%D \macros{ifsortbycite,iftypesetall,ifautohang,ifbibcitecompress} -%D -%D The module needs some new \type{\if} statements. - -%D Default sort order of the reference list is by citation. - -\newif\ifsortbycite \sortbycitetrue - -%D By default, only referenced publications are typeset - -\newif\iftypesetall \typesetallfalse - -%D Hanging indentation of the publication list -%D will not adjust itself according to the width of the label. - -\newif\ifautohang \autohangfalse - -%D Cite lists are compressed, if possible. - -\newif\ifbibcitecompress \bibcitecompresstrue - -\def\setuppublications - {\dosingleargument\dosetuppublications} - -\def\bibleftnumber#1% - {#1\hfill~} - - -\def\dosetuppublications[#1]% - {\getparameters - [\??pb] - [\c!alternative=,#1]% - \doifsomething\@@pbalternative - {\readsysfile - {bibl-\@@pbalternative.tex} - {\showmessage\m!bib{6}{bibl-\@@pbalternative}\let\@@pbalternative\empty} - {\showmessage\m!bib{1}{bibl-\@@pbalternative}\let\@@pbalternative\empty}}% - \getparameters - [\??pb] - [#1]% - \processaction - [\@@pbcriterium] - [ \v!all=>\typesetalltrue, - \s!unknown=>\typesetallfalse]% - \processaction - [\@@pbautohang] - [ \v!yes=>\autohangtrue, - \s!unknown=>\autohangfalse]% - \processaction - [\@@pbsorttype] - [ \v!cite=>\sortbycitetrue, - \v!bbl=>\sortbycitefalse, - \s!default=>\sortbycitetrue, - \s!unknown=>\sortbycitefalse]% - \processaction - [\@@pbnumbering] - [ \v!yes=>\let\@@pbinumbercommand\firstofoneargument, - \v!no=>\let\@@pbinumbercommand\gobbleoneargument, - \v!short=>\def\@@pbinumbercommand##1{\getvalue{pbds-\@@pbk}}, - \v!bib=>\def\@@pbinumbercommand##1{\getvalue{pbdn-\@@pbk}}, - \s!unknown=>\let\@@pbinumbercommand\firstofoneargument]% - \processaction - [\@@pbrefcommand] - [\s!default=>\edef\@@citedefault{\@@pbrefcommand}, - \s!unknown=>\edef\@@citedefault{\@@pbrefcommand}]} - -% initialize - -\def\@@pbrefcommand{num} -\def\@@pbnumbercommand{\bibleftnumber} - -%D \macros{usepublications} -%D -%D We need \type{\usereferences} so that it is possible to -%D refer to page and/or appearance number for publications -%D in the other document. - -\def\usepublications[#1]% - {\usereferences[#1]\processcommalist[#1]\dousepublications} - -\def\dousepublications#1% - {\doonlyonce - {#1.\f!bibextension} - {\readfile{#1.\f!bibextension} - {\showmessage\m!bib{4}{#1.\f!bibextension}} - {\showmessage\m!bib{2}{#1.\f!bibextension}}}} - -%D \macros{setuppublicationlist} -%D -%D This will be the first command in (\BIBTEX-generated) \type{bbl} -%D files. `samplesize' is a sample value (in case of \BIBTEX-generated -%D files, this will be the longest `short' key). `totalnumber' -%D is the total number of entries that will follow in this -%D file. - -%D Both values are only needed for the label calculation -%D if `autohang' is `true', so by default the command is -%D not even needed, and therefore I saw no need to give -%D it it's own system variable and it just re-uses \type{pb}. - -\def\setuppublicationlist - {\dosingleempty\dosetuppublicationlist} - -\def\dosetuppublicationlist[#1]% - {\getparameters[\??pv data][#1]% - \setuplist - [pubs] - [\c!samplesize={AA99},\c!totalnumber={99}, - \c!alternative=a,\c!interaction=,\c!pagenumber=\v!no,#1]} - -\def\setuppublicationlayout[#1]#2% - {\setvalue{\??pv data#1}{#2\unskip}} - -%D \macros{bibalternative} -%D -%D A nice little shorthand that will be used so we don't have to -%D key in the weird \type{\@@pv} parameter names all the time. - -\def\bibalternative#1% - {\getvalue{\??pv\@@currentalternative#1}} - -%D \macros{simplebibdef,bibcommandlist} -%D -%D \type{\simplebibdef} defines \type{bib@#1}, which in turn will -%D use one argument that is stored in \type{@@pb@#1}. -%D -%D \type{\simplebibdef} also defines \type{insert#1}, which can be -%D used in the argument of \type{\setuppublicationlayout} to fetch -%D one of the \type{@@pb@} data entries. \type{insert#1} then has -%D three arguments: \type{#1} are commands to be executed before the -%D data, \type{#2} are commands to be executed after the data, and -%D \type{#3} are commands to be executed if the data is not found. - -%D \type{\bibcommandlist} is the list of commands that is affected -%D by this approach. Later on, it will be used to do a series -%D of assignments from \type{#1} to \type{bib@#1}: e.g -%D \type{\title} becomes \type{\bib@title} when used within -%D a publication. - -\def\simplebibdef#1% hh: funny expansion ? - {\@EA\long\@EA\def\csname bib@#1\endcsname##1% - {\setvalue{\??pb @#1}{##1}% - \ignorespaces}% - \@EA\def\csname insert#1\endcsname##1##2##3% - {\@EA\bibdoifelse - \@EA{\csname @@pb@#1\endcsname}% - {##1\csname @@pb@#1\endcsname##2}% - {##3}% - }} - -\def\bibcommandlist - {abstract, annotate, arttitle, assignee, bibnumber, bibtype, biburl, chapter, city, - comment, country, day, dayfiled, doi, edition, eprint, howpublished, isbn, issn, - issue, journal, keyword, keywords, lastchecked, month, monthfiled, names, nationality, - note, notes, organization, pages, pubname, pubyear, revision, series, size, thekey, - title, volume, yearfiled} - - -\processcommacommand[\bibcommandlist]\simplebibdef - -\def\insertdoi#1#2#3% - {{\bibdoifelse{\@@pb@doi}% - {\edef\ascii{\@EA\detokenize\@EA{\@@pb@doi}}% - #1\expanded{\gotoDOI{\@@pb@thekey}{\ascii}}#2}{#3}}} - -\def\insertbiburl#1#2#3% - {{\bibdoifelse{\@@pb@biburl}% - {\edef\ascii{\@EA\detokenize\@EA{\@@pb@biburl}}% - #1\expanded{\gotoURL{\@@pb@thekey}{\ascii}}#2}{#3}}} - -\def\insertmonth#1#2#3% - {\bibdoifelse{\@@pb@month}% - {#1\doifnumberelse{\@@pb@month}% - {\doifconversiondefinedelse\@@pbmonthconversion - {\convertnumber\@@pbmonthconversion{\@@pb@month}}{\@@pb@month}}% - {\@@pb@month}#2}{#3}} - -\let\inserturl \insertbiburl % for backward compat. -\let\inserttype\insertbibtype % for backward compat. - -\def\newbibfield[#1]% - {\simplebibdef{#1}% - \edef\bibcommandlist{\bibcommandlist,#1}} - -%D \macros{bib@crossref} -%D -%D \type{\crossref} is used in database files to point to another -%D entry. Because of this special situation, it has to be defined -%D separately. Since this command will not be seen until at -%D \type{\placepublications}, it may force extra runs. The same is -%D true for \type{\cite} commands inside of publications. - -\def\bib@crossref#1% - {\setvalue{\??pb @crossref}{#1}\ignorespaces} - -\def\insertcrossref#1#2#3% - {\bibdoifelse{\@@pb@crossref} - {#1\@EA\cite\@EA[\@@pb@crossref]#2} - {#3}} - -%D \macros{complexbibdef,specialbibinsert} -%D -%D The commands \type{\artauthor}, \type{\author} and -%D \type{\editor} are more complex than the other commands. -%D Their argument lists have this form: -%D -%D \type{\author[junior]{firstnames}[inits]{von}{surname}} -%D -%D (bracketed stuff is optional) -%D -%D And not only that, but there also might be more than one of each of -%D these commands. This is why a special command is needed to insert -%D them, as well as one extra counter for each command. - -%D All of these \type{\@EA}'s and \type{\csnames} make this code -%D look far more complex than it really is. For example, the argument -%D \type{author} defines the macro \type{\bib@author} to do two -%D things: increment the counter \type{\author@num} (let's say to 2) -%D and next store it's arguments in the macro \type{\@@pb@author2}. -%D And it defines \type{\insertauthors} to expand into -%D \starttyping -%D \specialbibinsert{author}{\author@num}{<before>}{<after>}{<not>} -%D \stoptyping - -\def\docomplexbibdef#1% - {\def\currentype{#1}% - \dosingleempty\dodocomplexbibdef} - -\def\dodocomplexbibdef[#1]#2% - {\def\firstarg{#1}\def\secondarg{#2}% - \dosingleempty\dododocomplexbibdef} - -\def\dododocomplexbibdef[#1]#2#3% - {\@EA\increment\csname \currentype @num\endcsname - \setevalue{\??pb @\currentype\csname \currentype @num\endcsname}% - {{\secondarg}{#2}{#3}{#1}{\firstarg}}\ignorespaces}% - -\def\complexbibdef#1% - {\@EA\newcounter\csname #1@num\endcsname - \@EA\def\csname bib@#1\endcsname{\docomplexbibdef{#1}}% - \@EA\def\csname insert#1s\endcsname##1##2##3% - {\specialbibinsert{#1}{\csname #1@num\endcsname}{##1}{\unskip ##2}{##3}}} - -\processcommalist[author,artauthor,editor]\complexbibdef - -%D Another level of indirection is needed to control the -%D typesetting of all of these arguments. - -%D Btw, there is a conflict between `author' and the predefined interface -%D variable `auteur'. The old version is overruled `auteur' is -%D overruled by the systemconstant definition at the top of this file! - - -\newcount\etallimitcounter -\newcount\etaldisplaycounter -\newcount\todocounter - -\def\specialbibinsert#1#2#3#4#5% - {\bgroup - \ifnum#2>\zerocount - \etallimitcounter =0\bibalternative{#1etallimit}\relax - \etaldisplaycounter=0\bibalternative{#1etaldisplay}\relax - \ifnum #2>\etallimitcounter - \todocounter\etaldisplaycounter - % just in case ... - \ifnum\todocounter>\etallimitcounter - \todocounter\etallimitcounter - \fi - \else - \todocounter#2\relax - \fi - \ifnum\todocounter>\zerocount - % find the current author list - \let\templist\empty - \dorecurse{#2} - {\toks0=\@EA\@EA\@EA{\csname @@pb@#1\recurselevel\endcsname}% - \ifx\templist\empty \edef\templist{\the\toks0}% - \else \edef\templist{\templist,\the\toks0}\fi }% - \doifdefinedelse - {\??pv data#1\c!command} - {\doifemptyelsevalue - {\??pv data#1\c!command} - {#3\dospecialbibinsert{#1}{\todocounter}{\templist}#4}% - {#3\getvalue{\??pv data#1\c!command}{#1}{\todocounter}{\templist}#4}}% - {#3\dospecialbibinsert{#1}{\todocounter}{\templist}#4}% - \else - #5% - \fi - \else - #5% - \fi - \egroup} - -%D This macro does the hard work of inserting a list of people in the -%D output, with proper regard of all the inbetween strings that can -%D arise depending on length of the list of people. - -%D \#1 = type -%D \#2 = number of items to be typeset -%D \#3 = commacommand containing authors - -\def\dospecialbibinsert#1#2#3% - {\getcommacommandsize[#3]% - \scratchcounter 0 - \def\processauthoritem##1% - {\advance\scratchcounter1 - \ifnum \numexpr\scratchcounter-1\relax<#2\relax - \getvalue{\??pv data#1}##1% - \ifnum \scratchcounter=#2\relax - \ifnum\etallimitcounter<\commalistsize\relax \bibalternative{#1etaltext}\fi - \else \ifnum\numexpr\scratchcounter+1 = #2\relax - \ifnum \commalistsize > \plustwo \bibalternative\c!finalnamesep - \else \bibalternative\c!lastnamesep \fi - \else - \bibalternative\c!namesep - \fi \fi - \fi}% - \processcommacommand[#3]\processauthoritem } - - -%D \macros{invertedauthor,normalauthor,invertedshortauthor,normalshortauthor} -%D -%D Just some commands that can be used in \type{\setuppublicationparameters} -%D If you want to write an extension to the styles, you might -%D as well define some of these commands yourself. -%D -%D The argument liust has been reordered here, and the meanings -%D are: -%D -%D {\obeylines\parskip0pt -%D \type{#1} firstnames -%D \type{#2} von -%D \type{#3} surname -%D \type{#4} inits -%D \type{#5} junior -%D } -%D - -\def\normalauthor#1#2#3#4#5% - {\bibdoif{#1}{#1\bibalternative\c!firstnamesep}% - \bibdoif{#2}{#2\bibalternative\c!vonsep}% - #3% - \bibdoif{#5}{\bibalternative\c!surnamesep#5\unskip}} - -\def\normalshortauthor#1#2#3#4#5% - {\bibdoif{#4}{#4\bibalternative\c!firstnamesep}% - \bibdoif{#2}{#2\bibalternative\c!vonsep}% - #3% - \bibdoif{#5}{\bibalternative\c!surnamesep#5\unskip}} - -\def\invertedauthor#1#2#3#4#5% - {\bibdoif{#2}{#2\bibalternative\c!vonsep}% - #3% - \bibdoif{#5}{\bibalternative\c!juniorsep #5}% - \bibdoif{#1}{\bibalternative\c!surnamesep #1\unskip}} - -\def\invertedshortauthor#1#2#3#4#5% - {\bibdoif{#2}{#2\bibalternative\c!vonsep}% - #3% - \bibdoif{#5}{\bibalternative\c!juniorsep #5}% - \bibdoif{#4}{\bibalternative\c!surnamesep #4\unskip}} - -%D \macros{clearbibitem,clearbibitemtwo,bibitemdefs} -%D -%D These are used in \type{\typesetapublication} to do -%D initializations and cleanups. - -\def\clearbibitem#1{\setvalue{\??pb @#1}{}}% - -\def\clearbibitemtwo#1% - {\letvalue{#1@num}\!!zerocount - \scratchcounter\plusone - \doloop - {\doifdefinedelse{\??pb @#1\the\scratchcounter} - {\letvalue{\??pb @#1\the\scratchcounter}\empty - \advance\scratchcounter\plusone}% - {\exitloop}}} - -\def\bibitemdefs#1% - {\@EA\let\@EA\tempa \csname bib@#1\endcsname - \@EA\let\csname #1\endcsname \tempa } - -%D \macros{startpublication} -%D -%D We are coming to the end of this module, to the macros that -%D do typesetting and read the \type{bbl} file. - -\newcounter\bibcounter - -%D Just a \type{\dosingleempty} is the most friendly -%D of doing this: there need not even be an argument -%D to \type{\startpublication}. Of course, then there -%D is no key either, and it had better be an -%D article (otherwise the layout will be all screwed up). -%D -%D Now prohibits comments, so % can be used for urls - -\def\startpublication - {\edef\bibmodsavedpercent{\the\catcode`\%}% - \catcode`\%=12 - \dosingleempty\dostartpublication} - -\def\stoppublication - {} % the \catcode of % is reset below - -%D Only specifying the key in the argument is also -%D legal. In storing this stuff into macros, some trickery with -%D token registers is needed to fix the expansion problems. Even so, -%D this appears to not always be 100\% safe, so people are -%D urgently advised to use \ETEX\ instead of traditional \TEX. -%D -%D In \ETEX, all expansion problems are conviniently solved by -%D the primitive \type{\protected}. To put that another way: -%D -%D It's not a bug in this module if it does not appear in \ETEX! - -\long\def\dostartpublication[#1]#2\stoppublication% - {\increment\bibcounter - \bgroup - \doifassignmentelse{#1}% - {\getparameters[\??pb][k=,t=article,n=,s=,a=,y=,o=,u=,#1]}% - {\getparameters[\??pb][k=#1,t=article,n=,s=,a=,y=,o=,u=]}% - \@EA\toks\@EA2\@EA{\@@pba}% - \@EA\toks\@EA4\@EA{\@@pbs}% - \toks0={\ignorespaces #2}% - \setxvalue{pbdk-\@@pbk}{\@@pbk} - \setxvalue{pbda-\@@pbk}{\the\toks2} - \setxvalue{pbdy-\@@pbk}{\@@pby} - \setxvalue{pbds-\@@pbk}{\the\toks4} - \setxvalue{pbdn-\@@pbk}{\@@pbn} - \setxvalue{pbdt-\@@pbk}{\@@pbt} - \setxvalue{pbdo-\@@pbk}{\@@pbo} - \setxvalue{pbdu-\@@pbk}{\@@pbu} - \setxvalue{pbdd-\@@pbk}{\the\toks0} - \xdef\allrefs{\allrefs,\@@pbk}% - \egroup - \catcode`\%=\bibmodsavedpercent\relax } - -% intialization of the order-list: - -\let\allrefs\empty - -%D The next macro is needed because the number command of the -%D publist sometimes needs to fetch something from the current -%D item (like the 'short' key). For this, the ID of the current -%D item is passed in the implict parameter \type{\@@pbk} - -\def\makepbkvalue#1{\def\@@pbk{#1}} - -%D - -\newif\ifinpublist - -% from Hans - -\def\ignoresectionconversion - {\let\@@sectionconversion\secondoftwoarguments} - -\let\normaldosetfilterlevel\dosetfilterlevel - -\def\patcheddosetfilterlevel#1#2% beware: this one is \let - {\bgroup - \ignoresectionconversion - \edef\askedlevel{#1}% - \edef\askedfilter{#2}% -% \message{ASKD: \meaning\askedlevel}% -% \message{PREV: \meaning\v!previous}% - \ifx\askedlevel\v!current - \dosetcurrentlevel\askedlevel - \else\ifx\askedlevel\v!previous - \dosetpreviouslevel\askedlevel - \else\ifx\askedlevel\v!all - \global\chardef\alltoclevels\plusone - \else\ifx\askedlevel\v!text - \global\chardef\alltoclevels\plusone - \else - \edef\byaskedlevel{\csname\??by\askedlevel\endcsname}% - \ifx\byaskedlevel\v!text - \dosettextlevel\askedlevel - \else - \dosetotherlevel\askedlevel - \fi - \fi\fi\fi\fi - % experiment - \ifx\askedfilter\empty \else - \xdef\currentlevel{\currentlevel\sectionseparator\askedfilter}% - \fi - \egroup} - - -\unless\ifcsname currentlocationrefence\endcsname - -\let\currentlocationreference\empty - -\def\dogetreferenceelements#1#2#3#4#5% - {\chardef\currentreferencetype=\ifx#1\relax0\else#1\fi\relax - \ifnum\currentreferencetype<2 - \edef\currentpagereference{#2}% - \let \currentdatareference\empty - \edef\currentlocationreference{#2}% - \ifx\currentpagereference \empty - \let\currentfolioreference\folio - \else - \def \currentpagereference {\referencepagenumber[#2]}% - \edef\currentfolioreference{\dosplitofffoliopart[#2]}% - \fi - \edef\currentrealreference{#3}% - \settextreferences#4\end - \ifnum0#5<\crossreferencenumber - \forwardreferencetrue - \else - \forwardreferencefalse - \fi - \else - \let \currentlocationreference\empty - \edef\currentrealreference {#3}% - \def \currentdatareference {#2}% - \let \currentfolioreference\folio - \settextreferences#4\end - \forwardreferencefalse - \fi - \ifodd\currentreferencetype - \realreferencepagefalse - \else - \docheckrealreferencepage\currentrealreference - \ifrealreferencepage \else - \docheckrealreferencepage\currentdatareference - \fi - \fi} - -\fi - -\def\filllocalpublist% - {\doifdefinedelse{\alltoclevels} - {\let\dosetfilterlevel\patcheddosetfilterlevel - \dosettoclevel\??li{pubs}% - \let\dosetfilterlevel\normaldosetfilterlevel }% - {\dosettoclevel\??li{pubs}}% - \global\let\glocalpublist\empty - \doloop - {\doifdefinedelse - {\r!cross cite-\jobname-\recurselevel} - {\doifreferencefoundelse - {cite-\jobname-\recurselevel} - {\@EA\doifreglevelelse\@EA[\currentlocationreference] - {\@EA\doglobal\@EA\addtocommalist\@EA - {\currenttextreference}\glocalpublist}{}} - {}}% - {\exitloop}}% - \let\localpublist\glocalpublist} - -% -\def\typesetpubslist - {\dobeginoflist - \edef\askedlevel{\csname \??li pubs\c!criterium\endcsname}% - \ifx\askedlevel\v!all - \def\bibrefprefix{}% - \else % - \preparebibrefprefix - \fi - \ifsortbycite - \filllocalpublist - \iftypesetall - \let\localallrefs\allrefs - \processcommacommand[\localpublist]\typesetapublication - \def\removefromallrefs##1% - {\removefromcommalist{##1}\localallrefs }% - \processcommacommand[\localpublist]\removefromallrefs - \processcommacommand[\localallrefs]\typesetapublication - \else - \processcommacommand[\localpublist]\typesetapublication - \fi - \else - \iftypesetall - \processcommacommand[\allrefs]\typesetapublication - \else - % - \filllocalpublist - \processcommacommand[\allrefs]\maybetypesetapublication - \fi - \fi - \doendoflist } - -\newif\ifinpublist -\def\maybetypesetapublication#1% - {\global\inpublistfalse - \def\test{#1}% - \def\runtest##1% - {\def\tempa{##1}\ifx \test\tempa \global\inpublisttrue \fi}% - \processcommacommand[\localpublist]\runtest - \ifinpublist \typesetapublication{#1}\fi} - -\def\initializepubslist - {\edef\@@pbnumbering{\@@pbnumbering}% - \ifautohang - \ifx\@@pbnumbering\v!short - \setbox\scratchbox\hbox{\@@pbnumbercommand{\getvalue{\??li pubs\c!samplesize}}}% - \else\iftypesetall - \setbox\scratchbox\hbox{\@@pbnumbercommand{\getvalue{\??li pubs\c!totalnumber}}}% - \else - \setbox\scratchbox\hbox{\@@pbnumbercommand{\numreferred}}% - \fi\fi - \edef\samplewidth{\the\wd\scratchbox}% - \setuplist[pubs][\c!width=\samplewidth,\c!distance=0pt]% - \ifx\@@pbnumbering\v!short - \def\@@pblimitednumber##1{\hbox to \samplewidth - {\@@pbnumbercommand{\getvalue{pbds-\@@pbk}}}}% - \else \ifx \@@pbnumbering\v!bib - \def\@@pblimitednumber##1{\hbox to \samplewidth - {\@@pbnumbercommand{\getvalue{pbdn-\@@pbk}}}}% - \else - \def\@@pblimitednumber##1{\hbox to \samplewidth{\@@pbnumbercommand{##1}}}% - \fi \fi - \else - \ifx\@@pbnumbering\v!short - \doifemptyelse - {\getvalue{\??li pubs\c!width}} - {\def\@@pblimitednumber##1{\hbox - {\@@pbnumbercommand{\getvalue{pbds-\@@pbk}}}}}% - {\def\@@pblimitednumber##1{\hbox to \getvalue{\??li pubs\c!width}% - {\@@pbnumbercommand{\getvalue{pbds-\@@pbk}}}}}% - \else \ifx \@@pbnumbering\v!bib - \doifemptyelse - {\getvalue{\??li pubs\c!width}} - {\def\@@pblimitednumber##1{\hbox - {\@@pbnumbercommand{\getvalue{pbdn-\@@pbk}}}}}% - {\def\@@pblimitednumber##1{\hbox to \getvalue{\??li pubs\c!width}% - {\@@pbnumbercommand{\getvalue{pbdn-\@@pbk}}}}}% - \else - \doifemptyelse - {\getvalue{\??li pubs\c!width}} - {\def\@@pblimitednumber##1{\hbox{\@@pbnumbercommand{##1}}}}% - {\def\@@pblimitednumber##1{\hbox to \getvalue{\??li pubs\c!width}{\@@pbnumbercommand{##1}}}}% - \fi - \fi - \fi - \ifx\@@pbnumbering\v!no - \setuplist[pubs][\c!numbercommand=,\c!symbol=\v!none,\c!textcommand=\outdented]% - \else - \setuplist[pubs][\c!numbercommand=\@@pblimitednumber]% - \fi - \doifelsevalue - {\??pv datamaybeyear}{\v!off}{\def\maybeyear##1{}}{\def\maybeyear##1{##1}}% - \forgetall % bugfix 2005/03/18 -} - - -\def\outdented#1% move to supp-box ? - {\hskip -\hangindent #1} - - -%D The full list of publications - -\def\completepublications - {\dosingleempty\docompletepublications} - -\def\bibdogetupsometextprefix#1#2#3% - {\ifcsname#2#1#3\endcsname - \csname#2#1#3\endcsname - \else\ifcsname\??la#1\c!default\endcsname - \@EA\ifx\csname\??la#1\c!default\endcsname\empty - \ifcsname#2#3\endcsname - \csname#2#3\endcsname - \else\ifcsname#2\s!en#3\endcsname - \csname#2\s!en#3\endcsname - \fi\fi - \else - \expandafter\bibdogetupsometextprefix - \csname\??la#1\c!default\endcsname{#2}{#3}% - \fi - \else - \ifcsname#2#3\endcsname - \csname#2#3\endcsname - \else\ifcsname#2\s!en#3\endcsname - \csname#2\s!en#3\endcsname - \fi\fi - \fi\fi} - -\def\docompletepublications[#1]% - {\begingroup - \setuplist[pubs][\c!criterium=\v!previous,#1] - \begingroup - \let\handletextprefix\firstoftwoarguments - \edef\headtextpubs{\bibdogetupsometextprefix\headlanguage\c!title{pubs}}% - \doifdefinedelse - {\??pv data\v!title} - {\doifemptyelsevalue - {\??pv data\v!title} - {\expanded{\systemsuppliedtitle[pubs]{\headtextpubs}}}% - {\expanded{\getvalue{\??pv data\v!title}{\headtextpubs}}}% - }% - {\expanded{\systemsuppliedtitle[pubs]{\headtextpubs}}}% - \endgroup - \dodoplacepublications } - -%D And the portion with the entries only. - -\def\placepublications - {\dosingleempty\doplacepublications} - -\def\doplacepublications[#1]% - {%\getparameters[\??pv data][#1] - \begingroup - \setuplist[pubs][\c!criterium=\v!previous,#1]% - \dodoplacepublications }% - -\def\dodoplacepublications% - {\initializepubslist - \doifelsevalue - {\??li pubs\c!option}{\v!continue}% - {}% - {\global\let\bibcounter\!!zerocount }% - \inpublisttrue - \typesetpubslist - \inpublistfalse - \endgroup } - - -%D \subsubject{What's in a publication} -%D - -\unexpanded\def\typesetapublication#1% - {\doifsomething{#1}{\doglobal\increment\bibcounter - \bgroup - \makepbkvalue{#1}% - \ifgridsnapping - \snaptogrid\vbox{\dodolistelement{pubs}{}{\bibcounter}% - {\expanded{\reference[\bibrefprefix#1]{\bibcounter}}% - \strut \dotypesetapublication{#1}\strut }{}{}}% - \else - \dodolistelement{pubs}{}{\bibcounter}% - {\expanded{\reference[\bibrefprefix#1]{\bibcounter}}% - \strut \dotypesetapublication{#1}\strut }{}{}% - \fi - \egroup }} - -\def\dotypesetapublication#1% - {\bgroup - \def\@@currentalternative{data}% - \processcommacommand[\bibcommandlist,crossref]\clearbibitem - \processcommalist [artauthor,author,editor]\clearbibitemtwo - \processcommacommand[\bibcommandlist]\bibitemdefs - \processcommalist [artauthor,author,editor,crossref]\bibitemdefs - \let\biblanguage\empty - \getvalue{pbdd-#1}% - \ifcsname pbdt-#1\endcsname \bibalternative{\getvalue{pbdt-#1}}\fi - \egroup } - - -%D An afterthought - -\def\maybeyear#1{} - -%D An another - -\def\noopsort#1{} - -%D This is the result of bibtex's `language' field. - -\def\setbiblanguage#1#2{\setvalue{\??pb @lang@#1}{#2}} - -\def\lang#1% - {\def\biblanguage{#1}% - \ifcsname \??pb @lang@#1\endcsname - \expanded{\language[\getvalue{\??pb @lang@#1}]}% - \fi \ignorespaces} - - -%D \subject{Citations} - -%D \macros{cite,bibref} -%D -%D The indirection with \type{\dobibref} allows \LATEX\ style -%D \type{\cite} commands with a braced argument (these might appear -%D in included data from the \type{.bib} file). - -\unexpanded\def\cite - {\doifnextcharelse{[} - {\dodocite} - {\dobibref}} - -\def\dobibref#1% - {\docite[#1][]} - -\def\dodocite[#1]% - {\startstrictinspectnextcharacter - \dodoubleempty\dododocite[#1]} - -\def\dododocite[#1][#2]{% - \stopstrictinspectnextcharacter - \docite[#1][#2]} - -\def\docite[#1][#2]% - {\begingroup - \setupinteraction[\c!style=]% - \edef\temp{#2}% - \ifx\empty\temp \secondargumentfalse - \else \secondargumenttrue \fi - \ifsecondargument - \processcommalist[#2]\docitation - \doifassignmentelse - {#1}% - {\getparameters[LO][\c!alternative=,\c!extras=,#1]% - \edef\@@currentalternative{\LOalternative}% - \ifx\@@currentalternative\empty - \edef\@@currentalternative{\@@citedefault}% - \fi - \ifx\LOextras\empty - \setupcite[\@@currentalternative][#1]% - \else - \expandafter\ifx\csname LOright\endcsname \relax - \edef\LOextras{{\LOextras\bibalternative\c!right}}% - \else - \edef\LOextras{{\LOextras\LOright}}% - \fi - \expanded{\setupcite[\@@currentalternative][#1,\c!right=\LOextras]}% - \fi - }% - {\def\@@currentalternative{#1}}% - \expanded{% - \processaction[\csname @@pv\@@currentalternative \c!compress\endcsname]} - [ \v!yes=>\bibcitecompresstrue, - \v!no=>\bibcitecompressfalse, - \s!default=>\bibcitecompresstrue, - \s!unknown=>\bibcitecompresstrue]% - \getvalue{bib\@@currentalternative ref}[#2]% - \else - \processcommalist[#1]\docitation - \expanded{\processaction[\csname @@pv\@@citedefault \c!compress\endcsname]} - [ \v!yes=>\bibcitecompresstrue, - \v!no=>\bibcitecompressfalse, - \s!default=>\bibcitecompresstrue, - \s!unknown=>\bibcitecompresstrue]% - \edef\@@currentalternative{\@@citedefault}% - \getvalue{bib\@@citedefault ref}[#1]% - \fi - \endgroup} - -%D \macros{nocite} - -\def\nocite[#1]% - {\processcommalist[#1]\addthisref - \processcommalist[#1]\docitation } - -%D \macros{setupcite} - -\def\setupcite{\dodoubleempty\dosetupcite} - -\def\dosetupcite[#1][#2]% - {\ifsecondargument - \def\dodosetupcite##1{\getparameters[\??pv##1][#2]}% - \processcommalist[#1]\dodosetupcite - \else % default case - \getparameters[\??pv\@@citedefault][#1]% - \fi } - - -%D Low-level stuff - -\def\getcitedata#1[#2]#3[#4]#5to#6% - {\bgroup - \addthisref{#4}% - \dofetchapublication{#4}% - \doifdefinedelse{@@pb@bib#2}% - {\xdef#6{\getvalue{@@pb@bib#2}}}% - {\xdef#6{\getvalue{@@pb@#2}}}% - \egroup } - - -\def\dofetchapublication#1% - {\makepbkvalue{#1}% - \processcommacommand[\bibcommandlist,crossref]\clearbibitem - \processcommalist [artauthor,author,editor]\clearbibitemtwo - \processcommacommand[\bibcommandlist]\bibitemdefs - \processcommalist [artauthor,author,editor,crossref]\bibitemdefs - \getvalue{pbdd-#1}} - -%D This new version writes a reference out to the tui file for every -%D \type{\cite}. This will allow backlinking. -%D -%D Some special care is needed so that references are not added from -%D weird locations like in the TOC or within a \type{\setbox} command. - -\newcounter\citationnumber - -\def\docitation#1{% - \iftrialtypesetting \else - \ifdoinpututilities\else - \doglobal\increment\citationnumber - \expanded{\rawreference{}{cite-\jobname-\citationnumber}{#1}}% - \fi \fi } - - -%D \macros{numreferred,doifreferredelse,addthisref,publist} -%D -%D The interesting command here is \type{\addthisref}, which maintains -%D the global list of references. -%D -%D \type{\numreferred} is needed to do automatic calculations on -%D the label width, and \type{\doifreferredelse} will be used -%D to implement \type{criterium=cite}. - -\newcounter\numreferred - -\long\def\doifreferredelse#1{\doifdefinedelse{pbr-#1}} - -\def\addthisref#1% - {\doifundefinedelse{pbr-#1} - {\setxvalue{pbr-#1}{\citationnumber}% - \doglobal\increment\numreferred - \ifx\publist\empty \gdef\publist{#1}\else\appended\gdef\publist{,#1}\fi} - {\setxvalue{pbr-#1}{\getvalue{pbr-#1},\citationnumber}}} - -\let\publist\empty - -%D \macros{doifbibreferencefoundelse} -%D -%D Some macros to fetch the information provided by -%D \type{\startpublication}. - -\def\doifbibreferencefoundelse#1#2#3% - {\doifdefinedelse{pbdk-#1} - {#2} - {\showmessage\m!bib{5}{#1 is unknown}#3}} - -%D \macros{ixbibauthoryear,thebibauthors,thebibyears} -%D -%D If compression of \type{\cite}'s argument expansion is on, -%D the macros that deal with authors and years call this internal -%D command to do the actual typesetting. -%D -%D Two entries with same author but with different years may -%D be condensed into ``Author (year1,year2)''. This is about the -%D only optimization that makes sense for the (author,year) -%D style of citations (years within one author have to be unique -%D anyway so no need to test for that, and ``Author1, Author2 (year)'' -%D creates more confusion than it does good). -%D -%D In the code below, -%D the macro \type{\thebibauthors} holds the names of the alternative -%D author info fields for the current list. This is a commalist, -%D and \type{\thebibyears} holds the (collection of) year(s) that go with -%D this author (possibly as a nested commalist). -%D -%D There had better be an author for all cases, but there -%D does not have to be year info always. \type{\thebibyears} is -%D pre-initialized because this makes the insertion macros simpler. -%D -%D In `normal' \TeX, of course there are expansion problems again. - -\def\ixbibauthoryear#1#2#3#4% - {\bgroup - \gdef\ixlastcommand {#4}% - \gdef\ixsecondcommand{#3}% - \gdef\ixfirstcommand {#2}% - \glet\thebibauthors \empty - \glet\thebibyears \empty - \getcommalistsize[#1]% - \ifbibcitecompress - \dorecurse\commalistsize{\xdef\thebibyears{\thebibyears,}}% - \processcommalist[#1]\docompressbibauthoryear - \else - \processcommalist[#1]\donormalbibauthoryear - \fi - \egroup - \dobibauthoryear} - -%D \macros{dodobibauthoryear} -%D -%D This macro only has to make sure that the lists -%D \type{\thebibauthors} and \type{\thebibyears} are printed. - -\def\dobibauthoryear - {\scratchcounter\zerocount - \getcommacommandsize[\thebibauthors]% - \edef\authorcount{\commalistsize}% - \@EA\processcommalist\@EA[\thebibauthors]\dodobibauthoryear} - -\def\dodobibauthoryear#1% - {\advance\scratchcounter\plusone - \edef\wantednumber{\the\scratchcounter}% - \getfromcommacommand[\thebibyears][\wantednumber]% - \@EA\def\@EA\currentbibyear\@EA{\commalistelement}% - \setcurrentbibauthor{#1}% - \ifnum\scratchcounter=\plusone - \ixfirstcommand - \else\ifnum \scratchcounter=\authorcount\relax - \ixlastcommand - \else - \ixsecondcommand - \fi\fi} - -\def\setcurrentbibauthor#1% - {\getcommacommandsize[#1]% - \ifcase\commalistsize - % anonymous? - \def\currentbibauthor{}% - \or - \def\currentbibauthor{#1}% - \or - \expanded{\docurrentbibauthor#1}% - \else - \handlemultiplebibauthors{\commalistsize}{#1}% - \fi } - - -\newcount\citescratchcounter - -\def\handlemultiplebibauthors#1#2% - {\citescratchcounter 0 - \def\currentbibauthor{}% - \def\bibprocessauthoritem##1% - {\advance\citescratchcounter1 - \ifnum \citescratchcounter=#1\relax - \edef\currentbibauthor{\currentbibauthor##1}% - \else \ifnum\numexpr\citescratchcounter+1 = #1\relax - \edef\currentbibauthor{\currentbibauthor ##1\bibalternative{andtext}}% - \else - \edef\currentbibauthor{\currentbibauthor ##1\bibalternative{namesep}}% - \fi - \fi }% - \processcommalist[#2]\bibprocessauthoritem } - - -\setupcite - [author,authoryear,authoryears] - [\c!namesep={, }] - -%D This discovery of authoretallimit is not the best one, -%D but it will do for now. - -\def\docurrentbibauthor#1,#2% - {\doifemptyelse{#2} - {\def\currentbibauthor{#1\bibalternative{otherstext}}} - {\@EA - \ifx\csname \??pv\@@currentalternative authoretallimit\endcsname\relax - \edef\currentbibauthor{#1\bibalternative{andtext}#2}% - \else - \edef\currentbibauthor{#1% - \ifcase0\bibalternative{authoretallimit}\relax\or - \bibalternative{otherstext}\else\bibalternative{andtext}#2\fi}% - \fi}} - -%D This is not the one Hans made for me, because I need a global -%D edef, and the \type{\robustdoifinsetelse} doesn't listen to -%D \type{\doglobal } - -\def\robustaddtocommalist#1#2% {item} \cs - {\robustdoifinsetelse{#1}#2\resetglobal - {\dodoglobal\xdef#2{\ifx#2\empty\else#2,\fi#1}}} - - -%D \macros{donormalbibauthoryear} -%D -%D Now we get to the macros that fill the two lists. -%D The `simple' one really is quite simple. - -\def\donormalbibauthoryear#1% - {\addthisref{#1}% - \def\myauthor{Xxxxxxxxxx}% - \def\myyear{0000}% - \doifbibreferencefoundelse{#1} - {\def\myauthor{{\getvalue{pbda-#1}}}% - \def\myyear {\getvalue{pbdy-#1}}}% - {}% - \@EA\doglobal\@EA\appendtocommalist\@EA{\myauthor}\thebibauthors - \@EA\doglobal\@EA\appendtocommalist\@EA{\myyear }\thebibyears} - -%D \macros{docompressbibauthoryear} -%D -%D So much for the easy parts. Nothing at all will be done if -%D the reference is not found or the reference does not contain -%D author data. No questions marks o.s.s. (to be fixed later) - -\def\docompressbibauthoryear#1% - {\addthisref{#1}% - \def\myauthor{Xxxxxxxxxx}% - \def\myyear {0000}% - \doifbibreferencefoundelse{#1} - {\xdef\myauthor{\csname pbda-#1\endcsname }% - \xdef\myyear {\csname pbdy-#1\endcsname }} - {}% - \ifx\myauthor\empty\else - \checkifmyauthoralreadyexists - \findmatchingyear - \fi} - -%D two temporary counters. One of these two can possibly be replaced -%D by \type{\scratchcounter}. - -\newcount\bibitemcounter -\newcount\bibitemwanted - -%D The first portion is simple enough: if this is the very first author -%D it is quite straightforward to add it. \type{\bibitemcounter} and -%D \type{\bibitemwanted} are needed later to insert the year -%D information in the correct item of \type{\thebibyears} - -\def\checkifmyauthoralreadyexists - {\doifemptyelsevalue{thebibauthors} - {\global\bibitemwanted \plusone - \global\bibitemcounter \plusone - \xdef\thebibauthors{{\myauthor}}} - {% the next weirdness is because according to \getcommalistsize, - % the length of \type{[{{},{}}]} is 2. - \@EA\getcommalistsize\@EA[\thebibauthors,]% - \global\bibitemcounter\commalistsize - \global\advance\bibitemcounter\minusone - \global\bibitemwanted \zerocount - \processcommacommand[\thebibauthors]\docomparemyauthor}} - -%D The outer \type{\ifnum} accomplishes the addition of -%D a new author to \type{\thebibauthors}. The messing about with -%D the two counters is again to make sure that \type{\thebibyears} -%D will be updated correctly.If the author {\it was} found, -%D the counters will stay at their present values and everything -%D will be setup properly to insert the year info. - -\def\docomparemyauthor#1% - {\global\advance\bibitemwanted \plusone - \def\mytempc{#1}% -% \message{authors: \myauthor <=>\mytempc \ifx\mytempc\myauthor :Y \else :N -% \meaning \myauthor, \meaning\mytempc\fi (\the\bibitemwanted = \the\bibitemcounter)}% - \ifx\mytempc\myauthor - \quitcommalist - \else - \ifnum\bibitemwanted = \bibitemcounter\relax - \global\advance\bibitemwanted \plusone - \global\bibitemcounter\bibitemwanted\relax - \@EA\doglobal\@EA\robustaddtocommalist\@EA{{\myauthor}}\thebibauthors - \fi - \fi} - -%D This macro should be clear now. - -\def\findmatchingyear - {\edef\wantednumber{\the\bibitemwanted}% - \getfromcommacommand[\thebibyears][\wantednumber]% - \ifx\commalistelement\empty - \edef\myyear{{\myyear}}% - \else - \edef\myyear{{\commalistelement, \myyear}}% - \fi - \edef\newcommalistelement{\myyear}% - \doglobal\replaceincommalist \thebibyears \wantednumber} - - -%D \macros{preparebibrefprefix} -%D -%D The reference list only writes bare references when the criterium -%D is `all'. Otherwise, a prefix is added to make sure that pdfTeX -%D does not encounter duplicate named references. On the generation -%D side, this is not a big problem. \type{\preparebibrefprefix} -%D creates a suitable string to prepend if a prefix is needed. -%D -%D Because this macro is used within \type{\cite } that itself -%D can be used within lists like the table of contents, it needs -%D to save and restore \type{\savedalltoclevels} and -%D \type{\currentlevel} (\type{\dosetfilterlevel} needs to change -%D their values globally). - -\def\preparebibrefprefix - {\chardef\savedalltoclevels \alltoclevels - \let\savedcurrentlevel\currentlevel - \let\dosetfilterlevel\patcheddosetfilterlevel - \dosettoclevel\??li{pubs}% - \edef\bibrefprefix{\@@sectiontype\currentlevel\sectionseparator}% - \let\dosetfilterlevel\normaldosetfilterlevel - \global\let\currentlevel\savedcurrentlevel - \global\chardef\alltoclevels \savedalltoclevels } - -%D \macros{preparebibreflist} -%D -%D But this optional prefixing is a bit of a problem on the -%D other side. We would like to do \type{\goto{}[article-full]} -%D but can't do it like that, because the actual label may be -%D \type{1:2:0:3:4:article-full]} or so. The problem is solved -%D by building a commalist that looks like this: -%D \starttyping -%D \def\bibreflist% -%D {1:2:0:3:4:article-full, -%D 1:2:0:3:article-full, -%D 1:2:0:article-full, -%D 1:2:article-full, -%D 1:article-full, -%D article-full} -%D \stoptyping - -\def\preparebibreflist#1% - {\let\bibreflist\empty - \def\storeitem##1% - {\ifx\bibreflist\empty - \edef\prefix{##1\sectionseparator}% - \edef\bibreflist{\prefix#1,#1}% - \else - \edef\prefix{\prefix##1\sectionseparator}% - \edef\bibreflist{\prefix#1,\bibreflist}% - \fi}% - \expanded{\processseparatedlist[\bibrefprefix][\sectionseparator]}\storeitem } - -%D \macros{gotobiblink,inbiblink,atbiblink} -%D -%D The final task is looping over that list until a match is found. - -\newif\ifbibreffound - -\def\gotobiblink#1[#2]% - {\bgroup - \preparebibrefprefix - \preparebibreflist{#2}% - \global\bibreffoundfalse - \def\setuplink##1% - {\ifbibreffound\else - \doifreferencefoundelse - {##1} - {\global\bibreffoundtrue \goto{#1}[##1]}% - {}\fi}% - \processcommacommand[\bibreflist]\setuplink - \ifbibreffound \else \unknownreference{#2}\fi - \egroup } - -\def\atbiblink[#1]% - {\bgroup - \preparebibrefprefix - \preparebibreflist{#1}% - \global\bibreffoundfalse - \def\setuplink##1% - {\ifbibreffound\else - \doifreferencefoundelse - {##1} - {\global\bibreffoundtrue \at[##1]}% - {}\fi}% - \processcommacommand[\bibreflist]\setuplink - \ifbibreffound \else \unknownreference{#1}\fi - \egroup } - -\def\inbiblink[#1]% - {\bgroup - \preparebibrefprefix - \preparebibreflist{#1}% - \global\bibreffoundfalse - \def\setuplink##1% - {\ifbibreffound\else - \doifreferencefoundelse - {##1} - {\global\bibreffoundtrue \in[##1]}% - {}\fi}% - \processcommacommand[\bibreflist]\setuplink - \ifbibreffound \else \unknownreference{#1}\fi - \egroup } - -%D \macros{bibauthoryearref,bibauthoryearsref,bibauthorref,bibyearref} -%D -%D Now that all the hard work has been done, these are simple. -%D \type{\ixbibauthoryearref} stores the data in the macros -%D \type{\currentbibauthor} and \type{\currentbibyear}. - -\def\ifbibinteractionelse% - {\iflocation - \edef\test{\bibalternative\c!interaction}% - \ifx\test\v!stop - \@EA\@EA\@EA\secondoftwoarguments - \else - \@EA\@EA\@EA\firstoftwoarguments - \fi - \else - \@EA\secondoftwoarguments - \fi - } - -\def\bibmaybeinteractive#1#2% - {\ifbibcitecompress #2\else - \ifbibinteractionelse{\gotobiblink{#2}[#1]}{#2}\fi } - -\def\bibauthoryearref[#1]% - {\ixbibauthoryear{#1}% - {\bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween - \bibalternative\v!left{\currentbibyear}\bibalternative\v!right}} - {\bibalternative\c!pubsep - \bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween - \bibalternative\v!left {\currentbibyear}\bibalternative\v!right}} - {\bibalternative\c!lastpubsep - \bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween - \bibalternative\v!left {\currentbibyear}\bibalternative\v!right}}} - -\def\bibauthoryearsref[#1]% - {\bibalternative\v!left - \ixbibauthoryear{#1} - {\bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween{\currentbibyear}}} - {\bibalternative\c!pubsep - \bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween{\currentbibyear}}} - {\bibalternative\c!lastpubsep - \bibmaybeinteractive{#1}{{\currentbibauthor}\bibalternative\c!inbetween{\currentbibyear}}}% - \bibalternative\v!right} - -\def\bibauthorref[#1]% - {\bibalternative\v!left - \ixbibauthoryear{#1}% - {\bibmaybeinteractive{#1}{{\currentbibauthor}}} - {\bibalternative\c!pubsep \bibmaybeinteractive{#1}{{\currentbibauthor}}} - {\bibalternative\c!lastpubsep\bibmaybeinteractive{#1}{{\currentbibauthor}}}% - \bibalternative\v!right} - -\def\bibyearref[#1]% - {\bibalternative\v!left - \ixbibauthoryear{#1}% - {\bibmaybeinteractive{#1}{{\currentbibyear}}} - {\bibalternative\c!pubsep \bibmaybeinteractive{#1}{{\currentbibyear}}} - {\bibalternative\c!lastpubsep\bibmaybeinteractive{#1}{{\currentbibyear}}}% - \bibalternative\v!right} - -%D ML problems: - -%D \macros{bibshortref,bibkeyref,bibpageref,bibtyperef,bibserialref} -%D -%D There is hardly any point in trying to compress these. The only -%D thing that needs to be done is making sure that -%D the separations are inserted correctly. And that is -%D what \type{\refsep} does. - -\newif\iffirstref - -\def\refsep{\iffirstref\firstreffalse\else\bibalternative\c!pubsep\fi} - -\def\bibshortref[#1]% - {\bibalternative\v!left - \firstreftrue\processcommalist[#1]\dobibshortref - \bibalternative\v!right} - -\def\dobibshortref#1% - {\addthisref{#1}\refsep - \doifbibreferencefoundelse{#1}{\gotobiblink{\getvalue{pbds-#1}}[#1]} - {\unknownreference{#1}}} - - -\def\bibserialref[#1]% - {\bibalternative\v!left - \firstreftrue\processcommalist[#1]\dobibserialref - \bibalternative\v!right} - -\def\dobibserialref#1% - {\addthisref{#1}\refsep - \doifbibreferencefoundelse{#1}{\gotobiblink{\getvalue{pbdn-#1}}[#1]} - {\unknownreference{#1}}} - -\def\bibkeyref[#1]% - {\bibalternative\v!left - \firstreftrue\processcommalist[#1]\dobibkeyref - \bibalternative\v!right} - -\def\dobibkeyref#1% - {\addthisref{#1}\refsep\gotobiblink{#1}[#1]} - -\def\gotoDOI#1#2% - {\ifbibinteractionelse - {\useURL[bibfooDoi#1][#2]% - \useURL[bibfoo#1][http://dx.doi.org/#2]% - \goto{\url[bibfooDoi#1]}[url(bibfoo#1)]} - {\hyphenatedurl{#2}}} - -\def\bibdoiref[#1]% - {\bibalternative\v!left - \firstreftrue\processcommalist[#1]\dobibdoiref - \bibalternative\v!right} - -\def\dobibdoiref#1% - {\addthisref{#1}\refsep - \doifbibreferencefoundelse{#1}{\expanded{\gotoDOI{#1}{\getvalue{pbdo-#1}}}} - {\unknownreference{#1}}} - - -\def\biburlref[#1]% - {\bibalternative\v!left - \firstreftrue\processcommalist[#1]\dobiburlref - \bibalternative\v!right} - -\def\gotoURL#1#2% - {\ifbibinteractionelse - {\useURL[bibfoo#1][#2]\goto{\url[bibfoo#1]}[url(bibfoo#1)]} - {\hyphenatedurl{#2}}} - -\def\dobiburlref#1% - {\addthisref{#1}\refsep - \doifbibreferencefoundelse{#1}{\expanded{\gotoURL{#1}{\getvalue{pbdu-#1}}}} - {\unknownreference{#1}}} - -\def\bibtyperef[#1]% - {\bibalternative\v!left - \firstreftrue\processcommalist[#1]\dobibtyperef - \bibalternative\v!right} - -\def\dobibtyperef#1% - {\addthisref{#1}\refsep - \doifbibreferencefoundelse{#1}{\gotobiblink{\getvalue{pbdt-#1}}[#1]} - {\unknownreference{#1}}} - -\def\bibpageref[#1]% - {\bibalternative\v!left - \firstreftrue\processcommalist[#1]\dobibpageref - \bibalternative\v!right} - -\def\dobibpageref#1% - {\addthisref{#1}\refsep - \ifbibinteractionelse{\atbiblink[#1]}{{\referencingfalse\at[#1]}}} - -\def\bibdataref[#1]% - {\bibalternative\v!left - \firstreftrue\processcommalist[#1]\dobibdata - \bibalternative\v!right} - -\def\dobibdata#1% - {\addthisref{#1}\refsep - \doifbibreferencefoundelse{#1}{\dotypesetapublication{#1}} - {\unknownreference{#1}}} - -\let\bibnoneref\nocite - -%D \macros{bibnumref} -%D -%D It makes sense to try and compress the argument list of -%D \type{\bibnumref}. There are two things involved: the actual -%D compression, and a sort routine. The idea is to store the -%D found values in a new commalist called \type{\therefs}. - -%D But that is not too straight-forward, because \type{\in} is -%D not expandable, -%D so that the macro \type{\expandrefs} is needed. - -\def\expandrefs#1% - {\bgroup - \preparebibrefprefix - \preparebibreflist{#1}% - \global\bibreffoundfalse - \def\setuplink##1% - {\ifbibreffound\else - \doifreferencefoundelse - {##1} - {\global\bibreffoundtrue - \@EA\doglobal\@EA\addtocommalist\@EA{\reftypet}\therefs }% - {}\fi}% - \processcommacommand[\bibreflist]\setuplink - \ifbibreffound \else \showmessage\m!bib{5}{#1 unknown}% - \doglobal\addtocommalist{0}\therefs\fi - \egroup } - - -%D But at least the actual sorting code is simple (note that sorting -%D a list with exactly one entry fails to return anything, which -%D is why the \type{\ifx} is needed). - -\def\bibnumref[#1]% - {\bibalternative\v!left - \penalty\!!tenthousand - \processcommalist[#1]\addthisref - \firstreftrue - \ifbibcitecompress - \glet\therefs\empty - \processcommalist[#1]\expandrefs - \sortcommacommand[\therefs]\donumericcompare - \ifx\empty\sortedcommalist\else - \let\therefs\sortedcommalist - \fi - \compresscommacommandnrs[\therefs]% - \processcommacommand[\compressedlist]\verysimplebibnumref - \else - \processcommalist[#1]\dosimplebibnumref - \fi - \bibalternative\v!right} - -\def\dosimplebibnumref #1% - {\refsep\ifbibinteractionelse - {\inbiblink[#1]}{{\referencingfalse\inbiblink[#1]}}} - -\def\verysimplebibnumref#1{\doverysimplebibnumref#1} - -\def\doverysimplebibnumref#1#2% - {\refsep - \ifcase#1\relax \unknownreference{#1}\else - \def\tempa{#2}\ifx\empty\tempa#1\else#1\bibalternative\c!inbetween#2\fi - \fi} - -%D By request from Sanjoy. This makes it easier to implement -%D \type{\citeasnoun}. - -\def\bibauthornumref[#1]% - {\getcommalistsize[#1]% - \global\bibitemcounter\commalistsize - \firstreftrue - \processcommalist[#1]\dobibauthornumref } - -\def\dobibauthornumref#1% - {\addthisref{#1}\refsep - \doifbibreferencefoundelse{#1} - {\getvalue{pbda-#1}% - \bibalternative\c!inbetween - \bibalternative\v!left - \ifbibinteractionelse{\inbiblink[#1]} - {{\referencingfalse\inbiblink[#1]}}% - \bibalternative\v!right} - {\unknownreference{#1}}} - -%D And some defaults are loaded from bibl-apa: - -\setuppublications - [\v!month\v!conversion=,\c!alternative=apa] - -\loadmarkfile{t-bib} - -\preloadbiblist - -\protect \endinput diff --git a/tex/context/bib/t-bibltx.tex b/tex/context/bib/t-bibltx.tex deleted file mode 100644 index cb9e787ad..000000000 --- a/tex/context/bib/t-bibltx.tex +++ /dev/null @@ -1,75 +0,0 @@ -%D \module -%D [ file=t-bibltx, -%D version=2005.01.04, -%D title=\CONTEXT\ Publication Module, -%D subtitle=Publications, -%D author={Taco Hoekwater}, -%D date=\currentdate, -%D copyright={Public Domain}] -%C -%C Donated to the public domain. - -%D \macros{newcommand} -%D -%D Just about all databases define something that uses -%D \type {\newcommand}. This fake version does not cover -%D everything \type {\newcommand} does, but it should be -%D enough for simple definitions like the ones found in -%D \BIBTEX\ files. - -\unprotect - -\def\@star@or@long#1% - {\doifnextcharelse*{\afterassignment#1\let\next=}{#1}} - -\def\newcommand - {\@star@or@long\new@command} - -\def\new@command#1% - {\@testopt{\@newcommand#1}0} - -\def\@newcommand#1[#2]% - {\doifnextcharelse[{\@xargdef#1[#2]}{\@argdef#1[#2]}} - -\long\def\@argdef#1[#2]#3% - {\@yargdef#1\@ne{#2}{#3}} - -\long\def\@xargdef#1[#2][#3]#4% - {\@EA\def\@EA#1\@EA{\@EA\do@testopt\@EA#1\csname\string#1\endcsname{#3}}% - \@EA\@yargdef\csname\string#1\endcsname\tw@{#2}{#4}} - -\def\@testopt#1#2% - {\doifnextcharelse[{#1}{#1[#2]}} - -\def\do@testopt#1% - {\expandafter\@testopt} - -\long\def\@yargdef#1#2#3% - {\!!counta#3\relax - \advance \!!counta \@ne - \let\@hash@\relax - \edef\!!tempa{\ifx#2\tw@ [\@hash@1]\fi}% - \!!countb #2% - \loop - \ifnum\!!countb <\!!counta - \edef\!!tempa{\!!tempa\@hash@\the\!!countb}% - \advance\!!countb \@ne - \repeat - \let\@hash@##% - \long\@EA\def\@EA#1\!!tempa} - -\long\def\@reargdef#1[#2]% - {\@yargdef#1\@ne{#2}} - -%D Something like the following is needed to support the -%D average \LATEX-based \BIBTEX\ databases. -%D -%D \starttyping -%D \let\textsc\kap -%D \def\emph#1{{\em#1}} -%D \let\sf\ss -%D \stoptyping -%D -%D But we happily leave that to the user. - -\protect \endinput diff --git a/tex/context/interface/cont-cs.xml b/tex/context/interface/cont-cs.xml index 53d861c90..f8dc85a84 100644 --- a/tex/context/interface/cont-cs.xml +++ b/tex/context/interface/cont-cs.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?><!-- versions: +<?xml version="1.0" encoding="UTF-8"?> <!-- versions: comment : user interface definitions of ConTeXt diff --git a/tex/context/interface/cont-de.xml b/tex/context/interface/cont-de.xml index aaea5d00c..aeb0a4acd 100644 --- a/tex/context/interface/cont-de.xml +++ b/tex/context/interface/cont-de.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?><!-- versions: +<?xml version="1.0" encoding="UTF-8"?> <!-- versions: comment : user interface definitions of ConTeXt diff --git a/tex/context/interface/cont-en.xml b/tex/context/interface/cont-en.xml index 4f0237627..95e8a4ab9 100644 --- a/tex/context/interface/cont-en.xml +++ b/tex/context/interface/cont-en.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?><!-- versions: +<?xml version="1.0" encoding="UTF-8"?> <!-- versions: comment : user interface definitions of ConTeXt diff --git a/tex/context/interface/cont-fr.xml b/tex/context/interface/cont-fr.xml index 690903ca4..3e6df06ed 100644 --- a/tex/context/interface/cont-fr.xml +++ b/tex/context/interface/cont-fr.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?><!-- versions: +<?xml version="1.0" encoding="UTF-8"?> <!-- versions: comment : user interface definitions of ConTeXt diff --git a/tex/context/interface/cont-it.xml b/tex/context/interface/cont-it.xml index e2502d5cd..c670feb92 100644 --- a/tex/context/interface/cont-it.xml +++ b/tex/context/interface/cont-it.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?><!-- versions: +<?xml version="1.0" encoding="UTF-8"?> <!-- versions: comment : user interface definitions of ConTeXt diff --git a/tex/context/interface/cont-nl.xml b/tex/context/interface/cont-nl.xml index 47ce230b5..3c3fbca5f 100644 --- a/tex/context/interface/cont-nl.xml +++ b/tex/context/interface/cont-nl.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?><!-- versions: +<?xml version="1.0" encoding="UTF-8"?> <!-- versions: comment : user interface definitions of ConTeXt diff --git a/tex/context/interface/cont-pe.xml b/tex/context/interface/cont-pe.xml index bcf164d8d..3b1f8c6c0 100644 --- a/tex/context/interface/cont-pe.xml +++ b/tex/context/interface/cont-pe.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?><!-- versions: +<?xml version="1.0" encoding="UTF-8"?> <!-- versions: comment : user interface definitions of ConTeXt diff --git a/tex/context/interface/cont-ro.xml b/tex/context/interface/cont-ro.xml index 2f6dd9f3e..e63ca67d1 100644 --- a/tex/context/interface/cont-ro.xml +++ b/tex/context/interface/cont-ro.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?><!-- versions: +<?xml version="1.0" encoding="UTF-8"?> <!-- versions: comment : user interface definitions of ConTeXt diff --git a/tex/context/interface/t-bib.xml b/tex/context/interface/t-bib.xml deleted file mode 100644 index faa728fa6..000000000 --- a/tex/context/interface/t-bib.xml +++ /dev/null @@ -1,411 +0,0 @@ -<?xml version="1.0"?> - -<cd:interface xmlns:cd="http://www.pragma-ade.com/commands" - name="context/third/t-bib" - language="all" version="2006.08.04"> - -<cd:command name="setuppublications" file="t-bib.tex"> - <cd:sequence> - <cd:string value="setuppublications"/> - </cd:sequence> - <cd:arguments> - <cd:assignments list="yes"> - <cd:parameter name="alternative"> - <cd:constant type="ams"/> - <cd:constant type="apa" default="yes"/> - <cd:constant type="apa-de"/> - <cd:constant type="apa-fr"/> - <cd:constant type="aps"/> - <cd:constant type="num"/> - <cd:constant type="num-fr"/> - <cd:constant type="ssa"/> - </cd:parameter> - <cd:parameter name="refcommand"> - <cd:constant type="author"/> - <cd:constant type="authornum"/> - <cd:constant type="authoryear"/> - <cd:constant type="authoryears" default="yes"/> - <cd:constant type="key"/> - <cd:constant type="num"/> - <cd:constant type="serial"/> - <cd:constant type="page"/> - <cd:constant type="short"/> - <cd:constant type="type"/> - <cd:constant type="year"/> - <cd:constant type="data"/> - <cd:constant type="doi"/> - <cd:constant type="url"/> - </cd:parameter> - <cd:parameter name="sorttype"> - <cd:constant type="cite" default="yes"/> - <cd:constant type="bbl"/> - </cd:parameter> - <cd:parameter name="criterium"> - <cd:constant type="all" /> - <cd:constant type="cite" default="yes"/> - </cd:parameter> - <cd:parameter name="numbering"> - <cd:constant type="yes"/> - <cd:constant type="no" default="yes"/> - <cd:constant type="short"/> - <cd:constant type="bib"/> - </cd:parameter> - <cd:parameter name="autohang"> - <cd:constant type="yes"/> - <cd:constant type="no" default="yes"/> - </cd:parameter> - <cd:parameter name="monthconversion"> - <cd:constant type="cd:command"/> - </cd:parameter> - </cd:assignments> - </cd:arguments> -</cd:command> - -<cd:command name="setuppublicationlist" file="t-bib.tex"> - <cd:sequence> - <cd:string value="setuppublicationlist"/> - </cd:sequence> - <cd:arguments> - <cd:assignments list="yes"> - <cd:parameter name="totalnumber"> - <cd:constant type="cd:number"/> - </cd:parameter> - <cd:parameter name="samplesize"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="title"> - <cd:constant type="cd:command"/> - </cd:parameter> - <cd:parameter name="maybeyear"> - <cd:constant type="off"/> - <cd:constant type="on" default="yes"/> - </cd:parameter> - <cd:parameter name="criterium"> - <cd:constant type="cd:section"/> - <cd:constant type="local"/> - <cd:constant type="previous"/> - <cd:constant type="current" default="yes"/> - <cd:constant type="all"/> - </cd:parameter> - <cd:parameter name="pagenumber"> - <cd:constant type="yes"/> - <cd:constant type="no" default="yes"/> - </cd:parameter> - <cd:parameter name="artauthor"> - <cd:constant type="\normalauthor"/> - <cd:constant type="\normalshortauthor"/> - <cd:constant type="\invertedauthor"/> - <cd:constant type="\invertedshortauthor" default="yes"/> - </cd:parameter> - <cd:parameter name="author"> - <cd:constant type="\normalauthor"/> - <cd:constant type="\normalshortauthor"/> - <cd:constant type="\invertedauthor"/> - <cd:constant type="\invertedshortauthor" default="yes"/> - </cd:parameter> - <cd:parameter name="editor"> - <cd:constant type="\normalauthor"/> - <cd:constant type="\normalshortauthor"/> - <cd:constant type="\invertedauthor"/> - <cd:constant type="\invertedshortauthor" default="yes"/> - </cd:parameter> - <cd:parameter name="namesep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="lastnamesep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="finalnamesep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="firstnamesep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="juniorsep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="vonsep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="surnamesep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="authoretallimit"> - <cd:constant type="cd:number"/> - </cd:parameter> - <cd:parameter name="authoretaltext"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="authoretaldisplay"> - <cd:constant type="cd:number"/> - </cd:parameter> - <cd:parameter name="authorcommand"> - <cd:constant type="cd:threearguments"/> - </cd:parameter> - <cd:parameter name="artauthoretallimit"> - <cd:constant type="cd:number"/> - </cd:parameter> - <cd:parameter name="artauthoretaltext"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="artauthoretaldisplay"> - <cd:constant type="cd:number"/> - </cd:parameter> - <cd:parameter name="artauthorcommand"> - <cd:constant type="cd:threearguments"/> - </cd:parameter> - <cd:parameter name="editoretallimit"> - <cd:constant type="cd:number"/> - </cd:parameter> - <cd:parameter name="editoretaltext"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="editoretaldisplay"> - <cd:constant type="cd:number"/> - </cd:parameter> - <cd:parameter name="editorcommand"> - <cd:constant type="cd:threearguments"/> - </cd:parameter> - <cd:inherit name="setuplist"/> - </cd:assignments> - </cd:arguments> -</cd:command> - - -<cd:command name="setupcite" file="t-bib.tex"> - <cd:sequence> - <cd:string value="setupcite"/> - </cd:sequence> - <cd:arguments> - <cd:keywords list="yes"> - <cd:constant type="author"/> - <cd:constant type="year"/> - <cd:constant type="authoryear"/> - <cd:constant type="authoryears"/> - <cd:constant type="key"/> - <cd:constant type="serial"/> - <cd:constant type="page"/> - <cd:constant type="short"/> - <cd:constant type="type"/> - <cd:constant type="data"/> - <cd:constant type="doi"/> - <cd:constant type="url"/> - <cd:constant type="num"/> - </cd:keywords> - <cd:assignments list="yes"> - <cd:parameter name="andtext"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="otherstext"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="namesep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="pubsep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="lastpubsep"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="compress"> - <cd:constant type="yes" default="yes"/> - <cd:constant type="no"/> - </cd:parameter> - <cd:parameter name="inbetween"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="left"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="right"> - <cd:constant type="cd:text"/> - </cd:parameter> - </cd:assignments> - </cd:arguments> -</cd:command> - -<cd:command name="setuppublicationlayout" file="t-bib.tex"> - <cd:sequence> - <cd:string value="setuppublicationlayout"/> - </cd:sequence> - <cd:arguments> - <cd:keywords list="yes"> - <cd:constant type="article"/> - <cd:constant type="book"/> - <cd:constant type="booklet"/> - <cd:constant type="conference"/> - <cd:constant type="electronic"/> - <cd:constant type="inbook"/> - <cd:constant type="incollection"/> - <cd:constant type="inproceedings"/> - <cd:constant type="manual"/> - <cd:constant type="mastersthesis"/> - <cd:constant type="misc"/> - <cd:constant type="patent"/> - <cd:constant type="periodical"/> - <cd:constant type="phdthesis"/> - <cd:constant type="proceedings"/> - <cd:constant type="standard"/> - <cd:constant type="techreport"/> - <cd:constant type="unpublished"/> - </cd:keywords> - <cd:content/> - </cd:arguments> -</cd:command> - - -<cd:command name="setupbibtex" file="t-bib.tex"> - <cd:sequence> - <cd:string value="setupbibtex"/> - </cd:sequence> - <cd:arguments> - <cd:assignments list="yes"> - <cd:parameter name="database"> - <cd:constant type="cd:file"/> - </cd:parameter> - <cd:parameter name="sort"> - <cd:constant type="title"/> - <cd:constant type="author"/> - <cd:constant type="short"/> - <cd:constant type="no" default="yes"/> - <cd:constant type="cd:file"/> - </cd:parameter> - </cd:assignments> - </cd:arguments> -</cd:command> - - -<cd:command name="usepublications" file="t-bib.tex"> - <cd:sequence> - <cd:string value="usepublications"/> - </cd:sequence> - <cd:arguments list="yes"> - <cd:keywords> - <cd:constant type="cd:file"/> - </cd:keywords> - </cd:arguments> -</cd:command> - - -<cd:command name="publication" file="t-bib.tex" type="environment"> - <cd:sequence> - <cd:string value="publication"/> - </cd:sequence> - <cd:arguments> - <cd:assignments list="yes"> - <cd:parameter name="k"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="a"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="y"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="n"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="s"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="t"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="u"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:parameter name="o"> - <cd:constant type="cd:text"/> - </cd:parameter> - </cd:assignments> - </cd:arguments> -</cd:command> - -<cd:command name="cite" file="t-bib.tex"> - <cd:sequence> - <cd:string value="cite"/> - </cd:sequence> - <cd:arguments> - <cd:keywords optional="yes"> - <cd:constant type="author"/> - <cd:constant type="year"/> - <cd:constant type="authoryear"/> - <cd:constant type="authoryears"/> - <cd:constant type="key"/> - <cd:constant type="serial"/> - <cd:constant type="page"/> - <cd:constant type="short"/> - <cd:constant type="type"/> - <cd:constant type="doi"/> - <cd:constant type="data"/> - <cd:constant type="url"/> - <cd:constant type="num"/> - </cd:keywords> - <cd:reference list="yes"/> - </cd:arguments> -</cd:command> - -<cd:command name="nocite" file="t-bib.tex"> - <cd:sequence> - <cd:string value="nocite"/> - </cd:sequence> - <cd:arguments> - <cd:reference list="yes"/> - </cd:arguments> -</cd:command> - -<cd:command name="citealt" file="t-bib.tex"> - <cd:sequence> - <cd:string value="cite"/> - </cd:sequence> - <cd:arguments> - <cd:assignments optional="yes" list="yes"> - <cd:parameter name="alternative"> - <cd:constant type="author"/> - <cd:constant type="year"/> - <cd:constant type="authoryear"/> - <cd:constant type="authoryears"/> - <cd:constant type="key"/> - <cd:constant type="serial"/> - <cd:constant type="page"/> - <cd:constant type="short"/> - <cd:constant type="type"/> - <cd:constant type="doi"/> - <cd:constant type="data"/> - <cd:constant type="url"/> - <cd:constant type="num"/> - </cd:parameter> - <cd:parameter name="extras"> - <cd:constant type="cd:text"/> - </cd:parameter> - <cd:inherit name="setupcite"/> - </cd:assignments> - <cd:reference list="yes"/> - </cd:arguments> -</cd:command> - -<cd:command name="placepublications" file="t-bib.tex"> - <cd:sequence> - <cd:string value="placepublications"/> - </cd:sequence> - <cd:arguments> - <cd:assignments optional="yes" list="yes"> - <cd:inherit name="setuplist"/> - </cd:assignments> - </cd:arguments> -</cd:command> - -<cd:command name="completepublications" file="t-bib.tex"> - <cd:sequence> - <cd:string value="completepublications"/> - </cd:sequence> - <cd:arguments> - <cd:assignments optional="yes" list="yes"> - <cd:inherit name="setuplist"/> - </cd:assignments> - </cd:arguments> -</cd:command> - - -</cd:interface> diff --git a/tex/generic/context/luatex-fonts-merged.lua b/tex/generic/context/luatex-fonts-merged.lua index d0e7f9ae9..a1775edb5 100644 --- a/tex/generic/context/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/texmf/tex/generic/context/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/texmf/tex/generic/context/luatex-fonts.lua --- merge date : 10/02/09 13:19:15 +-- merge date : 10/16/09 16:21:21 do -- begin closure to overcome local limits and interference @@ -369,6 +369,18 @@ function string:split(separator) return c:match(self) end +--~ function lpeg.L(list,pp) +--~ local p = pp +--~ for l=1,#list do +--~ if p then +--~ p = p + lpeg.P(list[l]) +--~ else +--~ p = lpeg.P(list[l]) +--~ end +--~ end +--~ return p +--~ end + end -- closure do -- begin closure to overcome local limits and interference @@ -507,6 +519,14 @@ function table.strip(tab) return lst end +function table.keys(t) + local k = { } + for key,_ in next, t do + k[#k+1] = key + end + return k +end + local function compare(a,b) return (tostring(a) < tostring(b)) end @@ -1279,22 +1299,6 @@ function table.reverse(t) return tt end ---~ function table.keys(t) ---~ local k = { } ---~ for k,_ in next, t do ---~ k[#k+1] = k ---~ end ---~ return k ---~ end - ---~ function table.keys_as_string(t) ---~ local k = { } ---~ for k,_ in next, t do ---~ k[#k+1] = k ---~ end ---~ return concat(k,"") ---~ end - function table.insert_before_value(t,value,extra) for i=1,#t do if t[i] == extra then @@ -1561,11 +1565,11 @@ local rootbased = lpeg.P("/") + letter*lpeg.P(":") -- ./name ../name /name c: :// name/name function file.is_qualified_path(filename) - return qualified:match(filename) + return qualified:match(filename) ~= nil end function file.is_rootbased_path(filename) - return rootbased:match(filename) + return rootbased:match(filename) ~= nil end local slash = lpeg.S("\\/") @@ -2674,11 +2678,12 @@ function nodes.inject_kerns(head,where,keep) -- if rlmode and rlmode < 0 then -- n.xoffset = p.xoffset + d[1] -- else +local k = wx[p] +if k then + n.xoffset = p.xoffset - d[1] - k[2] +else n.xoffset = p.xoffset - d[1] ---~ local k = wx[p] ---~ if k then ---~ wx[n] = k ---~ end +end -- end if mk[p] then n.yoffset = p.yoffset + d[2] @@ -3025,7 +3030,6 @@ else do -- X000 1100 = 12 = 0x1C = leftghost -- X001 0100 = 20 = 0x14 = rightghost - function nodes.protect_glyphs(head) local done = false for g in traverse_id(glyph,head) do @@ -3093,6 +3097,8 @@ if not modules then modules = { } end modules ['font-ini'] = { --ldx]]-- local utf = unicode.utf8 +local format, serialize = string.format, table.serialize +local write_nl = texio.write_nl if not fontloader then fontloader = fontforge end @@ -3173,7 +3179,24 @@ function fonts.show_char_data(n) end local chr = tfmdata.characters[n] if chr then - texio.write_nl(table.serialize(chr,string.format("U_%04X",n))) + write_nl(format("%s @ %s => U%04X => %s => ",tfmdata.fullname,tfmdata.size,n,utf.char(n)) .. serialize(chr,false)) + end + end +end + +function fonts.show_font_parameters() + local tfmdata = fonts.ids[font.current()] + if tfmdata then + local parameters, mathconstants = tfmdata.parameters, tfmdata.MathConstants + local hasparameters, hasmathconstants = parameters and next(parameters), mathconstants and next(mathconstants) + if hasparameters then + write_nl(format("%s @ %s => parameters => ",tfmdata.fullname,tfmdata.size) .. serialize(parameters,false)) + end + if hasmathconstants then + write_nl(format("%s @ %s => math constants => ",tfmdata.fullname,tfmdata.size) .. serialize(mathconstants,false)) + end + if not hasparameters and not hasmathconstants then + write_nl(format("%s @ %s => no parameters and/or mathconstants",tfmdata.fullname,tfmdata.size)) end end end @@ -3442,6 +3465,7 @@ function tfm.do_scale(tfmtable, scaledpoints) t.unicodes = tfmtable.unicodes t.indices = tfmtable.indices t.marks = tfmtable.marks +t.colorscheme = tfmtable.colorscheme t.descriptions = descriptions if tfmtable.fonts then t.fonts = table.fastcopy(tfmtable.fonts) -- hm also at the end @@ -5205,7 +5229,7 @@ otf.features.default = otf.features.default or { } otf.enhancers = otf.enhancers or { } otf.glists = { "gsub", "gpos" } -otf.version = 2.628 -- beware: also sync font-mis.lua +otf.version = 2.631 -- beware: also sync font-mis.lua otf.pack = true -- beware: also sync font-mis.lua otf.syncspace = true otf.notdef = false @@ -5325,6 +5349,7 @@ local enhancers = { "patch bugs", "merge cid fonts", "prepare unicode", "cleanup ttf tables", "compact glyphs", "reverse coverage", "cleanup aat", "enrich with features", "add some missing characters", +--~ "reorganize mark classes", "reorganize kerns", -- moved here "flatten glyph lookups", "flatten anchor tables", "flatten feature tables", "prepare luatex tables", @@ -5477,6 +5502,22 @@ end -- todo: normalize, design_size => designsize +otf.enhancers["reorganize mark classes"] = function(data,filename) + if data.mark_classes then + local unicodes = data.luatex.unicodes + local reverse = { } + for name, class in next, data.mark_classes do + local t = { } + for s in gmatch(class,"[^ ]+") do + t[unicodes[s]] = true + end + reverse[name] = t + end + data.luatex.markclasses = reverse + data.mark_classes = nil + end +end + otf.enhancers["prepare luatex tables"] = function(data,filename) data.luatex = data.luatex or { } local luatex = data.luatex @@ -5780,12 +5821,21 @@ otf.enhancers["analyse subtables"] = function(data,filename) end local flags = gk.flags if flags then +--~ gk.flags = { -- forcing false packs nicer +--~ (flags.ignorecombiningmarks and "mark") or false, +--~ (flags.ignoreligatures and "ligature") or false, +--~ (flags.ignorebaseglyphs and "base") or false, +--~ flags.r2l or false, +--~ } gk.flags = { -- forcing false packs nicer - (flags.ignorecombiningmarks and "mark") or false, - (flags.ignoreligatures and "ligature") or false, - (flags.ignorebaseglyphs and "base") or false, - flags.r2l or false + ((flags.ignorecombiningmarks or flags.mark_class) and "mark") or false, + ( flags.ignoreligatures and "ligature") or false, + ( flags.ignorebaseglyphs and "base") or false, + flags.r2l or false, } +--~ if flags.mark_class then +--~ gk.markclass = luatex.markclasses[flags.mark_class] +--~ end end end end @@ -7394,6 +7444,7 @@ local trace_bugs = false trackers.register("otf.bugs", function local trace_details = false trackers.register("otf.details", function(v) trace_details = v end) local trace_applied = false trackers.register("otf.applied", function(v) trace_applied = v end) local trace_steps = false trackers.register("otf.steps", function(v) trace_steps = v end) +local trace_skips = false trackers.register("otf.skips", function(v) trace_skips = v end) trackers.register("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) trackers.register("otf.normal_chain", function(v) otf.setcontextchain(v and "normal") end) @@ -8764,16 +8815,26 @@ end -- we don't need to pass the currentcontext, saves a bit -- make a slow variant then can be activated but with more tracing +local function show_skip(kind,chainname,char,ck,class) + if ck[9] then + logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s (%s=>%s)",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) + else + logwarning("%s: skipping char %s (%s) in rule %s, lookuptype %s",cref(kind,chainname),gref(char),class,ck[1],ck[2]) + end +end + local function normal_handle_contextchain(start,kind,chainname,contexts,sequence,cache) -- local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] local flags, done = sequence.flags, false local skipmark, skipligature, skipbase = flags[1], flags[2], flags[3] local someskip = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) + local markclass = sequence.markclass for k=1,#contexts do local match, current, last = true, start, start local ck = contexts[k] local sequence = ck[3] local s = #sequence + -- f..l = mid string if s == 1 then -- never happens match = current.id == glyph and current.subtype<256 and current.font == currentfont and sequence[1][current.char] @@ -8801,9 +8862,13 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local ccd = descriptions[char] if ccd then local class = ccd.class +--~ if class == skipmark or class == skipligature or class == skipbase or (markclass and not markclass[char]) then if class == skipmark or class == skipligature or class == skipbase then ---~ if someskip and class == skipmark or class == skipligature or class == skipbase then +--~ if someskip and (class == skipmark or class == skipligature or class == skipbase) then -- skip 'm + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end last = last.next elseif sequence[n][char] then if n < l then @@ -8831,6 +8896,7 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence -- end end if match and f > 1 then + -- before local prev = start.prev if prev then local n = f-1 @@ -8843,9 +8909,13 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local ccd = descriptions[char] if ccd then local class = ccd.class +--~ if class == skipmark or class == skipligature or class == skipbase or (markclass and not markclass[char]) then if class == skipmark or class == skipligature or class == skipbase then --~ if someskip and class == skipmark or class == skipligature or class == skipbase then -- skip 'm + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end elseif sequence[n][char] then n = n -1 else @@ -8882,9 +8952,10 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence end end if match and s > l then + -- after local current = last.next if current then - -- removed optimiziation for s-l == 1, we have to deal with marks anyway + -- removed optimization for s-l == 1, we have to deal with marks anyway local n = l + 1 while n <= s do if current then @@ -8895,9 +8966,13 @@ local function normal_handle_contextchain(start,kind,chainname,contexts,sequence local ccd = descriptions[char] if ccd then local class = ccd.class +--~ if class == skipmark or class == skipligature or class == skipbase or (markclass and not markclass[char]) then if class == skipmark or class == skipligature or class == skipbase then --~ if someskip and class == skipmark or class == skipligature or class == skipbase then -- skip 'm + if trace_skips then + show_skip(kind,chainname,char,ck,class) + end elseif sequence[n][char] then n = n + 1 else @@ -9058,6 +9133,8 @@ end local resolved = { } -- we only resolve a font,script,language pair once +-- todo: pass all these 'locals' in a table + function fonts.methods.node.otf.features(head,font,attr) if trace_steps then checkstep(head) @@ -9217,6 +9294,7 @@ function fonts.methods.node.otf.features(head,font,attr) -- sequence kan weg local ok start, ok = handler(start,r[4],lookupname,lookupmatch,sequence,featuredata,1) +--~ texio.write_nl(tostring(lookupname),tostring(lookupmatch),tostring(ok)) if ok then success = true end diff --git a/tpm/t-bib.tpm b/tpm/t-bib.tpm deleted file mode 100644 index 171cc7f1d..000000000 --- a/tpm/t-bib.tpm +++ /dev/null @@ -1,46 +0,0 @@ -<!DOCTYPE rdf:RDF SYSTEM "tpm.dtd"> -<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:TPM="http://texlive.dante.de/"> - <rdf:Description about="http://dl.contextgarden.net/modules/t-bib.zip"> - <TPM:Name>t-bib</TPM:Name> - <TPM:Type>Package</TPM:Type> - <TPM:Date>2006/12/07 18:00:00</TPM:Date> - <TPM:Version>2006.12.07</TPM:Version> - <TPM:Creator>taco</TPM:Creator> - <TPM:Title>ConTeXt Bibliographies</TPM:Title> - <TPM:Description> -A bibliographic subsystem for ConTeXt - </TPM:Description> - <TPM:Author>Taco Hoekwater</TPM:Author> - <TPM:Size>575599</TPM:Size> - <TPM:Build/> - <TPM:RunFiles size="258797"> -tex/context/bib/bibl-ams.tex -tex/context/bib/bibl-apa-de.tex -tex/context/bib/bibl-apa-fr.tex -tex/context/bib/bibl-apa.tex -tex/context/bib/bibl-aps.tex -tex/context/bib/bibl-num-fr.tex -tex/context/bib/bibl-num.tex -tex/context/bib/bibl-ssa.tex -tex/context/bib/t-bib.tex -tex/context/bib/t-bib.mkii -tex/context/bib/t-bib.mkiv -tex/context/bib/t-bibltx.tex -tex/context/interface/t-bib.xml -bibtex/bst/context/cont-ab.bst -bibtex/bst/context/cont-au.bst -bibtex/bst/context/cont-no.bst -bibtex/bst/context/cont-ti.bst -tpm/t-bib.tpm - </TPM:RunFiles> - <TPM:DocFiles size="316802"> -doc/context/bib/bibmod-doc.pdf -doc/context/bib/bibmod-doc.tex - </TPM:DocFiles> - <TPM:Requires> - <TPM:Package name="context"/> - </TPM:Requires> - <TPM:Provides>Package/t-bib</TPM:Provides> - </rdf:Description> -</rdf:RDF> - diff --git a/tpm/u-texshow.tpm b/tpm/u-texshow.tpm deleted file mode 100644 index 4afb95300..000000000 --- a/tpm/u-texshow.tpm +++ /dev/null @@ -1,30 +0,0 @@ -<!DOCTYPE rdf:RDF SYSTEM "tpm.dtd"> -<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:TPM="http://texlive.dante.de/"> - <rdf:Description about="http://dl.contextgarden.net/modules/u-texshow.zip"> - <TPM:Name>u-texshow</TPM:Name> - <TPM:Type>Package</TPM:Type> - <TPM:Date>2006/11/20 00:00:00</TPM:Date> - <TPM:Version>0.3</TPM:Version> - <TPM:Creator>taco</TPM:Creator> - <TPM:Title>TeXShow / XML</TPM:Title> - <TPM:Description> -ConTeXt command and parameter reference - </TPM:Description> - <TPM:Author>Taco Hoekwater</TPM:Author> - <TPM:Size>36129</TPM:Size> - <TPM:Build/> - <TPM:RunFiles size="30212"> -scripts/context/perl/texshow.pl -tpm/u-texshow.tpm - </TPM:RunFiles> - <TPM:DocFiles size="5917"> -doc/context/scripts/perl/texshow.1 -doc/context/scripts/perl/texshow.html - </TPM:DocFiles> - <TPM:Requires> - <TPM:Package name="context"/> - </TPM:Requires> - <TPM:Provides>Package/u-texshow</TPM:Provides> - </rdf:Description> -</rdf:RDF> - |