aboutsummaryrefslogblamecommitdiff
path: root/flx/fct1.c
blob: c074a7a81a1c9d29e1d9dc66dbf96d33430deedc (plain) (tree)













































































                                                                                  


                                                                                  



















                                                                                
             

                                                                   


























































































































                                                                                      

                                                       









                                                         




                                                                 
























































































































                                                                                   
                                                    

                                               
                                                               



                                                                                                                     
                                           

                                                                                                               
                                            



                 




                                                                    










































































                                                                  
/* 
 * Signfs version 0.6, Copyright Benoit DOLEZ, 2002
 */

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

#include "flx.h"
#include "utils.h"


/* function that complete desc from file system informations */
t_file_desc *complete_info_from_file(char *path, t_file_desc *desc, int flag) {
    struct stat    stat;

    /* try to stat it */
    if (lstat(path, &stat)) {
	PFERROR("stat(%s)", path);
	return (0);
    }
    if (!desc) { 
	/* have to create it */
	desc = MALLOC(sizeof(*desc));
	bzero(desc, sizeof(*desc));
    }
    /* copy stat informations */
    memcpy(&desc->stat, &stat, sizeof(stat));
    
    if (S_ISREG(stat.st_mode) && !desc->md5)     /* get md5 checksum */
        desc->md5 = checksum_md5_from_file(path);
    else if (S_ISLNK(stat.st_mode) && !desc->link) { 
	/* get link and md5 associed */
        char temp[BUFFER_LENGTH];
        int  l;

        if ((l = readlink(path, temp, BUFFER_LENGTH)) < 0) {
            PFERROR("readlink(%s)", path);
        } else {
            temp[l] = 0;
            desc->link = strdup(temp);
            desc->md5 = checksum_md5_from_data(temp, l);
        }
    }   
    return (desc);
}



/* function that compare 2 tree entry, complete checksum and link if needed */
int  files_are_the_same(t_file_desc *f1, t_file_desc *f2, int Diff, char *path) {
    int diff = 0;
    
    // if (!IS(f1->status, STATUS_FILLED) || !IS(f2->status, STATUS_FILLED))
    //	return (diff = DIFF_NOTFILLED, 0);
    
    if (DIFF(TYPE)  && (f1->stat.st_mode & S_IFMT) != (f2->stat.st_mode & S_IFMT))
	diff |= DIFF_TYPE; /* types differ */
    if (DIFF(MODE)  && (f1->stat.st_mode & 07777) != (f2->stat.st_mode & 07777))
	diff |= DIFF_MODE; /* modes differ */
    if (DIFF(OWNER) && (f1->stat.st_uid != f2->stat.st_uid || 
			f1->stat.st_gid != f2->stat.st_gid))
	diff |= DIFF_OWNER; /* uid or gid differ */
    if (DIFF(SIZE) && S_ISREG(f1->stat.st_mode) && S_ISREG(f2->stat.st_mode) &&
	f1->stat.st_size != f2->stat.st_size)
	diff |= DIFF_SIZE; /* sizes differ for regular files */ 
    if (DIFF(DEV) && ((S_ISBLK(f1->stat.st_mode) && S_ISBLK(f2->stat.st_mode)) ||
		      (S_ISCHR(f1->stat.st_mode) && S_ISCHR(f2->stat.st_mode))) &&
	f1->stat.st_rdev != f2->stat.st_rdev)
	diff |= DIFF_DEV; /* minor/major differ for device files */
    if (DIFF(TIME) && f1->stat.st_mtime != f2->stat.st_mtime) {
	if (DIFF(DIR) || !S_ISDIR(f1->stat.st_mode) || !S_ISDIR(f2->stat.st_mode))
	    if (DIFF(LDATE) || !(S_ISLNK(f1->stat.st_mode)))
	        diff |= DIFF_TIME; /* modification times diff */
    }
    if (DIFF(LINK) && S_ISLNK(f1->stat.st_mode) && S_ISLNK(f2->stat.st_mode)) {
	char temp[BUFFER_LENGTH];
	int  l;
	
	if (f1->link != f2->link) {
	    if (!f1->link || !f2->link) {
		if (!path) diff |= DIFF_LINK;
		else {
		    /* rebuild link and link's checksum */
		    if ((l = readlink(path, temp, BUFFER_LENGTH)) < 0) {
			PFERROR("readlink(%s)",path);
		    } else {
			temp[l] = 0;
			if (!f1->link) f1->link = strdup(temp);
			if (!f2->link) f2->link = strdup(temp);
			if (!f1->md5) f1->md5 = checksum_md5_from_data(temp, l);
			if (!f2->md5) f2->md5 = checksum_md5_from_data(temp, l);
		    }
		}
	    }
	    if (!(diff & DIFF_LINK) && strcmp(f1->link, f2->link)) 
		diff |= DIFF_LINK; /* links differ */
	}
    }
    if (DIFF(CHECKSUM) && S_ISREG(f1->stat.st_mode) && S_ISREG(f2->stat.st_mode)) {
	if (f1->md5 || f2->md5) {
	    if (!f1->md5 || !f2->md5) diff |= DIFF_CHECKSUM;
	    else if (memcmp(f1->md5, f2->md5, 16))
		diff |= DIFF_CHECKSUM; /* checksums differ */
	}
    }
    return (diff);
}

