/*
  (c) Yann Guidon 2000 (whygee@f-cpu.org)

  fichier créé le 21 octobre 2000

gdups4.c : ajout de l'option -x (exclude) pour
eviter de balayer des repertoires specifies.
ajout de -v pour afficher ou non les messages
a l'ecran.

gdups3.c : portage pour SunOS du 30/11/2000,
reparation d'une petite erreur et passage
de la taille d'un fichier vers off_t.
Maintenant, ca a l'air assez stable.

gdups2.c : version préliminaire de GDUPS
sans le rapport final. Le nom est la contractions
de "GNU DUPlicates", il cherche des fichiers
en double en explorant récursivement les 
sous-répertoires du répertoire courant. Il dispose
aussi d'une liste de répertoires à ignorer.
Ce fichier est une inclusion
des fichiers signature.c, arbres3.c et explore5.c
dans un seul source monolithique plus simple.


Pour compiler (mode autonome) :
   gcc -o gdups gdups4.c
(options -g, -ansi, -Wall et -Ox à volonté)

Utilisation :
   $ gdups > dups.txt
(pour ne pas mélanger le rapport avec les messages)
options : -v : "verbose"
  -x path/ : exclue path/ de la recherche

Pour plus d'informations :
   voir les fichiers sources du répertoire "exemples"


 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 */

/************************************
 Définitions des types et tailles : *
 ************************************/

#define MAXPATHSIZE 255 /* taille maximale du nom d'un fichier */
#define CRC_BUFFER_CHUNK_SIZE 65536 /* peut être changé */
#define TAILLE_BLOC 524288
/* 512KO devraient suffire pour avoir peu de blocs si on scanne
   tout un disque dur. Réduire ou augmenter si nécessaire. */

/***************************************
 * inclusion des librairies standard : *
 ***************************************/

#include <stdio.h>      /* pour printf, fprintf, fopen, fread, fclose */
#include <dirent.h>     /* pour readdir, opendir */
#include <sys/types.h>
#include <time.h>       /* pour ctime */
/* #include <sys/dir.h> : enlevé pour SunOS */
#include <sys/stat.h>   /* pour lstat */
#include <unistd.h>
#include <string.h>     /* pour strcmp, memcpy */
#include <stdlib.h>     /* pour malloc */

/* Remarque :
Les variables et constantes globales sont souvent
réunies et classées par ordre décroissant de taille
pour favoriser l'alignement des données. */

/************************************
 * Variables globales pour le CRC : *
 ************************************/

unsigned long int
 CRC32tab[256] = { /* table CRC32 de  poly=0x04C11DB7*/
   0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
   0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
   0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
   0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
   0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
   0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
   0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
   0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
   0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
   0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
   0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
   0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
   0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
   0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
   0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
   0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
   0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
   0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
   0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
   0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
   0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
   0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
   0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
   0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
   0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
   0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
   0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
   0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
   0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
   0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
   0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
   0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
   0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
   0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
   0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
   0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
   0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
   0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
   0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
   0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
   0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
   0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
   0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };

unsigned long int
 CRC_val,
 CRCSeed = 0xffffffff;

unsigned char *CRC_buffer=NULL;

/**********************************
 * Variables globales générales : *
 **********************************/

char *avancement[]={
  "-%c",
  "\%c",
  "|%c",
  "/%c"
};
int rotation=0;
int verbose=0;

char *CRCstring[]={
  "c%c",
  "C%c"
};
int crc_avancement=0;

#define NB_FIXED_PATHS 5
char *const_chemins_interdits[NB_FIXED_PATHS]= {
  "/dev/",
  "/proc/",
  "/tmp/",
  "/usr/tmp/",
  "/var/tmp/"
};

char **chemins_interdits=const_chemins_interdits;
int max_profondeur_chemins_interdits=3,
    nb_chemins_interdits=NB_FIXED_PATHS;


/* Les données suivantes sont globales pour réduire l'occupation
   de la pile et éviter des opérations redondantes */
