#!/bin/sh

#
# Syntaxe :
#   multilp [-f<fonte>] [-l<lignes/page>] [-c <nb de col>] [-m <mode>][-s]
#				[ <file> ]
#
#	-f : nom de la fonte PostScript utilisee pour le texte
#		Defaut : Courier
#	-l : nombre de lignes par page
#		Defaut : 66
#	-c : nombre de colonnes par page
#		Defaut : 1
#	-s : pas de ligne de separation entre les colonnes
#		Defaut : ligne de separation active
#	-m : mode ('l'andscape ou 'p'ortrait)
#		Defaut : landscape
#
# Exemple :
#   multilp -fTimes-Roman -l50 -c4 -ml -s toto.c
#

#
# Valeurs par defaut
#

textfont=Courier			# fonte pour le texte
boldfont=Courier-Bold			# fonte pour le texte gras (man ...)
italfont=Courier-Oblique		# fonte pour le texte souligne (man ...)
lpp=66					# nombre de lignes par page
sep=true				# separation des colonnes
ppp=1					# nb de colonnes (page logique) par page
# cset="roman8"				# jeu de caracteres
cset="iso_8859_1"			# jeu de caracteres


##############################################################################
# Les differents vecteurs de codage
#

roman8encoding ()
{
    cat <<'EOF'
    /MyEncoding [
	161/Agrave/Acircumflex/Egrave/Ecirumflex/Edieresis
	/Icircumflex/Idieresis/acute/grave/circumflex/dieresis
	/tilde/Ugrave/Ucircumflex/sterling/.notdef/Yacute/yacute
	/degree/Ccedilla/ccedilla/Ntilde/ntilde/exclamdown
	/questiondown/currency/sterling/yen/section/florin/cent
	/acircumflex/ecircumflex/ocircumflex/ucircumflex/aacute
	/eacute/oacute/uacute/agrave/egrave/ograve/ugrave
	/adieresis/edieresis/odieresis/udieresis/Aring
	/icircumflex/Oslash/AE/aring/iacute/oslash/ae/Adieresis
	/igrave/Odieresis/Udieresis/Eacute/idieresis/germandbls
	/Ocircumflex/Aacute/Atilde/atilde/Eth/eth/Iacute/Igrave
	/Oacute/Ograve/Otilde/otilde/Scaron/scaron/Uacute
	/Ydieresis/ydieresis/Thorn/thorn/periodcentered/mu
	/paragraph/threequarters/emdash/onequarter/onehalf
	/ordfeminine/ordmasculine/guillemotleft/bullet
	/guillemotright/plusminus/.notdef
    ] def
EOF
}

iso88591encoding ()
{
    cat <<'EOF'
    /MyEncoding [
	161/exclamdown/cent/sterling/currency/yen/brokenbar
	/section/dieresis/copyright/ordfeminine/guillemotleft
	/logicalnot/endash/registered/macron/degree/plusminus
	/twosuperior/threesuperior/acute/mu/paragraph/periodcentered
	/cedilla/onesuperior/ordmasculine/guillemotright
	/onequarter/onehalf/threequarters/questiondown/Agrave
	/Aacute/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla
	/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute
	/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute
	/Ocircumflex/Otilde/Odieresis/multiply/Oslash/Ugrave
	/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls
	/agrave/aacute/acircumflex/atilde/adieresis/aring/ae
	/ccedilla/egrave/eacute/ecircumflex/edieresis/igrave
	/iacute/icircumflex/idieresis/eth/ntilde/ograve/oacute
	/ocircumflex/otilde/odieresis/divide/oslash/ugrave
	/uacute/ucircumflex/udieresis/yacute/thorn/ydieresis
    ] def
EOF
}

###############################################################################
# L'en-tete du listing.
#

prologuePS ()
{
    case "$cset" in
	roman8)	roman8encoding ;;
	*)	iso88591encoding ;;
    esac

    if [ "$mode" = landscape ]
    then
	echo "/tourner { 90.0 rotate } def"
    else
	echo "/tourner { } def"
    fi

    cat <<'EOF'
    /ReEncode { %def
	findfont
	begin
	    currentdict dup length dict begin
		{ %forall
			1 index /FID ne {def} {pop pop} ifelse
		} forall
		/FontName exch def dup length 0 ne { %if
			/Encoding Encoding 256 array copy def
			0 exch { %forall
				dup type /nametype eq { %ifelse
					Encoding 2 index 2 index put
					pop 1 add
				}{ %else
					exch pop
				} ifelse
			} forall
		} if pop
	currentdict dup end end
	/FontName get exch definefont pop
    } bind def

    % Calcul automatique des parametres de page

    tourner
    clippath pathbbox		% (llx lly urx ury)
    pop pop translate		% ()

    clippath pathbbox		% (0 0 urx ury)

    /deltay    exch def
    /deltax    exch def

    pop pop			% ()

    /margin    deltax ppp 20 mul div def
    /colwidth  deltax margin ppp mul sub ppp div def

    %
    % colx : (colnum) -- (offset)
    %
    % Renvoie l'offset (x) de la i-eme colonne (0 <= colnum < ppp)
    %
    /colx			% 
    {
	margin colwidth add mul
	margin add
    } def

    %
    % colnum : () -- (colnum)
    %
    % Renvoie le numero (0 <= colnum < ppp) de la colonne courante
    %
    /colnum
    {
	lineno lpp ppp mul mod		% numero de la ligne dans la page
	lpp div				% numero de colonne
    } def

    /linepitch deltay lpp 2 add div neg def
    /top       linepitch neg lpp mul def

    /fontsize  linepitch neg 0.92 mul def

    % Variables de travail

    /curx	0 colx def

    % eject page

    /ejectpage
    {
	lineno lpp mod 0 eq
	{
	    showpage
	    tourner
	    clippath pathbbox
	    pop pop translate
	}
	if
    } def

    /n				% chaine en fonte normale
    {
	textfont findfont fontsize scalefont setfont
	show
    } bind def

    /b				% chaine en fonte grasse
    {
	boldfont findfont fontsize scalefont setfont
	show
    } bind def

    /i				% chaine en fonte italique
    {
	italfont findfont fontsize scalefont setfont
	show
    } bind def

    %
    % p : () -- ()
    %
    % Termine une ligne
    %
    % Algorithme
    %   si (lineno mod lpp) == 1
    %     alors tracer eventuellement une ligne de separation verticale
    %   finsi
    %   si (lineno mod lpp) == 0
    %     alors positionner pour une nouvelle "page"
    %   fin si
    %

    /p
    {
	grestore

	% Imprimer une ligne verticale de separation apres la premiere
	% ligne d'une nouvelle colonne

	lineno lpp mod 1 eq			% premiere ligne d'une colonne
	    colnum 0 ne and			% pas la premiere colonne
	    sep and				% separation demandee
	{
	    gsave
		newpath
		    colnum colx
		    margin 2 div sub
		    dup				% (x x)
		    0 moveto
		    top linepitch 1.3 mul sub lineto
		    0.25 setlinewidth
		stroke
	    grestore
	}
	if

	% Detecter les fins de colonne. Si c'est une fin de page
	% ejecter la page courante. De toutes manieres, repositionner
	% le curseur

	lineno lpp mod 0 eq
	{
	    % Finit-on une page ?
	    colnum 0 eq { ejectpage } if

	    % Positionner le curseur
	    /curx colnum colx def
	    curx top moveto
	}
	{
	    0 linepitch rmoveto
	}
	ifelse

	/lineno lineno 1 add def
    } bind def

    curx top moveto
