#include "prehtml.h"

static int refermeauto=1; /* referme le fichier quand EOF rencontré ? */

int flag_reinclude=0; /* 1 si le <INCLUDE> est un <REINCLUDE> */
    
void *monalloc(size_t size){
  void *p;
  if (!(p=malloc(size))) erreur("monalloc(): pas assez de mémoire");
  return p;
}

fichier *fichier_courant=NULL;

void ouvrir_fichier(char *nom){ /* ouvre le premier fichier*/
  char ligne[LONGUEUR_LIGNE];
  if (fichier_courant) erreur("ouvrir_fichier: déjà ouvert");
  fichier_courant=monalloc(sizeof(fichier));
  if (!(fichier_courant->fd=fopen(nom,"rb"))) 
    erreur("ouvrir_fichier: Impossible d'ouvrir le fichier %s",nom);
  fichier_courant->pere=NULL;
  fichier_courant->nom_fichier=strdup(nom);
  fichier_courant->numero_ligne=1;
  snprintf(ligne,LONGUEUR_LIGNE,"file='%s'",nom); execshell(ligne);
  snprintf(ligne,LONGUEUR_LIGNE,"thisfile='%s'",nom); execshell(ligne);
}

void inclure_fichier(char *nom){ /* ouvre un fichier inclus*/
  char ligne[LONGUEUR_LIGNE];
  int i;
  fichier *pere;

  if (!fichier_courant) erreur("inclure_fichier: pas de fichier principal");
  pere=fichier_courant;
  fichier_courant=monalloc(sizeof(fichier));
  if (!(fichier_courant->fd=fopen(nom,"rb"))){
    fichier_courant=pere;
    erreur("inclure_fichier: Impossible d'ouvrir le fichier %s",nom);
  }
  fichier_courant->pere=pere;
  fichier_courant->nom_fichier=strdup(nom);
  fichier_courant->numero_ligne=1;
  snprintf(ligne,LONGUEUR_LIGNE,"thisfile='%s'",nom); execshell(ligne);
  if (!flag_reinclude){ /* inclusion normale */
    /* on cherche s'il y a un <BODY>, pour exclure l'entête */
    do{
      refermeauto=0; /* il ne faut pas que le fichier se referme si je trouve
		      * EOF (pour cause d'absence de <BODY>) */
      i=decode();
      refermeauto=1;
    }while (i!=EOF && (i!=EST_FONCTION || strcmp(fonction,"body")));
    fseek(fichier_courant->fd,0,SEEK_SET);
    fichier_courant->numero_ligne=1;
    if (i==EST_FONCTION){ /* il y a un <BODY>, on va le sauter */
      do{
	i=decode();
      }while (i!=EST_FONCTION || strcmp(fonction,"body"));
    }  /* s'il n'y en a pas, on reste au début du fichier */
  }
  else { /* inclusion spéciale avec <REINCLUDE> */
    /* Gestion du <GENERATE> : 
     * engenerate=0: pas dans <GENERATE>, rien de spécial à faire;
     * engenerate=1: dans <GENERATE>, tronquer le fichier initial à zéro
     * engenerate=2: après </GENERATE>, c'est une erreur */
    if (engenerate==2) 
      erreur("<REINCLUDE %s> rencontré après </GENERATE>,\n"
	     "   c'est interdit car sans intérêt",chaine);
    else if (engenerate==1)
      /* on tronque aussi le fichier commun du début, qui est fermé */
      truncate(fich_temp,0);
    /* on ne cherche pas <BODY> car on n'enlève pas l'entête */
    /* par contre, on revient au début du fichier de sortie */
    fflush(sortie); /* on vide le buffer */
    rewind(sortie); /* on revient au début */
    ftruncate(fileno(sortie),0); /* puis on efface tout */
  }
}

/*private*/ fichier *fermer_fichier(void){ /*renvoie le fichier père, ou NULL*/
  char ligne[LONGUEUR_LIGNE];
  fichier *pere;
  free(fichier_courant->nom_fichier);
  fclose(fichier_courant->fd);
  pere=fichier_courant->pere;
  free(fichier_courant);
  fichier_courant=pere;
  if (fichier_courant){
    snprintf(ligne,LONGUEUR_LIGNE,"thisfile='%s'",fichier_courant->nom_fichier);
    execshell(ligne);
  }
  return pere;
}

/* private */ int lit_un_caractere(void){ /* lit un caractère du système de fichier*/
  int c;
  infoinclude *ceinc;
  while(1){
    c=getc(fichier_courant->fd);
    if (c=='\n') fichier_courant->numero_ligne ++;
    if (c!=EOF) return c;
    if (!refermeauto) return c; /* si on me demande de renvoyer EOF sans
				 * rien faire d'autre, j'obéis */
    fermer_fichier(); /* c'est EOF, on ferme le fichier */
    /* en fait ce serait mieux de gérer les fichiers inclus
     * entièrement comme une imbrication, et EOF comme une balise,
     * mais je n'ai pas envie de tout reprendre */
    if (fichier_courant==NULL){
      if (imbriqueactu->type==MAIN)
	return c; /* c'était vraiment le top-niveau, EOF signale la fin */
      else erreur("Dernier EOF rencontré à l'intérieur d'un <%s>",
		  typeimbrique(imbriqueactu));
    }
    /* si je suis là, c'est EOF et il y a un fichier au-dessus */
    if (imbriqueactu->type==MAIN) 
      erreur("lit_un_caractere(): Erreur interne: je suis dans "
	     "l'imbrication MAIN et je n'ai aucun fichier au-dessus de moi");
    if (imbriqueactu->type!=INCLUDE)
      erreur("fin d'un fichier inclus rencontrée à l'intérieur d'un <%s>",
	     typeimbrique(imbriqueactu));
    /* OK, c'est l'EOF d'un fichier inclus, je traite l'imbrication INCLUDE*/
    ceinc=&(imbriqueactu->info.pourinclude);
    ceinc->numfich++;
    if (ceinc->liste[ceinc->numfich]) /* ce n'est pas le dernier...*/
      inclure_fichier(ceinc->liste[ceinc->numfich]); /* ...au suivant */
    else{ /* là, c'était le dernier */
      affiche=ceinc->affiche;
      referme_imbrique();
    }
    /* si on est toujours là, c'est qu'il faut retenter de lire un caractère */
  }
}