char chemin[MAXPATHSIZE];
struct stat state;
int taille_chemin, max_imbrication=MAXPATHSIZE; /* il ne peut y avoir plus
de répertoires que de caractères dans le chemin... */


/**************************************************
 * Variables globales pour le wrapper de malloc : *
 **************************************************/

int nb_blocs=0, nb_elements=0; /* nombre de blocs alloués et d'appels à mon_malloc */
char * bloc_courant; /* pointe sur le dernier bloc alloué */
int index_bloc_courant=TAILLE_BLOC; /* nombre d'octets occupés dans le bloc,
  initialisé afin de déclencher l'allocation dès le premier appel à mon_malloc() */


/***********************************************
 * Structures de données et variables globales *
 ***********************************************/

/* description d'un chemin */
struct noeud_chemin {
  /* Il sera possible d'ajouter des données à cet endroit. */

  unsigned char nom[0];      /* chaine de type Pascal : le caractère 0
est la taille de la chaine en ASCIIZ. La chaine commence donc
au caractère #1 et se termine par un zéro. Il faut toujours placer
cette partie à taille variable à la fin de la structure.
Un avantage important est que cela accélère la comparaison avec strcmp() ! */
};

/* description d'un fichier : */
struct noeud_fic {
  /* propriétés/caractéristiques du fichier : */
  off_t taille;  /* taille du fichier en octets  */
  time_t date; /* long int, défini par <time.h> et <bits/types.h> */
  long int CRC; /* signature, ignorée si arbre_taille_pareil==NULL */
  struct noeud_chemin *chemin; /* pointeur vers le nom du chemin */

  /* arbres entrelacés : */
  struct noeud_fic
    *arbre_taille_prec,
    *arbre_taille_pareil,
    *arbre_taille_suiv,
    *arbre_nom_prec,
    *arbre_nom_pareil,
    *arbre_nom_suiv;

  /* Il sera possible d'ajouter des données à cet endroit. */

  /* partie à taille variable du noeud, située à la fin : */
  unsigned char nom[0];      /* nom du fichier, voir plus haut pour le format */  
};

/* arbre du descripteur pour l'arbre de fichiers identiques : */
struct noeud_arbre_id {
  /* arbres d'arbres d'arbres entrelacés : */
  struct noeud_arbre_id *arbre_prec, *arbre_suiv;
  struct noeud_fic *noeud;
};


/****************************************
 * Variables globales pour les arbres : *
 ****************************************/

/* arbre de tri des tailles et des noms */
struct noeud_fic
  * racine_arbre_nom[MAXPATHSIZE], /* pré-hachage */
  * racine_arbre_taille=NULL;

/* arbre des descripteurs identiques */
struct noeud_arbre_id
  * racine_taille_id=NULL,
  * racine_nom_id=NULL;


/***************************
 * CRC32, version inline : *
 ***************************/

/*
algorithme de base en Pascal :

vCRC32 := CRCSeed;
For P := 1 to Size DO
  vCRC32 :=CRC32tab[Byte(vCRC32 xor LongInt(Bytes[P]))] xor ((vCRC32 shr 8) and 0x00ffffff);
vCRC32 := vCRC32 xor CRCSeed;
(on peut oublier la dernière ligne, ça change la signature mais
deux fichiers identiques auront quand même une signature identique)

*/

void calcule_CRC(char *nom_fic){
  unsigned char *c;
  unsigned int i,taille;
  FILE *in;

  if (CRC_buffer==NULL) {
    /* initialisation du buffer */
    CRC_buffer=malloc(CRC_BUFFER_CHUNK_SIZE);
    if (CRC_buffer==NULL) {
      fprintf(stderr,"\nerreur de malloc() dans calcule_CRC\n");
      exit(6);
    }
  }
  CRC_val=CRCSeed;

  in=fopen(nom_fic,"rb");
  if (in==NULL) {
    printf("CRC impossible, lecture de %s refusée\n",nom_fic);
    return;
  }
  if (verbose)
    fprintf(stderr,"CRCing %s                             %c",nom_fic,0xD);

  while ((taille=fread(CRC_buffer,1,CRC_BUFFER_CHUNK_SIZE,in))!=0) {
    fprintf(stderr,CRCstring[crc_avancement],0xD);
    fflush(stderr);
    crc_avancement^=1;
    c=CRC_buffer;
    while (taille--) {
      i=(CRC_val&255)^(*c++);
      CRC_val=CRC32tab[i]^(CRC_val>>8);
    }
  }
  fclose(in);

  if (verbose) {
    fprintf(stderr,"                                                      %c",0xD);
  } else {
    fprintf(stderr,avancement[rotation],0xD);
    rotation=(rotation+1)&3;
  }
  fflush(stderr);
}


