/*
 *      search.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"


/* Verify packages list retreived.
 * If `verbose' is false, never print warnings.
 * If `quit' is true and packages list was never updated,
 * print a warning and quit.
 *
 * Return number of missing files.  
 */
unsigned
packages_list_check (bool verbose, bool quit)
{
    const char *strbranch[BRANCHES] = { "/patches", "", "/extra" };
    const char *strfiles []         = { PACKAGES, CHECKSUMS, NULL };
    struct stat s;
    
    unsigned r, b, f, missing = 0, ok = 0;
    char path[BUFFSIZE0];
    bool slackware = false;
    
    r = 0;
    while (r < REPOS[0].N)
    {
        if (REPOS[r].SLACKWARE && slackware) {
            r++;
            continue;
        }

        for (b = 0; b < BRANCHES; b++)
        {

        if (!REPOS[r].SLACKWARE)
         b++; /* first branch is patches, extra repositories have not it */
        
        for (f = 0; strfiles[f] != NULL; f++)
        {
            snprintf (path, BUFFSIZE, "%s/%s%s/%s",
                DATADIR,
                REPOS[r].name,
                strbranch[b],
                strfiles [f]);

            if (file_exist (path, &s) == false)
            {
                if (verbose && opt.warn)
                 fprintf (stderr, "Warning: missing file `%s'\n", path);
                missing++;
            }
            else
            if (!S_ISREG (s.st_mode))
            {
                _error (3,
                "Fatal error: `", path, "' isn't a regular file.\n"
                "Remove it and force slackyd update.\n\n");
            }
            else
             ok++;
        }
        
        if (!REPOS[r].SLACKWARE)
         break; /* next branch is extra, extra mirror have not it */
        else
         slackware = true;
        
        }
        
        r++;
    }

    if (quit && ok == 0) {
        if (verbose)
         fprintf (stderr, "%s"
         "Error, you need to update packages list.\n"
         "Run `slackyd -u' or `slackyd -f -u' to force.\n\n", (verbose && opt.warn) ? "\n" : "");
        exit (1);
    }

    if (verbose && opt.warn && missing > 0)
     fprintf (stderr, "Try to update packages list.\n\n");

    return missing;
}


/* Test installed packages for `pkg' pkg_t type.
 * If package is installed return PACKAGE_STATUS_INSTALLED, else
 * if package is not installed return PACKAGE_STATUS_NOT_INSTALLED.
 * If is installed a package with same name, but with different version, arch and/or
 * build, return his index.
 */
static int
pkg_inst_status (pkg_t *pkg)
{
    unsigned i = 0;
    
    for (; i < nginst; i++)
    {
        if (strcmp (ginst[i].pstruct.name, pkg->pstruct.name) == 0)
         return (strncmp (ginst[i].name, pkg->name, strlen (ginst[i].name)) == 0) ? PACKAGE_STATUS_INSTALLED : (int) i;
    }

    return PACKAGE_STATUS_NOT_INSTALLED;
 }


/* Verify `s' for package operator.
 * We call this func from save_each_pkg() when split packages required/suggested
 * list.
 */
static bool
isop (char *s)
{
    
	static const char *op[] = { ">=", ">", "=", "==", "<=", "<", NULL };
	unsigned i = 0;
	
	if (!s || !s[0])
	_error (1, "\n\n"
	"Fatal error on package operator check. Void data found.\n"
	"Please report this.\n\n");
	
	while (op[i])
	 if (strcmp (s, op[i++]) == 0)
	  return true;
	 
	return false;
}

/* Save each package from `data' string in `dlist' apposite structure, see slackyd.h.
 * Save in `n' number of packages read.
 * If `regval' is 0 and `data' in NULL, we assume that regex is failed and keyword
 * not present, then we set `n' to FEATURE_UNAVAILABLE. Otherwise, if `regval' is non zero
 * and `s' is NULL, set `n' to FEATURE_NOTHIN_SPEC.
 */ 
