%D \module
%D   [       file=core-uti,
%D        version=1997.03.31,
%D          title=\CONTEXT\ Core Macros,
%D       subtitle=Utility File Handling,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA / Hans Hagen \& Ton Otten}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{Context Core Macros / Utility File Handling}

\unprotect

% todo : safe lan etc too
% todo : load all commands at once (tok)
% todo : merge status info patch into tui file (language, encoding, etc),

% Utility-file
%
% De onderstaande macro's ondersteunen het gebruik van de
% zogeheten utility-file. Alle extern onder te brengen
% informatie wordt opgeslagen in de file \jobname.tui, tenzij
% er selectief pagina's worden gezet. In dat geval wordt de
% file \jobname.tmp gebruikt. Informatie wordt ingelezen uit
% de file \jobname.tuo, welke door TeXUtil wordt aangemaakt.

\edef\utilityversion{1998.07.07} % was: 1996.03.15  % status variables
\edef\utilityversion{1998.12.20} % was: 1998.07.07  % index attributes
\edef\utilityversion{2003.07.19} % was: 1998.12.20  % object pages

% Bepaalde commando's worden als string weggeschreven. Deze
% zijn aan het eind van deze file gedefinieerd.

% Om een opbouw van spaties te voorkomen (???) moet ^^M een
% andere betekenis krijgen:
%
% \catcode`\^^M=14 (comment)
%
% read file
%
% \catcode`\^^M=5  (end of line)

\newwrite\uti
\newif\ifutilitydone

\def\@@utilityerrormessage
  {\showmessage\m!systems8\empty
   \globallet\@@utilityerrormessage\relax}