/*****************************************************************
 * Gestion fine de l'allocation de la mémoire (sans libération)  *
 *****************************************************************/

void * mon_malloc(int taille) {
  void *temporaire;
  nb_elements++;
  taille=(taille+7) & -8; /* arrondit la taille aux 8 octets supérieurs
       afin d'éviter des fautes de pointeurs sur les architectures 64 bits */

  /* ce type d'erreur ne devrait jamais arriver mais on ne sait jamais... */
  if (taille > TAILLE_BLOC) {
    fprintf(stderr,"\n erreur : bloc trop gros\n");
    exit(4);
  }

  index_bloc_courant+=taille;
  if (index_bloc_courant > TAILLE_BLOC) {
    bloc_courant=malloc(TAILLE_BLOC);
    if (bloc_courant == NULL) {
      fprintf(stderr,"\n erreur de malloc\n");
      exit(5);
    }
    nb_blocs++;
    index_bloc_courant=0;
  }

  temporaire=bloc_courant+index_bloc_courant;
  index_bloc_courant+=taille;
  return temporaire;
}


/****************************************
 * Création en mémoire des descripteurs *
 ****************************************/


struct noeud_chemin * cree_noeud_chemin(
 char * nom_fic, int taille_nom) {
  struct noeud_chemin * chemin_courant
      =mon_malloc(sizeof(struct noeud_chemin)+taille_nom+2);
  /* il ne faut pas oublier les caractères de début et de fin du nom */

  /* initialisation des données du noeud : */
  chemin_courant->nom[0]=taille_nom;
  memcpy((chemin_courant->nom)+1,nom_fic,taille_nom+1);
  return chemin_courant;
}

struct noeud_fic * cree_noeud_fic(char * nom_fic, int taille_nom,
  off_t taille_fic, struct noeud_chemin * chemin_courant) {
  struct noeud_fic * fichier_courant
      =mon_malloc(sizeof(struct noeud_fic)+taille_nom+2);
  /* il ne faut pas oublier les caractères de début et de fin du nom */

  /* initialisation des données du noeud : */
  memset(fichier_courant,0,sizeof (*fichier_courant));
  fichier_courant->taille=taille_fic; /* taille du fichier en octets  */
  fichier_courant->date=state.st_mtime; /* date de dernière modification du fichier */
  fichier_courant->chemin=chemin_courant; /* pointeur vers le nom du chemin */

  fichier_courant->nom[0]=taille_nom;
  memcpy((fichier_courant->nom)+1,nom_fic,taille_nom+1);
  return fichier_courant;
}

/* arbre des descriptions de fichiers identiques : */

struct noeud_arbre_id * cree_noeud_id (struct noeud_fic *noeud) {
  struct noeud_arbre_id *id_courant
     =mon_malloc(sizeof(struct noeud_arbre_id));
  id_courant->arbre_prec=NULL;
  id_courant->arbre_suiv=NULL;
  id_courant->noeud=noeud;
  return id_courant;
};


/**************************************************
 * crée l'arbre des fichiers de taille identique: *
 **************************************************/

