/* exclude.c - creates as sorted list of file names to exclude
   This has nothing to do with cryptography.
   Copyright (C) 1996, 1997 Paul Sheer

   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., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.  
 */


#include "mostincludes.h"
#include "mirrordir.h"
#include "exclude.h"
#include "diffie/compat.h"
#include "mad.h"

/* returns 0 on error/eof. result must be free'd */
static int read_one_line (char **line, FILE * f)
{
    char *p;
    int c, r = 0, i = 0;

    *line = 0;
    p = malloc (MAX_PATH_LEN);
    for (;;) {
	c = fgetc (f);
	if (c == -1) {
	    r = 0;
	    break;
	} else if (c == '\n') {
	    r = 1;
	    break;
	} else {
	    if (i < MAX_PATH_LEN - 1)
		p[i++] = c;
	}
    }
    p[i] = 0;
    *line = p;
    return r;
}

static int compare_completion (const char **a, const char **b)
{
    return strcmp (*a, *b);
}

/* reads lines to an array of strings - the sorts the array, returns non-zero on error */
int fastsearch_init (FastSearch * fast_search, char *filename)
{
    char *p;
    FILE *f;
    size_t num_lines, count;

    memset (fast_search, 0, sizeof (FastSearch));
    f = fopen (filename, "r");
    if (!f) {
	progmess_strerror ("unable to open exclude file list for reading", filename);
	return 1;
    }
    num_lines = 0;
    while (read_one_line (&p, f)) {
	if (p)
	    free (p);
	else
	    break;
	num_lines++;
    }

    fast_search->paths = malloc (sizeof (char *) * (num_lines + 1));
    fast_search->filename = (char *) strdup (filename);

    fseek (f, 0, SEEK_SET);
    count = 0;
    while (read_one_line (&p, f)) {
	char *q;
	if (p) {
	    if (*p && *p != '#') {	/* not a comment nor an empty line */
		q = p;
		while (strchr ("\t ", *q))
		    q++;
		if (*q) {	/* not an empty line */
		    char *r;
		    r = p + strlen (p) - 1;
		    while (strchr ("\t ", *r))
			*r-- = '\0';
		    fast_search->paths[count++] = pathdup (q);
		    if (verbose > 1)
			verbmess ("literal path to exclude", fast_search->paths[count - 1]);
		}
	    }
	    free (p);
	} else
	    break;
	num_lines++;
    }
    fast_search->num_paths = count;
    fast_search->paths[count] = 0;
    qsort (fast_search->paths, fast_search->num_paths, sizeof (char *),
	    (int (*)(const void *, const void *)) compare_completion);
    fclose (f);
    return 0;
}

/* binary search through array, returns non-zero if present */
static int find_string (char **strings, size_t num_strings, char *p)
{
    long i, j = -1, o, k;
    o = i = num_strings / 2;
    if (!strings || !num_strings)
	return 0;
    if (!p)
	return 0;
    if (!*p)
	return 0;
    for (;;) {			/* binary search */
	int comp;
	k = j;
	j = i;
	i = (i + 1) >> 1;
	if (o >= num_strings)
	    o = num_strings - 1;
	if (o < 0)
	    o = 0;
	comp = strcmp (strings[o], p);
	if (!comp)		/* found */
	    return 1;
	if (comp < 0)
	    o += i;
	else
	    o -= i;
	if (k == j)		/* k == j == 1, so we are done */
	    break;
    }
    return 0;
}

void fastsearch_shut (FastSearch * fast_search)
{
    if (fast_search->paths)
	free (fast_search->paths);
    if (fast_search->filename)
	free (fast_search->filename);
    memset (fast_search, 0, sizeof (FastSearch));
}

/* result returns 0 on not found */
int fastsearch_islisted (FastSearch * fast_search, char *path)
{
    if (!fast_search->num_paths)
	return 0;
    return find_string (fast_search->paths, fast_search->num_paths, path);
}

