#!/bin/awk -f

# C++2ltx.awk: C/C++ to LaTeX konvertor (version 22. 11. 1999)

# c Petr Mikulik
# Laboratory of Thin Films and Nanostructures
# Masaryk University, Brno, Czech Republic
# mikulik@physics.muni.cz, http://www.sci.muni.cz/~mikulik/

# This program is free (open source), but copyrighted and it must 
# be distributed with the whole accompanying stuff (see C++2ltx.zip)


# Brief documentation:
# --------------------
# Keywords in the file: 
#   "//C++2ltx+"   on a single line: all "//" and "/*" comments are taken as TeX enhanced
#   "//C++2ltx-"   on a single line: all "//" and "/*" comments are taken as programmer's 
#		    comments (the default setting)
# //+ and /*+ ... */  ... enhanced comments (supposed LaTeX code there)
# //- and /*- ... */  ... usual C/C++ comment. This is useful when the 
#			  default setting are enhanced comments
# //!		      ... this line is completely ignored
#			  (in principle, equivalent to  //- % blablabla)



### The program:

BEGIN {
END_quietly=0
cerr="/dev/stderr" # standard error output

if (ARGC!=2) { 
  print "\nScript C++2ltx.awk (c Petr Mikulik, Brno, August 1998)"  >cerr
  print "Syntax:  (g)awk -f C++2ltx.awk _input_C_or_C++_file_ >output_file.tex\n" >cerr
  print "This program is freeware distributed under GNU General Public License." >cerr
  print "Homepage: http://www.sci.muni.cz/~mikulik" >cerr
  END_quietly=1
  exit
  }

# Remarks:
#   -- There can fit up to 65 characters "x" (in 10 pt) on line 250 mm width
#	(see CharsPerLine below)

print "% Format: latex\n"
print "\\documentclass[a4paper]{article}"
print "\\special{landscape}"
print "\\textwidth=274mm"
print "\\oddsidemargin=-15.6mm"
print "\\evensidemargin=-15.6mm"
print "\\textheight=182.7mm"
print "\\topmargin=-15.7mm"
print "\n\\twocolumn \\columnsep=16mm \\columnseprule=1.0pt"
print "\\parindent=0pt \\parskip=0pt"
print "\\rightskip=0pt plus 0.3\\textwidth"
print "\\hfuzz=1em \\vfuzz=1em"
print "\n\\let\\Huge\\huge \\let\\huge\\LARGE \\let\\LARGE\\Large \\let\\Large\\large"
print "\\let\\large\\normalsize \\let\\normalsize\\small \\let\\small\\footnotesize"
print "\\let\\footnotesize\\scriptsize \\let\\scriptsize\\tiny"
print "\\let\\bigskip\\medskip \\let\\medskip\\smallskip\n"
print "\\usepackage{fancyhdr} \\pagestyle{fancy}"
print "\\def\\headrulewidth{0pt} \\def\\footrulewidth{0pt} \\headsep=12pt"
print "\\lhead{} \\rhead{} \\lfoot{} \\cfoot{} \\rfoot{}"
print "\\chead{\\fboxrule=1.0pt \\framebox[\\hsize]{"
  "ls -l --full-time "ARGV[1] | getline # find the creation date of the file
  S=$3" "$4" "$5" "$7" "$6; close("ls -l --full-time "ARGV[1])
i=ARGV[1]
gsub("_","\\_",i)	# since \verb cannot be used in \chead, precede TeX's special
gsub("\\$","\\$",i)	# characters here
if (index(i,"\\")) { # translation \ => $\backslash$ in the input filename
  gsub("\\\\","C2LTX",i)
  gsub("C2LTX","$\\backslash$",i)
  }
print "  \\quad \\textbf{"i"} \\quad ("S") \\hfill"
  S=ENVIRON["LOGNAME"]; 
  if (S=="") { S=ENVIRON["USER"]; if (S=="") S=root }
  S=S" from "ENVIRON["HOSTNAME"]" ("strftime("%a %b %d %Y %H:%M:%S",systime())
print "  printed by "S") \\hfill Page \\thepage \\quad}}"

print "\n\\usepackage{C++2ltx}"
print "\\def\\up{\\unskip\\penalty0}"
print "\\def\\CLTXVF{\\normalfont\\ttfamily\\slshape}"
print "\n\\begin{document}\n"


#### DEFAULT SETTINGS ####
CharsPerLine=80 # limit for cutting the line into words (i.e. into the \hboxes)
		   # depends on the default font and page size used---found manually
DefaultEnhancedComment=0 # default enhanced comments yes/no
#### END of the default setttings

### USUAL THINGS ###
# cerr ... see above

### GLOBAL VARIABLES ###
# separators for \verb command
Separators="|!+=-@;:.0123456789[]()" # the array of choosable separators
Separator=""				  # separator chosen for the current line
# line parsing
Line =""	# contains the line read from input (should always equal $0)
iLine =0	# index from which start or continue parsing of the Line
isLongLine =0	# is this line long, i.e. are there more chars than CharsPerLine??
wasBlankLine=0 # flag for separating paragraphs by blank lines
# comment treatment
cPresent =0	# is there still a comment on the current line?
c2slashes =0	# is it // or //+ comment?
cEnhanced =0	# is it TeX-enhanced comment?
cLeft =0; cRight =0	# parsing this piece of Line. If cOpen then cRight is unused
cOpen =0	# set to 1 if the current line $0 is continuation of an open long /* comment
cOpenFirstLine =0	# first line of open command opening
cParIndentation =0	# string with \parindent and \hangindent to be employed at each beginning
			# new paragraph of an open enhanced comment
#### END of global variables

}


