#!/usr/bin/perl

# texify 1.20 2008-10-22 11:29:00 mortehu
#
# texify -- format code for use with LaTeX.
#        Call as texify{abel,ada,asm,axiom,beta,c,c++,scheme,sim,sql
#			ml,java,logla,matlab,perl,vhdl,B,lisp
#                       lex,bison,ruby,promela,idl}
#
# Arne Georg Gleditsch <argggh@ifi.uio.no>
# Per Kristian Gjermshus <pergj@ifi.uio.no> SQL, C++, Ada, java
# Affi <alfh@ifi.uio.no> added beta stuff
# Olav Andree Brevik <olavb@ifi.uio.no> sml, ABEL
# Vegard Hanssen <vegardha@ifi.uio.no> logla
# Jens Toivo Berger Thielemann <jensthi@ifi.uio.no> matlab
# Martin Thornquist <martint@ifi.uio.no> vhdl, B, CLISP
# Ossama Othman <ossama@debian.org> OMG/CORBA IDL
# Siri Spjelkavik <siri@ifi.uio.no> promela, ruby, flex, bison, 
#                                   linenumber
# Dieter Schuster <didischuster@arcor.de> print mere each n-th 
#                                         linenumber, <texify:ignore>, 
#                                         Axiom, <texify:tex>
#
# 2008-10-22 [mortehu] Added another patch from Dieter Schuster
# 2007-04-08 [mortehu] Added a patch from Dieter Schuster
# 2004-07-15 [mortehu] Use \string" for double quotes
# 2003-01-31 [mortehu] Merged in Bison, Ruby and Promela support
# 2002-09-08 [mortehu] Added support for OMG/CORBA IDL
# 1997-08-12 [argggh] \begin{tex}\TeX\end{tex}-code in comments.
# 1997-09-29 [pergj]  Added logla suppport from vegardha
# 1997-10-29 [pergj]  Added matlab support from jensthi
# 1997-10-30 [argggh] Added perl support
# 1998-03-05 [argggh] Added scheme support
# 1998-04-06 [pergj]  Added abel support from olavb
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

######################################################################

%fontbeg   = (
	      ('ident',  '{\textbf{'),
	      ('string', '{\texttt{'),
	      ('comment','{\textit{')
	      );

$fontend   = '}}';

%tspecials = (
	      ("\$",'\$'),
	      ('*',"\$\\ast\$"),
	      ('&','\&'),
	      ('%','\%'),
	      ('#','\#'),
	      ('_','\_'),
	      ('^','\^{}'),
	      ('{','\{'),
	      ('}','\}'),
	      ('|',"\$|\$"),
	      ('[','{[}'),
	      (']','{]}'),
	      ("'","{'}"),
	      ("\"", "\\string\""),
	      ('~','\~{}'),
	      ('<',"\$<\$"),
	      ('>',"\$>\$"),
	      ("\\","\$\\backslash\$"),
	      ('-','\dash{}'),

	      ("\242", '?'),
	      ("\244", '?'),
	      ("\245", '?'),
	      ("\246", '?'),
	      ("\252", "\$\252\$"),
	      ("\254", "\$\254\$"),
	      ("\255", "\\dash{}"),
	      ("\260", "\$\260\$"),
	      ("\261", "\$\261\$"),
	      ("\262", "\$\262\$"),
	      ("\263", "\$\263\$"),
	      ("\265", "\$\265\$"),
	      ("\271", "\$\271\$"),
	      ("\272", "\$\272\$"),
	      ("\327","\$\327\$"),
	      ("\367","\$\367\$"),
	      );

$tspecials = join('\\',keys(%tspecials));  


$delim = '\(\)\[\]\{\}\<\>\n\s,;';

