#!%DESTDIR%/bin/htgtcl

#
#
# HTG : HTml (and other languages) Generator
#
# Usage :
#   htg		[-v 0|1]
#		[-l <langage>]
#		[-d <rpertoire de modles>]
#		[-i <rpertoire des images>]
#		[fichier]
#
#	-v : 1 si verbose
#	-l : langage de sortie (html, latex, etc.)
#	-d : rpertoire contenant les modles
#	-i : rpertoire des images
#
# Principe de fonctionnement :
# - le binaire htgtcl fournit les commandes suivantes :
#	htg reset   : remet les buffers  zro
#	htg insert  : ajoute  la position du curseur une nouvelle chane
#	htg getnext : analyse le token suivant, ou la suite entre {...} suivante
#	A chaque analyse d'une chane de la forme \toto, htgtcl appelle la
#	fonction htg_toto, qui doit donc tre code dans un des modles.
#	Cette fonction doit renvoyer sur sa sortie standard le code gnr,
#	et c'est ce qui est renvoy par "htg getnext".
# - le programme principal est "htg"
#	- il lit les arguments
#	- il commence  lire le document .htgt, qui doit contenir
#		comme premire directive : \modele {M} (M est le nom du modle)
#		Cette directive appelle htg_modele dfinie dans ce fichier.
#	- htg_modele lit le fichier .../modeles/M/L.tcl (L est le langage)
#		ce qui a pour effet :
#		- d'inclure .../modeles/includes/L/base.tcl
#		- puis de charger les fonctions htg_* spcifiques du modle
#	- htg continue d'analyser le document .htgt
#		ce qui fait que le reste du fichier htgt est analys
#		et le code HTML gnr par les htg_* est mis dans des
#		variables. Note : dans les modles fournis, ce sont les
#		lments du tableau partie().
#	- htg lit ensuite le fond du modle (fichier .../modeles/M/fond.L)
#		et l'analyse. Cette analyse va provoquer, suivant l'criture
#		du modle, la rcupration des variables gnres lors de
#		l'analyse du fichier .htgt.
#	- le rsultat gnr provient de cette lecture du fond du modle
#
# Historique
#   1998/06/06 : pda          : conception
#   1999/06/15 : pda          : gnralisation  d'autres types de sortie
#   1999/06/20 : pda          : reconception des premiers modles HTML
#   1999/07/26 : pda          : ajout de -i (pas encore utilis)
#   2008/02/13 : pda/moindrot : meilleure documentation du principe
#   2010/10/29 : pda/moindrot/jean : installation mieux parametree
#



#
# Options par dfaut
#

set conf(lang)		html
set conf(repmodel)	%DESTDIR%/modeles
set conf(defmodel)	default
set conf(verbose)	0
set conf(images)	{}
set conf(version)	1.1

#
# Paramtres  ne pas changer
#

set conf(langages)	{html latex}
set conf(usage)		{htg [-v 0|1] [-l lang] [-d repmodeles] [-m model] [-i rp] [fichier]}

##############################################################################
# Premire procdure appele par un fichier modle
##############################################################################

#
# Traitement des modles
#
# - les macros TCL sont lues immdiatement
# - on conserve le nom du fond de page pour le lire aprs le fichier courant
#
# Note : cette procdure est la seule procdure "htg_*"  tre
# prsente dans ce fichier. En effet, toutes les autres procdures
# sont propres aux langages ou aux modles. Elles sont donc dfinies
# lors du chargement du modle.
#

proc htg_modele {} {
    global conf

    # le nom du modele
    set m [htg getnext]

    set conf(macros)  "$conf(repmodel)/$conf(defmodel)/$m/$conf(lang).tcl"
    set conf(fond)    "$conf(repmodel)/$conf(defmodel)/$m/fond.$conf(lang)"

    if {! [file exists $conf(macros)]} then {
	error "Fichier '$conf(macros)' non trouv"
    }
    if {! [file exists $conf(fond)]} then {
	error "Fichier '$conf(fond)' non trouv"
    }

    inclure-tcl $conf(macros)

    return {}
}

##############################################################################
# Inclusion de fichiers
##############################################################################

#
# Trouve le fichier de nom donn dans le chemin
#
# Entre :
#   - paramtres :
#	- fichier : nom du fichier  trouver (peut tre un chemin absolu)
# Sortie :
#   - valeur de retour : le chemin trouv
#   - sortie en erreur si pas trouv
#
# Notes sur la liste des rpertoires :
#   conf(includepath) est une liste contenant les rpertoires o chercher
#   le fichier.
#	- un rpertoire vide signifie que l'on cherche  partir de la racine
#	    (ce qui permet de spcifier un chemin absolu)
#	- ne pas oublier de mettre "." dans ces rpertoires
#
# Historique
#   1999/06/20 : pda : conception
#

