/*
 *      func.c
 *      
 *      Copyright 2008 Giorgio "Dani" G. <dani@slacky.it>
 *      
 *      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., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */

#include "slackyd.h"

void
_error (u_short N, ...)
{
    register u_short i;
    va_list ap;

    fflush (stdout);
    fflush (stderr);

    va_start (ap, N);
    for (i = 0; i < N; i++)
     fprintf (stderr, "%s", va_arg(ap, char *));
    
    va_end(ap);

    exit(1);
}

void
clrbuff (void)
{
    int c;
    
    while ((c = getc(stdin)) != '\n' && c != EOF)
    ;
}

void
clrline (FILE * stream, u_short c)
{
    int i;

    fputc('\r', stream);

    for (i = 0; i < c; i++)
	 fputc(' ', stream);

    fputc('\r', stream);
    fflush (stream);

    return;
}

void *
xmalloc (size_t size)
{
	register void *value = malloc (size);
    if (value == 0)
     _error (1, "\nVirtual memory exhausted\n\n");
    return value;
}

void *
xcalloc (size_t nmemb, size_t size)
{
	register void *value = calloc (nmemb, size);
    if (value == 0)
     _error (1, "\n"
     "Virtual memory exhausted\n\n");
    return value;
}

void *
xrealloc (void *ptr, size_t size)
{
	register void *value = realloc (ptr, size);
    if (value == 0)
     _error (1, "\n"
     "Virtual memory exhausted\n\n");
    return value;
}

char *
xstrdup (const char *s)
{
	register char *value = (s) ? strdup (s) : NULL;

	if (!s)
	 return NULL;

    if (value == 0)
     _error (1, "\n"
     "Virtual memory exhausted\n\n");
    return value;
}

/* make a directory `path' with permission `mode' */
void
xmkdir (const char *path, mode_t mode)
{
	struct stat s_stat;
	mode_t mask;
	
	if (stat (path, &s_stat) == 0) {
		if (!S_ISDIR (s_stat.st_mode))
		 _error (3,
	 	 "Fatal error.\n",
	 	 path, " exist and is not a directory.\n"
	 	 "Try to remove.\n\n");
	 }
	 else {
	 	mask = umask (0000);
	    if (mkdir (path, mode) < 0)
		 _error (5,
		 "Cannot create ", path, ": ", strerror (errno), "\n"
		 "Check your installation.\n\n");
	    umask(mask);
	}
		
	return;
}

void
xregcomp (regex_t *preg, const char *regex, int cflags)
{
    int rval = regcomp (preg, regex, cflags);
    char buffer[BUFFSIZE0];

    if (!rval)
     return; /* success */

    regerror (rval, preg, buffer, BUFFSIZE); 

    _error (5, "\n\n"
    "Regex \"", regex, "\" failed: ", buffer, "\n\n");
}

bool
file_exist (const char *path, struct stat *statptr)
{
    struct stat s;

    if (stat (path, (statptr) ? statptr : &s) == 0)
     return true;
    else
    {
        if (errno == ENOENT)
         return false;
        else
        {
            fprintf (stderr, "\n\n"
            "Fatal error: stat() call failed on `%s': %s\n\n", path, strerror (errno));
            exit (EXIT_FAILURE);
        }
    }
    return 0;
}

/* move base of pointer `src' while (*src)[0] is not a char contained in `delim' string.
 * If `policy' is 0 move only initial characters.
 * If `policy' is 1 move only final characters.
 * If `policy' is 2 move initial and final characters.
 */