%align  = ('asm'	=> 1,	'beta'	=> 0,	'c'		=> 0,
	   'c++'	=> 0,	'sim'	=> 0,	'sql'		=> 0,
	   'ada'	=> 0,	'ml'	=> 0,	'matlab'	=> 0,
	   'perl'	=> 0,	'java'	=> 0,	'logla'		=> 0,
	   'scheme'	=> 0,	'abel'	=> 0,	'vhdl'		=> 0,
	   'python'	=> 0,	'lex'	=> 0,   'B'             => 0,
	   'lisp'	=> 0,   'bison' => 0,   'ruby'          => 0,
	   'promela'    => 0,   'idl'   => 0,   'axiom'         => 0,
	   );

@blksep = ('mode',	'asm',		'',
	   'comment',	'#',		"\n",
	   'string',	'"',		'"',
	   'string',	"'",		"'",

	   'mode',	'beta',		'',
	   'comment',   '\(\*',         '\*\)',
	   'string',	"'",		"'",

  	   'mode',	'c',		'',
	   'atom',	'\\\\.',	'',
	   'comment',	'/\*',		'\*/',
	   'string',	'"',		'"',
	   'string',	"'",		"'",

	   'mode',	'c++',		'',
	   'atom',	'\\\\.',	'',
	   'comment',	'/\*',		'\*/',
	   'comment',	'//',		"\n",
	   'string',	'"',		'"',
	   'string',	"'",		"'",

	   'mode',	'sim',		'',
	   'comment',	'!',		';',
	   'comment',	"\n%",		"\n",
	   'string',	'"',		'"',
	   'string',	"'.'",		'',
	   'string',	"'!\\d+!'",	'',

	   'mode',	'sql',		'',
	   'comment',	'/\*',		'\*/',
	   'string',	'"',		'"',
	   'string',	"'",		"'",
	   
	   'mode',	'ada',		'',
	   'comment',	'--',		"\n",
	   'string',	'"',		'"',

	   'mode',	'ml',		'',
	   'comment',	'\(\*',		'\*\)',
	   'atom',	'\\\\.',	'',
	   'string',	'#"."',		'',
	   'string',	'"',		'"',

	   'mode',	'matlab',	'',
	   'comment',	'%',		"\n",
	   'atom',	'[\]\)}\w.]\'',	'',
	   'string',	"'",		'[\]\)}\w.]?\'',

	   'mode',	'perl',		'',
	   'atom',	'\\$\\\\',	'', # '
	   'atom',	'\\\\.',	'',
	   'string',	'"',		'"',
	   'comment',	'#',		"\n",
	   'string',	"'",		"'",

	   'mode',	'java',		'',
	   'atom',	'\\\\.',	'',
	   'comment',	'/\*',		'\*/',
	   'comment',	'//',		"\n",
	   'string',	'"',		'"',
	   'string',	"'",		"'",

	   'mode',	'logla',	'',
	   'comment',	"\n%",		"\n",

	   'mode',	'scheme',	'',
	   'comment',	";",		"\n",
	   'comment',	"#!",		"!#",
	   'string',	'"',		'"',

	   'mode',      'abel',         '',
	   'comment',   '\(\*',         '\*\)',

	   'mode',      'vhdl',         '',
	   'comment',   '--',           "\n",
	   
	   'mode',	'python',	'',
	   'comment',	'#',		"\n",
	   'string',	'"',		'"',
	   'string',	"'",		"'",
	   'atom',	'\\\\.',	'',

  	   'mode',	'lex',		'',
	   'atom',	'\\\\.',	'',
	   'comment',	'/\*',		'\*/',
	   'comment',	'\n%',		'\n',
	   'string',	'"',		'"',
	   'string',	"'",		"'",

	   'mode',	'B',		'',
	   
	   'mode',	'lisp',		'',
	   'comment',	';',		"\n",
	   'comment',	'#\|',		'\|#',
	   'string',	'"',		'"',

  	   'mode',	'bison',	'',
	   'atom',	'\\\\.',	'',
	   'comment',	'/\*',		'\*/',
	   'string',	'"',		'"',
	   'string',	"'",		"'",
	   	   
	   'mode',      'ruby',         '',
	   'comment',   '#',            "\n",
	   'string',	'"',		'"',
	   'string',	"'",		"'",
	   'atom',	'\\\\.',	'',
	   
  	   'mode',	'promela',	'',
	   'comment',	'/\*',		'\*/',
	   'string',	'"',		'"',
	   'string',	"'",		"'",
	   'atom',      '\\\\.',        '',

	   'mode',      'idl',          '',
           'atom',      '\\\\.',        '',
           'comment',   '/\*',          '\*/',
           'comment',   '//',           "\n",
           'string',    '"',            '"',
           'string',    "'",            "'",

	   'mode',      'axiom',        '',
	   'comment',   '--',           '\n',
	   'string',    '"',            '"',
	   );