void tri_taille_id(struct noeud_fic *noeud) {
  struct noeud_arbre_id
    *temp,
    *p=racine_taille_id,
    *id_courant=cree_noeud_id (noeud);
  long long int taille=noeud->taille;

  /* nous allons ici, en plus, calculer le CRC du fichier */
  char chemin2[MAXPATHSIZE];
  struct noeud_chemin *chemin3=noeud->chemin;
  int i=chemin3->nom[0];

  /* reconstitution du chemin */
  memcpy(chemin2,&(chemin3->nom[1]),i);
  memcpy(&(chemin2[i]),&(noeud->nom[1]),noeud->nom[0]+1);
  /* vérification du CRC : */
  calcule_CRC(chemin2);
  noeud->CRC=CRC_val;
  
  /* vérifie la racine : */
  if (p==NULL) {
    racine_taille_id=id_courant;
    return;
  }

  do {
    /* procédure de recherche itérative dans l'arbre : */

    /* pas besoin de test d'égalité (la fonction est appelée
       à chaque fois qu'une nouvelle taille apparait) */

    if (taille<(p->noeud)->taille) {
      /* branche inférieure : */
      temp=p->arbre_prec;
      if (temp==NULL) {
        p->arbre_prec=id_courant;
        return;
      }      
    } else {
      /* branche supérieure : */
      temp=p->arbre_suiv;
      if (temp==NULL) {
        p->arbre_suiv=id_courant;
        return;
      }
    }
    p=temp;
  }
  while (1);
}


/*********************************
 * tri des fichiers par taille : *
 *********************************
(appelé chaque fois qu'un fichier est
 scanné dans la fonction récursive principale)
*/

void tri_taille(struct noeud_fic *noeud) {
  long long int taille=noeud->taille;
  struct noeud_fic * temp,
    * noeud_courant=racine_arbre_taille;

  /* vérifie la racine : */
  if (noeud_courant==NULL) {
    racine_arbre_taille=noeud;
    return;
  }

  do {
    /* procédure de recherche itérative dans l'arbre : */
    if (taille==noeud_courant->taille) {
      /* vérification du CRC : */
      calcule_CRC(chemin); /* pour simplifier, on utilise la variable
        globale, mais on ne peut plus faire de mode de test autonome. */
      noeud->CRC=CRC_val;
      /* vérifie si c'est le seul élément de la liste chainée : */
      if (noeud_courant->arbre_taille_pareil==NULL)
	tri_taille_id(noeud_courant);
      else {
        /* cherche la fin de la liste chainée */
	/* à remplacer par un arbre de tri/CRC */
        do
	  noeud_courant=noeud_courant->arbre_taille_pareil;
        while (noeud_courant->arbre_taille_pareil!=NULL);
      }
      noeud_courant->arbre_taille_pareil=noeud;
      return;
    } else {
      /* sélection du chemin dans l'arbre : */
      if (taille<noeud_courant->taille) {
	/* branche inférieure : */
	temp=noeud_courant->arbre_taille_prec;
	if (temp==NULL) {
	  noeud_courant->arbre_taille_prec=noeud;
	  return;
	}      
      } else {
	/* branche supérieure : */
	temp=noeud_courant->arbre_taille_suiv;
	if (temp==NULL) {
	  noeud_courant->arbre_taille_suiv=noeud;
	  return;
	}
      }
      noeud_courant=temp;
    }
  }
  while (1);
}

/*************************************************
 * crée l'arbre des fichiers de noms identiques: *
 *************************************************
(dérivé de tri_taille_id())
*/
void tri_nom_id(struct noeud_fic *noeud) {
  struct noeud_arbre_id
    *temp,
    *p=racine_nom_id,
    *id_courant=cree_noeud_id (noeud);

  /* vérifie la racine : */
  if (p==NULL) {
    racine_nom_id=id_courant;
    return;
  }

  do {
    /* procédure de recherche itérative dans l'arbre : */

    /* pas besoin de test d'égalité (la fonction est appelée
       à chaque fois qu'une nouvelle taille apparait) */

    /* remarquer ici l'astuce qui permet de trier
       d'abord par la taille du nom puis sa valeur */
    if (strncmp(noeud->nom,p->noeud->nom,MAXPATHSIZE)<0) {
      /* branche inférieure : */
      temp=p->arbre_prec;
      if (temp==NULL) {
        p->arbre_prec=id_courant;
        return;
      }
    } else {
      /* branche supérieure : */
      temp=p->arbre_suiv;
      if (temp==NULL) {
        p->arbre_suiv=id_courant;
        return;
      }
    }
    p=temp;
  }
  while (1);
}


