/* fichier copie.c
créé par Yann GUIDON (whygee@f-cpu.org)
version Mon Aug  1 13:14:06 CEST 2005

 fonction :
   copie un fichier le plus vite possible
   au moyen d'un gigantesque buffer !

 compilation :
   gcc -Wall -g -o copie copie.c

 invocation :
   copie nom_src nom_dest

*/

#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>  /* fprintf(), fflush() */
#include <stdlib.h> /* malloc() */
#include <unistd.h> /* exit(), read() */

#ifndef BLOCK_SIZE
#define BLOCK_SIZE (16*1024)
#endif

/* 18M*5s/2 /16384 */
#ifndef BLOCK_NUMBER
#define BLOCK_NUMBER (2746)
#endif

#define BUFFER_SIZE (BLOCK_SIZE*BLOCK_NUMBER)

int main (int argc, char *argv[]) {
  int fd_in, fd_out, i, j,
     t, u, v; /* taille du dernier bloc */
  char *buffer;

  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);
  }

  fd_in=open(argv[1], O_RDONLY);
  if (fd_in==-1) {
    perror("Erreur à l'ouverture de la source");
    exit(EXIT_FAILURE);
  }

  fd_out=open(argv[2], O_WRONLY|O_CREAT);
  if (fd_out==-1) {
    perror("Erreur à l'ouverture de la destination");
    exit(EXIT_FAILURE);
  }

  buffer=malloc(BUFFER_SIZE);
  if (buffer==NULL) {
    perror("Erreur de malloc() ");
    exit(EXIT_FAILURE);
  }

  while(1) {

/* remplissage du buffer */
    i=0;
    do {
      t=read(fd_in, buffer+(i*BLOCK_SIZE), BLOCK_SIZE);
      if (t<0) {
        perror("Erreur de lecture");
        exit(EXIT_FAILURE);
      }
    } while ((t==BLOCK_SIZE)
         && (++i<BLOCK_NUMBER));
        /* i n'est incrémenté que si (t==BLOCK_SIZE) */

/* vidange */
    j=0;
    do {
      if (j==i) { /* fin de la boucle interne */
        if (t==0)
          exit(EXIT_SUCCESS);
        else {
          u=t;
          t=BLOCK_SIZE;
/* t=0 serait plus radical (entrainant
 la sortie directe à la prochaine itération)
 mais man read dit que ce n'est pas nécessairement
 la fin d'un fichier si il retourne un bloc plus petit
 que demandé. */
        }
      }
      else
        u=BLOCK_SIZE;

      v=write(fd_out, buffer+(j*BLOCK_SIZE), u);
      if (u!=v) {
        perror("Erreur d'écriture");
        exit(EXIT_FAILURE);
      }

      j++;
    }
    while ((j<i) || (t!=BLOCK_SIZE));
     /* "off-by-one" s'il reste un morceau */
  }
}
