/* @(#)cmpdir.c	1.24 08/09/26 Copyright 2002-2008 J. Schilling */
#ifndef lint
static	char sccsid[] =
	"@(#)cmpdir.c	1.24 08/09/26 Copyright 2002-2008 J. Schilling";
#endif
/*
 *	Blocked directory sort/compare.
 *
 *	Copyright (c) 2002-2008 J. Schilling
 */
/*@@C@@*/

#include <schily/mconfig.h>
#include <stdio.h>
#include <schily/stdlib.h>
#include <schily/unistd.h>
#include <schily/standard.h>
#include <schily/utypes.h>
#include <schily/string.h>
#include <schily/schily.h>
#include <schily/fetchdir.h>

EXPORT	int	fdircomp	__PR((const void *p1, const void *p2));
EXPORT	char	**sortdir	__PR((char *dir, int *entp));
EXPORT	int	cmpdir		__PR((int ents1, int ents2,
					char **ep1, char **ep2,
					char **oa, char **od,
					int *alenp, int *dlenp));

/*
 * Compare directory entries from fetchdir().
 * Ignore first byte, it does not belong to the directory data.
 */
EXPORT int
fdircomp(p1, p2)
	const void	*p1;
	const void	*p2;
{
	register Uchar	*s1;
	register Uchar	*s2;

	s1 = *(Uchar **)p1;
	s2 = *(Uchar **)p2;
	s1++;
	s2++;
	while (*s1 == *s2) {
		if (*s1 == '\0')
			return (0);
		s1++;
		s2++;
	}
	return (*s1 - *s2);
}

/*
 * Sort a directory string as returned by fetchdir().
 *
 * Return allocated arry with string pointers.
 */
EXPORT char **
sortdir(dir, entp)
	char	*dir;
	int	*entp;
{
	int	ents = -1;
	char	**ea;
	char	*d;
	char	*p;
	int	i;

	if (entp)
		ents = *entp;
	if (ents < 0) {
		d = dir;
		ents = 0;
		while (*d) {
			ents++;
			p = strchr(d, '\0');
			d = &p[1];
		}
	}
	ea = ___malloc((ents+1)*sizeof (char *), "sortdir");
	for (i = 0; i < ents; i++) {
		ea[i] = NULL;
	}
	for (i = 0; i < ents; i++) {
		ea[i] = dir;
		p = strchr(dir, '\0');
		if (p == NULL)
			break;
		dir = ++p;
	}
	ea[ents] = NULL;
	qsort(ea, ents, sizeof (char *), fdircomp);
	if (entp)
		*entp = ents;
	return (ea);
}

EXPORT int
cmpdir(ents1, ents2, ep1, ep2, oa, od, alenp, dlenp)
	register int	ents1;	/* # of directory entries in arch	*/
	register int	ents2;	/* # of directory entries on disk	*/
	register char	**ep1;	/* Directory entry pointer array (arch)	*/
	register char	**ep2;	/* Directory entry pointer array (disk)	*/
	register char	**oa;	/* Only in arch pointer array		*/
	register char	**od;	/* Only on disk pointer array		*/
		int	*alenp;	/* Len pointer for "only in arch" array	*/
		int	*dlenp;	/* Len pointer for "only on disk" array	*/
{
	register int	i1;	/* Index for 'only in archive'		*/
	register int	i2;	/* Index for 'only on disk'		*/
	register int	d;	/* 'diff' amount (== 0 means equal)	*/
	register int	alen = 0; /* Number of ents only in archive	*/
	register int	dlen = 0; /* Number of ents only on disk	*/

	for (i1 = i2 = 0; i1 < ents1 && i2 < ents2; i1++, i2++) {
		d = fdircomp(&ep1[i1], &ep2[i2]);
		retry:

		if (d > 0) {
			do {
				d = fdircomp(&ep1[i1], &ep2[i2]);
				if (d <= 0)
					goto retry;
				if (od)
					od[dlen] = ep2[i2];
				dlen++;
				i2++;
			} while (i1 < ents1 && i2 < ents2);
		}
		if (d < 0) {
			do {
				d = fdircomp(&ep1[i1], &ep2[i2]);
				if (d >= 0)
					goto retry;
				if (oa)
					oa[alen] = ep1[i1];
				alen++;
				i1++;
			} while (i1 < ents1 && i2 < ents2);
		}
		/*
		 * Do not increment i1 & i2 in case that the last elements are
		 * not equal and need to be treaten as longer list elements in
		 * the code below.
		 */
		if (i1 >= ents1 || i2 >= ents2)
			break;
	}
	/*
	 * One of both lists is longer after some amount.
	 */
	if (od == NULL) {
		if (i2 < ents2)
			dlen += ents2 - i2;
	} else {
		for (; i2 < ents2; i2++) {
			od[dlen] = ep2[i2];
			dlen++;
		}
	}
	if (oa == NULL) {
		if (i1 < ents1)
			alen += ents1 - i1;
	} else {
		for (; i1 < ents1; i1++) {
			oa[alen] = ep1[i1];
			alen++;
		}
	}
	if (alenp)
		*alenp = alen;
	if (dlenp)
		*dlenp = dlen;

	return (alen + dlen);
}