\def\thisisutilityversion#1%
  {\doifelse\utilityversion{#1}%
     {\checksectionseparator}
     {\@@utilityerrormessage\resetutilities\endinput}}

\def\checksectionseparator               % catches backward compatibility conflict
  {\doifnot\sectionseparator:\endinput}  % this dependency may go in a few years

\def\dosplitofffoliopart[#1::#2::#3]{#3}

\def\thisissectionseparator#1%
  {\bgroup
   \globallet\checksectionseparator\relax
   \convertcommand \sectionseparator\to\asciiA
   \convertargument               #1\to\asciiB
   \expanded{\gdef\noexpand\dosplitofffoliopart[####1\sectionseparator
     \sectionseparator####2\sectionseparator\sectionseparator####3]{####3}}%
   \ifx\asciiA\asciiB
     \egroup
   \else
     \egroup
     % todo \@@utilityerrormessage
     \resetutilities
     \endinput
   \fi}

\def\writeutility         {\write\uti}
\def\writeutilitycommand#1{\write\uti{c \string#1}}

% less tokens
%
% \def\immediatewriteutility       {\immediate\writeutility}
% \def\immediatewriteutilitycommand{\immediate\writeutilitycommand}
%
% more flexible (for overloading)

\def\immediatewriteutility         {\immediate\write\uti}
\def\immediatewriteutilitycommand#1{\immediate\write\uti{c \string#1}}

% as in:

\def\cwriteutility#1%
  {\write\uti{\noexpand\checkedutility{\number\nofshipouts}{#1}}}

\def\cwriteutilitycommand#1%
  {\write\uti{\noexpand\checkedutility{\number\nofshipouts}{c \string#1}}}

% \def\cimmediatewriteutility
%   {\immediate\write\uti}
%
% \def\cimmediatewriteutilitycommand#1%
%   {\immediate\write\uti{c \string#1}}

\let\checkedutility\secondoftwoarguments

\def\docheckedutility#1#2{\ifnum#1=\nofshipouts#2\else\letterpercent\fi}

\prependtoks
  \let\checkedutility\docheckedutility
\to \everybeforeshipout

\edef\testbytesequence{\rawcharacter{7}\rawcharacter{27}\rawcharacter{227}}

\def\thisisbytesequence#1%
  {\ifx\testbytesequence\empty\else
     \convertcommand\testbytesequence\to\testbytesequence
     \convertargument#1\to\ascii
     \ifx\testbytesequence\ascii \else
       \writestatus\m!systems{possible problem with 8 bit output}%
     \fi
   \fi
   \global\let\thisisbytesequence\gobbleoneargument}

% Better use marks.

\def\checkutilities
  {\let\writeutility                \cwriteutility
   \let\writeutilitycommand         \cwriteutilitycommand
  %\let\immediatewriteutility       \cimmediatewriteutility
  %\let\immediatewriteutilitycommand\cimmediatewriteutilitycommand
   \let\checkutilities              \relax}

% this trickery is used in s-pre-50

\def\openutilities
  {\immediate\openout\uti\jobname.\f!inputextension
   \immediatewriteutilitycommand{\thisissectionseparator{\sectionseparator}}% for the moment
   \immediatewriteutilitycommand{\thisisutilityversion  {\utilityversion  }}% in this order
   \immediatewriteutilitycommand{\thisisbytesequence    {\testbytesequence}}}

\def\closeutilities
  {\immediate\closeout\uti % niet echt nodig
   \reportutilityproblems
   % should be a message :
   \let\writeutilitycommand         \gobbleoneargument
   \let\writeutility                \gobbleoneargument
   \let\immediatewriteutilitycommand\gobbleoneargument
   \let\immediatewriteutility       \gobbleoneargument}

\def\reopenutilities
  {\immediate\closeout\uti
   \openutilities}

\def\abortutilitygeneration
  {\immediatewriteutilitycommand\utilitygenerationaborted
   \immediatewriteutility{q {quit}}}

\def\utilitygenerationaborted
  {\showmessage\m!systems{21}\empty
   \globallet\utilitygenerationaborted\endinput
   \gdef\reportutilityproblems{\showmessage\m!systems{22}\empty}%
   \endinput}

\def\savecurrentvalue#1#2%
  {\immediatewriteutilitycommand{\initializevariable\string#1{#2}}}

\let\initializevariable\gdef

\def\disableinitializevariables
  {\globallet\initializevariable\gobbletwoarguments}

\let\reportutilityproblems=\relax

\let\utilityresetlist=\empty

% the original
%
% \def\addutilityreset#1%
%   {\addtocommalist{\s!reset#1}\utilityresetlist}
%
% \def\resetutilities%
%   {\processcommacommand[\utilityresetlist]\getvalue}
%
% the more efficient
%
% \def\addutilityreset#1%
%   {\addtocommalist{#1}\utilityresetlist}
%
% \def\doresetutility#1%
%   {\getvalue{\s!reset#1}}
%
% \def\resetutilities%
%   {\processcommacommand[\utilityresetlist]\doresetutility}
%
% the fastest, about two times, but who cares, since this
% can be neglected

\newtoks\utilityresetlist

\def\addutilityreset#1%
  {\@EA\appendtoks\csname\s!reset#1\endcsname\to\utilityresetlist}

\def\resetutilities
  {\the\utilityresetlist}

% #1=type
% #2=file
% #3=melding

% #4=voor
% #5=na

% Er wordt gegroepeerd. Als binnen een lijst (bijvoorbeeld) de
% \leftskip is aangepast, maar nog geen \par is gegeven, dan
% geldt buiten de groep de oude \leftskip. Aan #5 kan dan
% ook \par worden meegegeven om de paragraaf af te sluiten.

\newif\ifdoinpututilities
\newif\ifunprotectutilities   % voor't geval er \v!xxxxxx's zijn

% no longer needed, since texutil is now multi platform
%
% \def\utilitycheckmessage
%   {\showmessage\m!systems{12}{}%
%    \globallet\utilitycheckmessage=\relax}
%
% \def\saveutilityline#1 #2\txen% tricky maar ok, want achter \command
%   {\if     #1c% commands      % in \ascii staat een spatie; #1 kan
%      \write\scratchwrite{#2}% % \par in stringvorm zijn (eof)!
%    \else\if#1s% synoniems
%      \utilitycheckmessage
%    \else\if#1r% registers
%      \utilitycheckmessage
%    \fi\fi\fi}
%
% \def\checkutilityfile
%   {\doiflocfileelse{\jobname.\f!outputextension}
%      {}
%      {\doiflocfileelse{\jobname.\f!inputextension}
%         {\bgroup
%          \showmessage\m!systems{11}{}%
%          \openout\scratchwrite=\jobname.\f!outputextension
%          \openlocin\scratchread{\jobname.\f!inputextension}%
%          \def\doprocessline%
%            {\ifeof\scratchread
%               \def\doprocessline{\closein\scratchread}%
%             \else
%               \read\scratchread to \ascii
%               \convertcommand\ascii\to\ascii
%               \expandafter\saveutilityline\ascii\txen
%             \fi
%             \doprocessline}%
%          \doprocessline
%          \closeout\scratchwrite
%          \egroup}
%         {}}}

\def\currentutilityfilename{\jobname}

\long\def\doutilities#1#2#3#4#5% % introduceren in utility file
  {\restorecatcodes
   \resetutilities
   % more than one utility thing can be handled in one pass,
   % for instance lists, so we process ##1 as list
   \def\douticommand##1{\csname\s!set##1\endcsname}%
   \processcommacommand[#1]\douticommand
   \begingroup
   \def\currentutilityfilename{#2}%
   \notesenabledfalse
   \doinpututilitiestrue
   \global\utilitydonefalse
   \catcode`\\=\@@escape
   \catcode`\{=\@@begingroup
   \catcode`\}=\@@endgroup
   \catcode`\%=\@@comment\relax
   \pushendofline % geeft problemen zodra andere file wordt ingelezen
   \ifunprotectutilities % nog nodig ?
     \unprotect
   \fi
   \ifnum\catcode`\@=\@@active \else
     \catcode`\@=\@@letter % permits expanded commands with \@'s
   \fi
   \ifnum\catcode`\!=\@@active \else
     \catcode`\!=\@@letter % permits multilingual constants
   \fi
   #4%
   \the\everybeforeutilityread
   \readjobfile{#2.\f!outputextension}\donothing\donothing
   \the\everyafterutilityread
   #5%
   \relax
   \ifunprotectutilities
     \protect
   \fi
   \popendofline
   \ifutilitydone\else
     \doifsomething{#3}
       {\showmessage\m!systems9{{#3}}%
        \ifvoorlopig
          \blank
          \type{[\currentmessagetext]}%
          \blank
        \fi}%
   \fi
   \disableinitializevariables
   \endgroup}

% Saving the sort vector:

\def\savesortkeys
  {\startnointerference
   \def\flushsortkey##1##2##3##4%
     {\convertargument{##1}{##2}{##3}{##4}\to\ascii
      \immediatewriteutility{k {\currentlanguage}{\currentencoding}\ascii}}%
   \let\definesortkey\flushsortkey
   \flushsortkeys
   \globallet\savesortkeys\relax
   \stopnointerference}

\prependtoks \savesortkeys \to \everystarttext

% Commando's ten behoeve van two-pass lists. In principe
% kan alles in een keer worden ingelezen. Omdat de macro's
% groeien is de kans groot dat het (main) geheugen door
% (de)allocatie volloopt. Vandaar dat we het toch maar niet
% doen.
%
% \definetwopasslist{\s!xxx}
%
% \gettwopassdata{\s!xxx}
% \getfrompassdata{\s!xxx}{n}       n=index (getal)
% \findtwopassdata{\s!xxx}{tag}     bijvoorbeeld {label:}
% \iftwopassdatafound
% \twopassdata
%
% \twopassentry{\s!xxx}{nr}{data}   nr alleen voor testdoeleinden
%
% also:
%
% \definerawpasslist{\s!xxx}
% \moverawpasslist\s!xxx\to\somemacro

\let\alltwopasslists\empty % with    0,0 -> stepwise commalist
\let\allrawpasslists\empty % without 0,0 -> raw commalist

\newif\iftwopassdatafound

\def\settwopassentries
  {\def\twopassentry##1{\executeifdefined{@@##1\s!pass}\gobbletwoarguments}}

\def\resettwopassentries
  {\let\twopassentry\gobblethreearguments}

\addutilityreset{twopassentries}

\resettwopassentries

% \def\twopassentry#1%
%   {\executeifdefined{@@#1\s!pass}\gobbletwoarguments}

%\def\appendtwopasselement#1#2#3%
%  {%\debuggerinfo{\m!systems}{twopass data #1 - #2 = #3}%
%   \@EA\ifx\csname#1:\s!list\endcsname\empty
%     \setxvalue{#1:\s!list}{#3}%
%   \else
%     \setxvalue{#1:\s!list}{\getvalue{#1:\s!list},#3}%
%   \fi}

\def\appendtwopasselement#1#2#3% can sometimes become a large list
  {%\debuggerinfo{\m!systems}{twopass data #1 - #2 = #3}%
   \expandafter\xdef\csname#1:\s!list\endcsname
     {\@EA\ifx\csname#1:\s!list\endcsname\empty \else
        \csname#1:\s!list\endcsname,\fi#3}}

\def\dodefinetwopasslist#1%
  {\doifundefined{#1:\s!list}
     {%\debuggerinfo\m!systems{defining twopass class #1}%
      \doglobal\addutilityreset{#1\s!pass}%
      \setgvalue{\s!set  #1\s!pass}{\dosettwopasslist  {#1}}%
      \setgvalue{\s!reset#1\s!pass}{\doresettwopasslist{#1}}%
      \getvalue {\s!reset#1\s!pass}}}

\def\dosettwopasslist#1%
  {\letgvalue{\s!set#1\s!pass}\gobbletwoarguments
   \setgvalue{@@#1\s!pass}{\appendtwopasselement{#1}}}

\def\doresettwopasslist#1%
  {\letgvalue{@@#1\s!pass}\gobbletwoarguments}

\def\definetwopasslist#1%
  {\expanded{\dodefinetwopasslist{#1}}%
   \doglobal\addtocommalist{#1}\alltwopasslists}

\def\definerawpasslist#1%
  {\expanded{\dodefinetwopasslist{#1}}%
   \doglobal\addtocommalist{#1}\allrawpasslists}

\def\doloadtwopassdata#1%
  {\doifundefined{#1:\s!list}
     {\startnointerference
      \letgvalueempty{#1:\s!list}%
      \protectlabels
      \doutilities{twopassentries,#1\s!pass}\jobname\empty\relax\relax
      \ifx\twopassdata\empty\else
        \appendtwopasselement{#1}{0}\twopassdata
      \fi
      \stopnointerference}}

\def\loadtwopassdata
  {\ifx\alltwopasslists\empty\else
     \def\twopassdata{0,0}% end condition
     \processcommacommand[\alltwopasslists]\doloadtwopassdata
     \globallet\alltwopassdata\empty
   \fi
   \ifx\allrawpasslists\empty\else
     \let\twopassdata\empty
     \processcommacommand[\allrawpasslists]\doloadtwopassdata
     \globallet\allrawpassdata\empty
   \fi}

\def\moverawpasslist#1#2% erases the old one, like the others do
  {\loadtwopassdata
   \@EA\let\@EA#2\csname#1:\s!list\endcsname
   \@EA\let\csname#1:\s!list\endcsname\empty}

\let\twopassdata=\empty

%\def\dogettwopassdata[#1,#2]#3%
%  {\doifelse{#1}{0} % \ifcase truukje gaat fout
%     {\twopassdatafoundfalse
%      \let\twopassdata\empty}
%     {\twopassdatafoundtrue
%      \setxvalue{#3:\s!list}{#2}%
%      \edef\twopassdata{#1}}}

\def\notwopassdata{0}

% \def\dogettwopassdata[#1,#2]#3%
%   {\edef\twopassdata{#1}%
%    \ifx\twopassdata\notwopassdata
%      \twopassdatafoundfalse
%      \let\twopassdata\empty
%    \else
%      \twopassdatafoundtrue
%      \setxvalue{#3:\s!list}{#2}%
%    \fi}
%
% \def\gettwopassdata#1%
%   {\loadtwopassdata
%   %\edef\!!stringa{\csname#1:\s!list\endcsname}%
%   %\debuggerinfo{\m!systems}{twopass get #1 - \!!stringa}%
%   %\expandafter\dogettwopassdata\expandafter[\!!stringa]{#1}}
%    \expanded{\dogettwopassdata[\csname#1:\s!list\endcsname]}{#1}}
%
% \def\findtwopassdata#1#2%
%   {\loadtwopassdata
%    \expanded{\dofindtwopassdata{#1}{#2}}}
%
% \def\dofindtwopassdata#1#2%
%   {\edef\!!stringa{,\csname#1:\s!list\endcsname}%
%   %\debuggerinfo{\m!systems}{twopass find #2 - \!!stringa}%
%    \def\dodofindtwopassdata[##1,##2#2##3,##4]%
%      {\edef\twopassdata{##3}%
%       \ifx\twopassdata\empty
%         \twopassdatafoundfalse
%       \else
%         \twopassdatafoundtrue
%       \fi}%
%    \@EA\dodofindtwopassdata\@EA[\!!stringa,#2,#2,]}
%
% \def\getfirsttwopassdata#1%
%   {\loadtwopassdata
%    \edef\!!stringa{\getvalue{#1:\s!list}}%
%    \expandafter\dogetfirsttwopassdata\expandafter[\!!stringa]{#1}}
%
% \def\dogetfirsttwopassdata[#1,#2]#3%
%   {\doifelse{#1}{0}
%      {\twopassdatafoundfalse
%       \let\twopassdata\empty}
%      {\twopassdatafoundtrue
%       \edef\twopassdata{#1}}}
%
% \def\getlasttwopassdata#1%
%   {\loadtwopassdata
%    \edef\twopassdata{0}\twopassdatafoundfalse
%    \newcounter\noftwopassitems
%    \def\docommando##1%
%      {\doifnot{##1}{0}
%         {\increment\noftwopassitems
%          \edef\twopassdata{##1}\twopassdatafoundtrue}}%
%    \processcommacommand[\getvalue{#1:\s!list}]\docommando}
%
% \def\getfromtwopassdata#1#2%
%   {\loadtwopassdata
%    \getfromcommacommand[\csname#1:\s!list\endcsname][#2]%
%    \doifelsenothing{\commalistelement}
%      {\twopassdatafoundfalse
%       \let\twopassdata\empty}
%      {\twopassdatafoundtrue
%       \let\twopassdata\commalistelement}}

% todo: store each entry in hash, an load all uti commands at once

\def\dogettwopassdata[#1,#2]#3%
  {\edef\twopassdata{#1}%
   \ifx\twopassdata\notwopassdata
     \twopassdatafoundfalse
     \let\twopassdata\empty
   \else
     \twopassdatafoundtrue
     \setxvalue{#3:\s!list}{#2}%
   \fi}

\def\gettwopassdata#1%
  {\loadtwopassdata
   \@EAEAEA\dogettwopassdata\@EA\@EA\@EA[\csname#1:\s!list\endcsname]{#1}}

\def\findtwopassdata#1#2%
  {\loadtwopassdata
   \expanded{\dofindtwopassdata{#1}{#2}}}

\def\dofindtwopassdata#1#2%
  {\def\dodofindtwopassdata[##1,##2#2##3,##4]{\edef\twopassdata{##3}}%
   \@EAEAEA\dodofindtwopassdata\@EA\@EA\@EA[\@EA\@EA\@EA,\csname#1:\s!list\endcsname,#2,#2,]%
   \ifx\twopassdata\empty
     \twopassdatafoundfalse
   \else
     \twopassdatafoundtrue
   \fi}

\def\getfirsttwopassdata#1%
  {\loadtwopassdata
   \@EAEAEA\dogetfirsttwopassdata\@EA\@EA\@EA[\csname#1:\s!list\endcsname]{#1}}

\def\dogetfirsttwopassdata[#1,#2]#3%
  {\edef\twopassdata{#1}%
   \ifx\twopassdata\notwopassdata
     \twopassdatafoundfalse
     \let\twopassdata\empty
   \else
     \twopassdatafoundtrue
   \fi}

\def\dogetlasttwopassdata#1%
  {\edef\nexttwopassdata{#1}%
   \ifx\nexttwopassdata\notwopassdata \else
     \let\twopassdata\nexttwopassdata
     \advance\scratchcounter \plusone
     \twopassdatafoundtrue
   \fi}

\def\getlasttwopassdata#1%
  {\loadtwopassdata
   \scratchcounter\zerocount
   \@EAEAEA\rawprocesscommalist\@EA\@EA\@EA[\csname#1:\s!list\endcsname]\dogetlasttwopassdata
   \edef\noftwopassitems{\the\scratchcounter}%
   \iftwopassdatafound\else
     \let\twopassdata\empty
   \fi}

\def\getfromtwopassdata#1#2%
  {\loadtwopassdata
   \@EAEAEA\getfromcommalist\@EA\@EA\@EA[\csname#1:\s!list\endcsname][#2]%
   \ifx\commalistelement\empty
     \twopassdatafoundfalse
     \let\twopassdata\empty
   \else
     \twopassdatafoundtrue
     \let\twopassdata\commalistelement
   \fi}

% Default-instellingen (verborgen)

\prependtoks \resetutilities \to \everyjob

% Experiment
%
%\installprogram{Hello World}

\def\installprogram#1%
  {\immediatewriteutility{e p {#1}}}

% \writeplugindata{texutil}{{alpha}}
% \writeplugindata{texutil}{{beta}}
% \writeplugindata{texutil}{{gamma}}
% \writeplugindata{texutil}{{delta}}
%
% \loadplugindata {plugintest}

\def\immediatewriteplugindata#1#2%
  {\immediatewriteutility{p u {#1} #2}}

\def\writeplugindata#1#2%
  {\writeutility{p u {#1} #2}}

\def\loadplugindata#1%
  {\doutilities{#1}\jobname\empty\relax\relax}

% \plugincommand{\command{}{}{}}
%
% this way we can catch undefined commands

\long\def\plugincommand#1%
  {\doplugincommand#1\relax}

\long\def\doplugincommand#1%
  {\ifx#1\undefined
     \expandafter\noplugincommand
   \else
     \expandafter#1%
   \fi}

% shorter:
%
% \long\def\doplugincommand#1%
%   {\ifx#1\undefined\expandafter\noplugincommand\fi#1}

\long\def\noplugincommand#1\relax
  {}

% \addutilityreset{plugintest}
%
% \def\resetplugintest{\let\plugintest\gobbletwoarguments}
% \def\setplugintest  {\let\plugintest\writestatus}
%
% \installplugin
%   {plugintest}
%   {\let\plugintest\gobbletwoarguments}
%   {\let\plugintest\writestatus}

\long\def\installplugin#1#2#3%
  {\addutilityreset          {#1}%
   \long\setvalue{\s!reset#1}{#2}%
   \long\setvalue{\s!set  #1}{#3}}

\protect \endinput