EOF
}

###############################################################################
# Envoie le prologue contenant les parametres et le programme PostScript
#

prologue ()
{
    echo "%!"
    echo "/lpp $lpp def"
    echo "/sep $sep def"
    echo "/ppp $ppp def"

    prologuePS # | sed -e 's/^[ 	]*//' -e 's/^%.*//' -e '/^$/d'

    echo "MyEncoding /_$textfont /$textfont ReEncode"
    echo "MyEncoding /_$boldfont /$boldfont ReEncode"
    echo "MyEncoding /_$italfont /$italfont ReEncode"
    echo "/textfont /_$textfont def"
    echo "/boldfont /_$boldfont def"
    echo "/italfont /_$italfont def"
    echo "textfont findfont fontsize scalefont setfont"
}

bof ()
{
    echo "/lineno 1 def"
}

eof ()
{
    echo "lineno lpp 2 mul mod 1 ne { showpage } if"
}

epilogue ()
{
    #
    # Une maniere parfaitement portable de generer un CTRL D
    #
    awk 'END { printf "%c", 4 }' /dev/null
}

###############################################################################
#
# Le programme principal
#


#
# Analyse des options
#

set -- `getopt f:l:c:m:s $*`

if [ $? != 0 ]
then usage
fi

for i in $*
do
    case $i in
	-f)	textfont=$2
		case "$textfont" in
		    Courier)		boldfont=Courier-Bold
					italfont=Courier-Oblique
					;;
		    Times-Roman)	boldfont=Times-Bold
					italfont=Times-Italic
					;;
		    Helvetica)		boldfont=Helvetica-Bold
					italfont=Helvetica-Oblique
					;;
		    Helvetica-Narrow)	boldfont=Helvetica-Narrow-Bold
					italfont=Helvetica-Narrow-Oblique
					;;
		esac
		shift 2
		;;
	-l)	lpp=$2 ;	shift 2 ;;
	-c)	ppp=$2 ;	shift 2 ;;
	-m)	case "$2" in
		    [lL]*)	mode=landscape ;;
		    [pP]*)	mode=portrait ;;
		    *)		echo "unknown mode '$2' (should be 'l' or 'p')" >&2
				exit 1
				;;
		esac
		shift 2
		;;
	-s)	sep=false ;	shift ;;
	--)	shift ; break ;;
    esac
done

#
# Programme principal
#

if [ $# = 0 ]
then
    set stdin
    stdin=true
else
    stdin=false
fi

#
# La boucle est executee une fois si c'est l'entree standard.
#

#
# Ce qui suit est une astuce pour eviter un probleme du shell :
# lorsqu'on appelle une fonction, les parametres de celle-ci (ici
# vides) remplacent les parametres de notre programme.
#
( prologue )

for i
do
    if [ $stdin = true ]
    then file=
    else file="$i"
    fi

    bof
    expand $file |
	sed -e '{
		    :l
		    s/__/__/
		    t l
		}' \
	    -e 's/|-/+/g' \
	    -e 's/_\(.\)/\1/g' \
	    -e 's/\([^_]\)/\1/g' \
	    -e 's/.\([^]\)/\1/g' \
	    -e 's/.//g' \
	    -e 's/\(.\)/\1/g' \
	    -e 's///g' \
	    -e 's/\(.\)/\1/g' \
	    -e 's///g' \
	    |
	sed -e 's/\\/\\\\/g' \
	    -e 's/(/\\(/g' \
	    -e 's/)/\\)/g' \
	    |
	sed -e 's/^/gsave (/' \
	    -e 's//)n (/g' \
	    -e 's//)b (/g' \
	    -e 's//)n (/g' \
	    -e 's//)i (/g' \
	    -e 's/$/)n p/'
    eof
done
epilogue

exit 0
