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


/* make repository path for file `file' and mirror `rindex'
 * of external REPOS repository type list, from branch `branch'.
 */
char *
make_rpath (unsigned rindex, branches branch, const char *file) {

    const char *STR_BRANCH[BRANCHES] = { "patches", "", "extra" };
    static char rpath[BUFFSIZE0];
    unsigned r = rindex;
    branches b = branch;

    memset (rpath, 0, BUFFSIZE0);

    if (!file)
     _error (1, "\n\n"
     "Internal error making repository path, file is null !\n"
     "Please report this.\n\n");

    if (rindex > REPOS[0].N)
     _error (1, "\n\n"
     "Error making repository path, index is bigger of all repositories !\n"
     "Please report this.\n\n");

    if (branch > REPOS[0].N)
     _error (1, "\n\n"
     "Error making repository path, branch is bigger of all repository branches !\n"
     "Please report this.\n\n");

    if (REPOS[r].SLACKWARE == false && branch != _PACKAGES)
     _error (1, "\n\n"
     "Error making repository path, repository is extra but branch isn't packages !\n"
     "Please report this.\n\n");

     switch (b) {
        case _PACKAGES:
            snprintf (rpath, BUFFSIZE, "%s/%s/%s",
             DATADIR,
             REPOS[r].name,
             file);
            break;
        default:
            snprintf (rpath, BUFFSIZE, "%s/%s/%s/%s",
             DATADIR,
             REPOS[r].name,
             STR_BRANCH[b],
             file);
            break;
           }
    
    return rpath;
}


char *
pkg_type (pkg_t *package)
{
    static char type[BUFFSIZE0], strstat[WORDSIZE0];
    const char *sbranch[BRANCHES] = { "security fix ", "", "extra package " };
    unsigned r = package->N_REPO;
    branches b = package->branch;

    memset (type, 0, BUFFSIZE);

    /* If we are searching packages to upgrade skip package status string compiling.
     * Package is installed, stop :)
     */
    if (opt.verbose && opt.print_status)
    switch (package->status)
    {
        case PACKAGE_STATUS_NOT_CHECKED:
            strstat[0] = '\b';
            strstat[1] = 0x00;
            break;
        case PACKAGE_STATUS_NOT_INSTALLED:
            strncpy (strstat, "\b, not installed", WORDSIZE);
            break;
        case PACKAGE_STATUS_INSTALLED:
            strncpy (strstat, "\b, installed", WORDSIZE);
            break;
        default:
            snprintf (strstat, WORDSIZE, "\b, installed %s", package->installed);
            break;
    }
    else {
        strstat[0] = '\b';
        strstat[1] = 0x00;
    }

    /* paranoid, force branch if repository is extra */
    if (REPOS[r].SLACKWARE == false)
     package->branch = b = _PACKAGES;
    
    if (opt.verbose && package->size_c > 0 && package->size_u > 0)
     snprintf (type, BUFFSIZE, "[%d KB -> %d KB, %sfrom %s %s]",
        package->size_c,
        package->size_u,
        sbranch[b],
        REPOS[r].name,
        strstat);
    else
    if (opt.verbose)
     snprintf (type, BUFFSIZE, "[%sfrom %s %s]",
        sbranch[b],
        REPOS[r].name,
        strstat);
    else
     snprintf (type, BUFFSIZE, "[%sfrom %s]",
        sbranch[b],
        REPOS[r].name);
    
        
    return type;
}

char is_blacklisted (const char *name)
{
    unsigned i, n = opt.nblacklisted;

    if (!n)
     return 0;

    for (i = 0; i < n; i++)
     if (regexec (&opt.blacklisted[i], name, 0, NULL, 0) == 0x00)
      return (char) true; /* blacklisted */

    return (char) false;
}

/* print `n' `packages' data (name and if verbose switch is
 * actived also print package compressed/uncompressed size and package status).
 * See pkg_type() above !
 */
void
show_pkg_found (pkg_t **packages, unsigned n)
{
    unsigned i;
    
    for (i = 0; i < n; i++)
    {
        if (!opt.blacklist || !packages[i]->blacklisted)
         fprintf (stdout, "%s %s\n", packages[i]->name, pkg_type (packages[i]));
    }
    
    return;
}


