#include "prehtml.h"

FILE *sortie=NULL; /* stream dans lequel on écrit */
int affiche=1; /* vrai si on doit afficher ce qu'on lit*/
int chut=0; /* vrai si on a cache un morceau */
char *fin_read="pKJFDQSKFofIUEAZRJNWXC1245FDS";

void print(char *format, ...){ /* écrit dans le fichier cible */
  va_list ap;
  va_start(ap,format);
  if (debug && affiche && (!chut)) vfprintf(stdout,format,ap);
  if (affiche && (!chut)){
    if (sortie) vfprintf(sortie,format,ap);
    else erreur("print(): sortie==NULL");
  }
}
void put(char c){ /* idem, mais imite putc() au lieu de fprintf */
  if (debug && affiche && (!chut)) putc(c,stdout);
  if (affiche && (!chut)){
    if (sortie) putc(c,sortie);
    else erreur("put(): sortie==NULL");
  }
}
int vraifaux(char *commande,char *condition){ /* utilisé par IF et WHILE */
  int i;
  char ligne[LONGUEUR_LIGNE];
  snprintf(ligne,LONGUEUR_LIGNE,"if %s ; then echo VRAI; else echo FAUX; fi",condition);
  envoieshell(ligne);
  recoitshell(ligne,0);
  if (!strcmp(ligne,"VRAI")) i=1;
  else if (!strcmp(ligne,"FAUX")) i=0;
  else {
    erreur("La condition <%s %s> ne donne ni vrai ni faux, mais:\n"
	   "%s\n",commande,condition,ligne);
  }
  if (recoitshell(ligne,0)){
    erreur("La condition <%s %s> donne des lignes en trop après VRAI ou FAUX:\n"
	   "%s\n",commande,chaine);
  }
  return i;
}