static int stockage=0; /* en train de stocker? */
static int destockage=0; /* en train de lire une chaine stockee? */
static char *chainestock;
static int longueur,plitchaine,pecritchaine,nbcarchaine=0;
/*longueur=espace réservé pour chainestock, plitchaine=pointeur pour
 * le déstockage, pecritchaine=pointeur pour le stockage,
 * nbcarchaine=nb de caractères effectivement stockés*/
static int eof=0; /* EOF rencontré pendant le stockage? */

int mongetc(void){
  int c;
  if (destockage){
    c=chainestock[plitchaine++];
    if (plitchaine==nbcarchaine) {
      destockage=0;
      free(chainestock);
    }
  }
  else { 
    if (eof) c=EOF; else c=lit_un_caractere();
  }
  if (stockage) {
    if (c==EOF) eof=1;
    else{
      chainestock[pecritchaine++]=c;
      if (pecritchaine>nbcarchaine) nbcarchaine=pecritchaine;
      if (nbcarchaine>=longueur-3){
	chainestock=realloc(chainestock,longueur=(longueur+LONGUEUR_LIGNE));
	if (!chainestock) 
	  erreur("mongetc(): pas assez de mémoire pour le stockage()");
      }
    }
  }
  return c;
}

void stocke(void){ /* commence à accumuler des caractères, pour pouvoir
		    * les ressortir si la fonction qu'on croyait
		    * reconnaître n'était pas celle que l'on croyait*/
  if (stockage) erreur("stocke(): erreur car stockage est déjà vrai");
  stockage=1; eof=0;
  if (!destockage){ /* on est en train de lire depuis le fichier*/
    chainestock=monalloc((longueur=LONGUEUR_LIGNE));
    pecritchaine=0;
    nbcarchaine=0;
  }else{ /* on est déjà en train de lire la chaine stockee */
    /*on décale la chaine pour que ça commence bien au début*/
    /* (permet à destockage() de bien marcher */
    nbcarchaine-=plitchaine;
    memmove(chainestock,chainestock+plitchaine,nbcarchaine);
    pecritchaine=0;
    plitchaine=0;
  }
}

void finstocke(void){ /* élimine les caractères stockés */
  if (!stockage) erreur("finstocke(): erreur car stockage est faux");
  stockage=0;
  if (!destockage) /* on lisait depuis le fichier: la chaine doit être éliminée*/
    free(chainestock);
  /* sinon, on lisait déjà une chaine stockée: rien à faire, on continue*/
}

void destocke(void){ /* repart à l'endroit où on a stocké */
  if (!stockage) erreur("destocke(): erreur car stockage est faux");
  stockage=0;
  destockage=1;
  plitchaine=0;
}

/*private*/ void dit_ou_pere(fichier *fich){
  if (fich->pere){ /*celui-ci non plus n'est pas le top-niveau*/
    dit_ou_pere(fich->pere);
    fprintf(stderr,"  ");
  }
  fprintf(stderr,"dans fichier inclus depuis %s:%ld:\n",
	  fich->nom_fichier,fich->numero_ligne);
}

void dit_ou(void){ /* dit où on est dans le fichier, et via quelles
		    * inclusions, pour faire message d'erreur;
		    * sort dans stderr et n'inclut pas de '\n'*/
  if (destockage)
    fprintf(stderr,"attention: position approximative car en destockage\n");
  if (!fichier_courant) {
    fprintf(stderr,"à EOF ou fichier non ouvert: ");
    return;
  }
  if (fichier_courant->pere){ /* on est dans un fichier inclus*/
    dit_ou_pere(fichier_courant->pere);
    fprintf(stderr,"  ");
  }
  fprintf(stderr,"%s:%ld: ",fichier_courant->nom_fichier,
	  fichier_courant->numero_ligne);
}  

FILE *quelfichier(void){ /* dit dans quel fichier on se trouve */
  return fichier_courant->fd;
}

off_t queloffset(void){ /* donne la position actuelle dans le fichier */
  /* on gère le destockage mais normalement ça ne devrait jamais arriver */
  return ftell(fichier_courant->fd)+(destockage?plitchaine:0);
}

int quelleligne(void){ /* idem: dit à quelle ligne on est */
  return fichier_courant->numero_ligne;
}
  
void revient(FILE *fich,off_t pos,int ligne){ /* tente de revenir, mais vérifie
				     * qu'on est dans le même fichier*/
  if (fichier_courant->fd != fich){
    erreur("<%s>: on n'est plus dans le même fichier qu'au début!",fonction);
  }
  fseek(fich,pos,SEEK_SET);
  fichier_courant->numero_ligne=ligne;
  stockage=0;
  destockage=0;
}