%keywords = ('asm' =>
	     '\S+:|\.\S+',
	     
	     'beta' =>
	     'none|true|false|'.
	     'do|enter|exit|if|then|else|inner|for|repeat|leave|restart|'.
	     'suspend|not|and|or|div|mod|this|'.
	     'origin|body|mdbody|include|slot',
	     
	     'c' =>
	     'asm|auto|break|case|char|continue|default|do|double|else|enum|'.
	     'extern|float|for|fortran|goto|if|int|long|register|return|short|'.
	     'sizeof|static|struct|switch|typedef|union|unsigned|void|while|'.
	     '\\\#\s*define|\\\#\s*else|\\\#\s*endif|\\\#\s*if|\\\#\s*ifdef|'.
	     '\\\#\s*ifndef|\\\#\s*include|\\\#\s*undef',

	     'c++' =>
	     'asm|auto|break|case|catch|char|class|const|continue|default|'.
	     'delete|do|double|else|enum|extern|float|for|friend|goto|if|'.
	     'inline|int|long|new|operator|private|protected|public|register|'.
	     'return|short|signed|sizeof|static|struct|switch|template|this|'.
	     'throw|try|typedef|union|unsigned|virtual|void|volatile|while|'.
	     '\\\#\s*define|\\\#\s*else|\\\#\s*endif|\\\#\s*if|\\\#\s*ifdef|'.
	     '\\\#\s*ifndef|\\\#\s*include|\\\#\s*undef',

	     'sim' =>
	     'activate|after|and|array|at|before|begin|boolean|character|'.
	     'class|comment|delay|do|else|else|end|eq|eqv|external|false|'.
	     'for|ge|go|goto|gt|hidden|if|imp|in|inner|inspect|integer|is|'.
	     'label|le|long|lt|name|ne|new|none|not|notext|or|otherwise|'.
	     'prior|procedure|protected|qua|reactivate|real|ref|short|step|'.
	     'switch|text|then|this|to|true|until|value|virtual|when|while',
	       
	     'sql' =>  #SQL-89
	     'all|and|any|as|asc|authorization|avg|begin|between|by|char|'.
	     'character|check|close|commit|continue|count|create|current|'.
	     'cursor|dec|decimal|declare|default|delete|desc|distinct|'.
	     'double|end|escape|exec|exists|fetch|float|for|foreign|found|'.
	     'from|go|goto|grant|group|having|in|indicator|insert|int|'.
	     'integer|into|is|key|language|like|max|min|module|not|null|'.
	     'numeric|of|on|open|option|or|order|precision|primary|'.
	     'privileges|procedure|public|real|references|rollback|schema|'.
	     'section|select|set|smallint|some|sql|sqlcode|sqlerror|sum|'.
	     'table|to|union|unique|update|user|values|view|whenever|'.
	      'where|with|work',

	     'ada' =>
	     'abort|abs|abstract|accept|access|aliased|all|and|array|at|'.
	     'begin|body|case|constant|declare|delay|delta|digits|do|else|'.
	     'elsif|end|entry|exception|exit|for|function|generic|goto|'.
	     'if|in|is|limited|loop|mod|new|not|null|of|or|others|out|'.
	     'package|pragma|private|procedure|protected|raise|range|record|'.
	     'rem|renames|requeue|return|reverse|select|separate|subtype|'.
	     'tagged|task|terminate|then|type|until|use|when|while|with|xor',
	     
	     'ml' =>
	     'abstraction|abstype|and|andalso|as|include|case|datatype|'.
	     'else|end|eqtype|exception|do|fn|fun|'.
	     'functor|handle|if|in|infix|infixr|let|local|nonfix|of|'.
	     'op|open|overload|raise|rec|sharing|sig|signature|struct|'.
	      'structure|then|type|val|while|with|withtype|orelse',

	     'matlab' =>
	     'global|for|while|if|elseif|else|end|return|switch|case|'.
	     'otherwise|function|xor',

	     'perl' =>
	     'bless|caller|continue|dbmclose|dbmopen|die|do|dump|eval|exit|'.
	     'goto|import|last|local|my|next|no|package|redo|ref|require|'.
	     'return|sub|tie|tied|untie|use|wantarray',

	     'java' =>
	     'abstract|default|if|private|throw|boolean|do|implements|'.
	     'protected|throws|break|double|import|public|transient|'.
	     'byte|else|instanceof|return|try|case|extends|int|short|void|'.
	     'catch|final|interface|static|volatile|char|finally|long|'.
	     'super|while|class|float|native|switch|const|for|new|'.
	     'synchronized|continue|goto|package|this',

	     'logla' =>
	     'procedure|begin|var|call|assign|if|then|end|fi|\?|\!',
	     
	     'scheme' =>
	     'define-generic-procedure|define-generic|define-method|'.
	     'define-class|define-syntax|define|'.
	     'and|begin|call-with-current-continuation|'.
	     'call-with-input-file|call-with-output-file|call/cc|'.
	     'case|cond|delay|do|else|for-each|if|lambda|'.
	     'letrec-syntax|letrec|let\*|let-syntax|let|map|or|'.
	     'quasiquote|quote|syntax|syntax-rules|unquote|unquote-splicing',

	     'abel' =>
	     'module|endmodule|type|subtype|typevar|include|true|false|'.
	     'Bool|True|False|case|of|fo|let|in|ni|error|exist|ext|'.
	     'forall||all|with|by|as|array|where|if|then|else|fi|isin|qua|'.
	     'at|func|proc|def|genbas|obsbas|oneone|axioms|lemmas|'.
	     'implements|partially|convex|var|endvar|const|endconst|skip|'.
	     'for|to|loop|endloop|while|assert|invar',

	     'vhdl' =>
	     'and|architecture|attribute|begin|bit|bit_vector|case|'.
	     'component|constant|downto|else|elsif|end|entity|event'.
	     'for|generic|if|in|integer|is|library|loop|map|of|others|'.
	     'out|package|port|process|range|select|signal|signed|'.
	     'std_logic|std_logic_vector|string|then|to|type|unsigned|'.
	     'until|use|variable|wait|when',
	   
	     'python' =>
	     'and|assert|break|class|continue|def|del|elif|else|except|'.
	     'exec|for|from|global|if|import|in|is|lambda|not|or|pass|'.
	     'print|raise|return|while|else:|except:|finally:|try:',

	     'lex' =>
	     '\\\#\s*define|\\\#\s*include|'.
	     '%%|%token|extern|%{|%}|option|noyywrap|'.
	     'yylineno|:cntrl:|:lower:|:space:|:print:|:upper:|'.
	     ':graph:|:punct:|xdigit:|:alpha:|:alnum:|:digit:|'.
	     ':blank:|yylineno|yytext|extern|yyerror|yylex|yylval|yylloc',

	     'B' =>
	     'ANY|ASSERTIONS|BE|BEGIN|CASE|CHOICE|CONSTANTS|CONSTRAINTS|'.
	     'DEFINITIONS|DO|EITHER|ELSE|ELSIF|END|EXTENDS|IF|IMPLEMENTATION|'.
	     'IMPORTS|IN|INCLUDES|INITIALISATION|INVARIANT|LET|MACHINE|'.
	     'OPERATIONS|OR|PRE|PROMOTES|PROMOTES|PROPERTIES|REFINEMENT|'.
	     'REFINES|SEES|SELECT|SETS|THEN|USES|VAR|VARIABLES|VARIANT|WHEN|'.
	     'WHERE|WHILE',

	     'lisp' =>
	     'block|break|case|ccase|cerror|compiler-let|cond|ctypecase|'.
	     'declaim|declare|defadvice|defalias|defclass|defconstant|'.
	     'defcustom|defface|defgeneric|defgroup|define-compiler-macro|'.
	     'define-condition|define-derived-mode|define-function|'.
	     'define-method-combination|define-modify-macro|'.
	     'define-setf-expander|define-skeleton|define-symbol-macro|'.
	     'define-widget|defmacro|defmethod|defpackage|defparameter|defsetf|'.
	     'defstruct|defsubst|deftype|defun|defvar|destructuring-bind|do|'.
	     'do\*|dolist|dotimes|ecase|etypecase|flet|handler-bind|'.
	     'handler-case|if|ignore-errors|in-package|inline|labels|lambda|'.
	     'let|let*|lexical-let|lexical-let*|locally|loop|macrolet|proclaim|'.
	     'prog|prog*|prog1|prog2|progn|progv|restart-bind|restart-case|'.
	     'return|return-from|symbol-macrolet|tagbody|the|typecase|unless|'.
	     'unwind-protect|when|while',
	     
	     'ruby' =>
	     'end|def|if|else|elsif|while|case|then|for|unless|'.
	     'when|defined|until|loop|break|redo|next|retry|'.
	     'puts|class|return|yield|printf|protected|private|public|self|'.
	     'eql?|equal?|and|or|not|catch|throw|rescue|raise|ensure|'.
	     'eval|attr|attr_accessor|attr_reader|attr_writer|'.
	     'each|step|downto|upto|do|times|super|begin|each|upto|downto|'.
	     'foreach',
	     	     
	     'bison' =>
	     '\\\#\s*define|\\\#\s*include|extern|'.
	     'error|YYABORT|YYACCEPT|YYBACKUP|YYERROR|YYERROR\_VERBOSE|'.
	     'YYINITDEPTH|YYLEX_PARAM|YYLTYPE|yyltype|YYMAXDEPTH|'.
	     'YYPARSE_PARAM|YYRECOVERING|YYSTACK_USE_ALLOCA|YYSTYPE|'.
	     'yychar|yyclearin|yydebug|yyerrok|yyerror|yylex|yylval|yylloc|'.
	     'yynerrs|yyparse|yylineno|yytext|token|'.
	     '%debug|\%defines|\%file-prefix|\%left|'.
	     '\%name-prefix|\%no-lines|\%nonassoc|\%output|\%prec|\%right|'.
	     '\%pure-parser|\%start|%token|\%token-table|\%type|\%union',
	     
	     'promela' =>
	     'active|assert|atomic|bit|bool|break|byte|chan|d\_step|'.
	     'Dproctype|do|else|empty|enabled|fi|full|goto|hidden|if|'.
	     'init|int|len|mtype|nempty|never|nfull|od|of|pcvalue|printf|'.
	     'priority|proctype|provided|run|short|skip|timeout|typedef|'.
	     'unless|unsigned|\\\#\s*define|::',
	     
	     'idl' =>    # CORBA 3.0
             'abstract|exception|inout|provides|truncatable|'.
             'any|emits|interface|public|typedef|'.
             'attribute|enum|local|publishes|typeid|'.
             'boolean|eventtype|long|raises|typeprefix|'.
             'case|factory|module|readonly|unsigned|'.
             'char|FALSE|multiple|setraises|union|'.
             'component|finder|native|sequence|uses|'.
             'const|fixed|Object|short|ValueBase|'.
             'consumes|float|octet|string|valuetype|'.
             'context|getraises|oneway|struct|void|'.
             'custom|home|out|supports|wchar|'.
             'default|import|primarykey|switch|wstring|'.
	     'double|in|private|TRUE',

	     'axiom' => 
	     'for|in|repeat|while|return|break|if|then|else|local',
	     );