static void
save_each_pkg (char *data, dep_t **dlist, int *n, int regval)
{
    char *s = NULL, *p = NULL, *addr = NULL;
    dyns tokens = { NULL, 0, 0 };
    unsigned i;
    
    /* if data is NULL set n to feature unspecificed or to feature unavailable,
     * depending to `regval' value (regexec(), see parse_pkg_line() down). */
    if (!data) {
        *n = (regval == 0x00) ? FEATURE_NOTHING_SPEC : FEATURE_UNAVAILABLE;
		return;
    }
	
	s = xstrdup(data);
    addr = s;

    /* Package string maybe "abc >= 1,cde >= 1" or "abc >= 1 cde >= 1".
     * Comma or white space separated.
     */
    p = strtok(s, " ,\t");
    if (!p) {
		free (addr);
		*n = FEATURE_NOTHING_SPEC;
		return;
    }

    /* save each token in temporarey dynamic strings list */
    do {
        /* we ignore OR in data list.
         * -- FIX ME --
         */
         if (p[0] != '|')
          add_dyns (p, &tokens);
    } while ((p = strtok (NULL, " ,\t")) != NULL);

    *n = 0x00;
    /* for each token check for operator and save name, operator and version (if possibile) in apposite
     * structure.
     */
    for (i = 0; i < tokens.nlist; ) {
        
        if (!isop (tokens.list[i])
        &&  i + 2 < tokens.nlist
        &&  isop (tokens.list[i + 1])
        &&  !isop (tokens.list[i + 2]))
        {
            /* add name, operator and version */
            add_dep (tokens.list[i], tokens.list[i + 1], tokens.list[i + 2], 0, dlist, n);
            i += 3;
            /* check again for an operator, this must me an operator, otherwise we stop
             * package parsing.
             */
            if (i < tokens.nlist && isop (tokens.list[i])) {
                break;
            }
        }
        else
        {
            /* if current or next token is an operator, an error occured
             * and we stop package parsing.
             **/
            if (i + 1 < tokens.nlist
            && (isop (tokens.list[i]) || isop (tokens.list[i + 1])))
            {
                break;
            }
            /* add only name, operator and version not specified. */
            add_dep (tokens.list[i], NULL, NULL, 0, dlist, n);
            i++;
            
            
        }
    }

    free (addr);
    free_dyns (&tokens);

    return ;
}


/* Parse package data line `data'.
 * `act' set type of line, _PKG_NAME, _PKG_REQ, _PKG_LOC etc. See slackyd.h.
 * Save data matches in members of apposite `pkg' pkg_t struct.
 *
 * Note:
 *    We use `tlen' to set additional offset of our match.
 *    See strregdup() above.
 *    We use also `token' under `act' status to set keyword to match.
 *    See slackyd.h.
 */
static void
parse_pkgline (char *data, short act, pkg_t **pkg)
{
    char regex[WORDSIZE0];
    regex_t re;
    regmatch_t match;
    char *buff = NULL;
    int tlen = 0x00, cflags = 0x00, regval = 0x00;
    char *token = NULL;

    switch (act) {
        case _PKG_NAME  :  token = PKG_NAME;    break;
        case _PKG_LOC   :  token = PKG_LOC;     break;
        case _PKG_SIZE_C:  token = PKG_SIZE_C; tlen -= 2;  break; /* ignore '(' and ')' */
        case _PKG_SIZE_U:  token = PKG_SIZE_U; tlen -= 2;  break; /* ignore '(' and ')' */
        case _PKG_REQ   :  token = PKG_REQ;     break;
        case _PKG_CNFLT :  token = PKG_CNFLT;   break;
        case _PKG_SGST  :  token = PKG_SGST;    break;
        default:
            slassert (NULL);
            break;
    }
    tlen  += strlen (token);
    
    memset (regex, 0, WORDSIZE0);
    snprintf (regex, WORDSIZE, "(%s)(.*)$", token);
	cflags = REG_EXTENDED | REG_NEWLINE;
    xregcomp (&re, regex, cflags);

    if ((regval = regexec (&re, data, 1, &match, 0)) == 0x00) {
        buff = strregdup (data, match, tlen);
	}
	regfree (&re);
	
	
    switch (act) {
        case _PKG_NAME:
            (*pkg)->name = buff;
            break;
        case _PKG_LOC:
            (*pkg)->location = buff;
            break;
        case _PKG_SIZE_C:
            (*pkg)->size_c = (!regval) ? atoi (buff) : -1;
            free (buff);
            break;
        case _PKG_SIZE_U:
            (*pkg)->size_u = (!regval) ? atoi (buff) : -1;
            free (buff);
            break;
        case _PKG_REQ:
            save_each_pkg (buff, &(*pkg)->required,  &(*pkg)->nrequired, regval);
            free (buff);
            break;
        case _PKG_CNFLT:
            save_each_pkg (buff, &(*pkg)->conflicts, &(*pkg)->nconflicts, regval);
            free (buff);
            break;
        case _PKG_SGST:
            save_each_pkg (buff, &(*pkg)->suggests,  &(*pkg)->nsuggests, regval);
            free (buff);
            break;
        default:
            slassert (NULL);
            break;
    }

    return ;
}

/* Parse package description on global package data `data'.
 * Save description in `desc' member of `pkg' structure.
 * If description not found set `desc' member to NULL.
 */
