%%
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{program}
                [1998/07/30 1.0 Source program environment for LaTeX]
\RequirePackage{keyval}

%
% Style for writing programs
%
%\begin{program}[number,stretch=1.1]
%  even if you do not have options better put [] (but in Latex's \newcommand) 
%  Options: 
%     number[=true]  - numbers all the lines with \prognumstyle
%     firstline=xxx  - sets the first line number
%     numberevery[=1]- put a number every n lines (first is always numbered)
%     box[=true]     - puts the whole program in a minipage
%     valign[=t]     - the valign to be passed to minipage
%     width=xxx      - the width to be passed to minipage
%     style=xxx      - the default style of text in env. Can be "math" or "tt"
%                      Or, redefine \Programstyle{#1}{#2} so that #1 is the 
%                      style start and #2 is the style end      
%     stretch=xxx    - factor to stretch the distance between lines
%
%  line breaks are verbatim. Put % to prevent a newline
%  !foo! writes foo with \formatname{foo}. Default \tt
%  |bar| writes bar with \formatvariable{bar}. Default \mathit
%  @foo@ writes foo with \formattext. Default \mbox
%  \tab idents future lines here
%  \stab{dim} puts a \tab forward of this location and backs up
%  \qtab = \quad\tab
%  \untab undoes the last undone \tab
%  \rjust{txt} puts txt right justified  
%  \rcomment{txt} like \rjust  but with \formatcomment{txt}. Default \mbox
%  \lab{txt} puts a label leftjustified with \formatlabel{txt}
%  \labspace{txt} use on first line to create indentation that clears labels
%  \nonumber omits the line number in this line. The line number is not
%            incremented 
%  \donumber makes sure a line number is emitted
%  \bumpnumber{5} increments the line number by 5. Visible on current line
%
%  This environment is built on top of Latex tabbing environment. As such the 
%  tabbing commands work here as well. However, some tabbing commands will 
%  interfere with the program environment. The command that you can use are:
%  \>  advance to the next tab
%\end{program}
%
% Define the options
%
% Define a boolean key with a default value if used without =
\def\Prog@defboolkey#1[#2]{%
  \expandafter\newif\csname ifProg@#1\endcsname % Define the if
  \define@key{Prog}{#1}[#2]{\csname Prog@#1##1\endcsname}}
\def\Prog@defvalkey#1[#2]{%
  \define@key{Prog}{#1}[#2]{\expandafter\def\csname Prog@#1\endcsname{##1}}}
\def\Prog@defvalkeynodef#1{%
  \define@key{Prog}{#1}{\expandafter\def\csname Prog@#1\endcsname{##1}}}


\Prog@defboolkey{number}[true] % numbers lines
\Prog@defboolkey{box}[true]    % it puts the program in a parbox
\Prog@defboolkey{style}[math]  % Can be math or tt
\Prog@defvalkey{valign}[t]     % how to align the minipage (if in box)
\Prog@defvalkey{stretch}[1.0]  % Factor to stretch the distance betwee lines
\Prog@defvalkeynodef{width}
\Prog@defvalkeynodef{firstline}
\Prog@defvalkey{numberevery}[1]% Put a line number every n lines.

% Set the defaults
\setkeys{Prog}{box=true,number=false,firstline=1,numberevery=1,
               valign=t,style=math,stretch=1.0,width=\z@}

% Set the global defaults
%
% User parameters
%
\def\ProgramStyle#1#2{\def\ProgramStyleStart{#1}%
                      \def\ProgramStyleEnd{#1}}
\def\Prog@stylett{\ProgramStyle{\tt}{}}
\def\Prog@stylemath{\ProgramStyle{$}{$}}
\Prog@stylemath
                     
% How to print line numbers
\def\prognumstyle{\scriptsize\em}           % The style for printing line nos
\def\formatkeyword#1{\keepspaceafter{\underbar{\bfseries #1}}}
\def\formatvariable#1{\keepspaceafter{\mbox{$\mathit{#1}$}}}
\def\formatcomment#1{\mbox{#1}}
\def\formattext#1{\keepspaceafter{\mbox{#1}}}
\def\proglabelskip{1mm}
\def\formatlabel#1{#1:\hskip\proglabelskip}

\def\formatname#1{\keepspaceafter{\mbox{\ttfamily #1}}}


\def\tab{{}\=\+{}}%
\def\stab#1{\hskip #1\tab\hskip -#1}
\def\qtab{\quad\tab}%
\def\untab{{}\-{}}%
\def\rjust#1{\`#1}
\def\rcomment#1{\`\formatcomment{#1}}%
\def\lab#1{\gdef\prog@label{\formatlabel{#1}}}%
\def\labref#1{\mbox{#1}}
\def\labspace#1{\phantom{\formatlabel{#1}}}

\def\proglabelwidth{1em}

%%%%%%%%%%%%%%
\newcounter{programline}
\newcounter{programlineskip}
\newcounter{programid}\setcounter{programid}{0}
				%This is for hyperref to make the line numbers
				%unique in a document
\def\theHprogramline{\arabic{programid}.\arabic{programline}}

\let\exclmark=!
{% Set |...| to print the ... as a variable
 \catcode`\|=\active\relax
 \let\prog@bar=|%
 \gdef|#1|{\formatvariable{#1}}
% % Set ; to print a thick space after it in math mode
% \let\prog@semicolon=;
% \catcode`\;=\active\relax
% \gdef;{\ifmmode\prog@semicolon\;\else\prog@semicolon\fi}
 % Set !...! to print the ... as a name
 \catcode`\!=\active\relax
 \gdef!#1!{\formatname{#1}}
 % Set @...@ to print the ... as text
 \catcode`\@=\active\relax
 \gdef@#1@{\formattext{#1}}
}
 % Define prog@space
\def\@tmp.{\futurelet\prog@space\relax}\@tmp. \relax
 % Keep a space after something
\def\keepspaceafter#1{\def\@tmp{#1}%
                      \futurelet\@next\@keepspaceafteri}
\def\@keepspaceafteri{\ifx\@next\prog@space
                        \def\@tmpi{\@tmp\ }%
                      \else 
                        \def\@tmpi{\@tmp}\fi
                      \@tmpi}



%
% The next few macros override macros from the Latex tabbing environment
%
\def\prog@numberthisline{1}% Default is line numbering
\def\prog@nonumber{\gdef\prog@numberthisline{0}}
\def\prog@donumber{\gdef\prog@numberthisline{1}}
\def\prog@bumpnumber#1{\addtocounter{programline}{#1}}

\def\prog@startline{%
  \ifProg@number
     \refstepcounter{programline}% Increment the program line before each line
  \fi
  \prog@origstartline}

\def\prog@printlineno{\ifProg@number
                        \hskip\proglabelwidth
                        \ifnum\prog@numberthisline=1\relax
                          \ifnum\theprogramlineskip=0\relax
  		             \llap{\prognumstyle\theprogramline}%
                             \setcounter{programlineskip}{\Prog@numberevery}%
                          \fi
                          \addtocounter{programlineskip}{-1}%
                        \else
                          \addtocounter{programline}{-1}%
                          \prog@donumber
                        \fi
                      \fi
                      \hskip\labelsep
                      % Now put the current label
                      \ifx\prog@label\empty \else
                         \setbox\z@\hbox{\prog@label}%
                         \copy\z@ \hskip -\wd\z@
                         \global\let\prog@label=\empty
                      \fi
                     }

\def\prog@startfield{%
  \prog@origstartfield
  \ProgramStyleStart}

\def\prog@stopfield{%
  \ProgramStyleEnd
  \prog@origstopfield}

% A new version of @stopline which ignores blank lines (lines with 
% width 0pt) and prints line numbers. To print a blank line, put "\ \\" on it!
%
\def\prog@stopline{%
  \unskip\@stopfield
  \if@rjfield
    \global\@rjfieldfalse
    \@tempdima\@totalleftmargin \advance\@tempdima\linewidth
    \hbox to\@tempdima{\@itemfudge\prog@printlineno\hskip\dimen\@curtabmar
		       \box\@curline\hfil\box\@curfield}%
  \else
    \@addfield
    \ifdim\wd\@curline=0pt%
      \ifProg@number \addtocounter{programline}{-1}\fi
     \else
      \hbox{\@itemfudge\prog@printlineno\hskip\dimen\@curtabmar\box\@curline}%
  \fi
\fi
}


\newcommand\program[1][]{%
  \stepcounter{programid}%
  \bgroup % Start a group so that we can undo easily most assignments
  % Process the optional arguments
  \def\Prog@width{\z@}%
  \setkeys{Prog}{#1}%
  \ifdim\Prog@width=\z@ 
    \edef\Prog@width{\the\linewidth}%
  \fi
  % Line numbering
  \ifProg@number
    \prog@donumber
  \else
    \prog@nonumber
  \fi
  % Whether in a box
  \ifProg@box
    \begin{minipage}[\Prog@valign]{\Prog@width}%
  \fi
  % The distance between lines
  \setbox\strutbox\hbox{%
     \vrule\@height\Prog@stretch\ht\strutbox
     \@depth\Prog@stretch\dp\strutbox
     \@width\z@}%
  \setcounter{programline}{\Prog@firstline}%
  \addtocounter{programline}{-1}% Adjust for preincrement
  \let\prog@label=\empty
  \let\prog@origbar=|%
  \catcode`\|=\active\relax
  \catcode`\!=\active\relax
  \catcode`\@=\active\relax
  % Start a tabbing environment with obey lines
  \let\prog@origstartline=\@startline
  \let\@startline=\prog@startline
  \let\@stopline=\prog@stopline
  \let\prog@origstartfield=\@startfield
  \let\@startfield=\prog@startfield
  \let\prog@origstopfield=\@stopfield
  \let\@stopfield=\prog@stopfield
  \let\nonumber=\prog@nonumber
  \let\donumber=\prog@donumber
  \let\bumpnumber=\prog@bumpnumber
  \obeycr% All lines after this must end with a comment
  \tabbing% Everything after this is local to a field
%  \@gobblecr 
}

\def\endprogram{%
  \endtabbing%
  \restorecr%
  \ifProg@box
    \end{minipage}%
  \fi
  \egroup
}