$indent  = 6;
$tablen  = 8;
$linelen = 0;

$infile  = "<&STDIN";
$outfile = ">&STDOUT";

$modulus = 1;
$linenumbersize = '\footnotesize';

$codetype = $0;
$codetype =~ s/^.*texify([^\/]+)$/$1/;

unless (defined($align{$codetype})) {
    print(STDERR "** Don't know who I am.  You can call me: \n");
    foreach (sort(keys(%align))) {
	print(STDERR "\ttexify$_\n");
    }
    exit(1);
}

$keywords = $keywords{$codetype};

# Fill the array @btype, @open, @term with the
# values of the current selected language.
while (@_ = splice(@blksep,0,3)) {
    if ($_[0] eq "mode") {
	$_ = $_[1];
    } elsif ($_ eq $codetype) {
	push(@btype, $_[0]);
	push(@open, $_[1]);
	push(@term, $_[2]);
    }
}

foreach (@open) {
    $open .= "($_)|";
    $split .= "$_|";
}
chop($open);

foreach (@term) {
    next if $_ eq '';
    $split .= "$_|";
}
chop($split);


while ($_ = shift(@ARGV)) {
    if (/^-(.+)$/) {
	if ($1 eq 'i') {
	    $infile = shift(@ARGV);
	} elsif ($1 eq 'u' || $1 eq 'o') {
	    $outfile  = '>'.shift(@ARGV);
	} elsif ($1 eq 'l') {
	    $linelen = shift(@ARGV);
	} elsif ($1 eq 't') {
	    $indent = shift(@ARGV);
	} elsif ($1 eq "num") {
	    $linenum = 1;
	    $modulus = shift(@ARGV);
	} elsif ($1 eq "numsize") {
	    $linenumbersize = shift(@ARGV);
	} elsif ($1 eq '?' || 
		 $1 eq 'h' ||
		 $1 eq '-help') {
	    print(STDERR "** Usage: $0 [-i infile] [-o outfile] [-l linelength] [-t indent] [-num INTERVAL] [-numsize \\texsize]\n\n");
	    exit(0);
	}
    }
}