/* return the path describe by the structure */
char *build_path(t_ft *tree) {
    static char path[BUFFER_LENGTH];

    if (!tree || !tree->filename)
	path[0] = 0;
    else if (tree->parent && 
	     (IS_PDOTF(tree) || !tree->parent->filename))
	strcpy(path, tree->filename);
    else {
	build_path(tree->parent);
	strcat(path, "/");
	strcat(path, tree->filename);
    } 
    return (path);
}


/* free memory for an existant tree 
 * tree : tree to clean
 */
void free_tree(t_tree *tree) {
    t_tree *current, *todel;
    
    if (tree->link) FREE(tree->link);
    if (tree->checksum) FREE(tree->checksum);
    if (tree->filename) FREE(tree->filename);
    if ((current = tree->subtree)) {
	while (current != tree) {
	    todel = current;
	    current = current->next;
	    todel->prev->next = todel->next;
	    todel->next->prev = todel->prev;
	    free_tree(todel);
	}
    }
    if (tree->subtree) FREE(tree->subtree);
    FREE(tree);
}

/* browse only directories */
/* path : directory to treat
 * fct  : fonction that treat file and return a (new) pointer to data to recursive act
 * data : pointer for function fct
 */
int browse_over_path(char *path, PROTO_FS(*fct), void *data) {
    struct stat    stat;
    DIR            *dir;
    struct dirent  *dirent;
    off_t          current_dir;
    char           *new_filename;
    int            l;

    if (!path) return (0);

    /* initialise new_filename */
    new_filename = MALLOC((l = strlen(path)) + 1 + FILENAME_LENGTH);
    memcpy(new_filename, path, l);
    if (new_filename[l-1] != '/') strcpy(new_filename + l++, "/");

    if ((dir = opendir(path))) {
	/* each file of the directory */
	while ((dirent = readdir(dir))) {
	    /* build new filename */
	    strcpy(new_filename + l, dirent->d_name);
	    
	    /* get file informations */
	    if (lstat(new_filename, &stat)) {
		PFERROR("stat(%s)", new_filename);
		continue;
	    }

	    /* directory selection */
	    if (S_ISDIR(stat.st_mode)) {
		void *new_data;
		/* save current directory position */
		current_dir = telldir(dir);
		closedir(dir);
		
		/* fonction on file status */
		if ((new_data = fct(new_filename, new_filename+l, &stat, data))) {

		    browse_over_path(new_filename, fct, new_data);

		    /* if new pointer return, differs with old one, we should
		     * remove it before use (same, with filename NULL) */
		    if (new_data != data) fct(NULL, NULL, NULL, new_data);
		}
		/* restore directory position */
		dir = opendir(path);
		seekdir(dir, current_dir);
	    }
	    else {
		/* fonction on file status */
		fct(new_filename, new_filename+l, &stat, data);
	    }
	}
	closedir(dir);
    }
    else {
	PFERROR("opendir(%s)", path);
	return (0);
    }
    FREE(new_filename);
    return (1);
}