##### ENDING THE PROGRAM #####

END {
if (END_quietly);  # write nothing since there is no (useful) output
  else print "\\end{document}"
}


####### PROCESS INPUT: Blank line and //C++2ltx + and - switches #######

NF==0 { # blank line
  if (cOpen && cEnhanced) printf "\n\n\\medskip"
	else print "\\medskip\n"
  wasBlankLine=1
  next
  }

wasBlankLine!=0 {
  if (cOpen && cEnhanced) if (cParIndentation) printf cParIndentation
  wasBlankLine=0
  }

$0=="//C++2ltx+" {
  DefaultEnhancedComment=1
  next
  }
$0=="//C++2ltx-" {
  DefaultEnhancedComment=0
  next
  }

$0 ~ "//!" { # line contains comment
  if (index($1,"//!")==1) next # ignore this line with only ignorable comment
  i=index($0,"//!")
  $0=substr($0,1,i-1)
  $0=StripTrailingSpaces($0)
  }


################## MAIN PROGRAM PART ##################

{
Line=$0; ExpandTabulators()

iLine=1 # start parsing from the first column (of course)
OutHead=""; OutLine=""  # make the output strings empty
if (!( cOpen && cEnhanced )) OutTail="" # keep still OutTail, i.e. \par}

# Find a suitable \verb separator
i=1; flag=1
while (flag) {
  Separator=substr(Separators,i,1); i++
  flag=index(Line,Separator)
  }

if ( cOpen && cEnhanced ) isLongLine=0
  else	{
    isLongLine = (length(Line)>CharsPerLine) ? 1 : 0
    ### RUDE OF HANGINDENT:
    if (isLongLine) {
	iLine=match(Line,"[^ ]")
	ParIndent=iLine-1
	OutHead="{\\par\\leavevmode\\parindent="ParIndent*1.22"ex\\indent\\hangindent="(ParIndent+2)*1.22"ex"
	OutTail=OutTail "\\par}"
	}
    }

# Go over all pieces of code and comment until the whole line is processed
while (iLine<=length(Line)) {
  FindNextComment()  # find comment, and if it is there, then set its cXXXX staff
  if (!cPresent) { # there is no comment
	OutLine=OutLine VerbIt( substr(Line,iLine) )
	iLine=length(Line)+1
	}
    else { # there is a COMMENT on the line
      if (iLine>CharsPerLine-15) # parbox too narrow, thus shorten hangindent
	 OutLine=OutLine "\\advance\\hangindent-" 25*1.22 "ex"
      if (cLeft>iLine) # typeset the code (if exist) preceding the comment
	OutLine=OutLine VerbIt( substr(Line,iLine,cLeft-iLine) )
      if (c2slashes || cOpen) { # It is // comment or long (open) /* comment. Format it now
	  #! if (longComment...)
	  if (cEnhanced) { # enhanced comment, no surrounding code needed
		OutLine=OutLine "\n" StripRoundSpaces(substr(Line,cLeft))
		# isn't it the first thing on the line?
		if (cOpen && cLeft==match(Line,"[^ ]")-1) { # yes, it is, thus hangindent it
		  cParIndentation="\\parindent="(cLeft-1)*1.22"ex\\indent\\hangindent="(cLeft-1)*1.22"ex%"
		  OutHead="{" cParIndentation
		  if (!cOpenFirstLine) OutLine="" # clear that "\verb|    |"
		  cOpenFirstLine=0
		  OutTail="\\par}"
		  }
		}
	    else { # usual comment
		OutLine=OutLine "%\n{\\makeatletter\\let\\verbatim@font\\CLTXVF\\makeatother%\n"
		S=substr(Line,cLeft)
		if (isLongLine) S=StripLeadingSpaces(S)
		OutLine=OutLine VerbIt( S ) "\n}"
		}
	  iLine=length(Line)+1
	  }
	else { # It is /* */ comment. Format it now
	  if (cEnhanced) { # enhanced comment, no surrounding code needed
		OutLine=OutLine "\n" StripRoundSpaces(substr(Line,cLeft,cRight-cLeft-4)) "%\n"
		iLine=cRight-1
		}
	    else { # usual comment
		OutLine=OutLine "%\n{\\makeatletter\\let\\verbatim@font\\CLTXVF\\makeatother%\n"
		OutLine=OutLine VerbIt( substr(Line,cLeft,cRight-cLeft+1) ) "}%\n"
		iLine=cRight+1
		}
	  }
      }

  } # the whole line has been processed

# flush the output
if (OutHead!="") printf "%s",OutHead
if (OutLine!="") printf "%s",OutLine
if (cOpen && cEnhanced) {} # printf "\n"
  else
{
if (OutTail!="") {
	if (substr(OutLine,length(OutLine))!="\n") printf "\n" # print tail on a new line
	printf "%s",OutTail
	}
print "\n"
}

} # end of the Main Program Part