/******************
 * Tri par noms : *
 ******************
(dérivé de tri_taille())

   - remarquer que strncmp() n'est pas utilisé
 afin de pouvoir reprendre la comparaison
 au dernier endroit où les noms divergent.
   - remarquer aussi que nous utilisons un
 tableau de racines, afin de ne traiter que
 des chaines de caractères de même longueur.
 C'est un premier filtrage assez efficace.
*/

void tri_nom(struct noeud_fic *noeud) {
  int taille, /* taille du nom */
    index; /* début de la chaine de caractères */
  struct noeud_fic * temp, * noeud_courant;
  char *s1,*s2; /* variables temporaires pour comparer les noms */

  taille=noeud->nom[0];
  index=1;
  noeud_courant=racine_arbre_nom[taille];

  /* vérifie la racine : */
  if (noeud_courant==NULL) {
    racine_arbre_nom[taille]=noeud;
    return;
  }

  s1=noeud->nom;
  do { /* procédure de recherche itérative dans l'arbre : */
    s2=noeud_courant->nom;
    /* compare, caractère par caractère, le nom des fichiers : */
    while (s1[index]==s2[index]) {
      index++;
      if (index>taille) { /* nom identique */
	/* à cete endroit, on pourrait trier par la date ou le chemin. */
	/* La date serait mieux. Le chemin est déterminé par l'ordre de balayage. */

	/* vérifie si c'est le seul élément de la liste chainée : */
	if (noeud_courant->arbre_nom_pareil==NULL)
	/* crée un nouvel élément à la liste chainée d'éléments identiques */
	  tri_nom_id(noeud_courant);
	else {
        /* cherche la fin de la liste chainée */
	/* à remplacer par un arbre de tri/date */
	  do
	    noeud_courant=noeud_courant->arbre_nom_pareil;
	  while (noeud_courant->arbre_nom_pareil!=NULL);
	}
	noeud_courant->arbre_nom_pareil=noeud;
	return;
      }
    }

      /* sélection du chemin dans l'arbre : */
    if (s1[index]>s2[index]) { /* branche de gauche */
      temp=noeud_courant->arbre_nom_prec;
      if (temp==NULL) {
	noeud_courant->arbre_nom_prec=noeud;
	return;
      }  
    }
    /* le cas de l'égalité est déjà traité plus haut */
    else { /* branche de droite */
      temp=noeud_courant->arbre_nom_suiv;
      if (temp==NULL) {
	noeud_courant->arbre_nom_suiv=noeud;
	return;
      }
    }
    noeud_courant=temp;
  }
  while (1);
}


/***************************************
 * Fonctions d'impression du rapport : *
 ***************************************/

/* exploration récursive de l'arbre des tailles identiques */
void imprime_rapport_taille (struct noeud_arbre_id *noeud_taille) {
  struct noeud_fic *t=noeud_taille->noeud;
  char *date;
  /* branche de gauche */
  if (noeud_taille->arbre_prec != NULL)
    imprime_rapport_taille (noeud_taille->arbre_prec);

  /* milieu : imprime la liste */
  printf(" * taille : %Ld\n",t->taille);
  do {
    date=ctime(&(t->date));
    date[strlen(date)-1]=0; /* enlève le \n */
    printf(date);
    if (t->CRC==0) /* CRC non calculé */
      printf(" |              ");
    else
      printf(" | CRC: %08lX",t->CRC);
    printf(" | %s%s\n",&(t->chemin->nom[1]),&(t->nom[1]));
    t=t->arbre_taille_pareil;
  }
  while (t != NULL);
  printf("\n");

  /* branche de droite */
  if (noeud_taille->arbre_suiv != NULL)
    imprime_rapport_taille (noeud_taille->arbre_suiv);
}