/* build an MD5 checksum from data in file */
char *checksum_md5_from_file(char *file) {
    int       fd;
    ssize_t   size;
    char      *checksum_md5 = NULL, blk[BUFFER_LENGTH];
    MD5_CTX   md5_ctx;
    
    if ((fd = open(file, O_RDONLY)) < 0 ) {
	PFERROR("open(%s) for MD5 checksum", file);
    } 
    else {
	MD5_Init(&md5_ctx);
	while ((size = read(fd, blk, BUFFER_LENGTH)) > 0)
	    MD5_Update(&md5_ctx, blk, size);
	close(fd);
        // if size = -1, there is a read error, don't do anything
        if (size == 0) { // last read is null
	    checksum_md5 = MALLOC(16);
	    MD5_Final(checksum_md5, &md5_ctx);
        }
    }
    return (checksum_md5);
}

/* build an MD5 checksum from a string */
char *checksum_md5_from_data(char *data, int len) {
    char      *checksum_md5 = 0;
    MD5_CTX   md5_ctx;
    
    MD5_Init(&md5_ctx);
    MD5_Update(&md5_ctx, data, len);
    checksum_md5 = MALLOC(16);
    MD5_Final(checksum_md5, &md5_ctx);
    return (checksum_md5);
}


/* remove ended '/' */
char *remove_ended_slash(char *str) {
    static char temp[BUFFER_LENGTH];
    int         l = strlen(str);
    
    strcpy(temp, str);
    while (temp[l - 1] == '/') temp[--l] = 0;
    return (temp);
}

/* return pointer to the end of the current field */
char *end_field(char *line) {
    while (*line && *line != ' ' && *line != '\t' && *line != '\n') {
	if (*line++ == '\\' && (*line == '\\' || 
				*line == ' ' ||
				*line == '\t'||
				*line == '\n'))
	    line++;
    }
    if (*line) *line++ = 0;
    return (*line ? line : 0);
}


/* create directory and parent directory if needed */
int mkdir_with_parent(char *pathname, mode_t mode) {
    struct stat st;
    char        tmp[BUFFER_LENGTH], *ptmp = tmp;
    
    if (*pathname == '/') *ptmp++ = *pathname++;
    while (pathname && *pathname) {
	while (*pathname && *pathname != '/') *ptmp++ = *pathname++;
	while (*pathname == '/') pathname++;
	*ptmp = 0;
	if (stat(tmp, &st)) {
	    /* file cannot be stat, try to make directory */
	    if (mkdir(tmp, mode)) {
		PFERROR("mkdir(%s)", tmp);
		return (0);
	    }
	}
	else if (!S_ISDIR(st.st_mode)) {
	    error("%s exist and isn't a directory", tmp);
	    return (0);
	}
	*ptmp++ = '/';
    }
    return (1);
}