static void
parse_pkgdesc (char *data, pkg_t **pkg)
{
    char *begin = NULL, *end = NULL;
    int len;

    (*pkg)->desc = NULL;

    /* Match begin and last newlines of our description. */
    begin = strstr (data, PKG_DESC);
    end   = strstr (data, "\n\n");
    /* if description keyword not found return. */
    if (!begin)
     return;
    begin += strlen (PKG_DESC);

    /* Calculate description length.
     * If end is NULL we have not found "\n\n" and we will
     * assume that this is the last packages description il list.
     */
    if (end)
     len = (int) end - (int) begin;
    else
     len = strlen (begin);

    /* Move ptr to skip some characters, if they exist... */
    while (begin[0] == ' '
        || begin[0] == '\t'
        || begin[0] == '\n')
        {
            begin++;
            len--;
        }

    if (len <= 1)
     return;

    /* Ccreate space and copy package description. */
    (*pkg)->desc = xmalloc ((len + 1) * sizeof (char));
    strncpy ((*pkg)->desc, begin, len); 
    (*pkg)->desc[len--] = 0x00;

    /* Remove some character, if found, from our desc copyed. */
    while ((*pkg)->desc[len] == ' '
        || (*pkg)->desc[len] == '\t'
        || (*pkg)->desc[len] == '\n')
        {
            (*pkg)->desc[len--] = 0x00;
        }
    
    return;
}
        
    
/* Split packages data saved in `dpkgs` and save in pkg_t array struct data.
 * `data' will be allocated with malloc(), must be free with free_pkg_t() !
 * Packages data was read from `b' branch of `r' repository from config file.
 *
 */
void
save_pkg_data (dyns dpkgs, pkg_t **data[], unsigned *ndata, int r, int b)
{
	unsigned i, n;
	int inst_index = 0;
	bool need_realloc;

	if (dpkgs.nlist == 0)
     return ;

    need_realloc = true;

    for (i = 0; i < dpkgs.nlist; i++) {

    n = *ndata;

	if (need_realloc)
	{
	    (*data)    = (pkg_t **) xrealloc ((*data), (n + 1) * sizeof (pkg_t *));
	    (*data)[n] = (pkg_t *)  xmalloc (sizeof (pkg_t));
    }
    need_realloc = false;

    (*data)[n]->required   = NULL;
    (*data)[n]->nrequired  = FEATURE_DISABLED;
    (*data)[n]->mrequired  = NULL;
    (*data)[n]->nmrequired = FEATURE_DISABLED;
    (*data)[n]->conflicts  = NULL;
    (*data)[n]->nconflicts = FEATURE_DISABLED;
    (*data)[n]->suggests   = NULL;
    (*data)[n]->nsuggests  = FEATURE_DISABLED;
    (*data)[n]->installed  = NULL;
    (*data)[n]->status     = PACKAGE_STATUS_NOT_CHECKED;
    (*data)[n]->desc       = NULL;
        
	
	parse_pkgline (dpkgs.list[i], _PKG_NAME, &(*data)[n]);

	if (!(*data)[n]->name
     || split_pkg ((*data)[n]->name, &(*data)[n]->pstruct))
	{
	    free ((*data)[n]->name);
	    continue;
    }
    

    (*data)[n]->blacklisted = is_blacklisted ((*data)[n]->name);

    if (opt.blacklist && (*data)[n]->blacklisted)
    {
        free ((*data)[n]->name);
        continue;
    }


    parse_pkgline (dpkgs.list[i], _PKG_LOC, &(*data)[n]);

    if ((*data)[n]->location == NULL)
    {
        free ((*data)[n]->name);
        continue;
    }
	
    

    if (p_fold.size_c)
     parse_pkgline (dpkgs.list[i], _PKG_SIZE_C, &(*data)[n]);
	
	if (p_fold.size_u)
     parse_pkgline (dpkgs.list[i], _PKG_SIZE_U, &(*data)[n]);
	
	if (p_fold.req)
     parse_pkgline (dpkgs.list[i], _PKG_REQ,    &(*data)[n]);

	if (p_fold.conflict)
     parse_pkgline (dpkgs.list[i], _PKG_CNFLT,  &(*data)[n]);

	if (p_fold.suggest)
     parse_pkgline (dpkgs.list[i], _PKG_SGST,   &(*data)[n]);

    if (p_fold.desc)
     parse_pkgdesc (dpkgs.list[i], &(*data)[n]);


	inst_index = pkg_inst_status ((*data)[n]);
	(*data)[n]->status = inst_index;
	if (inst_index >= 0)
	 (*data)[n]->installed = xstrdup (ginst[inst_index].name);

    if (r >= 0)
     (*data)[n]->N_REPO = (unsigned) r;

    if (b >= 0)
     (*data)[n]->branch = (branches) b;
     
    
    ++*ndata;
    need_realloc = true;

    }	/* end for */

	
    /* If last package is abnormal or blacklisted, free
     * last member allocated.
     */
    if (need_realloc == false)
    {
        free ((*data)[n]);
        (*data)[n] = NULL;
    }

    if (*ndata == 0)
    {
        free ((*data));
        *data = NULL;
    }

    return;
}


/* Check version of package `found' required by `required'.
 * Note: `required' isn't a package type pkg_t, normally is
 * just a required type dep_t contained in a pkg_t structure (ex: mrequired)
 */