int
search (short flag, const char *name, pkg_t **packages[], unsigned *found)
{
    unsigned r, b;
    char path[BUFFSIZE0];
    dyns tmpdata = { NULL, 0, 0 };
    bool slackware = false;
    
    *packages = NULL;
    *found    = 0x00;

    /* for each repository ... */
    for (r = 0; r < REPOS[0].N; r++)
    {

    /* we use just one official repository */
    if (REPOS[r].SLACKWARE && slackware)
     continue;
    else
    if (REPOS[r].SLACKWARE && !slackware)
     slackware = true;

    /* for each branch ... */
    for (b = 0; b < BRANCHES; b++)
    {

    /* if current repository is extra we increment j.
     * First `sbranch' string is "patches", extra repository haven't them !
     */
    if (!REPOS[r].SLACKWARE)
     b++;

    snprintf (path, BUFFSIZE, "%s", make_rpath (r, b, PACKAGES));

    tmpdata = search_pkg (flag, path, name);

    save_pkg_data (tmpdata, packages, found, (int) r, (int) b);

    free_dyns (&tmpdata);
    
    if (!REPOS[r].SLACKWARE)
     break; /* extra repository haven't patches or extra */
    
	
	}
	}

	/* if nothing found return -1 */
    return (*found > 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

/* normally our packages list is alpha-sorted.
 * This function simply search same packages (only name).
 */ 
static int
same_pkg (pkg_t *packages[], unsigned n, unsigned index)
{
    unsigned i = index + 1;
    int p = 1;

    while (i < n)
    if (strcmp (packages[index]->pstruct.name, packages[i++]->pstruct.name) == 0)
     ++p;
    else
     break;

    return p;
}

/* this function remove from `n' elements list packages with same name of `packages[index]`.
 * All next package with same same will be removed !
 * This because on download, for example, we may want get just one package on all !
 */
static void
rm_same (pkg_t **packages[], unsigned *n, unsigned index)
{
    unsigned i = index;

    while (*n > 0 && i < *n - 1
        && strcasecmp ((*packages)[i]->pstruct.name, (*packages)[i + 1]->pstruct.name) == 0)
    {
        rm_pkg_t (packages, n, i + 1);
    }

    return;
}
    

/* download package(s) */
void
get (pkg_t **packages[], unsigned *n_pkgs)
{
    unsigned i, j, c, same;
    unsigned n_dep = 0, instcnt = 0, choiced = 0, not_choiced = 0, downloaded = 0;
    pkg_t **dep = NULL;
    bool ch;

    if (*n_pkgs == 0)
     return;

    putchar ('\n');
    
    /* check packages required and suggested
     * if feature wasn't disabled
     */
    if (opt.check_dep)
    {
        fprintf (stdout, "Checking dependencies: ");
    	 fflush (stdout);
        search_amreq (packages, *n_pkgs, 0);
	    search_amreq (packages, *n_pkgs, 1);
	    clrline (stdout, 36);
	}
	
  
    
    /* for each element in `package' array */
    for (i = 0; i < *n_pkgs; ) {

    /* check for package already installed */
    if (!opt.skip_status && (*packages)[i]->status >= PACKAGE_STATUS_INSTALLED)
    {
        if (opt.warn)
        {
            fprintf (stdout, "*** %s: already installed", (*packages)[i]->name);
            if ((*packages)[i]->status > PACKAGE_STATUS_INSTALLED)
             fprintf (stdout, " (%s)", (*packages)[i]->installed);
            fprintf (stdout, ".\n");
        }
        rm_pkg_t (packages, n_pkgs, i);
        instcnt++;
        continue;
    }

    ch = false;

    same = same_pkg (*packages, *n_pkgs, i);

    if (same > 1)
    {
        fprintf (stdout, "Found %d different version of %s:\n", same, (*packages)[i]->pstruct.name);
        for (j = 0; j < same; j++)
         fprintf (stdout, " --> %s\n", (*packages)[i + j]->name);
        if (opt.force)
        {
            fprintf (stdout, "Will be used %s %s\n", (*packages)[i]->name, pkg_type ((*packages)[i]));
            rm_same (packages, n_pkgs, i);
	    }
    }

    ch = false;
    if (opt.force)
     ch = true;
    else
    do
    {
        fprintf (stdout, "Download %s %s ? [y/N] ", (*packages)[i]->name, pkg_type ((*packages)[i]));
	    fflush (stdout);
	    if ((c = getc(stdin)) == 'Y' || c == 'y')
	    {
	        rm_same (packages, n_pkgs, i);
	        ch = true;
        }
        else
        {
            --same;
            rm_pkg_t (packages, n_pkgs, i);
        }
	    if (c != '\n')
         clrbuff();
    } while (!ch && same > 0 && *n_pkgs > 0);

    if (!ch)
    {
        not_choiced++;
        continue;
    }
     
    choiced++;

    
    /* search missing packages required in repository.
     * If found ask if download. */
	if (opt.check_dep && opt.resolve_dep)
    {
	    /* choice dependencies */
        choice_dep (&dep, &n_dep, (*packages)[i], *packages, *n_pkgs);
	}
	
	i++;

    } /* end for() */


    /* Free local static blacklist */
    choice_dep (NULL, NULL, NULL, NULL, 0);
    
    if (instcnt >= *n_pkgs
     && not_choiced == 0
     && choiced     == 0)
     {
         fprintf (stdout, "\n"
         "All packages available are installed.\n");
         return;
    }
    else
    if (choiced == 0) {
        fprintf (stdout, "\n"
        "Nothing was choiced.\n");
        return;
    }

    if (!opt.force)
     putchar ('\n');
    
    /* download dependencies if feature wasn't disabled and verify if check was completed successfull */
    for (i = 0; i < n_dep; i++) {
	    if (get_pkg (dep[i]) == EXIT_SUCCESS && opt.hash)
		 verify_md5 (dep[i]);
	}

    free_pkgs_t (&dep, n_dep);

	/* download packages */
    for (i = 0; i < *n_pkgs; i++) {
        if (get_pkg ((*packages)[i]) == EXIT_SUCCESS && opt.hash) {
            downloaded++;
            verify_md5 ((*packages)[i]);
    }
    }

    /* if notify of broken dependencies was enabled ... */
    if (downloaded > 0 && opt.notify_dep) {
		show_missing_req (packages, *n_pkgs);
		make_missing_req_list (packages, *n_pkgs);
    }

    return;
}

/* search available packages and download build sources or tarball sources if possibile */
void
get_src (char *pkgname)
{
	char buffer [BUFFSIZE0] = { 0 }, path_list[BUFFSIZE] = { 0 };
	char *list = NULL, *pwd = NULL;
    int sflag, same;
	pkg_t **pkg;
	unsigned i, j, n_pkg = 0, n = 0;
	char c;
	bool ch;

	pkgsrc_t *sources = NULL;
	unsigned n_sources = 0;
	pkg_struct pstruct;
	pid_t bpid;
	
	packages_list_check (true, true);

    fprintf (stdout, "Searching `%s': ", pkgname);
    fflush (stdout);
    
    sflag = (pkgname && opt.use_regex) ? MATCH_USING_REGEX : MATCH_ALL_NAME;
    if (search (sflag, pkgname, &pkg, &n_pkg) != EXIT_SUCCESS) {
		fprintf (stdout, "nothing found.\n");
		return;
    }
    fprintf (stdout, "found %d packages.\n\n", n_pkg);
    sort_pkg (&pkg, &n_pkg);

    /* select sources of packages found */
    if (!opt.force)
	for (i = 0; i < n_pkg; )
	{
	    if ((same = same_pkg (pkg, n_pkg, i)) > 1) {
	        fprintf (stdout, "Found different version of package `%s':\n", pkg[i]->pstruct.name);
	        for (j = 0; j < (unsigned) same; j++)
	        {
	            fprintf (stdout, "--> %s\n", pkg[j + i]->name);
            }
        }

        ch = false;
        do {

            fprintf (stdout, "Download %s sources %s ? [y/N] ", pkg[i]->name, pkg_type (pkg[i]));
            fflush (stdout);

            if ((c = getchar()) == 'Y' || c == 'y') {
                rm_same (&pkg, &n_pkg, i);
                i++;
                ch = true;
            }
            else {
                rm_pkg_t (&pkg, &n_pkg, i);
                --same;
            }
            if (c != '\n')
             clrbuff();
         } while (!ch && same > 0 && n_pkg > 0);
	}
	
	if (!n_pkg) {
	    fprintf (stderr, "\n"
	    "Nothing was choiced.\n");
	    return ;
    }

    putchar ('\n');
	
	/* now search sources and download, if possibile */
	
	for (i = 0; i < n_pkg; i++) {
	
	/* load FILELIST.TXT and search available sources */
	snprintf (path_list, BUFFSIZE, "%s/%s/%s", DATADIR, REPOS[pkg[i]->N_REPO].name, FILELIST);
	if (fsload(path_list, &list)) {
	    fprintf (stderr, "\n"
	    "Error: cannot open %s's files list.\n"
	    "Try to update or fix your slackyd.conf.\n",
		REPOS[pkg[i]->N_REPO].name);
	    continue;
	}

	/* if build sources and tarball aren't available continue with next package */
	if (search_src (list, pkg[i], &sources, &n_sources)) {
		sfree (list);
		continue;
	}

	/* download files */
	gepkgsrc_ts (pkg[i], sources, n_sources);
	
	/* cleanup */
	while (n_sources--)
	 free_dyns (&sources[n_sources].fsubpath);
	n_sources = 0x00;
	sfree (sources);
	sfree (list);
	
	n++;
	}
	
	
	/* if we want build... */
    if (n > 0 && opt.buildsrc) {
   	
   	if (getuid())
   	 _error (1, "Error: you must be root for build a program !\n\n");
   	
   	fprintf (stdout, "\n"
   	"Trying to build.\n");
   
   	pwd = getenv ("PWD");
   	for (i = 0; i < n_pkg; i++) {
	
		if (!pkg[i] || split_pkg (pkg[i]->name, &pstruct))
		continue;
    
    	snprintf (buffer, BUFFSIZE, "%s/%s-%s_%s/",
    	SRCDIR, pstruct.name, pstruct.version, REPOS[pkg[i]->N_REPO].name);
    
		fprintf (stdout, "Entering in %s: ", buffer);
    	if (chdir (buffer) < 0) {
    	 fprintf (stdout, "%s\n", strerror (errno));
    	 continue;
    	}
    	fprintf (stdout, "ok.\n");
   	
   		snprintf (buffer, BUFFSIZE, "sh %s.SlackBuild --cleanup", pstruct.name);
		
		if ((bpid = fork()) < 0)
		 _error (1, "\n"
		 "Cannot make a fork.\n\n");
		else
		if (bpid == 0) {
			/* temp code, must be fix */
			system (buffer);
		}
		else wait (NULL);
    
	}
	chdir (pwd);
	
	}

	
	free_pkgs_t (&pkg, n_pkg);
	return;
}


/* verify md5 sum ok package from argument. */
void
verify_md5 (pkg_t *package)
{
    char path[BUFFSIZE0] = { 0 };
    char *hash = NULL;
    unsigned r = package->N_REPO;
    branches b = package->branch;

    /* checksum path */
    hash = getmd5 (package->name, make_rpath (r, b, CHECKSUMS));
    
    if (hash) {
		/* downloaded package path */
		snprintf (path, BUFFSIZE, "%s/%s", DATADIR, package->name);
		md5file(path, hash);
    }
    
    free (hash);

    return;
}


/* show dependencies */
static void
show_packages_required (pkg_t *package)
{

    char depstatus[BUFFSIZE0];
    unsigned i;
    
    fprintf (stdout, "> Packages required: ");

    if (package->nrequired == FEATURE_DISABLED) {
		fprintf (stdout, "disabled.\n");
		return;
	}
	else
	if (package->nrequired == FEATURE_UNAVAILABLE) {
		fprintf (stdout, "not available.\n");
		return;
    }
    else
    if (package->nrequired == FEATURE_NOTHING_SPEC) {
		fprintf (stdout, "nothing.\n");
		return;
    }
	
    putchar('\n');
    
    for (i = 0; i < (unsigned) package->nrequired; i++) {

        /* verify if dependency is installed */
        if (missing_required_ok (&package->required[i]))
         snprintf (depstatus, BUFFSIZE, "INSTALLED");
        else
         snprintf (depstatus, BUFFSIZE, "NOT INSTALLED");
	    
	    switch (package->required[i].op[0]) {
            case 0x00:
                fprintf (stdout, "  |--- %s [%s]\n",
                    package->required[i].name,
                    depstatus);
	            break;
            default:
                fprintf (stdout, "  |--- %s %s %s [%s]\n",
                    package->required[i].name,
                    package->required[i].op,
                    package->required[i].version,
                    depstatus);
	            break;
    }
	}
	
    return;
}

/* show package info */
void
show_package_info (pkg_t *packages[], unsigned n_pkgs)
{
    unsigned i, j;

    putchar('\n');

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

	fprintf (stdout, "> %s\n", packages[i]->name);

	if (p_fold.desc == true)
	    fprintf (stdout, "%s\n",
	    (packages[i]->desc) ? packages[i]->desc : ">> Nothing desc.");


    
    switch (packages[i]->status)
    {
        case PACKAGE_STATUS_NOT_CHECKED:
            break;
        case PACKAGE_STATUS_NOT_INSTALLED:
            fprintf (stdout, "> Status  : Not installed.\n");
            break;
        case PACKAGE_STATUS_INSTALLED:
            fprintf (stdout, "> Status  : Installed.\n");
            break;
        default:
            fprintf (stdout, "> Status  : Installed (%s).\n", packages[i]->installed);
            break;
    }
        
	fprintf (stdout, "> Location: [%s] %s\n",
	    REPOS[packages[i]->N_REPO].name, packages[i]->location);

	if (p_fold.size_c == true) {
	    if (packages[i]->size_c > 0)
		 fprintf (stdout, "> Size Compressed: %d KB\n", packages[i]->size_c);
	    else
		 fprintf (stdout, "> Size Compressed: not specified.\n");
	}

	if (p_fold.size_u == true) {
	    if (packages[i]->size_u > 0)
		 fprintf (stdout, "> Size Decompressed: %d KB\n", packages[i]->size_u);
	    else
		 fprintf (stdout, "> Size Decompressed: not specificed.\n");
	}

	if (p_fold.conflict == true) {
	    fprintf (stdout, "> Package conflict with: ");

	    if (packages[i]->nconflicts == FEATURE_DISABLED) {
			fprintf (stdout, "disabled.\n");
	    }
	    else
	    if (packages[i]->nconflicts == FEATURE_UNAVAILABLE) {
			fprintf (stdout, "not available.\n");
	    }
	    else
	    if (packages[i]->nconflicts == FEATURE_NOTHING_SPEC) {
			fprintf (stdout, "nothing.\n");
	    }
	    else {
	    	putchar('\n');
			for (j = 0; j < (unsigned) packages[i]->nconflicts; j++)
		     fprintf (stdout, "    >>> %s\n", packages[i]->conflicts[j].name);
	    }
	}

	if (p_fold.suggest == true) {
	    fprintf (stdout, "> Packages suggest: ");

	    if (packages[i]->nsuggests == FEATURE_DISABLED) {
			fprintf (stdout, "disabled.\n");
	    }
	    else
	    if (packages[i]->nsuggests == FEATURE_UNAVAILABLE) {
			fprintf (stdout, "not available.\n");
	    }
	    else
	    if (packages[i]->nsuggests == FEATURE_NOTHING_SPEC) {
	    	fprintf (stdout, "nothing.\n");
	    }
	    else {
	    	putchar('\n');
			for (j = 0; j < (unsigned) packages[i]->nsuggests; j++)
		    	fprintf (stdout, "    >>> %s\n", packages[i]->suggests[j].name);
	    }
	}

	if (p_fold.req == true)
	 show_packages_required (packages[i]);
	
	if (i != (unsigned) n_pkgs - 1)
	 putchar('\n');
    }

    return;
}

/* show missing dependency for each packages required and downloaded */
void
show_missing_req (pkg_t ** packages[], unsigned n_pkgs)
{
    long i, j;

    putchar('\n');
    for (i = 0; (unsigned) i < n_pkgs; i++)
    {
    	if ((*packages)[i]->nmrequired > 0) 
    	{
    		fprintf (stdout, "Missing dependency of %s, %d:\n", (*packages)[i]->name, (*packages)[i]->nmrequired);
    		
    		for (j = 0; j < (*packages)[i]->nmrequired; j++)
    		{
    			fprintf (stdout, ">>>  %s", (*packages)[i]->mrequired[j].name);
    			if ((*packages)[i]->mrequired[j].op[0])
    			 fprintf (stdout, " %s %s", (*packages)[i]->mrequired[j].op, (*packages)[i]->mrequired[j].version);
    			putchar ('\n');
			}
		}
	}
	
    putchar('\n');
    return;

}

/* save all packages required not installed. Return total. */
int
make_missing_req_list (pkg_t **packages[], unsigned packages_found)
{
    dyns mreq_list = { NULL, 0, 0 };
    int tmp;
    long i, j;

    /* for each package ... */
    for (i = 0; (unsigned) i < packages_found; i++)
	/* for each missing package required ... */
	for (j = 0; j < (*packages)[i]->nmrequired; j++) {

	/* verify if dependency already is in list */
	for (tmp = 0; (unsigned) tmp < mreq_list.nlist; tmp++)
	 if (!strcasecmp (mreq_list.list[tmp], (*packages)[i]->mrequired[j].name)) {
	 	tmp = -1;
	 	break;
	 }

	/* if tmp is -1 missing package is already in list. Then skipping. */
	if (tmp != -1)
	add_dyns ((*packages)[i]->mrequired[j].name, &mreq_list);
		
	}
	
    /* print result */
    if (mreq_list.nlist > 0) {
		fprintf (stdout,
		"+----------------------------------+\n"
		"| Total missing packages, %d:\n" "| ", mreq_list.nlist);

		for (i = 1; (unsigned) i < mreq_list.nlist; i++)
	    fprintf (stdout, "%s,%s ",
	    mreq_list.list[i - 1], (!(i % 5)) ? "\n|" : "");
		
		fprintf (stdout, 
		"%s.\n"
		 "+----------------------------------+\n",
		mreq_list.list[i - 1]);
    }
	
	i = mreq_list.nlist;
	free_dyns (&mreq_list);
    return i;
}


/* Split package `pkg' struct (name, version, arch and build) and save tokens
 * to `data' package struct type.
 */
bool split_pkg
(const char *pkg, pkg_struct *pstruct)
{
    char name[PACKAGE_SIZE + 1], *ptr = NULL;
    int i = 0, len = strlen (pkg);
    unsigned t = 0;

    /* Normally a package containe 4 token: name, version, arch and build.
     * Each token is '-' separated.
     * Our package must have at least 3 '-' (name-version-arch-build) otherwise
     * we consider it as abnormal package, ignore and print a warning.
     */
    const unsigned short ntoken = 3;

    strncpy (name, pkg, PACKAGE_SIZE);
    while (i < len) {
        if (name[i++] == '-' && name[i] != '-')
         t++;
    }
    if (t < ntoken) {
        if (opt.warn)
         fprintf (stderr, "Warning: package `%s' isn't standard. Skipping.\n", pkg);
        return true;
    }

    len -= 4; /* ".tgz" length */
    if (len < 0) {
        if (opt.warn)
         fprintf (stderr, "Warning: unexpected error parsing package `%s'. Skipping.\n", pkg);
        return true;
    }
    
    if (strcasecmp (name + len, ".tgz") == 0)
     name[len] = 0x00;


    ptr = strrchr (name, '-');
    if (strlen (ptr) >= BUILDSIZE) {
        if (opt.warn)
         fprintf (stderr, "Warning: build token of package `%s' is too long. Skipping.\n", pkg);
        return true;
    }
    strncpy (pstruct->build, ptr + 1, BUILDSIZE);
    *ptr = 0x00;

    ptr = strrchr (name, '-');
    if (strlen (ptr) >= ARCHSIZE) {
        if (opt.warn)
         fprintf (stderr, "Warning: arch token of package `%s' is too long. Skipping.\n", pkg);
        return true;
    }
    strncpy (pstruct->arch, ptr + 1, ARCHSIZE);
    *ptr = 0x00;

    ptr = strrchr (name, '-');
    if (strlen (ptr) >= VERSIONSIZE) {
        if (opt.warn)
         fprintf (stderr, "Warning: version token of package `%s' is too long. Skipping.\n", pkg);
        return true;
    }
    strncpy (pstruct->version, ptr + 1, VERSIONSIZE);
    *ptr = 0x00;

    if (strlen (name) >= NAMESIZE) {
        if (opt.warn)
         fprintf (stderr, "Warning: name token of package `%s' is too long. Skipping.\n", pkg);
        return true;
    }
    strncpy (pstruct->name, name, NAMESIZE);
    
    return false;
}


static void
add_choiced (pkg_t *required, pkg_t **dep[], unsigned *ndep)
{

        unsigned n = *ndep;

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

        cp_pkg (required, &((*dep)[n]));
		
		++*ndep;

        return ;
}



/* for each missing `pkg''s dependencies or package suggest,
 * search it in repository and if found a package ask if it must be downloaded.
 * If dependency is choiced, we will add it to `dep' packages list and we will
 * increment `n_dep'.
 * Before select a package we search before for package already choiced (`all' list)
 * or package missing (`missing' local dynamic string list).
 *
 * Note:
 * Before search a required package we find it in a local static blacklist `missing'.
 * It must be free after last choice_dep() call, calling again this function with
 * all parameter NULL.
 */ 
void
choice_dep (pkg_t  **dep[], unsigned *n_dep, pkg_t *pkg,
            pkg_t *all[], unsigned nall)
{
    int i;
    unsigned j, nis;
    char c;
    unsigned found;
    pkg_t **dep_tmp = NULL;
    bool added, is;

    static dyns missing = { NULL, 0, 0 };

    if (!dep && !n_dep && !pkg) {
        free_dyns (&missing);
        return ;
    }

    /* For each package required by `pkg'
     * and missing ...
     */
    for (i = 0; i < pkg->nmrequired; i++) {

    /* Check for required package missing, and search exactly package by name
     * in repository.
     */
    /* Set temporarey found to 1, we don't repeat warning ;) */
    found = 1;
    if (check_dyns (pkg->mrequired[i].name, missing)
    || search (MATCH_EXACTLY_NAME, pkg->mrequired[i].name, &dep_tmp, &found) == EXIT_FAILURE)
    {       
       if (found == 0) /* nothing found in search() and package not present in missing packages list */
       { 
           if (opt.warn)
           {
               fprintf (stderr, "Warning: missing %s required by %s !\n",
                   pkg->mrequired[i].name,
                   pkg->pstruct.name);
           }
           add_dyns (pkg->mrequired[i].name, &missing);
       }
       continue;
    }

    /* test package required version or alredy choiced */
    j = nis = 0;
    while (found > 0 && j < found) {

        is = false;

        if (required_ok (&pkg->mrequired[i], dep_tmp[j])             /* package version ok */
        &&  !(is = is_pkg (dep_tmp[j]->name, *dep, *n_dep))          /* package already in dependencies choiced list */
        &&  !(is = is_pkg (dep_tmp[j]->pstruct.name, *dep, *n_dep))  /* package already in dependencies choiced list */
        &&  !(is = is_pkg (dep_tmp[j]->name, all, nall))             /* package already choiced */
        &&  !(is = is_pkg (dep_tmp[j]->pstruct.name, *dep, *n_dep))) /* package already choiced */
        {
            j++;
        }
        else
        {
            rm_pkg_t (&dep_tmp, &found, j);
            if (is) /* package removed because already choiced */
             nis++;
        }
    }

    /* At this point, if `found' is zero some packages was found but
     * they are already choiced, as packages to get or as packages required.
     * In other case is possible that one or more packages was found, but with
     * a version different of version required.
     * Then print a warning and go to begin for().
     */
    if (found == 0) {
        if (opt.warn && nis == 0) {
            if (pkg->mrequired[i].op[0] == 0x00)
                fprintf (stderr, "Warning: missing %s required by %s !\n",
                    pkg->mrequired[i].name,
                    pkg->pstruct.name);
            else
                fprintf (stderr, "Warning: missing %s %s %s required by %s !\n",
                    pkg->mrequired[i].name,
                    pkg->mrequired[i].op,
                    pkg->mrequired[i].version,
                    pkg->pstruct.name);
        }

        continue;
    }

    /* Print type of package, required or suggested.
     */
    slassert (pkg->mrequired[i].type == 0 || pkg->mrequired[i].type == 1);
    
    if (!opt.force || opt.verbose)
    {
        fprintf (stdout, ">> %s ", (pkg->mrequired[i].type == 0) ? "Required" : "Suggested");
        if (pkg->mrequired[i].op[0] == 0x00)
         fprintf (stdout, "%s [by %s]\n",
            pkg->mrequired[i].name,
            pkg->name);
	    else
	     fprintf (stdout, "%s %s %s [by %s]\n",
            pkg->mrequired[i].name,
            pkg->mrequired[i].op,
            pkg->mrequired[i].version,
            pkg->name);
	}
	
    sort_pkg (&dep_tmp, &found);
    
    /* If more packages required are available,
     * before ask what download, print all result.
     */
    if (!opt.force && found >= 2) {
        fprintf (stdout, "Found %d different packages:\n", found);
	    for (j = 0; j < found; j++)
	    {
            fprintf (stdout, "  --> %s %s\n", dep_tmp[j]->name, pkg_type (dep_tmp[j]));
        }
    }

    /* For each package found, while a package will be choiced, ask if download ... */
	added = false;
	c = '\n';
    for (j = 0; added == false && j < found; j++)
	{
	    if (!opt.force) {
            fprintf (stdout, "Download %s %s ? [y/N] ", dep_tmp[j]->name, pkg_type (dep_tmp[j]));
             fflush (stdout);
            c = getc(stdin);
        }
        
        if (opt.force || c == 'Y' || c == 'y') {

            add_choiced (dep_tmp[j], dep, n_dep);

            if (!opt.force && found >= 2)
            {
                fprintf (stdout, "Will be used %s %s\n", dep_tmp[j]->name, pkg_type (dep_tmp[j]));
            }

            added = true;
        }

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


    free_pkgs_t (&dep_tmp, found);

   /* If  a package required was found and choiced, check it dependencies
    * and, if something miss, call again recursively choice_dep()
    */ 
	if (added)
	{
	    search_mreq (&(*dep)[*n_dep - 1], 0);
	    choice_dep (dep, n_dep, (*dep)[*n_dep - 1], all, nall);
    }

    }	/* end for() */

    return;
}


/* clean slackyd directory from file with a determinate extension.
 * remove all if extension is NULL. */
int
purge (const char *extension)
{
    int i, r = 0;
    char buffer[BUFFSIZE0] = { 0 };
	
    /* incomplete downloads */
    r += clean_dir (DATADIR, "part");
	
    if (!extension) {
	/* remove slackware official mirror data */
	snprintf (buffer, BUFFSIZE, "%s/slackware/extra", DATADIR);
	r += clean_dir(buffer, NULL);
	snprintf (buffer, BUFFSIZE, "%s/slackware/patches", DATADIR);
	r += clean_dir(buffer, NULL);
	snprintf (buffer, BUFFSIZE, "%s/slackware", DATADIR);
	r += clean_dir(buffer, NULL);

	/* remove slackware extra mirror data */
	for (i = 0; i < REPOS[0].N; i++) {
	    if (REPOS[i].SLACKWARE)
		continue;

	    /* extra path */
	    snprintf (buffer, BUFFSIZE, "%s/%s/extra", DATADIR, REPOS[i].name);
	    r += clean_dir(buffer, NULL);
	    /* repositories directory patches path */
	    snprintf (buffer, BUFFSIZE, "%s/%s/patches", DATADIR, REPOS[i].name);
	    r += clean_dir(buffer, NULL);
	    /* repositories directory path */
	    snprintf (buffer, BUFFSIZE, "%s/%s", DATADIR, REPOS[i].name);
	    r += clean_dir(buffer, NULL);
	}
	r += clean_dir(DATADIR, NULL);
    }
    else
	r += clean_dir (DATADIR, extension);

    return r;
}

static void free_lpath (dyns *ptr)
{
    long int i;

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

    return;
}

int
check_shared_dep (const char *pkg)
{
    int cflags = REG_EXTENDED|REG_NOSUB;
    regex_t preg;
    
    m_need *mlibs = NULL;
    unsigned i, j, n_mlibs = 0, checked = 0;
    char buffer[BUFFSIZE0] = { 0 }, path[BUFFSIZE0] = { 0 };
    FILE *fd;
    dyns libpath = { NULL, 0, 0 };
    
    /* load paths where search shared libraries.
     * Now we can't know binary rpath, we use a fake string to reserve first array place.
     */
    process_lib_path ("FAKE_PATH !", &libpath);
	
    if (pkg && opt.use_regex)
    {
        if (opt.case_insensitive)
         cflags |= REG_ICASE;
        xregcomp (&preg, pkg, cflags);
    }
    
    /* for each package installed ... */
    for (i = 0; i < nginst; i++) {

    if ((pkg && opt.use_regex == true  && regexec (&preg, ginst[i].name, 0, NULL, 0))
     || (pkg && opt.use_regex == false && !xstrstr (ginst[i].name, pkg)))
    {
        continue;
    }
    	
    /* /var/log/packages/<pkgname> */
    snprintf (buffer, BUFFSIZE, "%s/%s", PKGDIR, ginst[i].name);

    fprintf (stdout, "Checking %s: ", ginst[i].name);
    fflush (stdout);

    if (!(fd = fopen (buffer, "r"))) {
		perror(buffer);
		continue;
	}

    /* move stream to skip package descriptions */
    while (1) {
	if (!fgets (buffer, BUFFSIZE, fd) || !strcmp (buffer, "FILE LIST:\n"))
	 break;
    }
    if (strcmp (buffer, "FILE LIST:\n") != 0) {
    	if (opt.warn)
    	 fprintf (stdout, "\n"
    	 "Warning: current package don't seem pkgtools standard ! Skipping.\n");
    	 fclose (fd);
    	 continue;
	 }
    

    while (fgets(buffer, BUFFSIZE, fd)) {

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

		snprintf (path, BUFFSIZE, "/%s", buffer);
		
		check_elf_libs (&libpath, path, &mlibs, &n_mlibs, ginst[i].name);
    }
	
	fclose (fd);
    fprintf (stdout, "done !\n");

    checked++;
    
    } /* end for() */

    if (pkg && opt.use_regex)
     regfree (&preg);
    
    if (checked == 0) {
     if (pkg)
      fprintf (stdout, "No packages \"%s\" are installed !\n", pkg);
     else
      _error (1, "No packages checked !?\n\n");
     free_lpath (&libpath);
     return EXIT_FAILURE;
    }
	     
    if (n_mlibs == 0) {
     fprintf (stdout, "\n"
	 "All packages \"%s\" checked are ok !\n", (pkg) ? pkg : "\b");
	 free_lpath (&libpath);
     return EXIT_SUCCESS;
    }


    fprintf (stdout, "\n"
    "Found %u missing dependencies:\n", n_mlibs);

	for (i = 0; i < n_mlibs; i++) {
	    fprintf (stdout, "%s required by:\n", mlibs[i].needed);

	    for (j = 0; j < mlibs[i].pkgname.nlist; j++)
	     fprintf (stdout,
	     " --> Package: %s (%s)\n",
	     mlibs[i].pkgname.list[j], mlibs[i].object.list[j]);
    }

    putchar ('\n');

	get_tgzile (mlibs, n_mlibs);
    
    /* cleanup */
    free_lpath (&libpath);
    for (i = 0; i < n_mlibs; i++) {
    	free_dyns (&mlibs[i].pkgname);
    	free_dyns (&mlibs[i].object);
    	free_dyns (&mlibs[i].res.packages);
	}
	free (mlibs);

    return EXIT_SUCCESS;
}


/* Obtain installed packages compressed and uncompressed size. */
static int
installed_packages_sz (const char *pkg, unsigned *compressed, unsigned *uncompressed)
{
	const unsigned maxline       = 5; /* We parse max 5 line */
	const char compressedstr  [] = "COMPRESSED PACKAGE SIZE:";
	const char uncompressedstr[] = "UNCOMPRESSED PACKAGE SIZE:";
	const int compressedlen      = strlen (compressedstr);
	const int uncompressedlen    = strlen (uncompressedstr);
	
	
	FILE *fd;
	char buffer[BUFFSIZE0], line[BUFFSIZE0];
	unsigned i;
	
	snprintf (buffer,BUFFSIZE, "%s/%s", PKGDIR, pkg);
	if (!(fd = fopen (buffer, "r")))
	 return -1;
	
	*compressed   = 0x00;
	*uncompressed = 0x00;
	
	for (i = 0; (!*compressed || !*uncompressed) && i < maxline; i++)
	{
		if (!fgets (line, BUFFSIZE, fd)) break;
		
		if (strncmp (line, compressedstr, compressedlen) == 0)
		{
			*compressed = atoi (line + compressedlen);
		}
		else
		if (strncmp (line, uncompressedstr, uncompressedlen) == 0)
		{
			*uncompressed = atoi (line + uncompressedlen);
		}
	}
	
	fclose (fd);
	return (*compressed > 0 && *uncompressed > 0) ? 0 : -1;
}
	
/* show packages installed */
void
installed_packages (const char *name)
{
    unsigned i, inst = 0, compressed = 0, uncompressed = 0;
    regex_t preg;
    int cflags = REG_EXTENDED | REG_NOSUB;
    
    if (!name)
     fprintf (stdout, "All packages installed: ");
    else
     fprintf (stdout, "Installed packages \"%s\": ", name);

    if (opt.case_insensitive)
     cflags |= REG_ICASE;

    if (name && opt.use_regex)
     xregcomp (&preg, name, cflags);
    
    for (i = 0; i < nginst; i++) {

        if (!name
        || (opt.use_regex  && !regexec (&preg, ginst[i].name, 0, NULL, 0))
        || (!opt.use_regex && xstrstr (ginst[i].name, name)))        
        {
		    fprintf (stdout, "%s%s.tgz", (!inst) ? "\n\n" : "", ginst[i].name);
		    
		    if (opt.verbose && !installed_packages_sz (ginst[i].name, &compressed, &uncompressed))
		     fprintf (stdout, " [%u KB -> %u KB]", compressed, uncompressed);
		    
		    putchar ('\n');
		    
			inst++;
		}
	}

    if (!inst)
     fprintf (stdout, "nothing.\n");

    if (name && opt.use_regex)
     regfree (&preg);
    return;
}


/* Search non-slackware packages. 
 * for each package installed, search it in slackware packages data. 
 */
bool
unofficial_packages (const char *name)
{
    unsigned r, i, ch = 0, missingbr = 0;
    branches j = 0;
    dyns tmpdata, packages = { NULL, 0, 0 }, unoff = { NULL, 0, 0 };
    char path[BUFFSIZE0], tgz[WORDSIZE0];
    char *p, *m;
    regex_t preg;
    int cflags = REG_EXTENDED | REG_NOSUB;
    
    packages_list_check (true, true);

    /* check official mirror in config file */
    for (r = 0; r < REPOS[0].N && REPOS[r].SLACKWARE == false; r++)
    ;
    if (r >= REPOS[0].N) {
        fprintf (stderr, "\n"
        "No official repository in your slackyd.conf.\n"
        "Please fix it.\n");
        return true;
    }

    
    do { /* for each branch ... */
    
    /* official files list path */
    snprintf (path, BUFFSIZE, "%s", make_rpath (r, j, PACKAGES));

    /* load all packages */
    tmpdata = search_pkg (MATCH_ALL_NAME, path, NULL);

    if (tmpdata.nlist == 0)
     missingbr++;
    else
    for (i = 0; i < tmpdata.nlist; i++) {

        m = tmpdata.list[i] + strlen (PKG_NAME); /* point after 'PACKAGE NAME:' */
        * (p = strchr (tmpdata.list[i], '\n')) = 0x00; /* close string, we ignore next data */
        movestrlim (&m, " \t\n:", 2);
       
        add_dyns (m, &packages);
    }
    free_dyns (&tmpdata);

    } while (++j < BRANCHES);


    if (missingbr > 0) {
        fprintf (stderr, "Broken slackware packages list. Have you update ?\n");
        return true;
    }


    if (!name)
     fprintf (stdout, "Unofficial packages: ");
    else
     fprintf (stdout, "Unofficial packages \"%s\": ", name);

    fflush (stdout);
    
    
    if (name && opt.use_regex) {
        if (opt.case_insensitive)
         cflags |= REG_ICASE;
        xregcomp (&preg, name, cflags);
    }

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

        /* if name is non null we wont test just installed packages
         * containing `name'.
         */
         if ((name && opt.use_regex == true  && regexec (&preg, ginst[i].name, 0, NULL, 0))
          || (name && opt.use_regex == false && !xstrstr (ginst[i].name, name)))
          {
              continue;
          }
         
        snprintf (tgz, WORDSIZE, "%s.tgz", ginst[i].name);
        /* search current installed package in all official packages loaded before */
        if (check_dyns (tgz, packages) == false) {
            * (p = strrchr (tgz, '.')) = 0x00;
            add_dyns (tgz, &unoff);
        }

        ch++;
    }
 
    if (name && opt.use_regex)
     regfree (&preg);
    free_dyns (&packages);
    
    /* print result */
    switch (ch) {
        case 0:
            if (name)
             fprintf (stdout, "no packages \"%s\" installed.\n", name);
            else
             _error (1, "\n"
             "Fatal error on official packages check.\n"
             "No available official packages found.\n"
             "Try to update packages list.\n\n");
             break;
        default:
            if (unoff.nlist == 0)
             fprintf (stdout, "nothing on %u installed.\n", ch);
            else
             fprintf (stdout, "%u\n\n", unoff.nlist);

            for (i = 0; i < unoff.nlist; i++)
             fprintf (stdout, " -> %s\n", unoff.list[i]);
            break;
    }

    free_dyns (&unoff);
    
    return false;
}


/* show available packages blacklisted by config file */
bool
blacklisted_packages (const char *pkgname)
{
    pkg_t **packages = NULL;
    unsigned i, n = 0x00;
    int sflags;

    if (opt.nblacklisted == 0) {
        fprintf (stderr, "No packages blacklisted in config file.\n");
        return true;
    }

    packages_list_check (true, true);

    /* force blacklist disable, otherwise
     * packages blacklisted will not saved.
     */
    opt.blacklist = false;

    if (!pkgname)
     fprintf (stdout, "Blacklisted packages: ");
    else
     fprintf (stdout, "Blacklisted packages `%s': ", pkgname);
    fflush (stdout);

    sflags = (!pkgname || !opt.use_regex) ? MATCH_ALL_NAME_BLACK : MATCH_ALL_NAME_BLACK_REG;

    if (search (sflags, pkgname, &packages, &n) == EXIT_FAILURE)
    {
        fprintf (stdout, "nothing.\n");
        return true;
    }

    printf ("%u\n\n", n);
    
    for (i = 0; i < n; i++)
    {
        if (packages[i]->blacklisted)
         fprintf (stdout, " --> %s\n", packages[i]->name);
        free_pkg_t (&packages[i]);
    }
    free (packages); fflush (stdout);

    return false;
}

/* search source tarball of package `pkg' in `list' files list.
 * Il found only one location save it in `srcline', otherwise nothing to do...
 */
static int
find_tarball (char *list, const char *pkg, char **srcline)
{
	pkg_struct pstruct;
	char buffer [BUFFSIZE0] = { 0 }, *ptr = list;
	char name[WORDSIZE0], version[WORDSIZE0];
    int rval, matches = 0;
	regex_t preg;
	regmatch_t match;
	
    
	if (split_pkg ((char *) pkg, &pstruct)) {
        fprintf (stderr, "\n"
        "Fatal error at line %d of file %s.\n"
        "Please report this.\n\n", __LINE__, __FILE__);
        abort();
    }
    strncpy (name, set_regex (pstruct.name), WORDSIZE);
    strncpy (version, set_regex (pstruct.version), WORDSIZE);

	
	snprintf (buffer, BUFFSIZE, "^.*/%s\\-%s\\.tar\\.[bz2g]{2,3}$", name, version);
	
	xregcomp (&preg, buffer, REG_EXTENDED | REG_NEWLINE);
	
	while (!(rval = regexec (&preg, ptr, 1, &match, 0))) {
		
		ptr += match.rm_eo;
		matches++;
	}
	
	regfree (&preg);
	
	if (matches == 1) {
	    *srcline = strregdup (list, match, 0);
    }
        
	return matches;
}


/* compile pkgsrc_t data structure 
 * 
 * regt: type of regex used
 * src : struct data to fill
 * s   : occurrance from files list (-rwxrwxrwx root root [...] /dir/.../build-file) 
 * pkgname: name of package
 */
static void
pkgsrccomp (int regt, pkgsrc_t *src, const char *s, const char *pkgname)
{
    char buffer[BUFFSIZE0] = { 0 };
    char *sdup = xstrdup(s), *sptr = sdup, *pbuff = { NULL };
    char name[WORDSIZE0], version[WORDSIZE0];
    pkg_struct pstruct;
    regex_t preg;
    regmatch_t match;
    int rval;

    
    if (split_pkg (pkgname, &pstruct)) {
        fprintf (stderr, "\n"
        "Fatal error at line %d of file %s.\n"
        "Please report this.\n\n", __LINE__, __FILE__);
        abort();
    }
    strncpy (name, set_regex (pstruct.name), WORDSIZE);
    strncpy (version, set_regex (pstruct.version), WORDSIZE);

    switch (regt) {

    case REGSRC_A:
	snprintf (buffer, BUFFSIZE, "/source/.*/%s/", name);
	break;
	
	case REGSRC_B:
	snprintf (buffer, BUFFSIZE, "/.*/source/%s/", name);
	break;
	
	case REGSRC_C:
	snprintf (buffer, BUFFSIZE, "/.*/%s/%s/src/", name, version);
	break;
	
	}
		
	/* get file location */
	xregcomp (&preg, buffer, REG_EXTENDED);

	if ((rval = regexec (&preg, s, 1, &match, 0)) && rval != REG_NOMATCH)
		_error (1, "\n"
		"Error: cannot execute regex on source subthree parsing.\n"
		"Please report this.\n\n");
	else
	if (rval == REG_NOMATCH)
		_error (5, "\n\n"
		"Error: matching failed, pattern not found [", buffer, " - ", s, "].\n\n");
	
	
	/* save build file repository base path */
	match.rm_eo--;
	pbuff = strregdup (sdup,match, 0); 
	sdup += match.rm_eo;
	strncpy (src->rpath, pbuff, BUFFSIZE);
	
	/* cleanup */
	sfree (pbuff);
	regfree (&preg);
	
	/* get file sub-location for adjust local source three */
	src->fsubpath.list = NULL;
	src->fsubpath.nlist = 0;
	while ((pbuff = strchr(sdup, '/'))) {
	   	*pbuff = 0x00;
	   	add_dyns (sdup, &src->fsubpath);
	   	sdup += (unsigned) pbuff - (unsigned) sdup + 1;
	}
	
	/* save file name */
	strncpy(src->file, sdup, WORDSIZE);
	
    sfree (sptr); /* free sptr -> sdup */
    return;
}


/* try to find repository source organization.
 * 
 * list: repository files list
 * pkgname: name of package
 * regex: location where save regex 
 */

static int
test_regsrc (const char *list, char *pkgname, char **regex)
{
    char buffer[BUFFSIZE0] = { 0 };
    int rval;
    pkg_struct pstruct;
    regex_t preg;
    char name[WORDSIZE0], version[WORDSIZE0];

    if (split_pkg (pkgname, &pstruct))
	 return EXIT_FAILURE;
    strncpy (name, set_regex (pstruct.name), WORDSIZE);
    strncpy (version, set_regex (pstruct.version), WORDSIZE);


    /* testing `data' for "/source/.../name" three */
    snprintf (buffer, BUFFSIZE, "^\\-.*/source/.*/%s/.*$", name);

    xregcomp (&preg, buffer, REG_EXTENDED | REG_NEWLINE | REG_NOSUB);

	rval = regexec (&preg, list, 0, NULL, 0);

    regfree (&preg);

    if (!rval) {
		*regex = xstrdup (buffer);
		return REGSRC_A;
    }


	/* testing `data' for "source/name" three */
    snprintf (buffer, BUFFSIZE, "^\\-.*/source/%s/.*$", name);

    xregcomp(&preg, buffer, REG_EXTENDED | REG_NEWLINE | REG_NOSUB);

	rval = regexec(&preg, list, 0, NULL, 0);

    regfree (&preg);

    if (!rval) {
		*regex = xstrdup (buffer);
		return REGSRC_B;
    }


    /* testing `data' for "/name/version/src" three */
    snprintf (buffer, BUFFSIZE, "^\\-.*/%s/%s/src/.*$", name, version);
    
    xregcomp (&preg, buffer, REG_EXTENDED | REG_NEWLINE | REG_NOSUB);

	rval = regexec(&preg, list, 0, NULL, 0);

    regfree (&preg);

    if (!rval) {
		*regex = xstrdup (buffer);
		return REGSRC_C;
    }

    return REGSRC_NOMATCH;	/* nothing found in `data' */
}

/* search package build source for package `pkg' */
bool
search_src
(char *list, pkg_t *pkg, pkgsrc_t **sources, unsigned *n_sources)
{
    int rval, tbrval;
    char *ptr = NULL, *fsrc = NULL,
    	 *exp = NULL, *tbline = NULL, *pbuff = NULL;
    
    u_short j;
    regex_t preg;
    regmatch_t match;
    bool rtarball = false;
	
	fprintf (stdout, "Searching build sources of %s: ", pkg->name);
	fflush (stdout);
	
	rval = test_regsrc (list, pkg->name, &exp);
	
	/* build sources not found; search tarball */
	if (rval != REGSRC_A && rval != REGSRC_B && rval != REGSRC_C) {
	    fprintf (stdout, "not found. Searching tarball: ");
	    fflush (stdout);
	    
	    tbrval = find_tarball (list, pkg->name, &tbline);
	    
	    if (!tbrval) 
	     fprintf (stdout, "not found.\n\n");
	    else
	    if (tbrval > 1)
	     fprintf (stdout, "too sources !.\n\n");
	    else
	     sfree (tbline);
	    	
	    if (tbrval != 1)
	     return true;
		}
		
	fprintf (stdout, "found.\n");
	
	if (rval == REGSRC_A || rval == REGSRC_B || rval == REGSRC_C) {
		
	if (regcomp(&preg, exp, REG_EXTENDED | REG_NEWLINE))
	 _error (1, "\n\n"
	 "Error: regex compilation failed. Please report this.\n\n");

	ptr = list;
	
	if (!regexec(&preg, ptr, 1, &match, 0))
	do {
	        fsrc = strregdup (ptr, match, 0); 
			*sources = xrealloc (*sources, (*n_sources + 1) * sizeof (pkgsrc_t));
			pkgsrccomp (rval, &(*sources)[*n_sources], fsrc, pkg->name);
			++*n_sources;
			sfree (fsrc);
			ptr += match.rm_eo;
	    }
	    while (!regexec(&preg, ptr, 1, &match, 0));
	else
	 _error (1, "\n"
	 "Error: regex execution failed. Please report this.\n\n");


	regfree (&preg);
	}

	tbrval = find_tarball (list, pkg->name, &tbline);
	if (tbrval == 1) { /* one tarball found, ok */
		
		/* make space for a new build entry */
		*sources = xrealloc (*sources, (*n_sources + 1) * sizeof (pkgsrc_t));
		
		j = *n_sources;
		
		(*sources)[j].fsubpath.nlist = 0;
		(*sources)[j].fsubpath.list = NULL;
		if (!(pbuff = strchr (tbline, '/')) || !(ptr = strrchr (tbline, '/')))
			_error (3, "\n"
			"Unexpected error on function search_src ().\n"
			"Please report this.\n\n");
		
		*ptr = 0x00;
		snprintf ((*sources)[j].rpath, BUFFSIZE, "%s/", pbuff);
		snprintf ((*sources)[j].file, BUFFSIZE, ptr + 1);
		sfree (tbline);
		
		++*n_sources;
		
		/* check for tarball download:
		 * see all build files found, if there isn't package tarball sources, download it */
		rtarball = false;
		for (j = 0; j < *n_sources - 1; j++)
		 if (!strcmp ((*sources)[j].file, (*sources)[*n_sources - 1].file)) {
		 	rtarball = true;
		 	break;
		}
	}
	else
	if (opt.warn)
	 fprintf (stderr, "Warning: source tarball maybe not found !\n");

	sfree (exp);
	return false;
}

static rstat_t
make_rstat (unsigned index, pkg_t **packages, unsigned n)
{
    unsigned i;
    rstat_t s;

    memset (&s, 0, sizeof (s));

    s.slackware = REPOS[index].SLACKWARE;
    snprintf (s.rname, BUFFSIZE, REPOS[index].name);
    snprintf (s.raddr, BUFFSIZE, REPOS[index].hostname);

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

        if (packages[i]->N_REPO != index)
         continue;

        switch ((int) packages[i]->branch) {
            case _PACKAGES:
                s.n_packages++;
                s.packages_sz[RS_COMPRESSED]   += packages[i]->size_c;
                s.packages_sz[RS_UNCOMPRESSED] += packages[i]->size_u;
                break;
            case _PATCHES:
                s.n_patches++;
                s.patches_sz[RS_COMPRESSED]   += packages[i]->size_c;
                s.patches_sz[RS_UNCOMPRESSED] += packages[i]->size_u;
                break;
            case _EXTRA:
                s.n_extra++;
                s.extra_sz[RS_COMPRESSED]   += packages[i]->size_c;
                s.extra_sz[RS_UNCOMPRESSED] += packages[i]->size_u;
                break;
            default:
                abort();
                break;
         }
         
     }

     s.n_total = s.n_packages + s.n_patches + s.n_extra;
     s.total_sz[RS_COMPRESSED] =
        s.packages_sz[RS_COMPRESSED] +
        s.patches_sz [RS_COMPRESSED] +
        s.extra_sz   [RS_COMPRESSED];
     s.total_sz[RS_UNCOMPRESSED] =
        s.packages_sz[RS_UNCOMPRESSED] +
        s.patches_sz [RS_UNCOMPRESSED] +
        s.extra_sz   [RS_UNCOMPRESSED];
        
     return s;
 }

