/* fichier pb.c
créé par Yann GUIDON (whygee@f-cpu.org)
version Sat Aug 6 19:28:37 CEST 2005

 fonction : "Pipe Buffer"
   prend un fichier, le bufferise puis le réécrit.

 compilation :
   gcc -D_REENTRANT -lpthread -o pb pb.c
 options:
      -DBUFFER_SIZE=taille_en_octets

 invocation :
   pb src dest [taille]

 src, dest : "-" si stdin/stdout, sinon : fichier standard
 taille (optionnelle) en Kilo Octets, défaut à 2MO
*/

#define _FILE_OFFSET_BITS 64
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>  /* open() */
#include <errno.h>  /* perror() */
#include <stdio.h>  /* printf() */
#include <stdlib.h> /* malloc(), strtol() */
#include <unistd.h> /* exit(), read() */
#include <signal.h> /* signal() */
#include <pthread.h>   /* threads */
#include <semaphore.h> /* semaphores */

#ifndef BLOCK_COUNT
#define BLOCK_COUNT (128)
#endif

#ifndef BLOCK_SIZE
#define BLOCK_SIZE (16*1024)
#endif

char *buffer;
int fd_in=0, fd_out=1,
  buffer_size, block_count=BLOCK_COUNT;
ssize_t t = 0, *byte_count;
sem_t sem_lu, sem_ecrit;
pthread_t thr_lecture;


void * thread_lecture (void * arg) {
  ssize_t t;
  int index=0, index_block=0;

  do {
    sem_wait(&sem_ecrit);
    t = read(fd_in, &buffer[index_block], BLOCK_SIZE);
    byte_count[index] = t;
    if (t < 0)
      perror("Erreur de lecture");
    else {
      index_block += BLOCK_SIZE;
      if (++index >= block_count)
        index = index_block = 0;
    }
    sem_post(&sem_lu);
  } while (t > 0);

  return NULL;
}


int main (int argc, char *argv[]) {
  int index=0, index_block=0;

  if (argc < 3) {
    printf("Erreur d'argument :\n\
 Spécifier un nom de fichier source et un nom de fichier destination.\n");
    exit(EXIT_FAILURE);
  }

  if ((argv[1][0]!='-') || (argv[1][1]!=0)) {
    fd_in = open(argv[1], O_RDONLY);
    if (fd_in == -1) {
      perror("Erreur à l'ouverture de la source");
      exit(EXIT_FAILURE);
    }
  }

  if ((argv[2][0]!='-') || (argv[2][1]!=0)) {
    fd_out = open(argv[2], O_WRONLY|O_CREAT);
    if (fd_out == -1) {
      perror("Erreur à l'ouverture du destinataire");
      exit(EXIT_FAILURE);
    }
  }

  if (argc > 3) {
    buffer_size=strtol(argv[3], NULL,10);
    if (buffer_size < 16) {
      printf("Erreur d'argument : taille invalide ?\n");
      exit(EXIT_FAILURE);
    }
    block_count = buffer_size / (BLOCK_SIZE>>10);
  }

  byte_count = malloc(block_count * sizeof(*byte_count));
  if (byte_count == NULL) {
    perror("Erreur de malloc() ");
    exit(EXIT_FAILURE);
  }

  buffer = malloc(block_count * BLOCK_SIZE);
  if (buffer == NULL) {
    perror("Erreur de malloc() ");
    exit(EXIT_FAILURE);
  }

  /* initialisation des sémaphores */
  sem_init(&sem_ecrit, 0, block_count);
  sem_init(&sem_lu, 0, 0);

  /* création du thread de lecture */
  if (pthread_create (&thr_lecture, NULL, thread_lecture, NULL)) {
    perror("Erreur à la création du thread de lecture");
    exit(EXIT_FAILURE);
  }

  while(1) {
    sem_wait(&sem_lu);
    t = byte_count[index];
    if (t > 0) {

      if (t != write(fd_out, &buffer[index_block], t)) {
        perror("Erreur d'écriture");
        exit(EXIT_FAILURE);
      }

      index_block += BLOCK_SIZE;
      if (++index >= block_count)
        index = index_block = 0;

      sem_post(&sem_ecrit);
    }
    else {
      pthread_join(thr_lecture, NULL);
      close(fd_in);
      close(fd_out);
      exit(EXIT_SUCCESS);
    }
  }
}
