/** \ingroup rpmio
 * \file rpmio/argv.c
 */

#include "system.h"

#include "rpmio_internal.h"	/* XXX fdGetFILE() */
#include <argv.h>

#include "debug.h"

/*@access FD_t @*/		/* XXX fdGetFILE() */

void argvPrint(const char * msg, ARGV_t argv, FILE * fp)
{
    ARGV_t av;

    if (fp == NULL) fp = stderr;

    if (msg)
	fprintf(fp, "===================================== %s\n", msg);

    if (argv)
    for (av = argv; *av; av++)
	fprintf(fp, "\t%s\n", *av);

}

ARGI_t argiFree(ARGI_t argi)
{
    if (argi) {
	argi->nvals = 0;
	argi->vals = _free(argi->vals);
    }
    argi = _free(argi);
    return NULL;
}

ARGV_t argvFree(/*@only@*/ /*@null@*/ ARGV_t argv)
{
    ARGV_t av;
    
    if (argv)
    for (av = argv; *av; av++)
	*av = _free(*av);
    argv = _free(argv);
    return NULL;
}

int argiCount(ARGI_t argi)
{
    int nvals = 0;
    if (argi)
	nvals = argi->nvals;
    return nvals;
}

ARGint_t argiData(ARGI_t argi)
{
    ARGint_t vals = NULL;
    if (argi && argi->nvals > 0)
	vals = argi->vals;
    return vals;
}

int argvCount(const ARGV_t argv)
{
    int argc = 0;
    if (argv)
    while (argv[argc] != NULL)
	argc++;
    return argc;
}

ARGV_t argvData(ARGV_t argv)
{
/*@-retalias -temptrans @*/
    return argv;
/*@=retalias =temptrans @*/
}

int argiCmp(const void * a, const void * b)
{
    unsigned aint = *(ARGint_t)a;
    unsigned bint = *(ARGint_t)b;
    return ((aint < bint) ? -1 :
	    (aint > bint) ? +1 : 0);
}

int argvCmp(const void * a, const void * b)
{
    ARGstr_t astr = *(ARGV_t)a;
    ARGstr_t bstr = *(ARGV_t)b;
    return strcmp(astr, bstr);
}

int argvStrcasecmp(const void * a, const void * b)
{
    ARGstr_t astr = *(ARGV_t)a;
    ARGstr_t bstr = *(ARGV_t)b;
    return xstrcasecmp(astr, bstr);
}

#if defined(RPM_VENDOR_OPENPKG) /* wildcard-matching-arbitrary-tagnames */
int argvFnmatch(const void * a, const void * b)
{
    ARGstr_t astr = *(ARGV_t)a;
    ARGstr_t bstr = *(ARGV_t)b;
    return (fnmatch(astr, bstr, 0) == 0 ? 0 : 1);
}

int argvFnmatchCasefold(const void * a, const void * b)
{
    ARGstr_t astr = *(ARGV_t)a;
    ARGstr_t bstr = *(ARGV_t)b;
    return (fnmatch(astr, bstr, FNM_CASEFOLD) == 0 ? 0 : 1);
}
#endif

int argiSort(ARGI_t argi, int (*compar)(const void *, const void *))
{
    unsigned nvals = argiCount(argi);
    ARGint_t vals = argiData(argi);
    if (compar == NULL)
	compar = argiCmp;
    if (nvals > 1)
	qsort(vals, nvals, sizeof(*vals), compar);
    return 0;
}

int argvSort(ARGV_t argv, int (*compar)(const void *, const void *))
{
    if (compar == NULL)
	compar = argvCmp;
    qsort(argv, argvCount(argv), sizeof(*argv), compar);
    return 0;
}

ARGV_t argvSearch(ARGV_t argv, ARGstr_t val,
		int (*compar)(const void *, const void *))
{
    if (argv == NULL)
	return NULL;
    if (compar == NULL)
	compar = argvCmp;
    return bsearch(&val, argv, argvCount(argv), sizeof(*argv), compar);
}

#if defined(RPM_VENDOR_OPENPKG) /* wildcard-matching-arbitrary-tagnames */
ARGV_t argvSearchLinear(ARGV_t argv, ARGstr_t val,
		int (*compar)(const void *, const void *))
{
    ARGV_t result;
    ARGV_t av;
    if (argv == NULL)
        return NULL;
    if (compar == NULL)
        compar = argvCmp;
    result = NULL;
    for (av = argv; *av != NULL; av++) {
        if (compar(av, &val) == 0) {
            result = av;
            break;
        }
    }
    return result;
}
#endif