static void
print_rstat (rstat_t s)
{
    fprintf (stdout,
    "Stats of %s [%s]:\n", s.rname, s.raddr);
    fprintf (stdout,
    "   Official: %s\n", (s.slackware) ? "yes" : "no");
    fprintf (stdout,
    "   Packages: [%u]\n", s.n_packages);

    if (s.n_packages > 0)
    fprintf (stdout,
    "      Compressed size  : [%u MB]\n"
    "      Uncompressed size: [%u MB]\n",
    s.packages_sz[RS_COMPRESSED]   / 1024,
    s.packages_sz[RS_UNCOMPRESSED] / 1024);
    
    if (s.slackware) {
        
    fprintf (stdout,
    "   Patches : [%u]\n", s.n_patches);
    if (s.n_patches > 0)
    fprintf (stdout,
    "      Compressed size  : [%u MB]\n"
    "      Uncompressed size: [%u MB]\n",
    s.patches_sz [RS_COMPRESSED]   / 1024,
    s.patches_sz [RS_UNCOMPRESSED] / 1024);

    fprintf (stdout,
    "   Extra   : [%u]\n", s.n_extra);
    if (s.n_extra > 0)
    fprintf (stdout,
    "      Compressed size  : [%u MB]\n"
    "      Uncompressed size: [%u MB]\n",
    s.extra_sz [RS_COMPRESSED]   / 1024,
    s.extra_sz [RS_UNCOMPRESSED] / 1024);
    
     fprintf (stdout,
    "   All     : [%u]\n", s.n_total);

    if (s.n_total > 0)
    fprintf (stdout,
    "      Compressed size  : [%u MB]\n"
    "      Uncompressed size: [%u MB]\n",
    s.total_sz[RS_COMPRESSED]   / 1024,
    s.total_sz[RS_UNCOMPRESSED] / 1024);

    }

    putchar ('\n');

    return;
}
 