bool
required_ok (dep_t *required, pkg_t *found)
{
    int diffver = 0;

    /* If no version is specificed we assume that all version available are ok */
    if (required->version == NULL)
     return true;

    diffver = strverscmp (found->pstruct.version, required->version);

    switch (required->op_t) {
        case VERSION_ALL:
            return true;
            break;
        case VERSION_MINOR:
            return (diffver < 0)  ?  true : false;
            break;
        case VERSION_MINOR_OR_EQUAL:
            return (diffver <= 0) ?  true : false;
            break;
        case VERSION_EQUAL:
            return (diffver == 0) ?  true : false;
            break;
        case VERSION_MAJOR:
            return (diffver > 0)  ?  true : false;
            break;
        case VERSION_MAJOR_OR_EQUAL:
            return (diffver >= 0) ?  true : false;
            break;
        default:
            slassert (NULL);
            break;
        
        }

    return 0;
}

/* Check status of a package required.
 * We can not call directly required_ok() function because it
 * receive also a pkg_t pointer.
 */
bool
missing_required_ok (dep_t *required)
{
    pkg_t tmp;
    int rval = 0;
    unsigned pos = 0;

    /* Check for package status, parsing only name.
     * If something was found return his index in global installed
     * packages list `ginst', see slackyd.h.
     */
    do {
        rval = is_installed (required->name, MATCH_EXACTLY_NAME, pos);
        if (rval == PACKAGE_STATUS_NOT_INSTALLED)
         return false;

        tmp.name    = ginst[rval].name;
        tmp.pstruct = ginst[rval].pstruct;

        /* return true if version is ok */
        if (required_ok (required, &tmp))
         return true;
        
        /* Continue with next packages, if they exist.
         * We parse all occurrances and make sure that
         * required package is really missing !
         * For example we can have installed abc 1.0 e abc 2.0;
         * if abc >= 2.0 is required, without this while slackyd assume
         * that abc is missing, because installed packages list is
         * alpha-sorted and slackyd will find abc 1.0 BEFORE abc 2.0 :).
         */
        pos = rval + 1;

     } while (rval != PACKAGE_STATUS_NOT_INSTALLED);

     return false;
}


/* A search_pkg() helper =D
 * Check if package string `s' is conform to match `set_match' (see search_pkg() above).
 */
static bool
pkg_ok (short set_match, regex_t *preg, char *s, const char *pkgname)
{
    char *ptr, buffer[BUFFSIZE0];
    int l;
    pkg_struct tmpstruct;
    
    /* test for initial package data, must be:
     * PACKAGE NAME:  packagename-version-arch-build.tgz
     */
    if (strncmp (s, PKG_NAME, strlen (PKG_NAME)))
     return false;

    /* Now `copy in a buffer package string without initial "PACKAGE NAME:"
     * and use `ptr' to move some characters from movestrlim(). 
     * `ptr' will containe exactly package name, we parse it, using regex or not.
     */
    strncpy (buffer, s + strlen (PKG_NAME), BUFFSIZE);
    ptr = buffer;
    if (movestrlim (&ptr, " :\n\t", 2) == EXIT_FAILURE) {
        return false;
    }

    switch (set_match) {
        case MATCH_ALL_NAME:
            if (!pkgname)
             return true; /* we want all */
            else
             return (xstrstr (ptr, pkgname)) ? true : false;
            break;
        case MATCH_EXACTLY_NAME:
            return (xstrstr (ptr, pkgname)
                 && regexec (preg, ptr, 0, NULL, 0) == 0) ? true : false;
            break;
        case MATCH_EXACTLY_PACKAGE:
            return (!strcmp (ptr, pkgname)) ? true : false;
            break;
            
        case MATCH_USING_REGEX:
            return (regexec (preg, ptr, 0, NULL, 0) == 0) ? true : false;
            break;


        case MATCH_EXACTLY_NAME_INST:
            /* Search package installed by name;
             * For example where `ptr' is "abc-1.2-i486-1.tgz" and we have
             * installed "abc-1.3...", it will be matched because here we
             * consider only package name.
             * If `pkgname' is NULL match all package available with name
             * of one or more packages installed !
             *
             * NOTE: If `pkgname' is non-NULL and isn't contained in `ptr',
             * we return immediatly.
             */
            return ( (!pkgname || xstrstr (ptr, pkgname))
                 &&  !split_pkg (ptr, &tmpstruct)
                 &&  is_installed (tmpstruct.name, MATCH_EXACTLY_NAME, 0) >= 0) ? true : false;
            break;
        
        case MATCH_EXACTLY_PACKAGE_INST:
            /* Search all packages available and installed.
             * Entire package string will be compared.
             */
            if (pkgname && !xstrstr (ptr, pkgname))
             return false;
            l = strlen  (ptr) - 4;
            if (!strncasecmp (ptr + l, ".tgz", 4))
             ptr[l] = 0x00; 
            return (is_installed (ptr, MATCH_EXACTLY_PACKAGE, 0) >= 0) ? true : false;
            break;

        case MATCH_ALL_NAME_BLACK:
            slassert (opt.blacklist == false);
            if (pkgname && !xstrstr (ptr, pkgname))
             return false;
            return (bool) is_blacklisted (ptr);
            break;
        case MATCH_ALL_NAME_BLACK_REG:
            slassert (opt.blacklist == false);
            return (!regexec (preg, ptr, 0, NULL, 0)
                 && is_blacklisted (ptr)) ? true : false;

        
        default:
            puts ("Not implemented");
            abort();
            break;
        
        }

    return false;
}

