summaryrefslogtreecommitdiff
path: root/tex/context/base/mkii/supp-fil.mkii
diff options
context:
space:
mode:
authorContext Git Mirror Bot <phg42.2a@gmail.com>2016-01-12 17:15:07 +0100
committerContext Git Mirror Bot <phg42.2a@gmail.com>2016-01-12 17:15:07 +0100
commit8d8d528d2ad52599f11250cfc567fea4f37f2a8b (patch)
tree94286bc131ef7d994f9432febaf03fe23d10eef8 /tex/context/base/mkii/supp-fil.mkii
parentf5aed2e51223c36c84c5f25a6cad238b2af59087 (diff)
downloadcontext-8d8d528d2ad52599f11250cfc567fea4f37f2a8b.tar.gz
2016-01-12 16:26:00
Diffstat (limited to 'tex/context/base/mkii/supp-fil.mkii')
-rw-r--r--tex/context/base/mkii/supp-fil.mkii722
1 files changed, 722 insertions, 0 deletions
diff --git a/tex/context/base/mkii/supp-fil.mkii b/tex/context/base/mkii/supp-fil.mkii
new file mode 100644
index 000000000..9a981797e
--- /dev/null
+++ b/tex/context/base/mkii/supp-fil.mkii
@@ -0,0 +1,722 @@
+%D \module
+%D [ file=supp-fil,
+%D version=1995.10.10,
+%D title=\CONTEXT\ Support Macros,
+%D subtitle=Files,
+%D author=Hans Hagen,
+%D date=\currentdate,
+%D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%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 \TEX\ operates on files, so one wouldn't wonder that there
+%D is a separate module for file support. In \CONTEXT\ files
+%D are used for several purposes:
+%D
+%D \startitemize[packed]
+%D \item general textual input
+%D \item logging status information
+%D \item saving registers, lists and references
+%D \item buffering defered textual input
+%D \stopitemize
+%D
+%D When dealing with files we can load them as a whole, using
+%D the \type{\input} primitive or load them on a line||by||line
+%D basis, using \type{\read}. Writing is always done line by
+%D line, using \type{\write}.
+
+\writestatus{loading}{ConTeXt Support Macros / Files}
+
+\unprotect
+
+\ifx\undefined\f!pathseparator
+ \def\f!pathseparator{/}
+ \def\f!currentpath {.}
+ \def\f!parentpath {..}
+\fi
+
+\ifnum\texengine=\luatexengine
+ \def\openinputfile #1#2{\openin #1{#2}\relax}
+ \def\openoutputfile#1#2{\immediate\openout#1{#2}\relax}
+\else
+ \def\openinputfile #1#2{\openin #1 "#2"\relax}
+ \def\openoutputfile#1#2{\immediate\openout#1 "#2"\relax}
+\fi
+
+\def\closeinputfile #1{\immediate\closein #1}
+\def\closeoutputfile#1{\immediate\closeout#1}
+
+%D \macros
+%D {pushendofline,popendofline}
+%D
+%D When we are loading files in the middle of the typesetting
+%D process, for instance when we load references, we have to be
+%D sure that the reading process does not generate so called
+%D 'spurious spaces'. This can be prevented by assigning the
+%D line ending character the \CATCODE\ comment. This is
+%D accomplished by
+%D
+%D \starttyping
+%D \pushendofline
+%D ... reading ...
+%D \popendofline
+%D \stoptyping
+%D
+%D Just to be sure, we save the current meaning of \type{^^M}
+%D in \type{\poppedendofline}.
+
+% \chardef\poppedendofline\catcode`\^^M
+%
+% \def\pushendofline
+% {\chardef\poppedendofline\catcode`\^^M\relax
+% \catcode`\^^M\@@comment\relax}
+%
+% \def\popendofline
+% {\catcode`\^^M\poppedendofline}
+%
+% support for nested usage:
+
+\newcount \endoflinelevel
+
+\ifx\newlinecode\undefined \chardef\newlinecode=`\^^M \fi
+
+\def\pushendofline
+ {\advance\endoflinelevel\plusone
+ \expandafter\chardef\csname :eol:\number\endoflinelevel\endcsname\catcode\newlinecode
+ \catcode\newlinecode\@@comment\relax}
+
+\def\popendofline
+ {\catcode\newlinecode\csname :eol:\number\endoflinelevel\endcsname
+ \advance\endoflinelevel\minusone}
+
+\def\restoreendofline
+ {\catcode\newlinecode\@@endofline}
+
+%D \macros
+%D {scratchread, scratchwrite}
+%D
+%D We define a scratch file for reading. Keep in mind that
+%D the number of files is limited to~16, so use this one when
+%D possible. We also define a scratch output file.
+
+\ifx\undefined\scratchread \newread \scratchread \fi
+\ifx\undefined\scratchwrite \newwrite\scratchwrite \fi
+
+%D \macros
+%D {unlinkfile}
+%D
+%D Sometimes we want to make sure a file is deleted, so here
+%D is a macro that does the job. It's named after the \PERL\
+%D one.
+
+\def\unlinkfile#1%
+ {\openoutputfile \scratchwrite{#1}%
+ \closeoutputfile\scratchwrite}
+
+%D \macros
+%D {writeln}
+%D
+%D This saves a few tokens:
+
+\def\writeln#1{\write#1{}}
+
+\def\doiffileexistselse#1%
+ {\doifelsenothing{#1}
+ {\secondoftwoarguments}
+ {\openinputfile\scratchread{#1}%
+ \ifeof\scratchread
+ \closeinputfile\scratchread
+ \expandafter\secondoftwoarguments
+ \else
+ \closeinputfile\scratchread
+ \expandafter\firstoftwoarguments
+ \fi}}
+
+%D \macros
+%D {doprocessfile,fileline,fileprocessedtrue,dofinishfile}
+%D
+%D The next macro offers a framework for processing files on a
+%D line by line basis.
+%D
+%D \starttyping
+%D \doprocessfile \identifier {name} \action
+%D \stoptyping
+%D
+%D The first argument can for instance be \type{\scratchread}.
+%D The action must do something with \type{\fileline}, which
+%D holds the current line. One can halfway step out using
+%D \type{\dofinishfile} and ise \type{\iffileprocessed} to
+%D see if indeed some content was found.
+
+\newif\iffileprocessed
+
+\let\fileline\empty
+
+\def\doprocessfile#1#2#3%
+ {\openinputfile{#1}{#2}%
+ \ifeof#1%
+ \fileprocessedfalse
+ \closeinputfile#1%
+ \else
+ \fileprocessedtrue
+ \gdef\dofinishfile
+ {\closeinputfile#1%
+ \global\let\doprocessline\relax}%
+ \gdef\doprocessline
+ {\ifeof#1%
+ \expandafter\dofinishfile
+ \else
+ \global\read#1 to \fileline
+ #3\relax
+ \expandafter\doprocessline
+ \fi}%
+ \expandafter\doprocessline
+ \fi}
+
+%D \macros
+%D {pathplusfile,assignfullfilename,sanitizefilename}
+%D
+%D Use \type{\pathplusfile} to compose a full file name, like
+%D in:
+%D
+%D \starttyping
+%D \pathplusfile{path}{file}
+%D \stoptyping
+%D
+%D By default, this expands into {\tt \pathplusfile{path}{file}}.
+
+\def\pathplusfile#1#2{#1\f!pathseparator#2}
+
+%D This one constructs a filename from a (possible empty)
+%D path and filename.
+
+\def\assignfullfilename#1#2\to#3%
+ {\doifelsenothing{#1}
+ {\edef#3{#2}}
+ {\edef#3{#1\f!pathseparator#2}}}
+
+%D For the moment, we limit sanitizing to taking care of
+%D active \type {/}.
+
+\bgroup % todo: _ cleanup
+
+\catcode`\/=\@@active
+\catcode`\:=\@@active
+\catcode`\~=\@@active
+\catcode`\_=\@@active
+
+\gdef\sanitizefilename#1\to#2%
+ {\bgroup
+ \edef/{\string/}%
+ \edef:{\string:}%
+ \edef~{\string~}%
+ \edef_{\string_}%
+ \retainlccodes
+ \lccode`\\=`\/
+ \lowercase{\expanded{\xdef\noexpand\sanitizedfilename{#1}}}%
+ \egroup
+ \let#2\sanitizedfilename}
+
+\egroup
+
+%D NEW:
+
+\chardef\kindoffile=0 % 0=normal 1=full path spec (or http)
+
+\def\checkfilename#1%
+ {\doifinstringelse{@@/}{@@#1}% unix: /full/path
+ {\chardef\kindoffile\plusone}
+ {\doifinstringelse{:/}{#1}% windows: e:/full/path or http://
+ {\chardef\kindoffile\plusone}
+ {\chardef\kindoffile\zerocount}}}
+
+%D \macros
+%D {input, normalinput}
+%D
+%D Sometimes we run into troubles when \type {\input} wants to get
+%D expanded, e.g. in a \type {\write} (which happens in the metafun
+%D manual when we permit long MP lines). So, instead of fixing that,
+%D we go for a redefinition of \type {\input}. Of course it's better
+%D to use \type {\readfile} or \type {\processfile}.
+
+\unexpanded\def\input{\normalinput}
+
+\ifnum\texengine=\luatexengine
+ \def\inputgivenfile#1{\normalinput{#1}\relax}
+\else
+ \def\inputgivenfile#1{\normalinput"#1"\relax}
+\fi
+
+%D \macros
+%D {readfile,ReadFile,maxreadlevel}
+%D
+%D One cannot be sure if a file exists. When no file can be
+%D found, the \type{\input} primitive gives an error message
+%D and switches to interactive mode. The macro \type{\readfile}
+%D takes care of non||existing files. This macro has two faces.
+%D
+%D \starttyping
+%D \ReadFile {filename}
+%D \readfile {filename} {before loading} {not found}
+%D \stoptyping
+%D
+%D Many \TEX\ implementations have laid out some strategy for
+%D locating files. This can lead to unexpected results,
+%D especially when one loads files that are not found in the
+%D current directory. Let's give an example of this. In
+%D \CONTEXT\ illustrations can be defined in an external file.
+%D The resizing macro first looks if an illustration is defined
+%D in the local definitions file. When no such file is found,
+%D it searches for a global file and when this file is not
+%D found either, the illustration itself is scanned for
+%D dimensions. One can imagine what happens if an adapted,
+%D localy stored illustration, is scaled according to
+%D dimensions stored somewhere else.
+%D
+%D When some \TEX\ implementation starts looking for a file, it
+%D normally first looks in the current directory. When no file
+%D is found, \TEX\ starts searching on the path where format
+%D and|/|or style files are stored. Depending on the implementation
+%D this can considerably slow down processing speed.
+%D
+%D In \CONTEXT, we support a project||wise ordening of files.
+%D In such an approach it seems feasible to store common files
+%D in a lower directory. When for instance searching for a
+%D general layout file, we therefore have to backtrack.
+%D
+%D These three considerations have lead to a more advanced
+%D approach for loading files.
+%D
+%D We first present an earlier implementation of
+%D \type{\readfile}. This command backtracks parent
+%D directories, upto a predefined level. Users can change this
+%D level, but we default to~3.
+%D
+%D \starttyping
+%D \def\maxreadlevel {3}
+%D \stoptyping
+%D
+%D This is a pseudo \COUNTER.
+%D
+%D We use \type{\normalinput} instead of \type{\input}
+%D because we want to be able to redefine the original
+%D \type{\input} when needed, for instance when loading third
+%D party libraries.
+
+\newevery \everybeforereadfile \EveryBeforeReadFile
+\newevery \everyafterreadfile \EveryAfterReadFile
+
+\let \everyreadfile \everybeforereadfile
+
+\newif\iftracefiles
+
+\newcount\readlevel
+
+\def\maxreadlevel{3}
+
+\newconditional\trackfilenames
+\let\trackedfilename\empty
+
+% We need to postpone loading, else we got frozen type-* files and so when
+% a format is generated on a source path.
+
+\def\doreadfile#1#2#3#4%
+ {\sanitizefilename#2\to\readfilename
+ \ifx\readfilename\empty
+ % silently ignore
+ \else
+ \let\trackedfilename\readfilename
+ \ifconditional\trackfilenames
+ \doifundefinedelse{fn..\trackedfilename}\donetrue\donefalse
+ \else
+ \donetrue
+ \fi
+ \ifdone
+ \checkfilename\readfilename
+ \ifcase\kindoffile
+ \iftracefiles\writestatus\m!systems{searching for \readfilename\space on #1}\fi
+ % not a full path or url, check for existence
+ \doifelsenothing{#1}
+ {\def\next{\redoreadfile\readfilename{#3}{#4}}}%
+ {\def\next{\redoreadfile{\pathplusfile{#1}{\readfilename}}{#3}{#4}}}%
+ \else
+ % a full path or url, no further checking done
+ \doiffileexistselse\readfilename
+ {\iftracefiles\writestatus\m!systems{located \readfilename}\fi
+ \def\next{#3\dodoreadfile}}%
+ {\iftracefiles\writestatus\m!systems{not found \readfilename}\fi
+ \def\next{#4}}%
+ \fi
+ \else
+ \edef\readfilename{\getvalue{fn..\readfilename}}%
+ \iftracefiles\writestatus\m!systems{already located \readfilename}\fi
+ \def\next{#3\dodoreadfile}%
+ \fi
+ \expandafter\next
+ \fi}
+
+\def\redoreadfile#1#2#3%
+ {\doiffileexistselse{#1}%
+ {\edef\readfilename{#1}%
+ \iftracefiles\writestatus\m!systems{#1 located}\fi
+ \def\next{#2\dodoreadfile}}%
+ {\iftracefiles\writestatus\m!systems{cannot locate #1}\fi
+ \advance\readlevel\minusone
+ \ifnum\readlevel>\zerocount
+ \edef\readfilename{\pathplusfile{\f!parentpath}{\readfilename}}%
+ \def\next{\redoreadfile\readfilename{#2}{#3}}%
+ \else
+ \def\next{#3}%
+ \fi}%
+ \next}
+
+\def\dodoreadfile % we provide hooks, for instance for \enableXML
+ {\ifconditional\trackfilenames
+ \setxvalue{fn..\trackedfilename}{\readfilename}%
+ \fi
+ \the\everybeforereadfile
+% \normalinput\readfilename\relax
+ \relax\inputgivenfile\readfilename\relax
+ \the\everyafterreadfile}
+
+% too less:
+%
+% \unexpanded\def\readfile% #1%
+% {\readlevel\maxreadlevel
+% \doreadfile\empty} % {#1}
+%
+% too much:
+%
+% \unexpanded\def\readfile#1#2#3%
+% {\readlocfile{#1}{#2}
+% {\readjobfile{#1}{#2}
+% {\readsysfile{#1}{#2}{#3}}}}
+%
+% just ok:
+
+\unexpanded\def\readfile#1#2#3%
+ {\readlocfile{#1}{#2}{\readsysfile{#1}{#2}{#3}}}
+
+\def\readtexfile#1#2#3%
+ {\pushcatcodetable \catcodetable \ctxcatcodes
+ \readfile{#1}{#2}{#3}%
+ \popcatcodetable}
+
+\ifdefined\xmlcatcodes \else \let\xmlcatcodes\xmlcatcodesn \fi
+
+\def\readxmlfile#1#2#3%
+ {\pushcatcodetable \catcodetable \xmlcatcodes
+ \readfile{#1}{#2}{#3}%
+ \popcatcodetable}
+
+\unexpanded\def\ReadFile#1%
+ {\readfile{#1}\donothing\donothing}
+
+%D \macros
+%D {readjobfile,readlocfile,readsysfile,
+%D readfixfile,readsetfile}
+%D
+%D This implementation honnors the third situation, but we
+%D still can get unwanted files loaded and/or can get involved
+%D in extensive searching.
+%D
+%D Due to different needs, we decided to offer four alternative
+%D loading commands. With \type{\readjobfile} we load a local
+%D file and do no backtracking, while \type{\readlocfile}
+%D backtracks~\number\readlevel\ directories, including the current
+%D one.
+
+\unexpanded\def\readjobfile % #1% current path, no backtracking
+ {\readlevel\zerocount
+ \doreadfile\f!currentpath} % {#1}}
+
+\unexpanded\def\readlocfile % #1% current path, backtracking
+ {\readlevel\maxreadlevel
+ \doreadfile\f!currentpath} % {#1}}
+
+%D System files can be anywhere and therefore
+%D \type{\readsysfile} is not bound to the current directory
+%D and obeys the \TEX\ implementation.
+
+\unexpanded\def\readsysfile % #1% current path, obeys tex search
+ {\readlevel\zerocount
+ \doreadfile\empty} % {#1}}
+
+%D Of the last two, \type{\readfixfile} searches on the
+%D directory specified and backtracks too, while
+%D \type{\readsetfile} does only search on the specified path.
+
+\unexpanded\def\readfixfile % #1#2% specified path, backtracking
+ {\readlevel\maxreadlevel
+ \doreadfile} % {#1}{#2}}
+
+\unexpanded\def\readsetfile % #1#2% specified path, no backtracking
+ {\readlevel\zerocount
+ \doreadfile} % {#1}{#2}}
+
+%D After having defined this commands, we reconsidered the
+%D previously defined \type{\readfile}. This time we more or
+%D less impose the search order.
+
+\unexpanded\def\readfile#1#2#3%
+ {\readlocfile{#1}{#2}
+ {\readjobfile{#1}{#2}
+ {\readsysfile{#1}{#2}{#3}}}}
+
+%D So now we've got ourselves five file loading commands:
+%D
+%D \starttyping
+%D \readfile {filename} {before loading} {not found}
+%D
+%D \readjobfile {filename} {before loading} {not found}
+%D \readlocfile {filename} {before loading} {not found}
+%D \readfixfile {filename} {before loading} {not found}
+%D \readsysfile {directory} {filename} {before loading} {not found}
+%D \stoptyping
+
+%D \macros
+%D {readjobfile,readlocfile,readsysfile,readfixfile}
+%D
+%D The next four alternatives can be used for opening files
+%D for reading on a line||by||line basis. These commands get
+%D an extra argument, the filetag. Explicit closing is done
+%D in the normal way by \type{\closein}.
+
+\def\doopenin#1#2%
+ {\sanitizefilename#2\to\readfilename
+ \checkfilename\readfilename
+ \ifcase\kindoffile
+ \advance\readlevel\plusone
+ \openinputfile{#1}\readfilename
+ \ifeof#1% \relax
+ \ifnum\readlevel>\maxreadlevel % \relax
+ \else
+ \closeinputfile#1% \relax
+ \doopenin{#1}{\pathplusfile\f!parentpath{#2}}%
+ \fi
+ \fi
+ \fi}
+
+\def\openjobin#1#2%
+ {\readlevel\zerocount
+ \doopenin{#1}{\pathplusfile\f!currentpath{#2}}}
+
+\def\opensysin % #1#2%
+ {\readlevel\maxreadlevel
+ \doopenin} % {#1}{#2}}
+
+\def\openlocin#1#2%
+ {\readlevel\maxreadlevel
+ \doopenin{#1}{\pathplusfile\f!currentpath{#2}}}
+
+\def\openfixin#1#2#3%
+ {\readlevel\maxreadlevel
+ \doopenin{#1}{\pathplusfile{#2}{#3}}}
+
+%D \macros
+%D {doiffileelse,doiflocfileelse}
+%D
+%D The next alternative only looks if a file is present. No
+%D loading is done. This one obeys the standard \TEX\
+%D implementation method.
+%D
+%D \starttyping
+%D \doiffileelse {filename} {found} {not found}
+%D \stoptyping
+%D
+%D \starttyping
+%D \doiflocfileelse {filename} {before loading} {not found}
+%D \stoptyping
+
+\def\doiffileelse {\doiffileexistselse}
+\def\doiffile #1{\doiffileexistselse{#1}\firstofoneargument\gobbleoneargument}
+\def\doifnotfile #1{\doiffileexistselse{#1}\gobbleoneargument\firstofoneargument}
+
+\def\doiflocfileelse#1%
+ {\makelocreadfilename{#1}%
+ \doiffileelse\readfilename}
+
+\def\makelocreadfilename#1%
+ {\sanitizefilename#1\to\readfilename
+ \checkfilename\readfilename
+ \ifcase\kindoffile
+ \edef\readfilename{\pathplusfile\f!currentpath{#1}}%
+ \fi}
+
+%D \macros
+%D {doonlyonce, doinputonce, doendinputonce}
+%D
+%D Especially macropackages need only be loaded once.
+%D Repetitive loading not only costs time, relocating registers
+%D often leads to abortion of the processing because \TEX's
+%D capacity is limited. One can prevent multiple execution and
+%D loading by using one of both:
+%D
+%D \starttyping
+%D \doonlyonce{actions}
+%D \doinputonce{filename}
+%D \doendinputonce{filename}
+%D \stoptyping
+%D
+%D This command obeys the standard method for locating files.
+
+\long\def\doonlyonce#1%
+ {\doifundefinedelse{@@@#1@@@}
+ {\letgvalue{@@@#1@@@}\empty
+ \firstofoneargument}
+ {\gobbleoneargument}}
+
+\def\doinputonce#1%
+% {\doonlyonce{#1}{\doiffileelse{#1}{\normalinput#1\relax}\donothing}}
+ {\doonlyonce{#1}{\doiffileelse{#1}{\inputgivenfile{#1}}\donothing}}
+
+\def\doendinputonce#1%
+ {\doifdefined{@@@#1@@@}\endinput}
+
+\def\forgetdoingonce#1%
+ {\global\letbeundefined{@@@#1@@@}}
+
+%D \macros
+%D {doifparentfileelse}
+%D
+%D The test \type{\doifelse{\jobname}{filename}} does not give
+%D the desired result, simply because \type{\jobname} expands
+%D to characters with \CATCODE~12, while the characters in
+%D \type{filename} have \CATCODE~11. So we can better use:
+%D
+%D \starttyping
+%D \doifparentfileelse{filename}{yes}{no}
+%D \stoptyping
+%D
+%D Since \TEXEXEC\ (and thereby \CONTEXT) supports renaming of
+%D the outputfile, we also need to check on that alternative
+%D name.
+
+\ifx\outputfilename\undefined \def\outputfilename{\jobname} \fi
+
+\def\doifparentfileelse#1%
+ {\doifsamestringelse{#1}{\jobname }\firstoftwoarguments
+ {\doifsamestringelse{#1}{\jobname.\c!tex}\firstoftwoarguments
+ {\doifsamestringelse{#1}{\outputfilename}\firstoftwoarguments\secondoftwoarguments}}}
+
+\def\normalless {<} % geen \let !
+\def\normalmore {>} % geen \let !
+\def\normalequal {=} % geen \let !
+\def\normaldblquote{"} % geen \let !
+
+\newcount\readingfilelevel
+
+\def\popfilecharacter#1#2%
+ {\ifnum\catcode`#1=\@@other \ifnum#2=\@@other \else
+ %\message{[popping catcode #1 to #2]}%
+ \catcode`#1=#2\relax
+ \fi \fi}
+
+\ifx\\\undefined \let\\\relax \fi
+
+%D This changing catcodes is a direct result from the fact
+%D that we support some long standing conventions with
+%D regards to active characters (german ", polish /,
+%D french : and ;).
+
+%D We need to redo this: catcode sets and such
+
+\newtoks \everystartreadingfile
+\newtoks \everystopreadingfile
+
+\def\startreadingfile% beter een every en \setnormalcatcodes
+ {\global\advance\readingfilelevel\plusone
+ \the\everystartreadingfile
+ \beginrestorecatcodes
+ \setcatcodetable\prtcatcodes}
+
+\def\stopreadingfile
+ {\endrestorecatcodes
+ \the\everystopreadingfile
+ \global\advance\readingfilelevel\minusone}
+
+\let\normalstartreadingfile\startreadingfile
+\let\normalstopreadingfile \stopreadingfile
+
+%D \macros
+%D {splitfilename}
+%D
+%D I should have made this one sooner. This macro was first needed when
+%D ran into graphic with a period in the pathpart.
+%D
+%D \startbuffer
+%D \def\showfilesplit
+%D {\bgroup \tttf
+%D \hbox{(full: \splitofffull)}\space
+%D \hbox{(path: \splitoffpath)}\space
+%D \hbox{(base: \splitoffbase)}\space
+%D \hbox{(name: \splitoffname)}\space
+%D \hbox{(type: \splitofftype)}\space
+%D \egroup}
+%D
+%D \splitfilename{c:/aa/bb/cc/dd.ee.ff} \showfilesplit \endgraf
+%D \splitfilename{c:/aa/bb/cc/dd.ee} \showfilesplit \endgraf
+%D \splitfilename{c:/aa/bb/cc/dd} \showfilesplit \endgraf
+%D
+%D \splitfilename{dd.ee.ff} \showfilesplit \endgraf
+%D \splitfilename{dd.ee} \showfilesplit \endgraf
+%D \splitfilename{dd} \showfilesplit \endgraf
+%D \stopbuffer
+%D
+%D \start \typebuffer \getbuffer \stop
+
+\def\splitoffroot{.} \chardef\splitoffkind\zerocount
+
+\let\splitofffull\empty
+\let\splitoffpath\empty
+\let\splitoffbase\empty
+\let\splitoffname\empty
+\let\splitofftype\empty
+
+% \def\splitfilename#1%
+% {\edef\splitofffull{#1}% normally outside this call: \sanitizefilename#1\to\sanitizedfilename
+% \greedysplitstring\splitofffull\at/\to\splitoffpath\and\splitoffbase
+% \ifx\splitoffbase\empty
+% \let\splitoffpath\empty
+% \let\splitoffbase\splitofffull
+% \fi
+% \greedysplitstring\splitoffbase\at.\to\splitoffname\and\splitofftype
+% \chardef\splitoffkind % can be used to test if pathpart was empty
+% \ifx\splitoffpath\empty \zerocount \else
+% \ifx\splitoffpath\splitoffroot \plusone \else
+% \plustwo \fi\fi
+% \ifx\splitoffname\empty\let\splitoffname\splitoffbase\fi
+% \ifx\splitoffpath\empty\let\splitoffpath\splitoffroot\fi}
+%
+% better, since it also handles leading /'s
+%
+% \splitfilename{oeps/test.pdf} [\splitoffpath\quad\splitoffname\quad\splitofftype]
+% \splitfilename{/oeps/test.pdf} [\splitoffpath\quad\splitoffname\quad\splitofftype]
+% \splitfilename{/test.pdf} [\splitoffpath\quad\splitoffname\quad\splitofftype]
+
+\def\splitfilename#1%
+ {\edef\splitofffull{#1}% normally outside this call: \sanitizefilename#1\to\sanitizedfilename
+ \greedysplitstring\splitofffull\at/\to\splitoffpath\and\splitoffbase
+ \ifx\splitoffpath\splitofffull
+ \let\splitoffpath\empty
+ \fi
+ \ifx\splitoffbase\empty
+ \let\splitoffpath\empty
+ \let\splitoffbase\splitofffull
+ \fi
+ \greedysplitstring\splitoffbase\at.\to\splitoffname\and\splitofftype
+ \chardef\splitoffkind % can be used to test if pathpart was empty
+ \ifx\splitoffpath\empty \zerocount \else
+ \ifx\splitoffpath\splitoffroot \plusone \else
+ \plustwo \fi\fi
+ \ifx\splitoffname\empty\let\splitoffname\splitoffbase\fi
+ \ifx\splitoffpath\empty\let\splitoffpath\splitoffroot\fi}
+
+\def\splitfiletype#1%
+ {\edef\splitofffull{#1}% normally outside this call: \sanitizefilename#1\to\sanitizedfilename
+ \let\splitoffpath\empty
+ \greedysplitstring\splitofffull\at.\to\splitoffname\and\splitofftype}
+
+\protect \endinput