Difference between revisions of "Esercizio 1, prova pratica 13/09/2013"

From Sistemi Operativi
Jump to navigation Jump to search
(Created page with "Consegna: <source lang="text"> 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 c...")
 
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
Consegna:
+
Consegna([http://www.cs.unibo.it/~renzo/so/pratiche/2013.09.13.pdf]):
 
<source lang="text">
 
<source lang="text">
 
Lo scopo del programma che dovrete scrivere e' di confrontare fra loro i file di una directory, se ne trovate due (o piu') che
 
Lo scopo del programma che dovrete scrivere e' di confrontare fra loro i file di una directory, se ne trovate due (o piu') che
Line 7: Line 7:
 
</source>
 
</source>
  
==Soluzione di Maldus==
+
==Soluzione di Maldus (ora con tabella hash!)==
 
<source lang="c">
 
<source lang="c">
 
#include <stdio.h>
 
#include <stdio.h>
 
#include <unistd.h>
 
#include <unistd.h>
 
#include <errno.h>
 
#include <errno.h>
#include <limits.h>
 
 
#include <sys/types.h>
 
#include <sys/types.h>
 
#include <sys/stat.h>
 
#include <sys/stat.h>
 
#include <dirent.h>
 
#include <dirent.h>
 
#include <fcntl.h>
 
#include <fcntl.h>
#include <string.h>
 
 
#include <stdlib.h>
 
#include <stdlib.h>
+
 
#define SIZE 1024
+
#define SIZE 1024  
+
 
char buff1[SIZE] ; /*buffer usati per leggere i file*/
+
char BUFF1[SIZE] ; /*buffer usati per leggere i file*/
char buff2[SIZE] ;
+
char BUFF2[SIZE] ;
+
 
int filecmp( int f1 , int f2 ){ /*funzione ausiliaria che confronta due file. Si comporta come strcmp*/
+
struct list{ /*struttura che uso per implementare la lista di file che hanno uguale somma crittografica*/
    int n1 , n2 ;
+
struct dirent* ent ;
    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*/
+
struct list *next ;
        n2 = read( f2 , buff2 , SIZE ) ;
+
struct list *prev ;
        buff1[n1] = '\0' ;  /*per evitare che vengano letti anche residui di read precedenti, pongo lo 0 terminatore dove finiscono i caratteri letti in questa iterazione del ciclo*/
+
};
        buff2[n2] = '\0' ;
+
 
        if( strcmp(buff1 , buff2 )!= 0 ) return 1 ; /*ritorna 1 se i due file sono diversi*/
+
 
    }