/* Find package name `pkgname' in `data' and return all occured.
 * If `pkgname' is NULL, force `set_match` to MATCH_ALL_ALL.
 *
 * 'set_match' value set regex:
 *  MATCH_ALL_ALL        : match all packages.
 *  MATCH_ALL_NAME       : match packages by name (*name*").
 *  MATCH_EXACTLY_NAME   : match packages by exactly name (name-*").
 *  MATCH_EXACTLY_PACKAGE: match packages by exactly string (name-version-arch-build.tgz).
 *
 *  If "_INST" variant is used, package must be also installed.
 *  For example MATCH_ALL_ALL_INST match all packages available but also installed.
 * 
 *  Return an array of dynamic strings containing result data.
 */
dyns 
search_pkg (short set_match, char *path, const char *pkgname)
{
    FILE *fd;
    dyns dlist = { NULL, 0, 0 };
    regex_t re;
    int rval, cflags = REG_EXTENDED | REG_NEWLINE | REG_NOSUB;
    unsigned long dsize = 0;
    char buffer[HUGE0], regex[BUFFSIZE0];

    if (!(fd = fopen (path, "r"))) {
        return dlist;
    }

    /* fix package name regex */
    if (!pkgname)
     slassert (set_match != MATCH_EXACTLY_NAME
            && set_match != MATCH_EXACTLY_PACKAGE);

    switch (set_match) {
        case MATCH_EXACTLY_NAME:
	        snprintf (regex, BUFFSIZE,
	          "^%s\\-[^-]+\\-[^-]+\\-[^-]+\\.tgz$" , set_regex (pkgname));
	        break;
        case MATCH_USING_REGEX:
        case MATCH_ALL_NAME_BLACK_REG:
            slassert (pkgname != NULL);
            strncpy (regex, pkgname, BUFFSIZE);
            break;
        default:
            strcpy (regex, ""); /* we compile anyway regex, also when we don't execute it */
            break;
    }
    
    /* compile expression */
    if (opt.case_insensitive)
     cflags |= REG_ICASE;
    xregcomp (&re, regex, cflags);
            
    /* search package in repository file */
    while (fgets (buffer, HUGE, fd)) {
    
    /* check for package ok */
    if (pkg_ok (set_match, &re, buffer, pkgname) == false)
     continue;

    
   /* find end of packages desc (\n\n) and add data to pkgfound array */
	dsize = strlen (buffer); /* "PACKAGE NAME: ..." */

   /* read other data untill end of file of a void line */
    while (fgets (buffer, HUGE, fd) && buffer[0] != '\n')
    {
        dsize += strlen (buffer);
    }

    /* check for max buffer size */
    if (dsize > HUGE) {
        if (opt.warn)
         fprintf (stderr,
         "*** Warning, some package data found in file `%s' seems to be too long. "
         "Continuing.\n", path);
        continue;
    }

    /* go back to package data begin */
    if (fseek (fd, -dsize - 1, SEEK_CUR) != 0) {
        if (opt.warn)
         fprintf (stderr,
         "*** Warning, seek of file `%s' failed: %s. "
         "Continuing.\n", path, strerror (errno));
        continue;
    }

    /* read again data, close string with a 0 and add to dynamic strings list. */
    if ((rval = fread (buffer, 1, dsize, fd)) < (int) dsize) {
        if (opt.warn)
         fprintf (stderr,
         "*** Warning, data read of file `%s' failed: %s. "
         "Continuing.\n", path, strerror (errno));

         fseek (fd, dsize - rval, SEEK_CUR);
         continue;
    }

    buffer[rval] = 0x00;
    
    add_dyns (buffer, &dlist);
    
    }

   regfree (&re);
   fclose (fd);
   
   /* return package(s) list data */
   return dlist;
}


/* read all packages installed and conform to pkgtools standard,
 * save entire name and split it in apposite installed_t pkg_struct sub structure.
 * Return number of packages read and saved, they must will free
 * with free_installed_t () !
 */
unsigned 
installed (installed_t **list)
{
    struct dirent **namelist;
    int n, i;
    unsigned total = 0x00;
    pkg_struct tmpstruct;

    *list = NULL;
    
    n = scandir (PKGDIR, &namelist, 0, alphasort);
    
    if (n < 0)
    _error (5, "\n"
    "Cannot open ", PKGDIR, ": ", strerror (errno), ".\n\n");
	
    if (n <= 2) /* . and .. */
	_error (3, "\n"
	"Directory ", PKGDIR, " seems to be void !?\n\n");
	
    /* ignore . and .. directories */
    free (namelist[0]);
    free (namelist[1]);
    n -= 2;
    for (i = 0; i < n; i++) {
        /* we want just packages conform to pkgtools :) */
        if (split_pkg (namelist[i + 2]->d_name, &tmpstruct) == false) {
            (*list) = (installed_t *)
                xrealloc ((*list), (total + 1) * sizeof (installed_t));
            (*list)[total].name    = xstrdup (namelist[i + 2]->d_name);
	        (*list)[total].pstruct = tmpstruct;
	        total++;
        }
        free (namelist[i + 2]);
    }

    free (namelist);
    return total;
}