unless (open(INF,$infile)) { 
    print(STDERR "** Could not open input file \"$infile\": $!\n"); 
    exit(-1); 
}

unless (open(OUTF,$outfile)) { 
    print(STDERR "** Could not open output file \"$outfile\": $!\n");
    exit(-2); 
}

sub quote {
    my $t = $_[0];
    $t =~ s/([$tspecials])/$tspecials{$1}/go;
    return($t);
}

sub texquote {
    my $t = '';
    my @a = split(/\\begin{tex}(.*?)\\end{tex}/, $_[0]);
    
    # Don't quote anything inside \begin{tex}...\end{tex}. (These
    # blocks wreak havoc with the tabbing algorithm, and probably
    # messes up if they span multiple lines as well.)
    while (@a) {
	$t .= &quote(shift(@a));
	$t .= shift(@a);
    }
    return($t);
}


sub pushtab {
    push(@tabstops, $_[0]);
    return('');
}

$code = "\n";
while ($line = <INF>) {
    chomp($line);

    # Expand tabs so that the length is right, but leave the original
    # tab as the last character of the expansion.  (We want to match
    # on it later.)
    $line =~ s/([^\t]*)\t/($1.
			   (' ' x ($tablen - (length($1) % $tablen) - 1)).
			   "\t")/ge;

    # This is a bit nave when using proportional fonts , but if your
    # lines are so long they wrap you probably ought to have another
    # look at your code anyway.
    $line =~ s/([^\n]{$linelen})/$1\n/g if $linelen > 0;

    # If "align" is true, we insert tabs all over the place to make
    # the tab-expander align for us.
    $line =~ s/ ([^ \t])/\t$1/g if $align{$codetype};

    # For all tabs: collect information on which column to tab to.
    # Remove unnecessary whitespace.
    $line =~ s/(([^\t\n]*?) *)\t/&pushtab(length($1) + 1)."$2\t"/ge;

    $line =~ s/\s*\n$//;
    $line =~ s/\n/\01/g;
    $code .= "$line\n";
}