/* exploration récursive de l'arbre des noms identiques */
void imprime_rapport_noms (struct noeud_arbre_id *noeud_nom) {
  struct noeud_fic *t=noeud_nom->noeud;
  char *date;

  /* branche de gauche */
  if (noeud_nom->arbre_prec != NULL)
    imprime_rapport_noms (noeud_nom->arbre_prec);

  /* milieu : imprime la liste */
  printf(" * nom : %s\n",&(t->nom[1]));
  do {
    date=ctime(&(t->date));
    date[strlen(date)-1]=0; /* enlève le \n */
    printf(date);
    if (t->CRC==0) /* CRC non calculé */
      printf(" |              ");
    else
      printf(" | CRC: %08lX",t->CRC);
    printf(" |  chemin : %s\n",&(t->chemin->nom[1]));
    t=t->arbre_nom_pareil;
  }
  while (t != NULL);
  printf("\n");

  /* branche de droite */
  if (noeud_nom->arbre_suiv != NULL)
    imprime_rapport_noms (noeud_nom->arbre_suiv);
}

/* procédure principale du rapport : */
void imprime_rapport () {
  printf ("\n rapport d'analyse\n\n");

  /* 1 : arbre des tailles identiques : */
  if (racine_taille_id!=NULL) {
    printf ("*** fichiers de tailles identiques : ***\n\n");
    imprime_rapport_taille (racine_taille_id);
  }
  else
    printf ("*** pas de fichiers de tailles identiques ***\n\n");

  /* 2 : arbre des noms identiques : */
  if (racine_nom_id!=NULL) {
    printf ("*** fichiers aux noms identiques : ***\n\n");
    imprime_rapport_noms (racine_nom_id);
  }
  else
    printf ("*** pas de fichiers aux noms identiques ***\n\n");
}


/***************************************************
 * Fonctions principale de balayage des fichiers : *
 ***************************************************/

void explore_recursif(int imbrication) {
  /* variables locales : elles sont allouées à chaque appel
    et elles peuvent donc exister en de multiples exemplaires */
  int temp_taille_chemin, taille_nom,i,inc_slash=0;
  DIR *dir;
  char *nom_local;
  struct dirent *file; /* erreur ici, "direct" au lieu de "dirent" */
  struct noeud_chemin *chemin_local;
  struct noeud_fic *fichier_local;

  if (imbrication>max_imbrication) /* profondeur admise */
    return;

  if (verbose) {
    fprintf(stderr,"scanning %s                 %c",chemin,0xD);
  } else {
    fprintf(stderr,avancement[rotation],0xD);
    rotation=(rotation+1)&3;
  }
  fflush(stderr);

  temp_taille_chemin=taille_chemin; /* sauvegarde */
  if (taille_chemin!=1) { /* si on est déjà dans la racine, ne pas ajouter le '/' */
    chemin[taille_chemin++]='/';
    chemin[taille_chemin]=0;
    inc_slash=1;
  }

  /* vérification du chemin pour éviter les tests inutiles ou interdits */
  if (imbrication <= max_profondeur_chemins_interdits) {
    /* degré d'imbrication maximal concerné par les chemins interdits */
    i=0;
    do {
      if (strcmp(chemins_interdits[i],chemin)==0) {
	printf("%s ignoré\n",chemin);
	return;
      }
      i++;
    } while (i<nb_chemins_interdits);
  }

  /* ouverture du répertoire */
  if ((dir=opendir(chemin))==NULL) {
    printf("impossible d'ouvrir le réperoire %s\n",chemin);
    return;
  }

  chemin_local=cree_noeud_chemin(chemin,taille_chemin);

  /* lecture des noms de fichiers */
  while ((file=readdir(dir))!=NULL) {   /* tant qu'il y a des entrées */
    nom_local=file->d_name; /* renommage */

    /* filtre "." et ".." */
    if ((nom_local[0] == '.')
      && ((nom_local[1] == 0)    /* "." */
        || ((nom_local[1] == '.')
          &&(nom_local[2] == 0)))) {    /* ".." */
      /* ne rien faire */
    } else {

      /* concaténation, placée avant le stat() pour pouvoir
         interroger n'importe quel fichier hors du répertoire courant */
      taille_nom=strlen(nom_local);
      taille_chemin=temp_taille_chemin+taille_nom+inc_slash;  
      if (taille_chemin > MAXPATHSIZE) {
        fprintf(stderr,"\n erreur : chemin trop long :\n%s\n",chemin);
        exit(3);
      }
      memcpy(&chemin[temp_taille_chemin+inc_slash],nom_local,taille_nom+1);

      if (lstat(chemin,&state)==0) {   /* déterminer le type de fichier */
        if (S_ISREG(state.st_mode)) {  /* fichier normal */
	  fichier_local=cree_noeud_fic(nom_local,taille_nom,state.st_size,chemin_local);
	  tri_taille(fichier_local);
	  tri_nom(fichier_local);
	}
        else {
          if (S_ISDIR(state.st_mode))  /* répertoire */
	    explore_recursif(imbrication+1); /* appel récursif */
	}
      }
      else {
	printf("lstat impossible sur %s\n",chemin);
      }
    }
  }
  closedir(dir);

  taille_chemin=temp_taille_chemin; /* restaure */
  chemin[taille_chemin]=0;          /* nettoie le chemin */
}