/* Verify packages required (if `act' is 0) or suggested (if `act' is 1) by `*package'.
 * If there are missing dependency save it in the same package struct.
 */
void
search_mreq (pkg_t **package, u_char act)
{
    int *n = NULL, *m = NULL, j = 0;
    char *name = NULL;
    dep_t **prequired = NULL, **pmissing = NULL;
    
    slassert (package != NULL);
	
    /* check for action, 0 packages required, 1 packages suggested */
    switch (act) {
        case 0:
            slassert (p_fold.req == true);
            if ((*package)->nrequired == FEATURE_UNAVAILABLE)
             return;
            else
            {
                prequired = &(*package)->required;
                n = &(*package)->nrequired;

                pmissing  = &(*package)->mrequired;
                m = &(*package)->nmrequired;
    	    }
            break;
        case 1:
            slassert (p_fold.suggest == true);
    	    if ((*package)->nsuggests == FEATURE_UNAVAILABLE)
             return ;
            else
            {
                prequired = &(*package)->suggests;
                n = &(*package)->nsuggests;

               /* We save missing packages suggest in
                * missing packages required list !
                */
                pmissing  = &(*package)->mrequired;
                m = &(*package)->nmrequired;
            }
            break;
        default:
            slassert (NULL);
            break;
        }

    /* At this point no dependencies indicated (or checked :?)
     * (we have already checked for FEATURE_UNAVAILABLE !)
     */
    if (*n <= FEATURE_NOTHING_SPEC)
     return ;

    if (*m <= 0) /* number on actual missing packages */
    {
        *m = 0x00;
    }

    /* for each package required or suggested... */
    for (j = 0; j < *n; j++) {

        if (!missing_required_ok (&(*prequired)[j])) {
            add_dep ((*prequired)[j].name, (*prequired)[j].op, (*prequired)[j].version, act, pmissing, m);
            (*pmissing)[*m - 1].type = act;
        }
	    sfree (name);
    }
	
	return ;
}

/* as search_mreq() but for two or more packages :p */ 
void
search_amreq (pkg_t **packages[], unsigned n_pkgs, u_char act)
{
    unsigned i;
        
    /* for each package... */
    for (i = 0; i < n_pkgs; i++)
    {
        search_mreq (&(*packages)[i], act);
    }

    return ;
}


static void
quick_pkg_t (pkg_t **v[], unsigned start, unsigned end)
{
    int l,r, val1, val2, val3;
    pkg_t *ptmp;
    
    
    if (end > start + 1)
    {
         
        l = start + 1;
        r = end;
	
        while (l < r) {
    	     	
        val1 = strcmp ((*v)[l]->pstruct.name,    (*v)[start]->pstruct.name);
        val2 = strcmp ((*v)[l]->pstruct.version, (*v)[start]->pstruct.version);
        val3 = atoi ((*v)[l]->pstruct.build) - atoi ((*v)[start]->pstruct.build);

        if ( (val1 < 0) || (!val1 && val2 > 0) || (!val1 && !val2 && val3 > 0) )
        {
            l++;
        }
        else
        {
            ptmp    = (*v)[l];
            (*v)[l] = (*v)[--r];
            (*v)[r] = ptmp;
        }

        }
        
			
        ptmp        = (*v)[--l];
        (*v)[l]     = (*v)[start];
        (*v)[start] = ptmp;

        quick_pkg_t (v, start, l);
        quick_pkg_t (v, r, end);
    }

    return;
}


/* sort a list of pkt_t and destroy packages not standard */
void sort_pkg (pkg_t **list[], unsigned *n_list)
{
	if (*n_list == 0)
	 return;
	
	quick_pkg_t (list, 0, *n_list);
}

/* search in all MANIFEST required file contained in `need' structure.
 * Add packages found to same structure, see slackyd.h.
 * If `verbose' is true print a warning on missing MANIFEST file.
 *
 * Retunr number of required packages found.
 */ 