@code = split(/($split)/,$code);

while ($#code >= 0) {
    $code = shift(@code);

    # Check if the current line belongs to a block.
    if ($code && (@_ = $code =~ /^$open$/o)) {
	$i = 1;
	$blktype = grep { $i = ($i && !defined($_)) } @_;

        # Copy the whole block to $code.
	if ($term[$blktype] ne '') {
	    while ($#code >= 0) {
		$_ = shift(@code);
		$code .= $_;
		last if /^$term[$blktype]$/;
	    }
	}
	$fbeg = $fontbeg{$btype[$blktype]};
	if ($btype[$blktype] eq 'comment') {
	    if ($code=~/.*<texify:ignore>.*/) {
		next;
	    } elsif ($code=~/.*<texify:tex>.*/) {
		$code=~ s/$open[$blktype]//o;
		$code=~ s/\<texify:tex\>//g;
		$code=~ s/$term[$blktype]//o;
		$output .= "\\end{tabbing}".$code."\\begin{tabbing}";
		next;
	    } else {
		$code = &texquote($code);
	    }
	} else {
	    $code = &quote($code);
	}
	$code =~ s/([^\n\t\01]+)/$fbeg$1$fontend/g if $fbeg ne '';
    } else {
	@_ = split(/([$delim])/,&quote($code));
	$code = '';
	foreach (@_) {
	    unless (/^[$delim]$/) {
		s/^($keywords)$/$fontbeg{'ident'}$1$fontend/gio;
	    }
	    $code .= $_;
	}
    }
    $code =~ s/ /'\\ '/ge;
    $code =~ s/\01/{}...\\\`\$\\Rightarrow\$\n...{}/g;

    $code =~ s/\n/\\\\\n/g;
    $output .= $code;
}

sub tabbox {
    return('\makebox['.
	   (shift(@tabstops)*$indent).
	   'pt][l]{'.$_[0].'}');
}			    

$output =~ s/^\\\\\n//;
$output =~ s/\\\\[\n\t ]*$/\n/;
$output =~ s/([^\n\t]*)\t/&tabbox($1)/ge;

$nl = "";
if ($linenum) {
    my(@alines) = split(/\\\\\n/,$output);
    my $lines = 0;
    foreach (@alines) {
	my $t;
	if (++$lines % $modulus == 0) { 
	    $t = "\n\\texifylineno{$lines}" . $_; 
	    $_ = $t;
	}
    }
    $output = join("\\\\",@alines);
    $nl = "\\providecommand{\\texifylineno}[1]{{$linenumbersize\\hspace{-4em}\\makebox[4em][r]{#1\\hspace{1.5ex}}}}";
}

print(OUTF
      '{',$nl,
      '{\def\dash',
      '{\raise2.1pt\hbox{\rule{',($indent-1),'pt}{0.3pt}}\hspace{1pt}}',
      '\begin{tabbing}',"\n",$output,'\end{tabbing}}}',"\n");