int
movestrlim (char *src[], const char *delim, short policy)
{
    unsigned l = 0x00;
    
    if (!src || !delim) {
        return EXIT_FAILURE;
    }

    if (policy == 0 || policy == 2) {
        while (strchr (delim, (*src[0])) != NULL) {
            (*src)++;
        }
    }

    if (policy == 1 || policy == 2) {
        l = strlen (*src) - 1;
        while (strchr (delim, (*src)[l]) != NULL) {
            (*src)[l--] = 0x00;
        }
    }

	return ((*src)[0] && strlen (*src) > 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

/* Create regex string `for name'.
 * If occured character some '+' '*' '\' ecc... prefix a '\'  
 */
const char *
set_regex (const char *name)
{
    const char characters[] = "+.-*[]\\";
    static char buffer[BUFFSIZE0];
    unsigned i = 0, l = 0;

    memset (buffer, 0, BUFFSIZE);
    if (!name)
	 return "";
	 
    while (name[i])
    {
        if (strchr (characters, name[i]))
        {
            buffer[l++] = '\\';
        }
        buffer[l++] = name[i++];
    }

    buffer[l] = 0x00;
    return buffer;
}


/* Extract a regex from a string data `s'.
 * Our match start from `match.rm_so` and finish to `match.rm_eo'.
 * They are begin and finish match offset.
 * If addoff is major to zero, add this to start offset: we maybe
 * want skip a part of string reentrant in our match.
 * Example:
 *    "PACKAGE NAME:  packagename-version-arch-build.tgz\n"
 *    In this case we want skip token "PACKAGE NAME:" and move pointer after
 *    initial spaces or tab.
 *
 * This function do it :)
 * Remove also last spaces, tab or newlines.
 *
 * Note: we use always REG_NEWLINE regex flags.
 * Normally our package data is one-line only.
 */
char *
strregdup (char *s, regmatch_t match, unsigned addoff)
{
    size_t len = match.rm_eo - match.rm_so - addoff;
    char *ptr = s + match.rm_so + addoff, *dest = NULL;
    
    if (s == NULL)
     return NULL;

    while (ptr[0] == ' '
        || ptr[0] == '\t')
        {
            ptr++;
            len--;
        }

    if (!ptr[0] || ptr[0] == '\n')
     return NULL;
   
    dest = xmalloc ((len + 1) * sizeof (char));
    strncpy (dest, ptr, len);
    dest[len--] = 0x00;

    movestrlim (&dest, " \t\n", 1);
    
    return dest;
}

/* empty directory `path', if `void_dir' is true remove also void directory */
unsigned
rmdir_rf (const char *path, bool void_dir, bool verbose)
{
	struct dirent **namelist;
	struct stat s_stat;
	int rval, n;
	char buffer [BUFFSIZE0] = { 0 };
	static unsigned removed = 0;
	
	if ((n = scandir (path, &namelist, 0, alphasort)) < 0) {
		if (errno != ENOENT)
		 fprintf (stderr, "Error; %s: %s\n", path, strerror (errno));
		return 0;
	}
	
	if (n == 2) {
		if (void_dir) {
			rval = remove (path);
			if (verbose)
			 fprintf (stdout, "Removing %s: %s\n",
					 path, (rval < 0) ? strerror (errno) : "done.");
			else
			if (rval < 0)
			 fprintf (stdout, "%s: %s\n", path, strerror (errno));
			
			if (!rval) removed++;
			free (namelist[0]);
			free (namelist[1]);
			free (namelist);
			return removed;
		 }
		 else {
		 	free (namelist[0]);
			free (namelist[1]);
			free (namelist);
			return removed;
		}
	}
	
	while (n-- > 2) {
		snprintf (buffer, BUFFSIZE, "%s/%s", path, namelist[n]->d_name);
		
		if (stat (buffer, &s_stat) < 0) {
			fprintf (stderr, "\n\nstat() failed on %s: %s\n\n", buffer, strerror (errno));
			return true;
		}
		
		if (S_ISDIR (s_stat.st_mode))
		 rmdir_rf (buffer, true, verbose);
		else {
			rval = remove (buffer);
			if (verbose)
			 fprintf (stdout, "Removing %s: %s\n",
			 namelist[n]->d_name, (rval < 0) ? strerror (errno) : "done.");
            else
            if (rval)
             fprintf (stderr, "%s: %s\n", buffer, strerror (errno));

			if (!rval)
			 removed++;
		}
			
		free (namelist[n]);
	}
	
	if (void_dir && remove (path) != 0)
	 fprintf (stdout, "%s: %s\n", path, strerror (errno));
	
	free (namelist[0]);
	free (namelist[1]);
	free (namelist);

	return removed;	
}
	


/* clean 'path' directory from file with a determinate extension.
 * remove all if extension is NULL. 
 * Return false if all was removed, else true.
 */
unsigned
clean_dir (const char *dir, const char *extension)
{
    const u_short ext_sz = (extension) ? strlen(extension) : 0;

    int i, n, rval, file_sz;
    u_short removed = 0;
    char path[BUFFSIZE0] = { 0 };
	
    struct dirent **namelist;

	n = scandir(dir, &namelist, 0, alphasort);
    if (n < 0) {
        fprintf (stderr, "%s: %s\n", dir, strerror (errno));
        return true;
    }

    /* ignore . and .. directories */
    free (namelist[0]);
    free (namelist[1]);

    if (n == 2) {
		/* print a message only if we aren't removing broken temp files */
		if (!extension || !strcmp (extension, "part"))
		 fprintf (stdout, "Directory %s is empty.\n", dir);
		free (namelist);
		return 0;
    }
	 
    i = 2;
    while (i < n) {
	
	file_sz = strlen(namelist[i]->d_name);

	if (!extension || !strncasecmp(namelist[i]->d_name + file_sz - ext_sz, extension, ext_sz)) {
	    
	    snprintf (path, BUFFSIZE, "%s/%s", dir, namelist[i]->d_name);
	    
	    if (opt.verbose)
		 fprintf (stdout, "Removing %s: ", namelist[i]->d_name);
		fflush (stdout);
	    
	    rval = remove (path);

	    if (opt.verbose) {
			if (!rval)
             fprintf (stdout, "done.\n");
			else
		     fprintf (stdout, "failed (%s)\n", strerror(errno));
	    }
	    else
	    if (rval < 0)
	    fprintf (stdout, "%s: %s\n", path, strerror (errno));
		

	    if (!rval)
		removed++;
	}
	free (namelist[i]);
	i++;
    
    }
    free (namelist);

	return removed;
}

/* open file_name in mode `mode', if fail exit */
FILE *
fopen_or_die (const char *file_name, const char *mode)
{
    FILE *fd = NULL;
    fd = fopen (file_name, mode);

    if (!fd) {
		if (*mode == 'w' || *mode == 'a')
	     _error (5, "\n"
	     "Cannot write ", file_name, ": ", strerror (errno), "\n\n");
		else
	     _error (5, "\n"
	     "Cannot open ", file_name, ": ", strerror (errno), "\n\n");
    }

    return fd;
}

/* return size of file fd, stream position not will modified */
size_t
get_file_size (FILE *fd)
{
    long pos = 0,  end = 0;
    int rval;

    if (!fd)
	return (u_long) NULL;

    /* save current position */
   if ((pos = ftell (fd)) < 0)
	_error (3, "\n"
	"ftell error: ", strerror (errno), "\n\n");
	
   /* set position at EOF and save it */
   if ((rval = fseek (fd, 0, SEEK_END)) < 0 || (end = ftell(fd)) < 0)
	_error (3, "\n"
	"fseek error: ", strerror (errno), "\n\n");
	
    /* restore old position */
    fseek(fd, pos, SEEK_SET);

    /* return size */
    return (size_t) end;
}

/* save contenent of file `fd', big `size' bytes, in *`data' */
void
fdload (char **data, FILE *fd, size_t size)
{
    if (size <= 0) {
        *data = NULL;
        return;
    }

    *data = (char *) xmalloc((size + 1) * sizeof (char));

    rewind (fd);
    
    if (fread (*data, 1, size, fd) != size)
    _error (3, "\n"
    "Error  on file loading, cannot read all data from file: ", strerror (errno), "\n"
    "Please report this.\n\n");
    	
   return;
}

/* open `path' file and load it in `dest'. */
bool
fsload (const char *path, char **dest)
{
    FILE *fd;

    if (!(fd = fopen (path, "rb")))
	return true;

    fdload (dest, fd, get_file_size(fd));
    fclose (fd);
    
    if (*dest == NULL) {
        return true;
    }
    
    return false;
}


/* check for repositories with same name */
static void
rep_dup (void) {

	unsigned i, j;
	
	for (i = 0; i < REPOS[0].N; i++)
	 for (j = 0; j < REPOS[0].N; j++)
	  if (i != j &&
	      strcmp (REPOS[i].name, REPOS[j].name) == 0 &&
	      strcmp (REPOS[i].name, "slackware")   != 0)
	      _error (3, "\n"
	      "Too repository with name \"", REPOS[i].name, "\".\n"
	      "Fix your slackyd.conf.\n\n");
	
	return;
}


int
load_config (void)
{
    char *p_buff   = NULL,
         *buffer   = NULL,
         *data     = NULL,
         *ptr      = NULL,
         *token[2] = { NULL, NULL };

    int i, rval = 1, offset = 0x00, reptoken = 0x00;
    const int cflags = REG_EXTENDED|REG_NEWLINE|REG_ICASE;
    u_short n_rep = 0;
   
    regex_t re;
    regmatch_t matches[5];



    if (opt.config == NULL)
	 opt.config = strdup (CONFIGFILE);

    fsload(opt.config, &data);
    if (!data)
     _error (5, "\n"
     "Config file `", opt.config, "' ", (errno == ENOENT) ? "not exist" : "is void.", "\n\n");
    ptr = data;
    

    REPOS = NULL;
    /*
       +------------------------------------+ 
       | load repositories from config file |
       +------------------------------------+
     */

    /* 
     * search SLACKWARE repository 
     */
    xregcomp (&re, "^(repository[\t ]+)([a-z0-9\\~/:\\.\\-]+[\t ]*)$", cflags);

    reptoken = 2; /* url */
    
    while (!(rval = regexec(&re, ptr, reptoken + 1, matches, 0))) {
	
	if ((offset = matches[reptoken].rm_eo - matches[reptoken].rm_so) > BUFFSIZE)
	 return EXIT_FAILURE;
	
	REPOS = (repositories *)
	    xrealloc (REPOS, (n_rep + 1) * sizeof (repositories));

	/* move `buffer' to url begin */
	buffer = strregdup (ptr, matches[reptoken], 0);
	p_buff = buffer;
	movestrlim (&buffer, " \t", 2);
	
	
	/* save repository type */
	REPOS[n_rep].proto_t = check_proto_t(buffer);
	REPOS[n_rep].SLACKWARE = true;

	/* go after protocol */
	if (!(token[0] = strchr (buffer, ':')))
	 return EXIT_FAILURE;
    movestrlim (&token[0], ":/", 0);

	/* get hostname and path */
	if (!(token[1] = strsep(&token[0], "/")))
	 return EXIT_FAILURE;
	movestrlim (&token[1], "/", 0);

	
    snprintf (REPOS[n_rep].name, WORDSIZE, "slackware");
	snprintf (REPOS[n_rep].hostname, WORDSIZE, token[1]);
	snprintf (REPOS[n_rep].path, WORDSIZE, "/%s", token[0]);

	free (p_buff);
	ptr += matches[0].rm_eo;
	n_rep++;

    }
    regfree (&re);


    /* 
     * search EXTRA repository 
     */

    xregcomp (&re, "^(repository[\t ]+)"
                   "([a-z0-9_\\-]{1,256}[\t ]+)" /* 256 = WORDSIZE */
                   "([a-z0-9\\~/:\\.\\-]+[\t ]*)$", cflags);

    reptoken = 3; /* url, preceded by repository name here */
    
    while (!(rval = regexec(&re, ptr, reptoken + 1, matches, 0))) {
	
	if ((offset = matches[reptoken].rm_eo - matches[reptoken].rm_so) > BUFFSIZE)
	 return EXIT_FAILURE;
	
	REPOS = (repositories *)
	    xrealloc (REPOS, (n_rep + 1) * sizeof (repositories));

	/* move `buffer' to name begin */
	buffer = strregdup (ptr, matches[reptoken - 1], 0);
	p_buff = buffer;
	movestrlim (&buffer, " \t", 2);
	snprintf (REPOS[n_rep].name, WORDSIZE, buffer);
	free (p_buff);

	buffer = strregdup (ptr, matches[reptoken], 0);
	p_buff = buffer;
	movestrlim (&buffer, " \t", 2);

	/* save repository type */
	REPOS[n_rep].proto_t   = check_proto_t (buffer);
	REPOS[n_rep].SLACKWARE = false;

	/* go after protocol */
	if (!(token[0] = strchr (buffer, ':')))
	 return EXIT_FAILURE;
    movestrlim (&token[0], ":/", 0);

	/* get hostname and path */
	if (!(token[1] = strsep(&token[0], "/")))
	 return EXIT_FAILURE;
	movestrlim (&token[1], "/", 0);

	
    snprintf (REPOS[n_rep].hostname, WORDSIZE, token[1]);
	snprintf (REPOS[n_rep].path, WORDSIZE, "/%s", token[0]);

	free (p_buff);
	ptr += matches[0].rm_eo;
	n_rep++;

    }
    regfree (&re);


    /*--------*/

    if (!n_rep)
	 _error (3, "\n"
	 "No repository in `", opt.config, "' config file.\n\n");

    /* set N for each member to number of total repositories */
    for (i = 0; i < n_rep; i++) {
        REPOS[i].N = n_rep;
    }
	
	/* check for repositories with same name */
	rep_dup ();
	
    /*
       +--------------------------------+ 
       | load connection timeout value  |
       +--------------------------------+
     */
    xregcomp (&re, "^(timeout[\t ]+)([0-9]{1,5}[\t ]*)$", cflags);
    reptoken = 2; /* integer value offset */
	if (regexec (&re, data, reptoken + 1, matches, 0) == 0) {
	    ptr = data + matches[reptoken].rm_so;
	    while (*ptr != 0 && !isdigit(*ptr))
	     ptr++;
        if (*ptr)
         opt.TIMEOUT = atoi(ptr);
    }
    regfree (&re);

    /*
       +--------------------------------+ 
       | load blacklisted packages      |
       +--------------------------------+
     */
    
    opt.blacklisted = NULL;
    opt.nblacklisted = 0x00;
    i = 0x00;

    xregcomp (&re, "^(blacklist[\t ]+)(.*)$", cflags);
    reptoken = 2;
    ptr = data;

    while (!regexec (&re, ptr, reptoken + 1, matches, 0))
    {
        buffer = strregdup (ptr, matches[reptoken], 0);
        if (!(p_buff = buffer) || !(token[0] = strtok (buffer, " ,\t")))
        {
            free (p_buff);
            break;
        }

        do {
            opt.blacklisted = (regex_t *)
                realloc (opt.blacklisted, (i + 1) * sizeof (regex_t));
            if (regcomp (&opt.blacklisted[i], token[0], REG_EXTENDED|REG_NOSUB))
            {
                fprintf (stderr, "Warning: invalid blacklisted package `%s', fix your slackyd.conf !\n", token[0]);
            }
            else
            {
                i++;
            }
        } while ((token[0] = strtok (NULL, " ,\t")));
        
        free (p_buff);
        ptr += matches[reptoken].rm_eo;
    }
    regfree (&re);
    opt.nblacklisted = i;
    
    free (data);
    return EXIT_SUCCESS;
}

static void
tooopt (void)
{
	fprintf (stderr, "Too options. See help.\n");
	exit (1);
}

int
parse_options (int argc, char **argv)
{
    char c;

    if (argc < 2)
	 usage(argv[0]);

    memset (&p_fold, 0, sizeof (pkg_fold));
    memset (&opt, 0, sizeof (options));
    opt.check_dep        = true;
    opt.resolve_dep      = true;
	opt.warn             = true;
	opt.blacklist        = true;

	while ((c = getopt(argc, argv, "B:D::IL::PSU::VX::" "b:c:d::efg:hl::mnpqrs::uvw:x")) != -1) {
	
	switch (c) {
	
	case 'B':		/* build package from sources */
	    opt.buildsrc = true;
	    opt.package = xstrdup(optarg);
	    opt.enabled++;
	    break;
	case 'D':		/* check for missing shared libraries */
	    opt.packager_dep = true;
	    opt.package      = (optind != argc && argv[optind][0] != '-') ? xstrdup(argv[optind]) : NULL;
	    opt.hash         = true;
	    opt.enabled++;
	    p_fold.req       = (opt.check_dep) ? true : false;
	    p_fold.suggest   = (opt.check_dep) ? true : false;
	    p_fold.conflict  = true;
	    break;
	case 'I':
	    opt.skip_status = true;
	    break;
	case 'L':
	    opt.nslack_installed = true;
	    opt.package          = (optind != argc && argv[optind][0] != '-') ? xstrdup(argv[optind]) : NULL;
	    opt.enabled++;
	    break;
	case 'P':
	    opt.purge_all = true;
	    opt.enabled++;
	    break;
	case 'S':		/* skip missing dependencies check */
	    opt.check_dep = false;
	    opt.resolve_dep = false;
	    opt.notify_dep = false;
	    p_fold.req = false;
	    break;
	case 'U':		/* search new packages */
	    opt.upgrade     = true;
	    opt.hash        = true;
	    opt.package     = (optind != argc && argv[optind][0] != '-') ? xstrdup(argv[optind]) : NULL;
	    opt.enabled++;
	    p_fold.req      = (opt.check_dep) ? true : false;
	    p_fold.suggest  = (opt.check_dep) ? true : false;
	    break;
	case 'V':
	    fprintf (stdout,
		    "+-----------------------------+\n"
	        "| Slackyd - Slacky Downloader |\n"
	        "+-----------------------------+\n"
            " Version: %s\n"
		    " Debug  : ", SLACKYD_VERSION);
        #if defined DEBUG
         printf ("yes\n");
        #else
         printf ("no\n");
        #endif
        fprintf (stdout,
		    " Mtrace : ");
		#if defined MTRACE
         printf ("yes\n");
        #else
         printf ("no\n");
        #endif
        fprintf (stdout,
		    " Profile: ");
		#if defined PROFILE
         printf ("yes\n");
        #else
         printf ("no\n");
        #endif
        fprintf (stdout, 
		    " Author : Giorgio \"Dani\" G.\n"
		    "          http://www.slacky.eu/~dani/\n"
		    " Please report bugs or suggests to <dani@slacky.it>.\n\n");
	    exit (EXIT_SUCCESS);
	    break;
    case 'X':
        opt.show_blacklisted = true;
        opt.package          = (optind != argc && argv[optind][0] != '-') ? xstrdup(argv[optind]) : NULL;
	    opt.blacklist        = false;
	    opt.enabled++;
        break;
	

	case 'b':		/* get package build sources */
	    opt.getsrc  = true;
	    opt.package = xstrdup (optarg);
	    opt.enabled++;
	    break;
	case 'c':
	    free (opt.config);
        opt.config = xstrdup (optarg);
	    break;
	case 'd':		/* check for missing shared libraries */
	    opt.shared_dep  = true;
	    opt.package     = (optind != argc && argv[optind][0] != '-') ? xstrdup(argv[optind]) : NULL;
	    opt.hash        = true;
	    opt.enabled++;
	    p_fold.req      = (opt.check_dep) ? true : false;
	    p_fold.suggest  = (opt.check_dep) ? true : false;
	    p_fold.conflict = true;
	    break;
	case 'e':
	    opt.use_regex = true;
	    break;
	case 'f':
	    opt.force = true;
	    break;
	case 'g':		/* get a package */
	    opt.get         = true;
	    opt.hash        = true;
	    opt.package     = xstrdup(optarg);
	    opt.enabled++;
	    p_fold.req      = (opt.check_dep) ? true : false;
	    p_fold.suggest  = (opt.check_dep) ? true : false;
	    p_fold.conflict = true;
	    break;
	case 'h':
		usage (argv[0]);
		break;
	case 'l':
	    opt.installed = true;
	   	opt.package   = (optind != argc && argv[optind][0] != '-') ? xstrdup(argv[optind]) : NULL;
	    opt.enabled++;
	    break;
	case 'm':
	    opt.case_insensitive = true;
	    break;
	case 'n':		/* not ask for missing dependencies download, show only a notify */
	    if (!opt.check_dep)	/* dependencies check disabled */
		 usage(argv[0]);
	    opt.resolve_dep = false;
	    opt.notify_dep  = true;
	    p_fold.req      = true;
	    break;
	case 'p':
	    opt.purge = true;
	    opt.enabled++;
	    break;
	case 'q':
	    opt.warn = false;
	    break;
	case 'r':		/* show repositories stat */
	    opt.rstat     = true;
	    p_fold.size_c = true;
	    p_fold.size_u = true;
	    opt.enabled++;
	    break;
	case 's':		/* search package in repository */
	    opt.search        = true;
	    opt.package       = (optind != argc && argv[optind][0] != '-') ? xstrdup(argv[optind]) : NULL;
	    opt.print_status  = true;
	    opt.enabled++;
        break;
	case 'u':		/* update file list */
	    opt.update = true;
	    opt.enabled++;
	    break;
	case 'v':
	    opt.verbose = true;
	    break;
	case 'w':		/* show info */
	    opt.view = true;
	    opt.package       = xstrdup(optarg);
	    opt.print_status  = true;
	    opt.enabled++;
	    p_fold.size_c     = true;
	    p_fold.size_u     = true;
	    p_fold.req        = true;
	    p_fold.conflict   = true;
	    p_fold.suggest    = true;
	    p_fold.desc       = true;
	    break;
    case 'x':
        opt.blacklist = false;
        break;
	
	
	case '?':
		exit (EXIT_FAILURE);
	default:		/* error */
	    usage(argv[0]);
	    break;

	}
    }

    if (opt.enabled > 1)
     tooopt ();
	    
    if (opt.verbose) {
        p_fold.size_c = true;
	    p_fold.size_u = true;
    }
	    
    if (opt.enabled < 1)
	 _error (1, "Nothing to do.\n");

	if (!opt.use_regex && opt.package && !isalnum (opt.package[0]))
	 _error (3, "Invalid argument \"", opt.package, "\".\n"
	            "Use -e switch to enable regex.\n");
	 
    
    return EXIT_SUCCESS;
}




/*--------------- slackyd function to manage types start ---------------*/

int
is_installed (const char *name, int flags, unsigned start)
{
    unsigned n = start;
    regex_t preg;
    int cflags = REG_EXTENDED | REG_NOSUB;

    slassert (name != NULL);

    switch (flags) {
        case MATCH_ALL_NAME:
            while (n < nginst)
             if (xstrstr (ginst[n].name, name))
              return n;
             else
              n++;
            break;

        case MATCH_EXACTLY_NAME:        
            while (n < nginst)
             if (xstrcmp (name, ginst[n].pstruct.name) == 0)
              return n;
             else
              n++;
            break;

        case MATCH_EXACTLY_PACKAGE:        
            while (n < nginst)
             if (xstrcmp (name, ginst[n].name) == 0)
              return n;
             else
              n++;
            break;

        case MATCH_USING_REGEX:
            if (opt.case_insensitive)
             cflags |= REG_ICASE;
            xregcomp (&preg, name, cflags);
            while (n < nginst)
             if (regexec (&preg, ginst[n++].name, 0, NULL, 0) == 0) {
                 regfree (&preg);
                 return n;
             }
            regfree (&preg);
            break;
            
        default:
            slassert (NULL);
            break;
    }
     
    return PACKAGE_STATUS_NOT_INSTALLED;
}


bool
is_pkg (const char *name, pkg_t *packages[], unsigned npackages)
{
    unsigned i;

    for (i = 0; i < npackages; i++)
     if (!strcmp (name, packages[i]->name)
      || !strcmp (name, packages[i]->pstruct.name))
    {
        return true;
    }

    return false;
}

/* search str `src' in array `dlist'. 
   Return true if found, false otherwise. */
bool
check_dyns (const char *src, dyns dlist)
{
    unsigned i;

    for (i = 0; i < dlist.nlist; i++)
	 if (!strcmp (src, dlist.list[i]))
	  return true;

    return false;
}


/* add string `src' to array `dest' */
bool
add_dyns (char *src, dyns *dlist)
{
    const int n = dlist->nlist;
	
	if (!src)
	return true;

    dlist->list = (char **)
        xrealloc (dlist->list, ((n + 1) * sizeof (char *)));
	dlist->list[n] = xstrdup(src);
    
    dlist->nlist++;

    return false;
}

/* check for package operator and return opportune macro.
 * we can use isop() function instead of this (see search.c) , but this ensure
 * more security because isop() can parse also dependency name or version.
 * In these case is hard understand what token we're parsing :)
 */
static char
op_type (const char *op)
{
    if (!op || op[0] == 0x00)
     return VERSION_ALL;
    else
    if (!strcmp (op, "<"))
     return VERSION_MINOR;
    else
    if (!strcmp (op, "<="))
     return VERSION_MINOR_OR_EQUAL;
    else
    if (!strcmp (op, "=") || !strcmp (op, "=="))
     return VERSION_EQUAL;
    else
    if (!strcmp (op, ">"))
     return VERSION_MAJOR;
    else
    if (!strcmp (op, ">="))
     return VERSION_MAJOR_OR_EQUAL;
    else
     _error (3, "\n\n"
     "Fatal error: bad package required operator (", op, ").\n"
     "Maybe a bug, please report this.\n\n"); 

    return 0;
}
    
/* add a dep "name >=|=|<= version" to a dep_t list `dest' of `n' elements.
 * If op and version is NULL will be add just name.
 */
void
add_dep (const char *name, const char *op, const char *version, u_char type, dep_t **dest, int *n)
{
    int i = *n;
    char *ptr = NULL;

    *dest = xrealloc (*dest, (i + 1) * sizeof (dep_t));
    memset (&(*dest)[i], 0 , sizeof (dep_t));

    slassert (name != NULL);
    (*dest)[i].name = xstrdup (name);
    

    memset (&(*dest)[i].op, 0, sizeof ((*dest)[i].op));
    /* Before check for NULL operator, after test first character.
     * We consider `op' NULL and `op[0]' NULL equal :)
     * This for skip program crash when NULL is passed as `op'.
     */
    if (op && op[0])
     strncpy ((*dest)[i].op, op, sizeof ((*dest)[i].op) - 1);
    
    (*dest)[i].op_t = op_type (op);
   
    if (version) {
        if ((ptr = strchr (version, '-')))
         *ptr = 0x00; /* sometimes version folder containe also arch and build */
        (*dest)[i].version = xstrdup (version);
        if (ptr)
         *ptr = '-';
    }
    else
     (*dest)[i].version = NULL;
     
    (*dest)[i].type = type;

     ++*n;
    return;
}


/* copy a dep_t array structure `src' to a dep_t array `dest'.
 * Array is maked of `n' structure.
 */ 
static void
cp_dep_t (dep_t *src, dep_t **dest, int n)
{
    int i;
    size_t memsz = n * sizeof (dep_t);
    
    if (n <= 0) {
        *dest = NULL;
        return;
    }

    *dest = (dep_t *) xmalloc (memsz);
    memset (*dest, 0, memsz);

    for (i = 0; i < n; )
     add_dep (src[i].name, src[i].op, src[i].version, src[i].type, dest, &i);

    return;
}

/* copy pkg_t `src' to `dest' using policy setted by external `p_fold'. */
void
cp_pkg (pkg_t *src, pkg_t **dest)
{
    memset (*dest, 0, sizeof (pkg_t));

	(*dest)->name = xstrdup(src->name);

    strncpy ((*dest)->pstruct.name,    src->pstruct.name,    NAMESIZE);
    strncpy ((*dest)->pstruct.version, src->pstruct.version, VERSIONSIZE);
    strncpy ((*dest)->pstruct.arch,    src->pstruct.arch,    ARCHSIZE);
    strncpy ((*dest)->pstruct.build,   src->pstruct.build,   BUILDSIZE);

    (*dest)->location = xstrdup (src->location);

	(*dest)->blacklisted = src->blacklisted;
	

    if (p_fold.size_c == true)
	 (*dest)->size_c = src->size_c;
    

    if (p_fold.size_u == true)
	 (*dest)->size_u = src->size_u;

    if (p_fold.req == true && src->nrequired > 0)
     cp_dep_t (src->required, &(*dest)->required, src->nrequired);
    else
     (*dest)->required = NULL;
    
    (*dest)->nrequired = src->nrequired;

    if (p_fold.req == true && src->nmrequired > 0)
     cp_dep_t (src->mrequired, &(*dest)->mrequired, src->nmrequired);
    else
     (*dest)->mrequired = NULL;

    (*dest)->nmrequired = src->nmrequired;
    

    if (p_fold.conflict == true && src->nconflicts > 0)
     cp_dep_t (src->conflicts, &(*dest)->conflicts, src->nconflicts);
    else
     (*dest)->conflicts = NULL;

    (*dest)->nconflicts = src->nconflicts;
    
    
    if (p_fold.suggest == true && src->nsuggests > 0)
     cp_dep_t (src->suggests, &(*dest)->suggests, src->nsuggests);
    else
     (*dest)->suggests = NULL;

    (*dest)->nsuggests = src->nsuggests;
    

    if (p_fold.desc == true)
		(*dest)->desc = xstrdup(src->desc);
    
    
    (*dest)->N_REPO = src->N_REPO;
    (*dest)->branch = src->branch;

    (*dest)->status    = src->status;
    (*dest)->installed = xstrdup (src->installed);
    
    return ;
}

void
add_pkg_t (pkg_t **packages[], unsigned *npackages, pkg_t *pnew)
{
    unsigned n = *npackages;

    (*packages)    = (pkg_t **) xrealloc ((*packages), (n + 1) * sizeof (pkg_t *));
    (*packages)[n] = (pkg_t * ) xmalloc  (sizeof (pkg_t));

    cp_pkg (pnew, &(*packages)[n]);

    ++*npackages;
}

/* free a dinamic string list */
void
free_dyns (dyns *addr)
{
    int i;

    for (i = addr->nlist - 1; i >= 0; i--)
	 free (addr->list[i]);
	
    free (addr->list);

    addr->list = NULL;
    addr->nlist = 0x00;
    
    return;
}

/* free a dep_t array `dep' on `n' structure
 * and set pointer to NULL
 */
static void
free_dep_t (dep_t *dep[], int n)
{
    int i;

    if (n <= 0)
     return;
    else
     for (i = n - 1; i >= 0; i--) {
         free ((*dep)[i].name);
         free ((*dep)[i].version);
     }

    free (*dep);
    *dep = NULL;
     
     return;
 }
     
/* free one pkg_t package data */
void
free_pkg_t (pkg_t **addr)
{
    if (addr == NULL)
	 return;

    free ((*addr)->name);
    
    free ((*addr)->location);

    if (p_fold.req == true && (*addr)->nrequired > 0)
     free_dep_t (&(*addr)->required, (*addr)->nrequired);

    if (p_fold.conflict == true)
     free_dep_t (&(*addr)->conflicts, (*addr)->nconflicts);
    
    if (p_fold.suggest == true)
     free_dep_t (&(*addr)->suggests, (*addr)->nsuggests);
    
    if (p_fold.desc == true)
	 free ((*addr)->desc);
	
    if (p_fold.req == true)
     free_dep_t (&(*addr)->mrequired, (*addr)->nmrequired);

    if ((*addr)->status >= 0)
     free ((*addr)->installed);

    free (*addr);
    *addr = NULL;
    
    return;
}

/* free all `elements' of pkg_t `addr' packages data */
void
free_pkgs_t (pkg_t **addr[], unsigned elements)
{
    int i;

    if (!addr || !elements)
     return;

    for (i = elements - 1; i >= 0; i--) {
	if ((*addr)[i])
	    free_pkg_t(&((*addr)[i]));
    }

    free (*addr);
    *addr = NULL;
    
    return;
}


void free_installed_t (installed_t **sp, unsigned n)
{

    if (!n)
     return;

    while (--n)
     free ((*sp)[n].name);
    
    free ((*sp)[0].name);
    free (*sp);
    *sp = NULL;
    
    return;
}



/* remove package `memb' from a pkg_t list `packages'.
 * `npackages' will be decremented.
 */
void rm_pkg_t (pkg_t **packages[], unsigned *npackages, unsigned memb)
{
    unsigned i = memb;

    /* check for end of list */
    if (i == *npackages) {
        free_pkg_t (&(*packages)[i]);
        --*npackages;
        return;
    }

    free_pkg_t (&(*packages)[i]);
    while (i < *npackages - 1) {
    	(*packages)[i] = (*packages)[i + 1];
        i++;
    }

    --*npackages;

    if (*npackages == 0) {
        free (*packages);
        *packages = NULL;
    }

    return;
}
    

/*--------------- slackyd function manage types end ---------------*/




/* process directory where we search shared libraries */
void
process_lib_path (char *rpath, dyns *path)
{
    FILE *fd;
    char *buffer = NULL, *ptr = NULL, *env = NULL;
    
    buffer = xmalloc (BUFFSIZE0 * sizeof (char));
    ptr = buffer; /* buffer can be moved; we use ptr for free memory */

    if (!(fd = fopen ("/etc/ld.so.conf", "r"))) {
		if (opt.warn)
	     fprintf (stderr, "\n"
	     "Warning:\n"
	     "Cannot open ld.so.conf: %s\n", strerror (errno));
		free (buffer);
	}

    if (rpath)
	 add_dyns (rpath, path);

    add_dyns ("/lib", path);
    add_dyns ("/usr/lib", path);

    while (fgets(buffer, BUFFSIZE, fd)) {
	if (buffer[0] == '\n')	/* void line ? skip ! :) */
	 continue;

	if (buffer[strlen(buffer) - 1] == '\n')
	    buffer[strlen(buffer) - 1] = 0x00;

    movestrlim (&buffer, " \t", 2);
    
	if (!check_dyns (buffer, *path))
	    add_dyns (buffer, path);
    }
    sfree (ptr);
    
    if (!(buffer = getenv ("LD_LIBRARY_PATH"))) {
		fclose (fd);
		return ;
    }

    ptr = buffer;
    movestrlim (&buffer, ":", 0);
    while ((env = strsep(&buffer, ":"))) {
		movestrlim (&env, ":", 0);
		if (!check_dyns (env, *path))
	     add_dyns (env, path);
    }

    fclose (fd);
    return ;
}


#define ISELF(header) \
		(header[EI_MAG0] == ELFMAG0 && \
		 header[EI_MAG1] == ELFMAG1 && \
		 header[EI_MAG2] == ELFMAG2 && \
		 header[EI_MAG3] == ELFMAG3)

/* check if `file' is a regular ELF file (executable or shared object) */
static bool
process_reg_elf(const char *file, FILE ** fd, ElfData * Elf)
{
    struct stat s_stat;

    if (stat(file, &s_stat) != 0 || !S_ISREG(s_stat.st_mode))
	 return true;
    if (!(*fd = fopen (file, "rb")))
	 return true;
    if (fread((unsigned char *) Elf->e_ident, 1, EI_NIDENT, *fd) != EI_NIDENT) {
	 fclose (*fd);
	 return true;
    }
    
    if (!ISELF(Elf->e_ident)) {
	 fclose (*fd);
	 return true;
    }
    
    return false;
}

/* read file header and check for ELF file */
static bool
process_elf_header(FILE * fd, ElfData * Elf)
{
    if (fseek(fd, 0, SEEK_SET))
	 return true;

    switch (Elf->e_ident[EI_CLASS]) {
    case ELFCLASSNONE:
	if (opt.verbose)
	    fprintf (stderr, "error: invalid elf class (0x%.2x).\n", Elf->e_ident[EI_CLASS]);
		return true;
		break;
    
    case ELFCLASS32:
	if (fread ((Elf32_Ehdr *) & Elf->Elf32_Header, sizeof (Elf32_Ehdr), 1, fd) == 1)
	    return false;
		break;
    
    case ELFCLASS64:
	if (fread ((Elf64_Ehdr *) & Elf->Elf64_Header, sizeof (Elf64_Ehdr), 1, fd) == 1)
	 return false;
	break;
    
    default:
	if (opt.verbose)
	 fprintf (stderr, "error: unknow elf class (0x%.2x).\n", Elf->e_ident[EI_CLASS]);
	return true;
	break;
    }
    
    return true; /* :-m */
}

/* check for ELF executable or shared object */
static bool
process_elf_type(ElfData * Elf)
{
    switch (Elf->e_ident[EI_CLASS]) {
    case ELFCLASS32:
		return
		(Elf->Elf32_Header.e_type == ET_EXEC || Elf->Elf32_Header.e_type == ET_DYN) ? false : true;
		break;
    
    case ELFCLASS64:
		return (Elf->Elf64_Header.e_type == ET_EXEC || Elf->Elf64_Header.e_type == ET_DYN) ? false : true;
		break;
    
    default:
	if (opt.verbose)
	    fprintf (stderr, "unexpected error processing elf type.\n");
		return true;
		break;
    }
    
    return true; /* :-m */
}

/* process ELF section header */
static bool
process_elf_section_header(FILE * fd, ElfData * Elf)
{
    int i;
    unsigned SH_Size;

    switch (Elf->e_ident[EI_CLASS]) {
    case ELFCLASS32:
	/* check for section header */
	if (Elf->Elf32_Header.e_shoff == 0) {
	if (opt.verbose)
	 fprintf (stderr, "error: elf haven't a section header.\n");
	return true;
	}
	if (fseek(fd, Elf->Elf32_Header.e_shoff, SEEK_SET))
	 return true;
	/* alloc memory for sections header */
	SH_Size =
	Elf->Elf32_Header.e_shnum * Elf->Elf32_Header.e_shentsize;
	Elf->Elf32_SectionHeader = (Elf32_Shdr *) xmalloc (SH_Size);
	
	for (i = 0; i < Elf->Elf32_Header.e_shnum; i++)
	if (fread ((Elf32_Shdr *) & Elf->Elf32_SectionHeader[i], Elf->Elf32_Header.e_shentsize, 1, fd) != 1)
	 return true;
	break;

    case ELFCLASS64:
	/* check for section header */
	if (Elf->Elf64_Header.e_shoff == 0) {
	if (opt.verbose)
	 fprintf (stderr, "error: elf haven't a section header.\n");
	return true;
	}
	if (fseek(fd, Elf->Elf64_Header.e_shoff, SEEK_SET))
	 return true;
	/* alloc memory for sections header */
	SH_Size =
	Elf->Elf64_Header.e_shnum * Elf->Elf64_Header.e_shentsize;
	Elf->Elf64_SectionHeader = (Elf64_Shdr *) xmalloc(SH_Size);
	
	for (i = 0; i < Elf->Elf64_Header.e_shnum; i++)
	if (fread ((Elf64_Shdr *) & Elf->Elf64_SectionHeader[i], Elf->Elf64_Header.e_shentsize, 1, fd) != 1)
	 return true;
	break;

    default:
	if (opt.verbose)
	 fprintf (stderr, "unexpected error processing elf section header.\n");
	return true;
	break;
    }
    
    return false;
}

/* search and save ELF string table offset */
static bool
process_elf_strtab(ElfData * Elf)
{
    int i;

    switch (Elf->e_ident[EI_CLASS]) {
    case ELFCLASS32:
	for (i = 0; i < Elf->Elf32_Header.e_shnum; i++)
	 if (Elf->Elf32_SectionHeader[i].sh_type == SHT_STRTAB) {
		 Elf->strtab_off = Elf->Elf32_SectionHeader[i].sh_offset;
		return false;
	}
	break;
    
    case ELFCLASS64:
	for (i = 0; i < Elf->Elf64_Header.e_shnum; i++)
	 if (Elf->Elf64_SectionHeader[i].sh_type == SHT_STRTAB) {
		 Elf->strtab_off = Elf->Elf64_SectionHeader[i].sh_offset;
		 return false;
	}
	break;
    
    default:
	if (opt.verbose)
	 fprintf (stderr, "unexpected error processing elf strtab offset.\n");
	return true;
	break;
    }
    
    return true;
}

/* find elf dynamic index */
static bool
process_elf_dyn_index(ElfData * Elf)
{
    int i;

    switch (Elf->e_ident[EI_CLASS]) {
    
    case ELFCLASS32:
	for (i = 0; i < Elf->Elf32_Header.e_shnum; i++)
	 if (Elf->Elf32_SectionHeader[i].sh_type == SHT_DYNAMIC) {
		 Elf->dyn_index = i;
		return false;
	}
	break;
    
    case ELFCLASS64:
	for (i = 0; i < Elf->Elf64_Header.e_shnum; i++)
	 if (Elf->Elf64_SectionHeader[i].sh_type == SHT_DYNAMIC) {
		 Elf->dyn_index = i;
		return false;
	}
	break;
    
    default:
	if (opt.verbose)
	 fprintf (stderr, "unexpected error processing elf dynamic index.\n");
	return true;
	break;
    }
    
    return true;
}


/* read and process ELF dynamic section */
static bool
process_elf_dynamic_section (FILE * fd, ElfData * Elf)
{
    unsigned i;
    unsigned short Dyn_Index = Elf->dyn_index;

    switch (Elf->e_ident[EI_CLASS]) {
    case ELFCLASS32:
	/* dynamic section sanity check */
	if (Elf->Elf32_SectionHeader[Dyn_Index].sh_entsize == 0)
	 return true;
	/* alloc memory for dynamic section entries */
	Elf->dyn_entries =
	 Elf->Elf32_SectionHeader[Dyn_Index].sh_size /
	 Elf->Elf32_SectionHeader[Dyn_Index].sh_entsize;

	Elf->Elf32_DynamicSection = (Elf32_Dyn *)
	xmalloc (Elf->dyn_entries * sizeof (Elf32_Dyn));
	
	/* read dynamic entries */
	if (fseek (fd, Elf->Elf32_SectionHeader[Dyn_Index].sh_offset, SEEK_SET))
	 return true;
	for (i = 0; i < Elf->dyn_entries; i++) {
	 if (fread ((Elf32_Dyn *) & Elf->Elf32_DynamicSection[i],
	 	 Elf->Elf32_SectionHeader[Dyn_Index].sh_entsize, 1, fd) != 1)
		return true;

	if (Elf->Elf32_DynamicSection[i].d_tag == DT_NULL) {
		Elf->dyn_entries = i + 1;	/* we include DT_NULL */
		break;
	}
	}
	break;

    case ELFCLASS64:
	/* dynamic section sanity check */
	if (Elf->Elf64_SectionHeader[Dyn_Index].sh_entsize == 0)
	 return true;
	/* alloc memory for dynamic section entries */
	Elf->dyn_entries =
	Elf->Elf64_SectionHeader[Dyn_Index].sh_size /
	Elf->Elf64_SectionHeader[Dyn_Index].sh_entsize;

	Elf->Elf64_DynamicSection = (Elf64_Dyn *)
	xmalloc (Elf->dyn_entries * sizeof (Elf64_Dyn));
	
	/* read dynamic entries */
	if (fseek (fd, Elf->Elf64_SectionHeader[Dyn_Index].sh_offset, SEEK_SET))
	 return true;
	for (i = 0; i < Elf->dyn_entries; i++) {
	 if (fread ((Elf64_Dyn *) & Elf->Elf64_DynamicSection[i],
	 			 Elf->Elf64_SectionHeader[Dyn_Index].sh_entsize, 1, fd) != 1)
		return true;

	 if (Elf->Elf64_DynamicSection[i].d_tag == DT_NULL) {
		Elf->dyn_entries = i + 1;	/* we include DT_NULL */
		break;
	}
	}
	break;
    
    default:
	if (opt.verbose)
	 fprintf (stderr, "unexpected error processing elf dynamic section.\n");
	return true;
	break;
    
    }
    return false;
}

/* get RPATH (deprecated :-?) */
static char *
process_elf_rpath (FILE *fd, ElfData *Elf)
{
    char rpath[BUFFSIZE0] = { 0 };
    unsigned i, offset;

    switch (Elf->e_ident[EI_CLASS]) {
    
    case ELFCLASS32:
	for (i = 0; i < Elf->dyn_entries; i++) {
	 if (Elf->Elf32_DynamicSection[i].d_tag == DT_RPATH) {
		offset =
		Elf->strtab_off +
		Elf->Elf32_DynamicSection[i].d_un.d_val;
		
		if (fseek(fd, offset, SEEK_SET))
		 return NULL;
		return (fgets(rpath, BUFFSIZE, fd)) ? xstrdup(rpath) : NULL;
	    }
	}
	break;
	
    case ELFCLASS64:
	for (i = 0; i < Elf->dyn_entries; i++) {
	 if (Elf->Elf64_DynamicSection[i].d_tag == DT_RPATH) {
		 offset =
		 Elf->strtab_off +
		 Elf->Elf64_DynamicSection[i].d_un.d_val;
		 
		 if (fseek(fd, offset, SEEK_SET))
		  return NULL;
		 return (fgets(rpath, BUFFSIZE, fd)) ? xstrdup(rpath) : NULL;
	     }
	}
	break;
    
    default:
	if (opt.verbose)
	 fprintf (stderr, "\n"
	 "Unexpected error processing elf rpath.\n");
	break;
    }
    
    return NULL;
}


/* search `needed' library in `path' directories */
static bool
search_needed(char *needed, dyns lpath)
{
    char buffer[BUFFSIZE0];
    unsigned i;
    struct stat s_stat;

    for (i = 0; i < lpath.nlist; i++) {
       /* Normally a member (string) of a dyns type canno't be true,
        * but in this case `lpath' containe a list of paths where we will search
        * libraries. Here first member (lpath.list[0]) is reserved to RPATH.
        * RPATH is a path put in binaries; we load paths from ld.so.conf and
        * $LD_LIBRARY_PATH *BEFORE* parse each ELF binary of each packages !
        * This for save system load.
        * Then, *here* maybe a NULL string in `lpath' dyns structure. :)
        */
        
        if (lpath.list[i] != NULL) {
        	
        	snprintf (buffer, BUFFSIZE, "%s/%s", lpath.list[i], needed);
        	
        	if (!lstat(buffer, &s_stat)) {
        		return false;
			}
		}
    }

    return true;
}

/* if dynamic section entry is a DT_NEEDED return offset of a string in
 * the string table, otherwise return 0
 */
static unsigned
get_needed_off (ElfData Elf, unsigned memb) {

    unsigned offset = 0;

    switch (Elf.e_ident[EI_CLASS]) {
	case ELFCLASS32:
	    if (Elf.Elf32_DynamicSection[memb].d_tag == DT_NEEDED) {
	        offset =
	        Elf.strtab_off + Elf.Elf32_DynamicSection[memb].d_un.d_val;
	    }
	break;
	
	case ELFCLASS64:
	    if (Elf.Elf64_DynamicSection[memb].d_tag == DT_NEEDED) {
	        offset =
	        Elf.strtab_off + Elf.Elf64_DynamicSection[memb].d_un.d_val;
        }
    break;
	
	default:
	 slassert (NULL);
	 break;
	}
	
    return offset;
}

/* return a string from the string table, started from offset and NULL terminated */
static char *
get_needed (FILE * fd, unsigned long offset)
{
    static char buffer[256];

    if (fseek(fd, offset, SEEK_SET))
	 return NULL;

    memset (buffer, sizeof (buffer), 0);

    return fgets (buffer, sizeof (buffer) - 1, fd);
}

/* check if file `file' is present on packages `tgz'
 * Sometimes shared libraries are in the same package !
 */
static bool
file_in_tgz (const char *file, const char *tgz)
{
    FILE *fd;
    char *dfile = NULL;
    char pfile[BUFFSIZE0] = { 0 };

    snprintf (pfile, BUFFSIZE, "%s/%s", PKGDIR, tgz);

    if (!(fd = fopen (pfile, "r"))) {
		if (opt.warn)
		fprintf (stderr, "\n"
		"Warning: cannot open %s !?\n", tgz);
		return false;
    }

    fdload (&dfile, fd, get_file_size(fd));
    fclose (fd);
    
    if (dfile == NULL) {
        fprintf (stderr, "Error, %s seem to be void.\n", pfile);
        return true;
    }


    if (strstr (dfile, file)) {
		free (dfile);
		return false;
    }

    free (dfile);
    return true;
}

/* check for libraries already in list */
static int
dup_needed (m_need *mlibs, unsigned n_mlibs, const char *entry)
{
    unsigned i;

    for (i = 0; i < n_mlibs; i++)
	 if (!strcmp (mlibs[i].needed, entry))
	  return (int) i;

    return -1;
}


#define cleanup(Elf_Data, err) {          \
	free (Elf_Data.Elf32_SectionHeader);  \
	free (Elf_Data.Elf64_SectionHeader);  \
	free (Elf_Data.Elf32_DynamicSection); \
	free (Elf_Data.Elf64_DynamicSection); \
	free (Elf_Data.rpath);   \
	if (err) {               \
		return EXIT_FAILURE; \
	}						 \
	}

/* check shared libraries of file `file' of package `tgz' */
int
check_elf_libs (dyns *libpath, const char *file, m_need *mlibs[], unsigned *n_mlibs, const char *tgz)
{
    FILE *fd;
    ElfData Elf;
    char *needed = NULL;
    int i, t_index;
    unsigned off_needed, m_index;
    
    memset (&Elf, 0, sizeof (Elf));

    if  (process_reg_elf             (file, &fd, &Elf))
     cleanup (Elf, 1);
    if  (process_elf_header          (fd, &Elf)
	  || process_elf_type            (&Elf)
	  || process_elf_section_header  (fd, &Elf)
	  || process_elf_strtab          (&Elf)
	  || process_elf_dyn_index       (&Elf)
	  || process_elf_dynamic_section (fd, &Elf)) {
	      fclose (fd);
	      cleanup(Elf, 1);
      }

    /* First path is reserved to RPATH string.
     * When we read library paths we could not know rpath and we have add
     * a fake string.
     */
     sfree (libpath->list[0]);
     if ((Elf.rpath = process_elf_rpath (fd, &Elf)) != NULL)
      libpath->list[0] = xstrdup (Elf.rpath);
    
    /* check for needed libraries */
    for (i = 0; (unsigned) i < Elf.dyn_entries; i++) {

    if ( (off_needed = get_needed_off (Elf, i)) == 0  /* entry isn't a DT_NEEDED or offset not obtained */
       ||(needed = get_needed (fd, off_needed)) == 0  /* string of needed object not read               */
	   || search_needed (needed, *libpath)      == 0  /* library found in the libraries path of system  */
	   || file_in_tgz (needed, tgz)             == 0) /* library is in the same package that require it */
	 continue;

    /* verify if missing object is already in list.
     * In this case we don't add a new entry in m_need array, but simply
     * add package name and object name that require library to an existant
     * array index !
     */
    t_index = dup_needed (*mlibs, *n_mlibs, needed);
    m_index = (t_index < 0) ? *n_mlibs : (unsigned) t_index;

	if (t_index < 0) {
	*mlibs = (m_need *)
	    xrealloc (*mlibs, (*n_mlibs + 1) * sizeof (m_need));
    memset (&(*mlibs)[*n_mlibs], 0, sizeof (m_need));
    }
		
    /* save missing libraries */
    if (t_index < 0)
	snprintf ((*mlibs)[m_index].needed,  NEEDSIZE, needed);
    /* add to list package with broken dependencies */
	add_dyns ((char *)tgz,  &((*mlibs)[m_index].pkgname));
    /* add to list object that require missing library */
    add_dyns ((char *)file, &(*mlibs)[m_index].object);
	    
	if (t_index < 0)
	 ++*n_mlibs;

    }

    fclose (fd);

    cleanup(Elf, 0);
    return EXIT_SUCCESS;
}

#undef cleanup