proc trouver-fichier {fichier} {
    global conf

    foreach d [list {} . $conf(repmodel)/$conf(defmodel)] {
	set f $d/$fichier
	if [file exists $f] then {
	    return $f
	}
    }
    erreur "Fichier '$fichier' non trouv"
}

#
# Historique
#   1999/06/20 : pda : conception pour permettre de tracer les ouvertures
#

proc ouvrir-fichier {chemin mode} {
    global conf

    if {$conf(verbose)} then { puts stderr "inclusion '$chemin'" }
    set fd [open $chemin $mode]
    return $fd
}

#
# Historique
#   1998/06/15 : pda : conception
#   1999/06/20 : pda : utilisation de trouver-fichier et ouvrir-fichier
#

proc htg_include {} {
    set fichier [htg getnext]
    set chemin  [trouver-fichier $fichier]
    set fd [ouvrir-fichier $chemin r]
    htg insert [read $fd] $fichier 1
    close $fd
    return {}
}

#
# Historique
#   1999/06/20 : pda : conception pour permettre de tracer les inclusions
#

proc inclure-tcl {fichier} {
    global conf

    set chemin [trouver-fichier $fichier]
    if {$conf(verbose)} then { puts stderr "inclusion TCL '$chemin'" }
    uplevel {#0} source $chemin
}

##############################################################################
# Analyse de fichiers
##############################################################################

proc analyser {fd fname} {
    htg reset all
    htg insert [read $fd] $fname 1
    if [catch {set resultat [htg getnext]} v] then {
	erreur "Error in $fname processing\n$v"
    }
    return $resultat
}

##############################################################################
# Procdures pour gnrer le fichier
##############################################################################

proc init-variables {date} {
    global partie
    global env

    if {[info exists env(LOGNAME)]} then {
	set partie(auteur) $env(LOGNAME)
    } else {
	set partie(auteur) webmaster
    }
    set partie(date)   [clock format $date -format "%d/%m/%Y"]
}

##############################################################################
# Petit utilitaire hlas trop souvent utile... ;-)
##############################################################################

proc erreur {msg} {
    global conf

    if {[string compare $msg ""]} then {
	puts stderr "htg: $msg"
    } else {
	puts stderr "usage: $conf(usage)"
	puts stderr "\toptions par dfaut :"
	puts stderr "\t\t-l : $conf(lang)"
	puts stderr "\t\t-d : $conf(repmodel)"
	puts stderr "\t\t-m : $conf(defmodel)"
	puts stderr "\t\t-v : $conf(verbose)"
	puts stderr "\t\t-i : $conf(images)"
    }

    exit 1
}

##############################################################################
# Programme principal
##############################################################################

proc main {} {
    global argc argv0 argv
    global fichierhtgm
    global conf

    #
    # Analyse des arguments
    # Les valeurs par dfaut sont dans le tableau global "conf"
    #

    while {[llength $argv]} {
	set opt [lindex $argv 0]
	set arg [lindex $argv 1]
	switch -glob -- $opt {
	    -l {
		set conf(lang) $arg
		set argv [lreplace $argv 0 1]
	    }
	    -v {
		set conf(verbose) $arg
		set argv [lreplace $argv 0 1]
	    }
	    -d {
		set conf(repmodel) $arg
		set argv [lreplace $argv 0 1]
	    }
	    -m {
		set conf(defmodel) $arg
		set argv [lreplace $argv 0 1]
	    }
	    -i {
		lappend conf(images) $arg
		set argv [lreplace $argv 0 1]
	    }
	    -* {
		erreur ""
	    }
	    * {
		break
	    }
	}
    }

    #
    # Vrification de validit des arguments
    #

    if {! [file isdirectory $conf(repmodel)]} then {
	erreur "Rpertoire de modles '$conf(repmodel)' inexistant"
    }

    if {! [file isdirectory $conf(repmodel)/$conf(defmodel)]} then {
	erreur "Modle '$conf(defmodel)' inexistant dans '$conf(repmodel)'"
    }

    if {[lsearch -exact $conf(langages) $conf(lang)] == -1} then {
	erreur "Langage '$conf(lang)' non reconnu"
    }

    #
    # Normalement, la lecture du fichier d'entre ne donne rien, si ce
    # n'est de remplir des "parties".
    #

    switch [llength $argv] {
	0 { set fname "(stdin)" ; set fd stdin }
	1 { set fname [lindex $argv 0] ; set fd [ouvrir-fichier $fname r] }
	* { erreur "" }
    }

    set date [htg getdate $fd]
    init-variables $date

    analyser $fd $fname
    close $fd

    #
    # Ces "parties" sont maintenant reprises et intgres pour former le
    # document final lors de l'analyse du fichier "fond".
    #

    if {! [info exists conf(fond)]} then {
	erreur "Aucun modle n'est dfini dans ce fichier"
    }
    set fd [ouvrir-fichier $conf(fond) r]
    puts [analyser $fd $conf(fond)]
    close $fd
}

# on y va !
main
