Esercizio 1, prova pratica 13/09/2013

From Sistemi Operativi
Jump to navigation Jump to search

Consegna([1]):

Lo scopo del programma che dovrete scrivere e' di confrontare fra loro i file di una directory, se ne trovate due (o piu') che
hanno lo stesso contenuto dovete unificarli. Alla fine del processo l'elenco dei file della directory deve rimanere invariato ma
i nomi dei file che avevano lo stesso contenuto devono essere link fisici che indicano lo stesso file.
In questo esercizio si richiede che l'intero contenuto dei file venga confrontato.

Soluzione di Maldus (ora con tabella hash!)

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>

#define SIZE 1024 

char BUFF1[SIZE] ;	/*buffer usati per leggere i file*/
char BUFF2[SIZE] ;

struct list{	/*struttura che uso per implementare la lista di file che hanno uguale somma crittografica*/
	struct dirent* ent ;
	struct list *next ;
	struct list *prev ;
};


inline void del(struct list* p){	/*rimuove p dalla lista in cui si trova*/
	(p->next)->prev = p->prev ;
	(p->prev)->next = p->next ;
	free(p) ;
}


int critsum( int f ){	/*somma crittografica per la tabella hash*/
	int i , n ;
	int x = 0 ;
	while(( n = read( f , BUFF1 , SIZE ) )> 0 ){
		for( i = 0 ; i < n ; i++ ){
			x += ((int) BUFF1[i])*((i%8)+1) ;	/*aggiungo ogni volta il valore numerico corrispondente al byte letto moltiplicato per un numero da 1 a 8*/
		}
	}
	if( x < 0) return -x ;/*restituisco sempre un valore positivo, perchè possa essere usato come indice di un vettore*/
	else return x ;
}
		

int buffcmp( char* buf1 , char* buf2 , int s1 , int s2 ){/*funzione che compara due buffer*/
	int i ;
	if( s1 != s2 ) return 1 ; /*se hanno lunghezza diversa sono diversi*/
	for( i = 0 ; i < s1 ; i++ ){
		if( buf1[i] != buf2[i] ) return 1 ;
	}
	return 0 ;
}


int filecmp( int f1 , int f2 ){	/*funzione ausiliaria che confronta due file. Si comporta come strcmp*/
	int n1 , n2 ;
	while(( n1 = read( f1 , BUFF1 , SIZE ) )> 0 ){	/*ho provato a inserire entrambe le read nel controllo del while con un &&; mi creava dei problemi, come se la seconda read venisse eseguita nel giro seguente del ciclo*/
		n2 = read( f2 , BUFF2 , SIZE ) ;
		if( buffcmp( BUFF1 , BUFF2 , n1 , n2 ) != 0 ) return 1 ;	/*ritorna 1 se i due file sono diversi*/
	}
	return 0 ;	/*ritorna 0 se sono uguali*/
}


void del_clones( struct list* p ){	/*cerca nella lista il cui primo elemento è p tutti i file con lo stesso contenuto*/
	struct list *aux1 , *aux2 , *tmp;
	int fd1, fd2 ;
	struct stat s1 , s2 ;
	aux1 = p ;
	aux2 = aux1->next ;
	while( aux1->next != p ){
		printf( "%s:\n" , (aux1->ent)->d_name ) ;
		if( stat( (aux1->ent)->d_name , &s1 ) != 0 ){
			perror("stat") ;
			exit(1) ;
		}
		if ( (fd1 = open((aux1->ent)->d_name, O_RDONLY ) ) == -1){	/*apro la entry*/
			perror("open") ;
			exit(1) ;
		}
		
		while( (aux2->next) != p && (aux2 != p) ){
			tmp = aux2->next ;
			if( stat( (aux2->ent)->d_name , &s2 ) != 0 ){
				perror("stat") ;
				exit(1) ;
			}
			if ( (fd2 = open((aux2->ent)->d_name, O_RDONLY ) ) == -1){	/*apro la entry*/
				perror("open") ;
				exit(1) ;
			}
			printf("\t%s" , (aux2->ent)->d_name) ;
			if( s1.st_ino != s2.st_ino){
				if( filecmp( fd1 , fd2 ) == 0 ){	/*controllo se i due file hanno lo stesso contenuto*/
						printf("<-uguale\t") ;
						unlink((aux2->ent)->d_name) ;	/*rimuovo il file uguale*/
						if( link( (aux1->ent)->d_name , (aux2->ent)->d_name) == -1 ){	/*creo un link fisico*/
							perror("link") ;
							exit(1) ;
						}
						del(aux2) ;
					}
			}
			else del(aux2) ;	/*se hanno lo stesso i-node toglili dalla lista*/
			aux2 = tmp;	/*tmp = aux2->next. Lo salvo in precedenza nel caso in cui aux2 sia stato cancellato*/
			close(fd2) ;
		}
		printf("\n") ;
		close(fd1) ;
		aux1 = aux1->next ;
		
	} 
}