/* return formatted info into a static string */
char *build_line(char *path, char *filename, t_file_desc *info) {
    struct stat *st = &(info->stat);
    static char blk[BUFFER_LENGTH], tmp[64];
    int         s;

    /* show informations */
    blk[s = 0] = 0;

    /* bad data */
    if (!path) { 
	sprintf(blk,"## anormal action : path=%s desc=%p", path, st); 
	return (blk); 
    }
    
    if (st) {
	/* display file type */
	s += sprintf(blk+s, "%c", FILE_TYPE(st->st_mode));
    
	/* display file mode */
	if (IS(Options, GOPT_HUMAN_READABLE))
	    s += sprintf(blk+s," %04o(%s) ", st->st_mode & 0007777, 
			 right(st->st_mode));
	else
	    s += sprintf(blk+s," %04o ", st->st_mode & 0007777);
	
	/* display user and group id */
	s += sprintf(blk+s,"%5d %5d ", st->st_uid, st->st_gid);
	
	/* display size of device info */
	if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) {
	    sprintf(tmp, "%d,%d", major(st->st_rdev), minor(st->st_rdev));
	    s += sprintf(blk+s, "%10s ", tmp);
	} else {
	    s += sprintf(blk+s, "%10ld ", S_ISDIR(st->st_mode) ? 0 : st->st_size );
	}
	
	/* display checksum */
	if (info->md5) {
	    int i;
	    for (i=0; i < 16; i++) s += sprintf(blk+s, "%02x", info->md5[i]);
	    s += sprintf(blk+s, " ");
	}
	else
	    s += sprintf(blk+s, "-------------------------------- ");
	
	/* display date */
	if (IS(Options, GOPT_HUMAN_READABLE)) {
	    strcpy(tmp, ctime(&(st->st_mtime))); tmp[strlen(tmp) - 1] = 0;
	    s += sprintf(blk+s, "%10ld(%s) ", st->st_mtime, tmp);
	}
	else 
	    s += sprintf(blk+s, "%10ld ", st->st_mtime);
	
	s += sprintf(blk+s, "%s", escape_str(path));
	
	if (S_ISLNK(st->st_mode) && info->link)
	    s += sprintf(blk+s, " %s", escape_str(info->link));
    }
    else {
	if (IS(Options, GOPT_HUMAN_READABLE))
	    s += sprintf(blk+s, "? 0000(-) % 5d % 5d % 10d -------------------------------- %10d(-) %s",0 , 0, 0, 0, 
			 escape_str(path));
	else 
	    s += sprintf(blk+s, "? 0000 % 5d % 5d % 10d -------------------------------- %10d %s",0 , 0, 0, 0, 
			  escape_str(path));
    }
    return (blk);
}

/* return formatted info into a static string */
char *show_filename(char *path, char *filename, t_file_desc *info) {
    return (path);
}


/* view files with recursive method */
void dump_tree_(t_ft *tree, int force) {
    static char   path[BUFFER_LENGTH];
    char          *ppath;

    ppath = path + strlen(path);
    while (1) {
	if (tree->filename) {
	    ADD_PATH(path, ppath, tree->filename);
	    printf("[%02x] %s\n", tree->status, path);
	    // UNSET(tree->status, CHANGED);
	}
	if (tree->subtree && (!IS_PDOTF(tree) || force)) {
	    dump_tree_(tree->subtree, 0);
	}
	tree = tree->next;
	if (IS(tree->status, BASE)) break;
    }
    *ppath = 0;
}

void dump_tree(t_ft *tree) {
    printf("** etat de l'arbre **\n");
    dump_tree_(tree, 1);
    printf("** end **\n");
}


/* find last good defined path (without ended '/') */
char *define_base_path(char *real) {
    static char tmp[BUFFER_LENGTH];
    char        *last, *breal, *lslash;

    lslash = last = tmp;
    while (*real) {
	/* pass multiple '/' */
	while (*real == '/') real++;
	if (!*real) break; /* end of pathname */
	breal = real;
	/* get directory name */
	while (*real && *real != '/') *last++ = *real++;
	/* last have no ended '/' */
	lslash = last;
	/* find . or .., restart */
	if (IS_DOTF(breal) || IS_DOTD(breal)) lslash = last = tmp;
	else                                  *last++ = *real;
    }
    /* remove last slash */
    *lslash = 0;
    return (tmp);
}

/* remove multiple '/' (and ended '/')  */
char *define_cleaned_path(char *real) {
    static char tmp[BUFFER_LENGTH];
    char        *ptmp = tmp;

    while (*real) {
	while ((*ptmp = *real)) { 
	    ptmp++; 
	    if (*real++ == '/') break; 
	}
	while (*real == '/') real++;
    }
    *ptmp = *real;
    /* do not remove ending '/' */
#if 1
    if (ptmp-1 > tmp && *(ptmp-1) == '/') *(ptmp-1) = 0; 
#endif
    return (tmp);
}