int argiAdd(/*@out@*/ ARGI_t * argip, int ix, int val)
{
    ARGI_t argi;

    if (argip == NULL)
	return -1;
    if (*argip == NULL)
	*argip = xcalloc(1, sizeof(**argip));
    argi = *argip;
    if (ix < 0)
	ix = argi->nvals;
    if (ix >= (int)argi->nvals) {
	argi->vals = xrealloc(argi->vals, (ix + 1) * sizeof(*argi->vals));
	memset(argi->vals + argi->nvals, 0,
		(ix - argi->nvals) * sizeof(*argi->vals));
	argi->nvals = ix + 1;
    }
    argi->vals[ix] = val;
    return 0;
}

int argvAdd(/*@out@*/ ARGV_t * argvp, ARGstr_t val)
{
    ARGV_t argv;
    int argc;

    if (argvp == NULL)
	return -1;
    argc = argvCount(*argvp);
/*@-unqualifiedtrans@*/
    *argvp = xrealloc(*argvp, (argc + 1 + 1) * sizeof(**argvp));
/*@=unqualifiedtrans@*/
    argv = *argvp;
    argv[argc++] = xstrdup(val);
    argv[argc  ] = NULL;
    return 0;
}

int argvAppend(/*@out@*/ ARGV_t * argvp, ARGV_t av)
{
    int ac = argvCount(av);

    if (av != NULL && ac > 0) {
	ARGV_t argv = *argvp;
	int argc = argvCount(argv);

	argv = xrealloc(argv, (argc + ac + 1) * sizeof(*argv));
	while (*av++)
	    argv[argc++] = xstrdup(av[-1]);
	argv[argc] = NULL;
	*argvp = argv;
    }
    return 0;
}

int argvSplit(ARGV_t * argvp, const char * str, const char * seps)
{
    static char whitespace[] = " \f\n\r\t\v";
    char * dest = xmalloc(strlen(str) + 1);
    ARGV_t argv;
    int argc = 1;
    const char * s;
    char * t;
    int c;

    if (seps == NULL)
	seps = whitespace;

    for (argc = 1, s = str, t = dest; (c = (int) *s); s++, t++) {
	if (strchr(seps, c) && !(s[0] == ':' && s[1] == '/' && s[2] == '/')) {
	    argc++;
	    c = (int) '\0';
	}
	*t = (char) c;
    }
    *t = '\0';

    argv = xmalloc( (argc + 1) * sizeof(*argv));

    for (c = 0, s = dest; s < t; s += strlen(s) + 1) {
	/* XXX Skip repeated seperators (i.e. whitespace). */
	if (seps == whitespace && s[0] == '\0')
	    continue;
	argv[c++] = xstrdup(s);
    }
    argv[c] = NULL;
    *argvp = argv;
/*@-nullstate@*/
    return 0;
/*@=nullstate@*/
}

char * argvJoin(ARGV_t argv)
{
    size_t nb = 0;
    int argc;
    char *t, *te;

    for (argc = 0; argv[argc] != NULL; argc++) {
	if (argc != 0)
	    nb++;
	nb += strlen(argv[argc]);
    }
    nb++;

    te = t = xmalloc(nb);
    *te = '\0';
    for (argc = 0; argv[argc] != NULL; argc++) {
	if (argc != 0)
	    *te++ = ' ';
	te = stpcpy(te, argv[argc]);
    }
    *te = '\0';
    return t;
}

/*@-mustmod@*/
int argvFgets(ARGV_t * argvp, void * fd)
{
    FILE * fp = (fd ? fdGetFILE(fd) : stdin);
    ARGV_t av = NULL;
    char buf[BUFSIZ];
    char * b, * be;
    int rc = 0;

    if (fp == NULL)
	return -2;
    while (!rc && (b = fgets(buf, (int)sizeof(buf), fp)) != NULL) {
	buf[sizeof(buf)-1] = '\0';
	be = b + strlen(buf);
	if (be > b) be--;
	while (strchr("\r\n", *be) != NULL)
	    *be-- = '\0';
	rc = argvAdd(&av, b);
    }

    if (!rc)
	rc = ferror(fp);
    if (!rc)
	rc = (feof(fp) ? 0 : 1);
    if (!rc && argvp)
	*argvp = av;
    else
	av = argvFree(av);
    
/*@-nullstate@*/	/* XXX *argvp may be NULL. */
    return rc;
/*@=nullstate@*/
}
/*@=mustmod@*/