+
inline void del(struct list* p){ /*rimuove p dalla lista in cui si trova*/
    return 0 ; /*ritorna 0 se sono uguali*/
+
(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[]){
 
int main( int argc, char* argv[]){
    struct stat s1 , s2;   /*struttura di informazioni su un file*/
+
struct stat s1 , s2; /*struttura di informazioni su un file*/
    DIR *dir1 , *dir2 ; /*puntatori a directory; con dir1 scorro le entry, e con dir2 le scorro di nuovo controllando se ci sono dei file con il contenuto uguale*/
+
DIR *dir  ; /*puntatore a directory*/
    struct dirent *entry_i ;   /*struttura di informazioni sulle entry di una directory*/
+
struct dirent *entry ; /*struttura di informazioni sulle entry di una directory*/
    int fd1, fd2 ; /*descrittori di file*/
+
int fd1; /*descrittore di file*/
    char *name ;
+
char *name ;
    if( stat( argv[1] , &s1 ) != 0 ){
+
int n = 0 ; /*contatore di file all'interno della directory*/
        perror("stat") ;
+
int cs , i ; /*cs è somma crittografica del file*/
        return 1 ;
+
struct list *hash ;
    }
+
struct list *tmp ;
    if( !S_ISDIR(s1.st_mode) ){
+
if( stat( argv[1] , &s1 ) != 0 ){
        printf( "argument is not a valid directory\n" ) ;
+
perror("stat") ;
        return 0 ;
+
return 1 ;
    }
+
}
    if( (dir1 = opendir( argv[1] )) == NULL ){
+
if( !S_ISDIR(s1.st_mode) ){
        perror("opendir") ;
+
printf( "argument is not a valid directory\n" ) ;
        return 1 ;
+
return 0 ;
    }
+
}
    if( (dir2 = opendir( argv[1] )) == NULL ){
+
if( (dir = opendir( argv[1] )) == NULL ){
        perror("opendir") ;
+
perror("opendir") ;
        return 1 ;
+
return 1 ;
    }
+
}
    if( chdir( argv[1] ) == -1 ){   /*sposto la directory corrente in quella di esecuzione per poter aprire i file*/
+
if( chdir( argv[1] ) == -1 ){ /*sposto la directory corrente in quella di esecuzione per poter aprire i file*/
        perror("chdir") ;
+
perror("chdir") ;
        return 1 ;
+
return 1 ;
    }
+
}
    while( (entry_i = readdir(dir1) ) != NULL){ /*scorro le entry della directory*/
+
while( (entry = readdir(dir) ) != NULL){ /*conto i file contenuti nella directory*/
        stat( entry_i->d_name , &s1 ) ;
+
stat( entry->d_name , &s1 ) ;
        if(!S_ISREG(s1.st_mode)) continue ; /*se la entry che sto considerando non è un file normale salta una iterazione */
+
if( S_ISREG(s1.st_mode) ) n++ ; /*se si tratta di un file normale contalo*/
        asprintf( &name , "%s" , entry_i->d_name ) ; /*salvo il nome della entry che sto considerando in name*/
+
}
        if ( (fd1 = open(name, O_RDONLY ) ) == -1){ /*apro la entry*/
+
printf("La directory contiene %i file\n" , n ) ;
            perror("open") ;
+
n = 2*n ; /*da questo momento n è il numero di indici nella mia hashtable*/
            exit(1) ;
+
hash = (struct list*) malloc( sizeof(struct list)*n) ; /*alloco una hash table con n = 2n indici*/
        }
+
rewinddir(dir) ;
        printf("1)%s %i\n" , name , (int) s1.st_ino ) ;
+
for( i = 0 ; i < n ; i++ ){ /*inizializzo le liste*/
        rewinddir( dir2 ) ; /*ogni volta devo ricontrollare tutte le entry della directory (complessità quadratica), quindi il puntatore del ciclo più interno deve ripartire dall'inizio ogni volta*/
+
hash[i].next = &hash[i] ;
        while( (entry_i = readdir(dir2) ) != NULL) {   /*secondo ciclo*/
+
hash[i].prev = &hash[i] ;
            lseek( fd1 , 0 , SEEK_SET ) ;  /*per poter indagare il contenuto della entry che sto considerando nel ciclo più esterno, faccio ripartire l'offset dall'inizio a ogni iterazione*/
+
hash[i].ent = NULL ;
            stat( entry_i->d_name , &s2 ) ;
+
}
            if(!S_ISREG(s2.st_mode)) continue ; /*come sopra*/
+
while( (entry = readdir(dir) ) != NULL){ /*con questo ciclo riempo la hashtable*/
            if( s1.st_ino != s2.st_ino){   /*se non si tratta di link fisici dello stesso file procedi*/
+
stat( entry->d_name , &s1 ) ;
                printf("2)%s %i\t" , entry_i->d_name , (int)s2.st_ino) ;
+
if( S_ISREG(s1.st_mode) ){ /*se si tratta di un file normale consideralo*/
                if( ( fd2 = open( entry_i->d_name, O_RDONLY ) ) == -1 ){
+
if ( (fd1 = open(entry->d_name, O_RDONLY ) ) == -1){ /*apro la entry*/
                    perror("open");
+
perror("open") ;
                    exit(1) ;
+
exit(1) ;
                }
+
}
                if(S_ISREG(s2.st_mode)){
+
cs = critsum( fd1 ) ;
                    if( filecmp( fd1 , fd2 ) == 0 ){    /*controllo se i due file hanno lo stesso contenuto*/
+
tmp = (struct list*) malloc( sizeof(struct list) ) ;
                        printf("<-uguali\n") ;
+
tmp->ent = entry ;
                        unlink(entry_i->d_name) ;   /*rimuovo il file uguale*/
+
(hash[cs%n].next)->prev = tmp ; /*inserisco il file in testa alla lista di indice cs%n*/
                        if( link( name , entry_i->d_name) == -1 ){   /*creo un link fisico*/
+
tmp->prev = &hash[cs%n] ;
                            perror("link") ;
+
tmp->next = hash[cs%n].next ;
                            return 1 ;
+
hash[cs%n].next = tmp ;
                        }
+
close(fd1) ;
                    }
+
}
                }
+
}
                close(fd2) ;
+
for( i = 0 ; i < n ; i++ ){ /*con questo ciclo cancello effettivamente i file uguali*/
            }
+
printf("%i: " , i ) ;
        }
+
if( hash[i].next != &hash[i] ){
        close(fd1) ;
+
del_clones( hash[i].next ) ;
        free( name ) ;
+
}
        printf("\n") ;
+
else printf("\n") ;
    }
+
}
+
return 0 ;
    return 0 ;
 
 
}
 
}
 
</source>
 
</source>
 
Il mio programma funziona correttamente finchè si tratta di gestire file di testo. Ho provato a creare copie di immagini (.png), ma le considera come file dal contenuto differente... Non riesco a capire se è normale così o se sto sbagliando qualcosa.
 
Inoltre, mi sembra di aver impiegato una quantità di tempo ( e codice ) spropositata considerando che si tratta solo del primo esercizio di una prova pratica; ci sono metodi più brevi per farlo?
 

Latest revision as of 21:55, 4 March 2015

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 ;
}