% \iffalse meta-comment
%
% Copyright (C) 2006 by Martin Scharrer <martin@scharrer-online.de>
% -----------------------------------------------------------------
% 
% This file may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.2
% of this license or (at your option) any later version.
% The latest version of this license is in:
%
%    http://www.latex-project.org/lppl.txt
%
% and version 1.2 or later is part of all distributions of LaTeX 
% version 1999/12/01 or later.
%
% \fi
%
% \iffalse
%<*driver>
\ProvidesFile{svnkw.dtx}
%</driver>
%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%<package>\ProvidesPackage{svnkw}
%<*package>
    [2006/05/27 v1.0 SVN Keywords]
%</package>
%
%<*driver>
\documentclass{ltxdoc}
\usepackage{svnkw}[2004/11/05]
\EnableCrossrefs         
\CodelineIndex
\RecordChanges
\begin{document}
  \DocInput{svnkw.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%</driver>
% \fi
%
% \CheckSum{0}
%
% \CharacterTable
%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%   Digits        \0\1\2\3\4\5\6\7\8\9
%   Exclamation   \!     Double quote  \"     Hash (number) \#
%   Dollar        \$     Percent       \%     Ampersand     \&
%   Acute accent  \'     Left paren    \(     Right paren   \)
%   Asterisk      \*     Plus          \+     Comma         \,
%   Minus         \-     Point         \.     Solidus       \/
%   Colon         \:     Semicolon     \;     Less than     \<
%   Equals        \=     Greater than  \>     Question mark \?
%   Commercial at \@     Left bracket  \[     Backslash     \\
%   Right bracket \]     Circumflex    \^     Underscore    \_
%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%   Right brace   \}     Tilde         \~}
%
%
% \changes{v1.0}{2006/05/27}{Initial version}
%
% \GetFileInfo{svnkw.dtx}
%
% \DoNotIndex{\newcommand,\newenvironment}
% 
%
% \title{The \textsf{svnkw} package\thanks{This document
%   corresponds to \textsf{svnkw}~\fileversion, dated \filedate.}}
% \author{Martin Scharrer \\ \texttt{martin@scharrer-online.de}}
%
% \maketitle
%
% \section{Introduction}
% This package lets you typeset keywords of the version control system Subversion\footnote
% {Subversion homepage: http://subversion.tigris.org/} (svn), which is the successor of the popular CVS,
% inside your \LaTeX\ files anywhere you like.
% Unlike the very similar package
% |svn|\footnote{CTAN:
% http://www.ctan.org/tex-archive/macros/latex/contrib/svn/} the usage of
% multiple files for one \LaTeX\ document is well supported. The package
% acquires the keywords of the last changed file and provides them to the user
% through macros. The package has to read all keywords of all files first and
% writes the most recent values in an axiliary file with an '.svn'
% extention. This file is read back at the next \LaTeX\ run which introduces a
% delay like by the table of contents.
%
% The special date features of the |svn| package are not (yet?) supported. If
% you need them you can try to use both packages together which should work
% but is not tested by the author.
%
% \section{Usage}
%
% \subsection{Including of the Subversion keywords}
% To include your Subversion Id keywords use |\svnid| or |\svnidlong|. 
% This macros should be written very
% early in each file, i.e.\ in the preamble of the main document soon after
% |\documentclass| and |\usepackage{svnkw}| and as first in every |\include|d
% subfile before the |\chapter| macro. They do not create any output.
%
% \DescribeMacro{\svnid}
% Macro for the svn Id keyword. 
% Write the macro as |\svnid{$||Id$}| into your files and set the subversion
% property |svn:keywords| of them
% to at least '|Id|'. Subversion will expand it. A trailing colon
% with spaces after the |Id| is also valid but \textbf{everything else} will
% cause a \TeX\ parse error.
%
% The keyword content will be interpreted and
% saved in the variables (ah, macros) |\svnfilerev|, |\svnfiledate| and
% |\svnfileauthor|. The most recent values (i.e.\ with the highest revision)
% are written as |\svnrev|, |\svndate| and |\svnauthor| in a .svn-file which
% is read back at the next \LaTeX\ run. The idea is that the first variables
% holding the local values for the current file and the otheris the values for 
% the complete document.
% Nevertheless all keywords can also be typeset with |\svnkw|\marg{keyword}
% which returns the latest saved value. This macro is used internally.
%
% \DescribeMacro{\svnidlong}
% Macro for a "long Id".
% Saves similar values like in '|Id|' but from the keywords
% |HeadURL|, |LastChangedDate|, |LastChangedRevision| and |LastChangedBy|.
% The usage of |\svnid| or |\svnidlong| is a matter of taste. The second is
% more readable inside the code and results in a nicer date and a full URL,
% not only the filename. Both can be used together.
%
% Write this as (order of arguments not meaningfull)\\
% |\svnidlong|\\
% |{$||HeadURL$}|\\
% |{$||LastChangedDate$}|\\
% |{$||LastChangedRevision$}|\\
% |{$||LastChangedBy$}|\\
% in your files and set the subversion
% property |svn:keywords| of them
% to\\'|HeadURL LastChangedDate LastChangedRevision LastChangedBy|'.
%
% \DescribeMacro{\svn}
% This macro lets you set svn keywords directly. The only argument is
% the svn keyword in the usual dollars. The dollars will be stripped
% by the macro and the rest is typeset as normal text. 
% This macro alone was the very first version of |svnkw| and is still included
% for fast and simple keyword typesetting.
% 
% \DescribeMacro{\svnkwsave}
% This macro lets you include and save any keyword you like. The only argument
% is the keyword wrapped in dollars. The keyword can be already expanded or
% not (no value and only "|:|" or nothing after the key name). This macro is
% also used internally and does not create any output. 
%
% \subsection{Access to the keyword values}
%
% \DescribeMacro{\svnkw}
% Keyword values can be typeset by this macro.
% It takes one argument which must be a subversion keyword name. It
% then returns the current value of this keyword or nothing (|\relax|) when
% the keyword was not set yet.\\
% Examples:\\|\textsl{Revision: \svnkw{Revision}}|\\
% |URL: \url{\svnkw{HeadURL}}|\\
% In the second example |\url| (|hyperref| package) is used to add a 
% hyperlink and to avoid problems with underscores (|_|) inside the URL. 
%
% \DescribeMacro{\svnkwdef}
% This macro is used to define the keyword values. This is normally only
% called internally but could be used by the user to override single keywords.
% The values can then be typeset by |\svnkw|.
% Note that |\svnid| and |\svnidlong| both sets some keywords to new values
% using this macro.
%
% Note that for |\svnkw| and |\svnkwdef| all different names for one keyword
% are valid and result in the access of the same variable. So e.g.\ subversion
% treats |Rev|, |Revision| and |LastChangedRev| the same way and so does this
% macros. You can e.g.\ say |\svnkwdef{Rev}| and then typeset it with
% |\svnkw{Revision}| or |\svnkw{LastChangedRev}| if you like.
%
% \subsection{Using full author names}
% If you like to have the full author\footnote{This means subversion authors,
% e.g.\ the persons who commit changes into the svn reprository.}
% names, not only the usernames, in your
% document you can use the following macros. First you have to register all
% authors of the document with |\svnRegisterAuthor| and then you can write
% e.g.\ |\svnFullAuthor{\svnauthor}| or |\svnFullAuthor{\svnfileauthor}|.
%
% \DescribeMacro{\svnRegisterAuthor}
% The usage is |\svnRegisterAuthor|\marg{username}\marg{full name} which
% registers \meta{full name} as full name for \meta{username}.
%
% \DescribeMacro{\svnFullAuthor}
% Takes the username as argument and returns the full name if it was
% registered first with |\svnRegisterAuthor| or nothing (|\relax|) else. There is
% also a star version |\svnFullAuthor*| which also returns the username in
% parentheses after the full name.

% \StopEventually{}
%
% \section{Implementation}
%
% \begin{macro}{\svn}
% Just calls TeX macro |\@svn| to strip the dollars.
%    \begin{macrocode}
\newcommand{\svn}[1]{\@svn#1}
\def\@svn$#1${#1}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svnkwdef}
% First we check if there is a 'setter'-macro for the keyword called
% |\svnkwdef@|\meta{keyword}. 
%    \begin{macrocode}
\newcommand{\svnkwdef}[2]{%
\@ifundefined{svnkwdef@#1}
%    \end{macrocode}
% If not we call the general macro |\svnkwdef@|.
%    \begin{macrocode}
{\svnkwdef@{#1}{#2}}
%    \end{macrocode}
% If yes we just call it with the value as argument.
%    \begin{macrocode}
{\csname svnkwdef@#1\endcsname{#2}}
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svnkwdef@}
% This macro defines the second argument under |\svnkw|\meta{2nd argument}.
% The |\xdef| is used to expand the content first (needed for internal use)
% and make the definition globally.
%    \begin{macrocode}
\newcommand{\svnkwdef@}[2]
{\expandafter\xdef\csname svnkw#1\endcsname{#2}}
%    \end{macrocode}
% Example: |\svnkwdef{Revision}{23}| will define |\svnkwRevision| as 23.
% \end{macro}

% \begin{macro}{\svnkwdef@...}
% 'Setter'-macros for single keywords, used by |\svnkwdef|.\\The keywords |Rev|, |Author| and |Date|
% are just calling |\svnkwdef@| with a fixed first argument.
%    \begin{macrocode}
\def\svnkwdef@Rev#1{\svnkwdef@{Rev}{#1}}
\def\svnkwdef@Author#1{\svnkwdef@{Author}{#1}}
\def\svnkwdef@Date#1{\svnkwdef@{Date}{#1}}
%    \end{macrocode}
% The long keywords are defined then as aliases of the short,\\
% first for writting
%    \begin{macrocode}
\let\svnkwdef@Revision=\svnkwdef@Rev
\let\svnkwdef@LastChangedRevision=\svnkwdef@Rev
\let\svnkwdef@LastChangedBy=\svnkwdef@Author
\let\svnkwdef@LastChangedAt=\svnkwdef@Date
%    \end{macrocode}
% and then for reading. 
%    \begin{macrocode}
\def\svnkwRevision{\svnkwRev}
\def\svnkwLastChangedRevision{\svnkwRev}
\def\svnkwLastChangedBy{\svnkwAuthor}
\def\svnkwLastChangedAt{\svnkwDate}
%    \end{macrocode}
% So e.g. |\svnkw{LastChangedRevision}| is always be the
% same as |\svnkw{Rev}|.
% \end{macro}
%
% We define default values for normal keywords. Keyword |Filename| is the name
% given by |Id| and not a real keyword.
%    \begin{macrocode}
\svnkwdef{Rev}{0} % must always be numerical
\svnkwdef{Date}{}
\svnkwdef{Author}{}
\svnkwdef{Filename}{}
\svnkwdef{HeadURL}{}
%    \end{macrocode}

% \begin{macro}{\svnkw}
% Macro to get keyword value. Just calls |\svnkwARGUMENT| where |ARGUMENT| is
% the argument interpreted as text. So e.g. |\svnkw{Date}| is the same as
% |\svnkwDate| but this could be changed later so always use this interface
% to get the keyword values.
%    \begin{macrocode}
\newcommand{\svnkw}[1]{\csname svnkw#1\endcsname}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svn@scanId}
% Scans svn Id (after it got parsed by |\svn@readkw|). 
% Awaits only Id value without leading '|Id:|' and a trailing dollar as end
% marker. It calls
% |\@svn@updateid| to update global Id values and also sets the approbriate
% keywords. 
%    \begin{macrocode}
\def\svn@scanId#1 #2 #3 #4 #5${%
% #1 is filename, #2 is revision, #3 is date (JJJJ-MM-DD), 
% #4 is time (HH:MM:SST), #5 is author (username)
\@svn@updateid{#2}{#3 #4}{#5}%
\svnkwdef{Filename}{#1}%
\svnkwdef{Date}{#3 #4}%
\svnkwdef{Revision}{#2}%
\svnkwdef{Author}{#5}%
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svn@readkw}
% General macro to read keywords.
% Saves key in |\svn@key| and the value in |\svn@value|.
% We have to analyse the argument but need it in full later. So we use the submacro
% |\svn@readkw@sub| to do the scan job. Sets |\svn@case| to one of three
% cases, where the first two cases result in identical code.
%    \begin{macrocode}
\def\svn@readkw$#1${%
\svn@readkw@sub$#1: $ % call submacro to get case
\ifnum\svn@case<3
%    \end{macrocode}
% If there is no value set |\svn@value| to empty and define |\svn@key| to the
% full argument.
%    \begin{macrocode}
\def\svn@key{#1}
\let\svn@value=\svn@empty
\svn@checkkeyforcolon$#1:$ % needed for case '$kw:$'
\relax
\else
%    \end{macrocode}
% If there is a value split the argument again by calling |\svn@readkw@def|.
%    \begin{macrocode}
\svn@readkw@def$#1$ % needed to remove leading space
\fi
}

\newcount\svn@case
\let\svn@empty=\empty
%    \end{macrocode}
%
% The submacro |\svn@readkw@sub| checks if the argument ends with '|: |'. In
% order to mach always we add a '|: |' at the macro call and then test here if
% |#2| is empty. This is the case if there was no '|: |' before. We return 1
% if there was no colon which means the keyword is not yet expanded by
% Subversion.
%    \begin{macrocode}
\def\svn@readkw@sub$#1: #2${%
\def\svn@temp{#2}
\ifx\svn@temp\empty
\svn@case=1
\else
%    \end{macrocode}
% If there was an tailing '|: |' we check if |#2| is now just '|: |'
% which would be the '|: |' we added at the macro call.
%    \begin{macrocode}
\svn@checkcolon$#2$
\ifx\svn@temp\empty
%    \end{macrocode}
% If we have nothing (empty value) in |#2| return 2.
%    \begin{macrocode}
\svn@case=2
\else
%    \end{macrocode}
% If we have something in |#2| return 3.
%    \begin{macrocode}
\svn@case=3
\fi\fi
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svn@readkw@def}
% Submacro for |\svn@readkw| to split argument and to save the parts.
%    \begin{macrocode}
\def\svn@readkw@def$#1: #2 ${%
\def\svn@key{#1}
\def\svn@value{#2}
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svn@checkcolon}
% Checks whether value is just '|: |',
% if yes |\svn@temp| is empty.
%    \begin{macrocode}
\def\svn@checkcolon$#1: ${%
\def\svn@temp{#1}
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svn@checkkeyforcolon}
% Checks whether key includes a trailing '|:|'
% and removes it. Must be called with an trailing '|:|' to always mach! So its
% check actually for tow trailing '|:|'.
%    \begin{macrocode}
\def\svn@checkkeyforcolon$#1:#2${%
\def\svn@temp{#2}
\ifx\svn@temp\empty
\else
\def\svn@key{#1}
\fi
}
%    \end{macrocode}
% \end{macro}

% Definition of  init values.
%    \begin{macrocode}
% Init values
\def\svnrev{0}  % \
\def\svndate{}  %  > Values for whole project
\def\svnauthor{}  % /
\def\svnfilerev{0} % \
\def\svnfiledate{} %  > Values for current file
\def\svnfileauthor{} % /
\def\@svn@rev{0} % \
\def\@svn@date{} %  > Values for packet internal use
\def\@svn@author{} % /
%    \end{macrocode}

% \begin{macro}{\svnid}
% We read the argument with |\svn@readkw| and provide the value to
% |\svn@scanId|.
%    \begin{macrocode}
\newcommand{\svnid}[1]{%
\svn@readkw#1 % Read keyword
\ifx\svn@value\empty % Check if value is empty
\else
\expandafter\svn@scanId\svn@value$ % Scan Id 
\fi
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svnidlong}
% We clear the keyword value first to reduce the risk though bad user input.
%    \begin{macrocode}
\newcommand{\svnidlong}[4]{
\svnkwdef{HeadURL}{}%
\svnkwdef{LastChangedDate}{}%
\svnkwdef{LastChangedRevision}{0}%
\svnkwdef{LastChangedBy}{}%
%    \end{macrocode}
% Then we save the four keywords/arguments using |\svnkwsave|.
%    \begin{macrocode}
\svnkwsave{#1}\svnkwsave{#2}
\svnkwsave{#3}\svnkwsave{#4}
%    \end{macrocode}
% And update the latest values.
%    \begin{macrocode}
\@svn@updateid{\svnkw{LastChangedRevision}}{\svnkw{LastChangedDate}}%
{\svnkw{LastChangedBy}}
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svnkwsave}
% Save macro. Takes a dollar wrapped keyword string, reads it though
% |\svn@readkw| and saves it using |\svnkwdef|.
%    \begin{macrocode}
\newcommand{\svnkwsave}[1]{%
\def\svn@temp{#1}
\ifx\svn@temp\empty
%% skip at empty argument
\else
\svn@readkw#1% read keyword
\svnkwdef{\svn@key}{\svn@value}
\fi
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\@svn@updateid}
% We first define the expanded arguments to variables for the user.
% The expansion is needed because the arguments content is mostly generic like
% |\svn@key| and |\svn@value| which can change very soon after this macro.
%    \begin{macrocode}
\def\@svn@updateid#1#2#3{% #1 = rev, #2 date, #3 author (username)
\edef\svnfilerev{#1}
\edef\svnfiledate{#2}
\edef\svnfileauthor{#3}
%    \end{macrocode}
% Then we check if the revision is non-empty (not yet expanded by
% subversion?) and larger then the current maximum value |\@svn@rev|.
% If yes we save all value to save them in the .svn-file later.
%    \begin{macrocode}
\ifx\svnfilerev\empty\else % skip rest if rev is empty
\ifnum\@svn@rev<\svnfilerev%
\edef\@svn@rev{#1}
\edef\@svn@date{#2}
\edef\@svn@author{#3}
\else\fi
\fi
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svnRegisterAuthor}
% Saves the author's name by defining
% |\svn@author@|\meta{username} to it.
%    \begin{macrocode}
\newcommand{\svnRegisterAuthor}[2]{%
\expandafter\def\csname svn@author@#1\endcsname{#2}
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\svnFullAuthor}
% We test if the starred or the normal version is used and call the
% appropriate submacro |\svnFullAuthor@star| or |\svnFullAuthor@normal|.
%    \begin{macrocode}
\newcommand{\svnFullAuthor}{%
\@ifnextchar{*}
{\svnFullAuthor@star}
{\svnFullAuthor@normal}
}
%    \end{macrocode}
% Both submacros are calling |\svnFullAuthor@| but with different arguments.
% The star macro also removes the star of course.
%    \begin{macrocode}
\def\svnFullAuthor@star*#1{\svnFullAuthor@{#1}{\ (#1)}}
\def\svnFullAuthor@normal#1{\svnFullAuthor@{#1}{}}
%    \end{macrocode}
% |\svnFullAuthor@| now sets the author's full name. Note that |#2| is empty
% when the normal version is called.
%    \begin{macrocode}
\def\svnFullAuthor@#1#2{%
\csname svn@author@#1\endcsname #2
}
%    \end{macrocode}
% \end{macro}

%
% At the end of document we write the values to an auxiliary file.
%    \begin{macrocode}
\AtEndDocument{
%    \end{macrocode}
% We first check if we have somthing to save. Revision, date and author must be
% non-empty. This suppresses the auxiliary file if the user doesn't use the
% appropriate macros but other provided by this package.
%    \begin{macrocode}
 \ifx\@svn@rev\empty\else
 \ifnum\@svn@rev=0\else
 \ifx\@svn@date\empty\else
 \ifx\@svn@author\empty\else
%    \end{macrocode}
% Open outfile to write project keywords.
%    \begin{macrocode}
 \newwrite\svnwrite
 \immediate\openout\svnwrite=\jobname.svn
 \immediate\write\svnwrite{\@percentchar\space SVN cache}
 \immediate\write\svnwrite{\noexpand\def\noexpand\svnrev{\@svn@rev}}
 \immediate\write\svnwrite{\noexpand\def\noexpand\svndate{\@svn@date}}
 \immediate\write\svnwrite{\noexpand\def\noexpand\svnauthor{\@svn@author}}
 \immediate\closeout\svnwrite
 \fi\fi\fi\fi
}

%    \end{macrocode}
% Reread output from last compile run if it exists.
%    \begin{macrocode}
\InputIfFileExists{\jobname.svn}{}{}
%    \end{macrocode}
%
% \Finale
\endinput