int main( int argc, char* argv[]){
	struct stat s1 , s2;	/*struttura di informazioni su un file*/
	DIR *dir  ;	/*puntatore a directory*/
	struct dirent *entry ;	/*struttura di informazioni sulle entry di una directory*/
	int fd1;	/*descrittore di file*/
	char *name ;
	int n = 0 ; /*contatore di file all'interno della directory*/
	int cs , i ; /*cs è somma crittografica del file*/
	struct list *hash ;
	struct list *tmp ;
	if( stat( argv[1] , &s1 ) != 0 ){
		perror("stat") ;
		return 1 ;
	}
	if( !S_ISDIR(s1.st_mode) ){
		printf( "argument is not a valid directory\n" ) ;
		return 0 ;
	}
	if( (dir = opendir( argv[1] )) == NULL ){
		perror("opendir") ;
		return 1 ;
	}
	if( chdir( argv[1] ) == -1 ){	/*sposto la directory corrente in quella di esecuzione per poter aprire i file*/
		perror("chdir") ;
		return 1 ;
	}
	while( (entry = readdir(dir) ) != NULL){	/*conto i file contenuti nella directory*/
		stat( entry->d_name , &s1 ) ;
		if( S_ISREG(s1.st_mode) ) n++ ;	/*se si tratta di un file normale contalo*/
	}
	printf("La directory contiene %i file\n" , n ) ;
	n = 2*n ;	/*da questo momento n è il numero di indici nella mia hashtable*/
	hash = (struct list*) malloc( sizeof(struct list)*n) ;	/*alloco una hash table con n = 2n indici*/
	rewinddir(dir) ;
	for( i = 0 ; i < n ; i++ ){	/*inizializzo le liste*/
		hash[i].next = &hash[i] ;
		hash[i].prev = &hash[i] ;
		hash[i].ent = NULL ;
	}
	while( (entry = readdir(dir) ) != NULL){	/*con questo ciclo riempo la hashtable*/
		stat( entry->d_name , &s1 ) ;
		if( S_ISREG(s1.st_mode) ){	/*se si tratta di un file normale consideralo*/
			if ( (fd1 = open(entry->d_name, O_RDONLY ) ) == -1){	/*apro la entry*/
				perror("open") ;
				exit(1) ;
			}
			cs = critsum( fd1 ) ;
			tmp = (struct list*) malloc( sizeof(struct list) ) ;
			tmp->ent = entry ;
			(hash[cs%n].next)->prev = tmp ;	/*inserisco il file in testa alla lista di indice cs%n*/
			tmp->prev = &hash[cs%n] ;
			tmp->next = hash[cs%n].next ;
			hash[cs%n].next = tmp ;
			close(fd1) ;
		}
	}
	for( i = 0 ; i < n ; i++ ){	/*con questo ciclo cancello effettivamente i file uguali*/
		printf("%i: " , i ) ;
		if( hash[i].next != &hash[i] ){
			del_clones( hash[i].next ) ;
		}
		else printf("\n") ;
	}
	return 0 ;
}