/**********
 * MAIN : *
 **********/

int main(int argc, char **argv) {
  int i=0, j,k, nb_slash=0;
  /* lit le chemin courant */
  if (getcwd(chemin,MAXPATHSIZE)==NULL)
    return 1;
  printf("      GDUPS v0.1 beta  (C) Yann Guidon 2000\n");
  /* initialise la variable globale */
  taille_chemin=strlen(chemin);
  while (i<taille_chemin) {
    if (chemin[i++]=='/')
      nb_slash++;
  }

  /* lit les parametres de ligne de commande : */
  if (argc>1){
    i=1;
    do {
      if (strncmp(argv[i],"-v",3)==0) {
        verbose=1;
      }
      else {
        if (strncmp(argv[i],"-x",3)==0) {
	  i++;
	  if (chemins_interdits==const_chemins_interdits) {
	    /* premier chemin de la ligne de commande : on doit changer le pointeur. */
	    chemins_interdits=malloc(sizeof(const_chemins_interdits[0])*64); /* ca devrait suffire... */
	    if (chemins_interdits==NULL){
	      fprintf(stderr,"\nmalloc failed.\n");
	      return -1;
	    }
	    /* copie la liste des chemins interdits */
	    k=0;
            do {
              chemins_interdits[k]=const_chemins_interdits[k];
	      k++;
	    } while (k<NB_FIXED_PATHS);
	    chemins_interdits[k]=argv[i];
	  }
	  else {
	    /* on ajoute encore un chemin interdit */
            if (nb_chemins_interdits>64) {
	      fprintf(stderr,"\ntoo many '-x' arguments.\n");
	      return -1;
	    }
	    chemins_interdits[nb_chemins_interdits]=argv[i];
	  }

	  /* dans tous les cas : */
	  nb_chemins_interdits++;

	  /* compte le nombre de '/' */
	  j=0;
	  k=0;
	  while (argv[i][k]) {
	    if (argv[i][k++]=='/')
	      j++;
	  }

	  if (argv[i][k-1] !='/') {
	    fprintf(stderr,"\nsorry, '-x' arguments need a '/' at the end of the path.\n");
	    return -1;
	  }

	  if (j>max_profondeur_chemins_interdits)
	    max_profondeur_chemins_interdits=j;
	}
      }
      i++;
    } while (i<argc);
  }

  /* c'est parti ! */
  printf("\nExploration des sous-répertoires :\n");
  explore_recursif(nb_slash);
  imprime_rapport();
  printf("statistiques : %d octets alloués et %d éléments\n",nb_blocs*TAILLE_BLOC,nb_elements);
  return 0;
}

<div align="center"><br /><script type="text/javascript"><!--
google_ad_client = "pub-7293844627074885";
//468x60, Created at 07. 11. 25
google_ad_slot = "8619794253";
google_ad_width = 468;
google_ad_height = 60;
//--></script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script><br />&nbsp;</div>