void traitefonction(void){
  char ligne[LONGUEUR_LIGNE],**liste;
  char *lignetotale;
  char *result;
  int i;
  int j;  
  infofor *cefor;
  infowhile *cewhile;
  infoinclude *ceinc;
  /************ FONCTIONS IMMÉDIATES ******************/
  /* <SHELL instruction à exécuter par sh sans regarder la réponse> */
  if (!strcmp(fonction,"shell"))execshell(chaine); /* c'est simple */
  /* <ECHO argument à sortir comme la commande echo> */
  else if (!strcmp(fonction,"echo")){ /* moins simple, mais ça va */
    snprintf(ligne,LONGUEUR_LIGNE,"echo %s",chaine);
    envoieshell(ligne);
    lignetotale=malloc(1);
    i=0;
    while(recoitshell(ligne,0)) {
      j=i;
      i+=strlen(ligne);
      lignetotale=realloc(lignetotale,i+1);
      if (!lignetotale) erreur("Manque de mémoire: %s!%d",__FILE__,__LINE__);
      strcpy(lignetotale+j,ligne);      
      result=recodeHTML(lignetotale);
    }
    free(lignetotale);
    print("%s",result);
    free(result);
  }
  /* <ECHOnoESC>: comme <ECHO> mais ne remet pas les échappements HTML */
  else if (!strcmp(fonction,"echonoesc")){ /* moins simple, mais ça va */
    snprintf(ligne,LONGUEUR_LIGNE,"echo %s",chaine);
    envoieshell(ligne);
    while(recoitshell(ligne,0))
      print("%s",ligne);
  }
  /* <SHELLECHO commande shell>, comme shell mais affiche le résultat */
  else if (!strcmp(fonction,"shellecho")){
    envoieshell(chaine);
    i=0;
    while(recoitshell(ligne,0)) {
      // if (i) put('\n'); /* sauts de ligne si plus d'une ligne */
      i=1;
      result=recodeHTML(ligne);
      print("%s",result);
      free(result);
    }
  }
  /* <SHELLECHOnoESC>: comme <SHELLECHO> mais ne remet pas les échappements HTML */
  else if (!strcmp(fonction,"shellechonoesc")){
    envoieshell(chaine);
    i=0;
    while(recoitshell(ligne,0)){
      // if (i) put('\n'); /* sauts de ligne si plus d'une ligne */
      i=1;
      print("%s",ligne);
    }
  }
  /* <CHUT> ..... </CHUT>  cache une partie (pratique avec Netscape) */
  else if (!strcmp(fonction,"chut")){
    chut++;
  }
  else if (!strcmp(fonction,"/chut")){
    if (chut) chut--;
    else warning("</chut> trouvé alors qu'on n'est pas dans <CHUT>");
  }
  /* <ERROR texte écrit comme pour ECHO>: stoppe prehtml avec une erreur */
  else if (!strcmp(fonction,"error")){
    snprintf(ligne,LONGUEUR_LIGNE,"echo %s",chaine);
    envoieshell(ligne);
//    recoitshell(ligne);
//    erreur("%s",ligne);
    lignetotale=malloc(1);
    i=0;
    while(recoitshell(ligne,0)) {
      j=i;
      i+=strlen(ligne);
      lignetotale=realloc(lignetotale,i+1);
      if (!lignetotale) erreur("Manque de mémoire: %s!%d",__FILE__,__LINE__);
      strcpy(lignetotale+j,ligne);      
    }
    erreur("%s",lignetotale);
    free(lignetotale);
  }
  /* <WARNING texte écrit comme pour ECHO>: affiche un avertissement */
  else if (!strcmp(fonction,"warning")){
    // bug retiré le 24/05/2003: pourquoi diminuer le niveau de "chut"???
    //    if (chut) chut--;
    snprintf(ligne,LONGUEUR_LIGNE,"echo %s",chaine);
    envoieshell(ligne);
//    while(recoitshell(ligne))
//      warning("%s",ligne);
    lignetotale=malloc(1);
    i=0;
    while(recoitshell(ligne,0)) {
      j=i;
      i+=strlen(ligne);
      lignetotale=realloc(lignetotale,i+1);
      if (!lignetotale) erreur("Manque de mémoire: %s!%d",__FILE__,__LINE__);
      strcpy(lignetotale+j,ligne);      
    }
    warning("%s",lignetotale);
    free(lignetotale);

  
  
  }
  /* <TAG texte écrit comme pour ECHO>: produit un TAG HTML */
  else if (!strcmp(fonction,"tag")){
    snprintf(ligne,LONGUEUR_LIGNE,"echo %s",chaine);
    envoieshell(ligne);
    while(recoitshell(ligne,1)) { // ici on supprime les fins de ligne: à chaque fois ça donne un <TAG>
      result=recodeHTML(ligne);
      print("<%s>",result);
      free(result);
    }
  }
  /************** FONCTIONS IMBRIQUÉES *****************/
  /* <IF condition de if_shell, sans ";"> ... [ <ELSE> ... ] <FI>  */
  else if (!strcmp(fonction,"if")){
    i=vraifaux("if",chaine);
    ouvre_imbrique();
    imbriqueactu->type=IF;
    imbriqueactu->info.pourif.estvrai=i;
    if (!i){ /* la condition est fausse */
      if (!attend("else")){ /* on a trouvé <fi> avant de trouver <else> */
	referme_imbrique();
      }
      /* sinon, c'est qu'on a trouvé <else>, donc on exécute */
    }
    /* sinon, c'est que la condition est vraie, donc on exécute */
  }
  else if (!strcmp(fonction,"else")){
    if (imbriqueactu->type != IF || !imbriqueactu->info.pourif.estvrai){
      /* on a <else> et on n'était pas dans <if>, ou on est déjà ds <else>*/
      erreur("<else> inattendu");
    }
    attend("fi"); /* si le <else> est correct, on le saute jusqu'à <fi> */
    referme_imbrique();
  }
  else if (!strcmp(fonction,"fi")){
    if (imbriqueactu->type != IF ){
      /* on a <fi> et on n'était pas dans <if> */
      erreur("<fi> inattendu");
    }
    referme_imbrique();
  }
  /* <FOR variable IN liste telle qu'avec echo> blabla <DONE> */
  else if (!strcmp(fonction,"for")){
    ouvre_imbrique();
    imbriqueactu->type=FOR;
    cefor=&(imbriqueactu->info.pourfor);
    cefor->fichier=quelfichier();
    cefor->pos=queloffset();
    cefor->ligne=quelleligne();
    cefor->numval=0;
    snprintf(ligne,LONGUEUR_LIGNE,"liste_shell %s",chaine);
    envoieshell(ligne);
    if (!recoitshell(ligne,1)){ // ici on supprime les fins de ligne: chaque ligne est un argument
      erreur("Pas d'argument dans <for>: <FOR %s>",chaine);
    }
    cefor->var=strdup(ligne);
    i=1;
    if (recoitshell(ligne,1)) if (!strcasecmp(ligne,"in")) i=0;
    if (i){
      erreur("Syntaxe: <FOR variable IN liste>: <FOR %s>",chaine);
    }
    i=0;/*nombre de valeurs rentrées*/
    liste=monalloc(sizeof(char *));
    while(recoitshell(ligne,1)){
      liste[i++]=strdup(ligne);
      liste=realloc(liste,sizeof(char *)*(i+1));
      if (!liste) {erreur("liste for: pas assez de mémoire");
      }
    }
    liste[i]=NULL;
    imbriqueactu->info.pourfor.liste=liste;
    if(liste[0]){
      snprintf(ligne,LONGUEUR_LIGNE,"read -r %s <<%s\n%s\n%s",cefor->var,
	      fin_read,liste[0],fin_read);
      execshell(ligne);
    }
    else{ /*for variable in <liste vide>: d'accord, on ne fait rien*/
      referme_imbrique();
      attend("done");
    }
  }
  /* <WHILE condition_vraie> .... <DONE> */
  else if (!strcmp(fonction,"while")){
    ouvre_imbrique();
    imbriqueactu->type=WHILE;
    cewhile=&(imbriqueactu->info.pourwhile);
    cewhile->fichier=quelfichier();
    cewhile->pos=queloffset();
    cewhile->ligne=quelleligne();
    cewhile->condition=strdup(chaine);
    if (!vraifaux("while",chaine)){ /* si c'est faux, je saute à <DONE> */
      attend("done");
      referme_imbrique();
    }
  }
    
  /* <DONE>, qui termine <FOR> ou bien <WHILE> */
  else if (!strcmp(fonction,"done")){
    if (imbriqueactu->type==FOR){
      cefor=&(imbriqueactu->info.pourfor);
      cefor->numval++;
      if (!cefor->liste[cefor->numval]){ /* on a fini la liste*/
	referme_imbrique();
      }else{ /* on passe à la valeur suivante, et on reboucle*/
	snprintf(ligne,LONGUEUR_LIGNE,"read -r %s <<%s\n%s\n%s",cefor->var,
		fin_read,cefor->liste[cefor->numval],fin_read);
	execshell(ligne);
	revient(cefor->fichier,cefor->pos,cefor->ligne);
      }
    }
    else if (imbriqueactu->type==WHILE){
      cewhile=&(imbriqueactu->info.pourwhile);
      if (vraifaux("while",cewhile->condition)){/* vrai, je retourne au while */
	revient(cewhile->fichier,cewhile->pos,cewhile->ligne);
      }else{ /* faux, je sors de l'imbrication */
	referme_imbrique();
      }
    }
  }
  /* <INCLUDE liste de fichiers> (format de cat) */
  /* et <REINCLUDE liste de fichiers> (idem mais recommence le fichier de
   *         sortie à zéro et inclut l'entête du fichier inclus */
  /* et <NOTINCLUDE liste de fichiers> (idem mais n'affiche pas le contenu) */
  /* <REINCLUDE> ne marche pas si on est déjà dans <NOTINCLUDE> */
  else if ((!strcmp(fonction,"reinclude")) && (!affiche)) {}
  else if ((!strcmp(fonction,"include")) || 
	   (!strcmp(fonction,"notinclude")) ||
	   (!strcmp(fonction,"reinclude")) ){
    ouvre_imbrique();
    imbriqueactu->type=INCLUDE;
    ceinc=&(imbriqueactu->info.pourinclude);
    ceinc->affiche=affiche;
    /* si on n'affichait pas, on n'affiche toujours pas, mais si on
     * affichait et qu'on trouve <notinclude>, on n'affiche plus */
    if (!strcmp(fonction,"notinclude")) affiche=0;
     /* J'interdisais <reinclude> dans les fichiers inclus, mais je ne le fais plus:
      * maintenant c'est autorisé sauf si on n'est pas en train d'afficher: reinclude
      * ne marche pas dans <notinclude>, mais marche dans <include> et <reinclude> */
    if ((!strcmp(fonction,"reinclude"))/* && (!fichier_courant->pere)*/)
      flag_reinclude=1;
    else
      flag_reinclude=0;
    snprintf(ligne,LONGUEUR_LIGNE,"liste_shell %s",chaine);
    envoieshell(ligne);
    i=0;/*nombre de valeurs rentrées*/
    liste=monalloc(sizeof(char *));
    while(recoitshell(ligne,1)){  // suppression des fins de ligne pour "liste_shell"
      liste[i++]=strdup(ligne);
      liste=realloc(liste,sizeof(char *)*(i+1));
      if (!liste) {
	erreur("liste [not|re]include: pas assez de mémoire");
      }
    }
    liste[i]=NULL;
    ceinc->liste=liste;
    ceinc->numfich=0;
    if(liste[0]) /* INCLUDE <fichier>... : on inclut le premier */
      inclure_fichier(liste[0]);
    else /* INCLUDE <liste vide>: d'accord, on ne fait rien*/
      referme_imbrique();
  }
  /* cas spéciaux du couple <GENERATE un fichier>....</GENERATE>
   * au lieu de créer le fichier du départ, crée une série de fichiers,
   * avec ces instructions mises dans une boucle */
  /* <GENERATE fichier> */
  else if (!strcmp(fonction,"generate")){
    snprintf(ligne,LONGUEUR_LIGNE,"echo %s",chaine);
    envoieshell(ligne);
    recoitshell(ligne,1);
    genere(ligne);
    if (recoitshell(ligne,1)) erreur("Trop de lignes dans la sortie de "
				   "<GENERATE %s>",chaine);
  }
  /* </GENERATE> */
  else if (!strcmp(fonction,"/generate")){
    degenere();
  }
    
  /* <FONCTION LISTÉE MAIS PAS ENCORE GÉRÉE > */    
  /* normalement, il n'y en a pas */
  
  /* <BODY> et </BODY>, pour n'inclure que le corps des fichiers */
  else if (!strcmp(fonction,"body")){
    /* rien à faire, si on est dans le main, sinon c'est anormal*/
    if (fichier_courant->pere)
      warning("balise <BODY> trouvée dans un fichier inclus: 2e exemplaire?");
  }
  else if (!strcmp(fonction,"/body")){
    /* si on est dans un fichier inclus, on sort du fichier */
    if (fichier_courant->pere)
      fseek(fichier_courant->fd,0,SEEK_END); /* le prochain car. sera EOF */
  }
  
  else erreur("Erreur interne: fonction <%s> (argument='%s') "
	      "listée mais pas gérée\n",fonction,chaine);
}
