/* signature.c - Dimitri Fontaine - fontaine@isty-info.uvsq.fr
 * On crée un tube nommé appellé ~/.signature.
 * A chaque lecture dans ce tube, on produit un nouveau texte.
 *
 * Si le fichier ~/.signature existe au lancement du programme,
 * son contenu sera le début du texte produit.
 *
 * On passe en argument un nom de fichier contenant des phrases
 * séparées par une ligne commencant par '%', l'une d'entre elles
 * (au hasard) sera choisie à chaque lecture du fichier de signature.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>

#include <fcntl.h>
#include <unistd.h>

#include <signal.h>

#define BUF		1024

struct phrases {
  char *p;
  struct phrases *s;
} *mes_phrases = NULL;

int nb_phrases = 0;		/* nombre de phrases chargées */

char sign[BUF];			/* nom du fichier de signature */
char signature[BUF]; 	        /* contenu de ce fichier au lancement */
char fichier[BUF];		/* fichier contenant les phrases */

void lecture();			/* lit le fichier de phrases */
void liste();			/* liste les phrases chargées */

void sortie(int code, char *msg);

void terminer(int sig);
void relecture(int sig);

int main(int argc, char **argv)
{
  char home[BUF], buf[BUF+BUF];
  int sig_fd, fd, nump;

  int cpt = 0;

  /* on arme les signaux */
  signal(SIGINT, terminer);
  signal(SIGTERM, terminer);

  signal(SIGHUP, relecture);
  signal(SIGUSR1, liste);

  /* attention aux arguments */
  if( argc != 2 || access(argv[1], R_OK) != 0 ) {
    sortie(2, "Et les phrases, elles sont où ?\n");
  }
  strcpy(fichier, argv[1]);

  /* init et création du tube nommé */

  strcpy(home, getenv("HOME"));
  sprintf(sign, "%s/.signature", home);

  strcpy(signature, "");

  /* Si le fichier ~/.signature existe déjà, on récupère
   * son contenu dans le buffer signature, puis on l'efface
   * afin de pouvoir créer le tube nommé.
   */
  if( access(sign, R_OK | W_OK) == 0 ) {
    fd = open(sign, O_RDONLY, 0);
    read(fd, signature, BUF);
    close(fd);

    unlink(sign);
  }

  /* on lit les phrases et on les stocke en mémoire */
  lecture();

  if( mkfifo(sign, 0644) != 0 )
    sortie(1, "Création du tube nommé impossible (droits ?)\n");

  printf("Fichier signature : %s\n", sign);

  /* boucle d'écriture */
  while( 1 ) {
    int i;
    struct phrases *tmp;

    /* choix de la phrase */
    nump = random() % nb_phrases;
    for(tmp = mes_phrases, i=0; i<nump; tmp=tmp->s, i++ );

    /* on crée la nouvelle signature */
    sprintf(buf, "%s\n%s", signature, tmp->p);

    /* ouverture du tube, on y écrit la nouvelle signature
     * et on ferme, on attends un peu que le lecteur du tube
     * il aie noté que c'est fini et on recommence
     */
    sig_fd = open(sign, O_WRONLY | O_TRUNC, 0);

    if( write(sig_fd, buf, strlen(buf)+1) == -1 )
      terminer(SIGINT);

    close(sig_fd);
    sleep(1);
  }

  /* on ne devrait pas atteindre ce code ! */
  unlink(sign);
  return 0;
}

/* Chargement des phrases 'fortunes' depuis le fichier
 * passé en argument, on les met dans la structure mes_phrases.
 */
void lecture()
{
  int i;
  FILE *fd;
  char *phrase = NULL, *buf;
  struct phrases *tmp;

  printf("Lecture des phrases depuis <%s>.\n", fichier);

  nb_phrases = 0;
  fd = fopen(fichier, "r");

  while( !feof(fd) ) {
    buf = (char *)malloc(BUF*sizeof(char));
    fgets(buf, BUF, fd);

    if( buf[0] == '%' && phrase != NULL ) {
      /* On a une phrase entière dans le buffer phrase */
      /* printf("##%03d##\n%s\n", nb_phrases, phrase); */

      tmp = (struct phrases *)malloc(sizeof(struct phrases));

      /* insertion en tete de liste.
       * vu que les phrases sont choisies au hasard,
       * l'ordre de stockage importe peu.
       */
      tmp->p = phrase;
      tmp->s = mes_phrases;

      mes_phrases = tmp;
      nb_phrases++;

      /* sans oublier de réinitialiser le buffer phrase */
      phrase = NULL;
    }
    else {
      if( buf[0] != '%' ) {
	/* On a le début d'une phrase dans le buffer buf */
	if( phrase == NULL ) {
	  /* On doit initialiser la nouvelle phrase */
	  phrase = (char *)malloc(BUF*sizeof(char));
	  phrase = strncpy(phrase, buf, strlen(buf)+1);
	}
	else {
	  /* On doit alors simplement ajouter la suite */
	  phrase = strncat(phrase, buf, strlen(buf)+1);
	}
      }
    }
  }
  fclose(fd);

  if( nb_phrases < 3 )
    printf("Ben t'iras pas loin avec %d phrases !\n", nb_phrases);
  else
    printf("%d phrases chargées\n", nb_phrases);
}

void sortie(int code, char *msg)
{
  fprintf(stderr, msg);
  exit(code);
}

void terminer(int sig)
{
  int fd;

  /* on détruit le tube nommé, et on le remplace
   * par un fichier régulier contenant ce qu'on y a trouvé
   * au démarrage.
   */
  unlink(sign);

  /* on restitue le fichier signature */
  fd = open(sign, O_WRONLY | O_CREAT, 0666);
  if( write(fd, signature, strlen(signature)) != -1 )
    printf("Fichier %s restitué. \n", sign);
  close(fd);

  exit(0);
}

void relecture(int sig)
{
  signal(SIGHUP, relecture);

  lecture();
}

void liste(int sig)
{
  struct phrases *tmp;

  signal(SIGUSR1, liste);

  for(tmp = mes_phrases; tmp!=NULL; tmp=tmp->s)
    printf("%s%\n", tmp->p);

}