####################### FUNCTIONS #######################


# This function expands tabulators in the global variable Line
function ExpandTabulators (  i,S,sp ) {
i=index(Line,"\t")
if (i>0) {
  S=""
  while (i!=0) {
    S=S""substr(Line,1,i-1)
    sp=" "
    for (p=8-i%8; p>0; p--) sp=sp" "
    S=S""sp
    Line=substr(Line,i+1)
    i=index(Line,"\t")
    }
  Line=S""Line
  }
}


# Verboses a code passed from line. If the line is short, then put \verb in front of the line
# enclosed by separators, otherwise makes hboxes of \verbed all words
function VerbIt ( line,   i,S ) {
if (!isLongLine) { # print \verb+<line>+
	return "\\verb"Separator line Separator
	}
# otherwise cut the line into \hbox{\verb+<word_of_line>+}
S=""
while (length(line)>0) {
  i=match(line," [^ ]")
  if (i!=0) { # ending space
      S = S "\\hbox{\\verb" Separator substr(line,1,i) Separator "} \\up"
      line=substr(line,i+1); }
    else { 
      S = S"\\hbox{\\verb" Separator line Separator "} "
      line = "" }
  }
return S
}


# Is there comment?
function FindNextComment (     line,S ) {
if (cOpen) { # multiline comment /* ... */ continues
  cPresent=1
  cLeft=1
  cRight=index( substr(Line,1), "*/" )
  cOpen = (cRight==0) ? 1:0
  if (cRight>0) cRight += cEnhanced ? 3:1
  return
  }
line=substr(Line,iLine)
cPresent=indexNotInQuotes(line,"//")
if (cPresent) { # there is // comment
	c2slashes=1
	cLeft=cPresent+iLine-1 # position of the comment in Line
	# cRight is unused
	}
  else {
	cPresent=indexNotInQuotes(line,"/*")
	if (!cPresent) return # there is no (more) comment on the current line
	c2slashes=0	# there is /* comment
	cLeft=cPresent+iLine-1 # position of the comment in Line
	cRight=index( substr(Line,cLeft+2), "*/" )
	cOpen = (cRight==0) ? 1:0
	if (cOpen) cOpenFirstLine=1
	if (cRight>0) cRight+=cLeft+2
	}
# find whether the comment is enhanced
S=substr(line,cPresent+2,1)
delSign=1
if (S=="-") cEnhanced=0; else	# there was //-
  if (S=="+") cEnhanced=1; else	# there was //+
    { cEnhanced=DefaultEnhancedComment # use the default value
      delSign=0 } # no delete of sign
# delete unnecessary parts of the comment: //+ and /*- (enhanced) or + and / for normal
if (cEnhanced)
    Line=substr(Line,1,cLeft-1) substr(Line,cLeft+2+delSign) # delete also the // or /* 
  else if (delSign)
    Line=substr(Line,1,cLeft+1) substr(Line,cLeft+3)
}


function StripTrailingSpaces ( S   ,i ) {
i=match(S," +$")
return (i==0) ? S : substr(S,1,i-1)
}

function StripLeadingSpaces ( S   ,i ) {
i=match(S,"[^ ]")
return (i==0) ? "" : substr(S,i)
}

function StripRoundSpaces ( S   ,i ) {
i=match(S,"[^ ]")
if (i==0) return ""
S=substr(S,i)
i=match(S," +$")
return (i==0) ? S : substr(S,1,i-1)
}


# This function works like index(S,pattern) with the exception that
# the pattern cannot appear in quotes (inside C/C++ strings)
function indexNotInQuotes ( S, pattern ) {
offset=0
while (1) {
  p=index(substr(S,offset+1),pattern)
  if (p==0) return 0 # no pattern found
  p+=offset
  c=countQuotes(S,1,p-1)
  if (c%2==0) return p # even number of quotes, thus return the found position
  offset=p
  }
}


# returns number of quotes in the specified region. Takes care of \"
function countQuotes ( S, from, to ) {
c=0
S=substr(S,from,to)
from=index(S,"\"")
while (from) { # there is (still) a quote
  # increment counter of quotes if the quote is not backslashed:
  if (!( from>0 && substr(S,from-1,1)=="\\" )) c++
  S=substr(S,from+1) 
  from=index(S,"\"")
  }
return c
}

# eof C++2ltx.awk
