/*
 * xscandir.c - wrapper for scandir 
 * Copyright (C) 1999, 2000 Rex Feany <laeos@laeos.net>
 * 
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_CTYPE_H
# include <ctype.h>
#endif
#ifdef HAVE_STDIO_H
# include <stdio.h>
#endif
#ifdef HAVE_ASSERT_H
# include <assert.h>
#endif
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

/* Small parts shamelessly stolen from ircII help.c */
#if defined(HAVE_DIRENT_H) || defined(_POSIX_SOURCE)
# include <dirent.h>
# define NLENGTH(d) (strlen((d)->d_name))
#else				/* DIRENT || _POSIX_SOURCE */
# define dirent direct
# define NLENGTH(d) ((d)->d_namlen)
# ifdef HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif				/* HAVE_SYS_NDIR_H */
# ifdef HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif				/* HAVE_SYS_DIR_H */
# ifdef HAVE_NDIR_H
#  include <ndir.h>
# endif				/* HAVE_NDIR_H */
#endif				/* HAVE_DIRENT_H || _POSIX_VERSION */

#include "irc.h"
#include "ircaux.h"
#include "output.h"
#include "misc.h"
#include "struct.h"
#include "window.h"
#include "tcommand.h"
#include "ircterm.h"
#include "fset.h"
#include "output.h"
#include "lister.h"

/* used by scandir to sort help entries */
static int dcompare(const struct dirent **d1, const struct dirent **d2)
{
    return strcasecmp((*d1)->d_name, (*d2)->d_name);
}

/* we use these to keep track of what we should look for */
static char *prefix_str;
static int prefix_len;

/* might as well find the longest, its easy */
static int the_longest;

/* used by scandir to select entries */
static int dselect(struct dirent *d)
{
    int t;

    if (*(d->d_name) == '.')
	return 0;

    if (prefix_len) {
	if (strncmp(prefix_str, d->d_name, prefix_len))
	    return 0;
    }

    t = NLENGTH(d);

    if (t > the_longest)
	the_longest = t;

    return 1;
}

/* return the name from the dentry, for list */
static const char *d_name(void *ent)
{
    return ((struct dirent *) ent)->d_name;
}

/*
 * This is a wrapper for scandir.
 * It matches, it lists, and it pretty prints. If there is only
 * one match, we return the name in **ret (xmalloc'ed of course)
 * we return the return value from scandir. 
 *
 * if an error occurs, we return a negative value,
 * and set **ret to a text error message.
 *
 */

int xscandir(char *dir, char *prefix, char **ret)
{
    struct dirent **list = NULL;
    int retval = 0;
    int cnt = 0;
    int i;

    assert(dir);
    assert(ret);

    prefix_len = 0;

    if (prefix) {
	prefix_str = prefix;
	prefix_len = strlen(prefix);
    }

    the_longest = 0;
    cnt = retval = scandir(dir, &list, (void *)dselect, dcompare);

    switch (retval) {

    case -1:
	*ret = strerror(errno);
	break;
    case 0:
	break;

    case 1:
	*ret = m_strdup((*list)->d_name);
	break;

    default:
	/* hrm. if we get an exact prefix match, we want it */
	if (prefix_len && strcmp(prefix, (*list)->d_name) == 0) {
	    *ret = m_strdup((*list)->d_name);
	    retval = 1;
	    break;
	}

	put_fmt(FORMAT_SCANDIR_LIST_HEADER_FSET, NULL);
	display_list_cl((ARRAY *) list, d_name, FORMAT_SCANDIR_LIST_LINE_FSET, retval, the_longest);
	put_fmt(FORMAT_SCANDIR_LIST_FOOTER_FSET, NULL);
	break;
    }

    for (i = 0; i < cnt; i++)
	free(list[i]);
    free(list);

    return retval;
}