static int
search_tgzfile (m_need *need)
{
    FILE *fd;
    char buffer[BUFFSIZE0];
    char *current = NULL;
    unsigned i, j, n, p = 0, ch = 0;
    bool slackware = false;
    
    char regex[BUFFSIZE0];
    regex_t preg;

    /* in our regex we can't match exactly library name because
     * on executable maybe linked a symbolic link, and it is not present in MANIFEST :(
     */
    snprintf (regex, BUFFSIZE, "^\\-.*/%s[\\.0-9]*$", set_regex (need->needed));
    
    xregcomp (&preg, regex, REG_EXTENDED | REG_NEWLINE | REG_NOSUB);
    
    /* for each repository ... */
    for (i = 0; i < REPOS[0].N; i++) {
    
    if (REPOS[i].SLACKWARE && slackware == true)
     continue; /* we have already search in official manifest */
    
    /* for each branch (if repository is official)... */
    for (j = 0; j < BRANCHES; j++) {
    
    /* if current repository is extra increment branch index
     * because we use _PATCHES as first branch !
     * See slackyd.h and packages.c (make_rpath function).
     */
    if (!REPOS[i].SLACKWARE)
     j++;
    snprintf (buffer, BUFFSIZE, "%s", make_rpath (i, j, MANIFEST));
         
    if (!(fd = fopen (buffer, "r")))
    {

        if (REPOS[i].SLACKWARE)
         continue;
        else
         break; /* extra repositories haven't patches or extra branch */ 
    }

    if (get_file_size (fd) == 0)
    {
        if (opt.warn)
        fprintf (stderr, "\n"
        "+----------------------------------------- \n"
        "| Warning: %s's MANIFEST seem to be void ! \n"
        "| Try to update or force update. Skipping. \n"
        "+----------------------------------------- \n", REPOS[i].name);

        if (REPOS[i].SLACKWARE)
         continue;
        else
         break; /* extra repositories haven't patches or extra branch */ 
    }

    while (fgets (buffer, BUFFSIZE, fd)) {
	
    if (strstr (buffer, "||   Package:")) {
     free (current);
     current = xstrdup (strrchr (buffer, '/') + 1);
	}
	
	/* we use before strstr() because is fast ;) */
	if (strstr (buffer, need->needed) &&
		regexec (&preg, buffer, 0, NULL, 0) == 0) {
	
		if (current[strlen (current) - 1] == '\n')
	 	 current[strlen (current) - 1] = 0x00;
    	
        add_dyns (current, &need->res.packages);
     	
     	n = need->res.packages.nlist - 1;

     	if (n > INDEXES) {
        	fprintf (stderr, "\n\n"
        	 "Warning:\n"
        	 " Memory for store indexes finished.\n"
        	 " Too packages found on libraries check !?\n"
        	 " Please report this.\n\n");
        	return EXIT_FAILURE;
     	}
     	
     	/* save index and if repository isn't slackware force branch,
     	 * extra mirror haven't extra or patches
     	 */
     	need->res.index[n]  = i;
     	need->res.branch[n] = (REPOS[i].SLACKWARE == false) ? _PACKAGES : j;
     	p++;
    }
    }

    ch++;
    fclose (fd);

    if (REPOS[i].SLACKWARE == false)
     break; /* we not will search in extra or patches branch */
    
 	} /* end branch for() */
        
    if (REPOS[i].SLACKWARE && slackware == false)
     slackware = true; 
        
    } /* end for() */	
    
    regfree (&preg);
    free (current);
    
    if (ch == 0)
    _error (1, "\n\n"
    "Error, no MANIFEST files found.\n"
    "Try to update or fix your slackyd.conf.\n\n");

    return (p > 0) ? (int) p : -1;
}

/* check for duplicate needed packages  */
static int
dup_needed (m_need *mlibs, unsigned current)
{
    unsigned i, j;

    if (current == 0)
     return -1;
     
    for (i = 0; i < current; i++) /* start from mlibs[0] to mlibs[current] */
    /* for each current packages with our missing shared library ... */
    for (j = 0; j < mlibs[current].res.packages.nlist; j++)
     if (mlibs[i].res.elist &&
         strcmp (mlibs[i].res.elist, mlibs[current].res.packages.list[j]) == 0)
      /* elist member point to only one package choiced.
       * If in current packages list there is elist, we return his index ;) */
      return (int) j;
      
    return -1;
}

/* download packages required.
 * If there are more packages with same needed library here
 * will choice one of them.
 */ 