int
rstat (void)
{
    unsigned long i, total = 0, total_sz[2] = { 0, 0 };
    unsigned npackages, blacklisted = 0;
    bool slackware = false;
    pkg_t **packages = NULL;
    rstat_t stats;

    packages_list_check (true, true);

    /* force to enable black list */
    opt.blacklist = true;

    fprintf (stdout, "Making repositories statistics.\n");
    
    fprintf (stdout, "Reading packages data: ");
    fflush (stdout);    

    if (search (MATCH_ALL_NAME, NULL, &packages, &npackages) != EXIT_SUCCESS) {
         fprintf (stderr, "failed. Try to update.\n");
         return EXIT_FAILURE;
    }

    /* count blacklisted */
    for (i = 0; i < npackages; i++)
     if (packages[i]->blacklisted)
      blacklisted++;
    
    fprintf (stdout, "ok.\n");

    fprintf (stdout,
    "-------------------------------\n");

    for (i = 0; i < REPOS[0].N; i++) {

        if (REPOS[i].SLACKWARE && slackware)
         continue;
         
        memset ((void *) &stats, 0, sizeof (rstat_t));
        stats = make_rstat (i, packages, npackages);

        total += stats.n_total;
        total_sz [RS_COMPRESSED]   += stats.total_sz[RS_COMPRESSED];
        total_sz [RS_UNCOMPRESSED] += stats.total_sz[RS_UNCOMPRESSED];
        
        print_rstat (stats);
    
        if (REPOS[i].SLACKWARE)
         slackware = true;
     }

    fprintf (stdout,
    "-------------------------------\n"
    "All packages: %lu", total);

    if (blacklisted > 0)
     fprintf (stdout, " (%u blacklisted)", blacklisted);
     
    fprintf (stdout, ".\n"
    "    Compressed size  : [%lu MB]\n"
    "    Uncompressed size: [%lu MB]\n",
    total_sz[RS_COMPRESSED] / 1024, total_sz[RS_UNCOMPRESSED] / 1024);
    
    free_pkgs_t (&packages, npackages);
    
    return EXIT_SUCCESS;
 }