int
get_tgzile (m_need *mlibs, unsigned n_mlibs)
{
	unsigned i = 0, j = 0, rindex, rbranch, npackages = 0, found = 0;
	int rval = 0;
	char c = '\n', rpath[BUFFSIZE0];
	dyns tmpdata = { NULL, 0, 0 };
	pkg_t **packages= NULL;

	
	/* force to disable blacklist */
    opt.blacklist = false;

    /* try to obtain name of required packages */
    for (i = 0; i < n_mlibs; i++) {
        fprintf (stdout, "Searching %s: ", mlibs[i].needed);
        fflush (stdout);
        
        rval = search_tgzfile (&mlibs[i]);
        if (rval > 0) {
         fprintf (stdout, "found %d package%s !\n", rval, (rval > 1) ? "s" : "");
         found++;
        }
        else
         fprintf (stdout, "nothing found !\n");
    }

    /* print packages found */
    fprintf (stdout, "\n"
    "Result found:\n"
    "-------------\n");
    for (i = 0; i < n_mlibs; i++) {
    	fprintf (stdout, "%s", mlibs[i].needed);

    	switch (mlibs[i].res.packages.nlist) {
    	    case 0:
    	        fprintf (stdout, ": nothing found.\n");
    	        break;
            case 1:
                fprintf (stdout, ": %s [from %s]\n",
                mlibs[i].res.packages.list[0], REPOS[mlibs[i].res.index[0]].name);
                break;
            default:
                fprintf (stdout, ", found different packages:\n");
                for (j = 0; j < mlibs[i].res.packages.nlist; j++)
    	 	     fprintf (stdout, "\t%s [from %s]\n",
    	 	     mlibs[i].res.packages.list[j], REPOS[mlibs[i].res.index[j]].name);
    	 	    break;
		}
	 }

    putchar ('\n');

    if (found == 0) {
        fprintf (stderr, "Nothing to do.\n");
        return EXIT_FAILURE;
    }

    for (i = 0; i < n_mlibs; i++) {

    mlibs[i].res.elist   = NULL;
    mlibs[i].res.eindex  = 0x00;
    mlibs[i].res.ebranch = 0x00;
    
        
    if (mlibs[i].res.packages.nlist == 0  /* no packages with missing library found */
    ||  dup_needed (mlibs, i)       >= 0) /* one (on all ?) package already in list */
     continue;
         
	if (mlibs[i].res.packages.nlist > 1) {

    fprintf (stdout, "Library %s is part of more packages:\n", mlibs[i].needed);

    for (j = 0; j < mlibs[i].res.packages.nlist; j++)
        fprintf (stdout, "%u - %s [from %s]\n",
        j + 1, mlibs[i].res.packages.list[j], REPOS[mlibs[i].res.index[j]].name);

    for (j = 0; j < mlibs[i].res.packages.nlist; j++) {
        
        if (j == mlibs[i].res.packages.nlist - 1) {
        	fprintf (stdout, "Will be used %s !\n\n", mlibs[i].res.packages.list[j]);
			mlibs[i].res.elist   = mlibs[i].res.packages.list  [j];
            mlibs[i].res.eindex  = mlibs[i].res.index [j];
            mlibs[i].res.ebranch = mlibs[i].res.branch[j];
            break;
        }
        
        if (!opt.force)
         fprintf (stdout, "Use %s ? [y/N] ", mlibs[i].res.packages.list[j]);
        if (opt.force || (c = getchar()) == 'y' || c == 'Y') {

            if (opt.force)
             fprintf (stdout, "Will be used %s !\n", mlibs[i].res.packages.list[j]);

            mlibs[i].res.elist   = mlibs[i].res.packages.list[j];
            mlibs[i].res.eindex  = mlibs[i].res.index [j];
            mlibs[i].res.ebranch = mlibs[i].res.branch[j];

            if (c != '\n')
             clrbuff();

            putchar ('\n');
            break;
        }
        else {
        	if (c != '\n')
             clrbuff();
		 }
    }
    }
    else {
        mlibs[i].res.elist   = mlibs[i].res.packages.list[0];
        mlibs[i].res.eindex  = mlibs[i].res.index [0];
        mlibs[i].res.ebranch = mlibs[i].res.branch[0];
    }
    }
    
    for (i = 0; i < n_mlibs; i++) {
	 	
		if (mlibs[i].res.elist == NULL)
		 continue;
		
		rindex  = mlibs[i].res.eindex;
		rbranch = mlibs[i].res.ebranch;
		snprintf (rpath, BUFFSIZE, "%s", make_rpath (rindex, rbranch, PACKAGES));

		tmpdata = search_pkg (MATCH_EXACTLY_PACKAGE, rpath, mlibs[i].res.elist);

	    if (tmpdata.nlist == 0) {
	        if (opt.warn)
	         fprintf (stderr, "\n"
	          "Warning: package %s result in MANIFEST but nothing found in PACKAGES !?\n"
	          "Skipping, try to update.\n",
	        mlibs[i].res.elist);
	        continue;
        }
	    else
	    if (tmpdata.nlist > 1) {
	        if (opt.warn)
	         fprintf (stderr, "\n"
	          "Warning: package %s result in MANIFEST but found more same packages in PACKAGES !?\n"
	          "Skipping, try to update.\n",
	        mlibs[i].res.elist);
	        free_dyns (&tmpdata);
	        continue;
		}
		
		save_pkg_data (tmpdata, &packages, &npackages, rindex, rbranch);
        free_dyns (&tmpdata);
    }

	if (!npackages)
	{
	    fprintf (stdout, "Nothing to do, bad files list. Fix your repository.\n");
	    return EXIT_FAILURE;
    }

    sort_pkg (&packages, &npackages);

    fprintf (stdout, "Packages to get %u: ", npackages);
	for (i = 0; i < npackages - 1; i++)
	 fprintf (stdout, "%s, ", packages[i]->name);
    fprintf (stdout, "%s\n", packages[i]->name);

	get (&packages, &npackages);
	
	free_pkgs_t (&packages, npackages);
	
	return EXIT_SUCCESS;
}