static int
pkg_occ (pkg_t *packages[], unsigned npackages, const char *name, unsigned *pindex)
{
    int rval = 0;
    unsigned n;

    for (n = 0; n < npackages; n++)
    {
        if (!strcmp (packages[n]->name, name))
        {
            *pindex = n;
            rval++;
        }
    }
    
    if (rval == 1 && packages[*pindex]->nrequired == FEATURE_UNAVAILABLE)
     rval = FEATURE_UNAVAILABLE;
     
    return rval;
}
        
void
search_packager_lib (const char *name)
{
    unsigned i, npackages = 0, ntocheck = 0, pindex = 0;
    unsigned unsupported = 0;
    int ch = 0, sk = 0, rval, cflags = REG_EXTENDED | REG_NOSUB, sflag;
    regex_t preg;
    pkg_t **packages = NULL, **tocheck = NULL;
    char tgz[WORDSIZE0];
    
    pkg_t **dep    = NULL;
    unsigned ndep  = 0;
    
    packages_list_check (true, true);

    if (name)
    {
        sflag = (!opt.use_regex) ? MATCH_ALL_NAME : MATCH_USING_REGEX;
        ch = is_installed (name, sflag, 0);
        if (ch == PACKAGE_STATUS_NOT_INSTALLED)
        {
            fprintf (stderr, "No packages \"%s\" are installed.\n", name);
            return;
        }
    }
    else
     ch = nginst;

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

    fprintf (stdout, "Loading packages data: ");
    fflush (stdout);

    sflag = (!name || !opt.use_regex) ? MATCH_EXACTLY_PACKAGE_INST : MATCH_USING_REGEX;
    search (sflag, name, &packages, &npackages);
    if (npackages == 0) {
        if (!name)
        _error (1, "error.\n\n"
        "No packages loaded.\n"
        "Try to update or fix your slackyd.conf.\n\n");

        fprintf (stdout, "no packages `%s' available.\n", name);
        return;
    }
    else
    fprintf (stdout, "done.\n");

        
    fprintf (stdout, "Searching dependencies: ");
     fflush (stdout);
    for (i = 0; i < npackages; i++)
    {
        search_mreq (&packages[i], 0);
        search_mreq (&packages[i], 1);
    }
    fprintf (stdout, "done.\n");

    if (name && opt.use_regex) {
        if (opt.case_insensitive)
         cflags |= REG_ICASE;
        xregcomp (&preg, name, cflags);
    }

    for (i = 0; i < nginst; i++) {
        
    if ((name && opt.use_regex  && regexec (&preg, ginst[i].name, 0, NULL, 0))
     || (name && !opt.use_regex && !xstrstr (ginst[i].name, name)))
     {
         continue;
     }
    

    snprintf (tgz, WORDSIZE, "%s.tgz", ginst[i].name);

    rval = pkg_occ (packages, npackages, tgz, &pindex);

    switch (rval) {
        case FEATURE_UNAVAILABLE:
            sk++;
            unsupported++;
            if (opt.verbose && opt.warn)
             fprintf (stderr, "Warning: package %s haven't dependencies indicated !\n", ginst[i].name);
            break;
        case 0:
            sk++;
            if (opt.verbose && opt.warn)
             fprintf (stderr, "Warning: package %s not found in repositories !\n", ginst[i].name);
            break;
        case 1:
            if (packages[pindex]->nmrequired > 0)
             add_pkg_t (&tocheck, &ntocheck, packages[pindex]);
            break;
        default:
            sk++;
            if (opt.verbose && opt.warn)
             fprintf (stderr, "Warning: too packages %s found in repositories ! Skipping.\n", ginst[i].name);
            break;
    }
    }

    if (name && opt.use_regex)
     regfree (&preg);
    free_pkgs_t (&packages, npackages);

    if (sk >= ch) { /* all skipped */
        if (!name)
        fprintf (stdout, "\n"
        "Nothing to do, %u skipped on %u, run in verbose mode to see details.\n", sk, ch);
        else
        fprintf (stdout, "\n"
        "Nothing to do on \"%s\", is it unsupported ?\n", name);

        return;
    }

    if (sk >= 1)
    {
        fprintf (stdout, "\n"
        "%u packages to check, %u successfull "
        "checked, %u unsupported on %u skipped.\n", ch, ch - sk, unsupported, sk);
        if (!opt.verbose)
         fprintf (stdout, "Run in verbose mode to see warnings.\n");
    }

    putchar ('\n');
    

    if (ntocheck == 0) {
        if (name)
         fprintf (stdout, "All packages \"%s\" verified are ok.\n", name);
        else
         fprintf (stdout, "All packages verified are ok.\n");
    }
    else
    for (i = 0; i < ntocheck; i++) {
        choice_dep (&dep, &ndep, tocheck[i], dep, ndep);
    }

    /* Free local missing packages list */
    choice_dep (NULL, NULL, NULL, 0, 0);

    free_pkgs_t (&tocheck, ntocheck);
    
    if (!ndep)
    {
        fprintf (stdout, "\nNothing to do.\n");
        return;
    }


    if (!opt.force)
     putchar ('\n');

    
    for (i = 0; i < ndep; i++)
    {
        if (get_pkg (dep[i]) == EXIT_SUCCESS)
        {
            verify_md5 (dep[i]);
        }
    }
    
    free_pkgs_t (&dep, ndep);
    
    return;
}
