/*  $Revision: 1.10 $
**
**  Build an active file from either an old copy or by calling find
**  to get the directory names.
*/
#include <stdio.h>
#include <sys/types.h>
#include "configdata.h"
#include "clibrary.h"
#include <ctype.h>
#include <sys/stat.h>
#include <errno.h>
#include "paths.h"
#include "libinn.h"
#include "mydir.h"
#include "macros.h"


STATIC char	ACTIVE[] = _PATH_ACTIVE;


/*
**  Given an newsgroup name, write the active file entry.
*/
STATIC BOOL
MakeEntry(name, rest, oldhimark, oldlomark, ComputeMarks)
    char		*name;
    char		*rest;
    long		oldhimark;
    long		oldlomark;
    BOOL		ComputeMarks;
{
    register long	himark;
    register long	lomark;
    register DIR	*dp;
    register DIRENTRY	*ep;
    register long	j;
    register char	*p;

    /* Turn group name into directory name. */
    for (p = name; *p; p++)
	if (*p == '.')
	    *p = '/';

    /* Set initial hi and lo marks. */
    if (ComputeMarks) {
	himark = 0;
	lomark = 0;
    }
    else {
	himark = oldhimark;
	lomark = oldlomark;
    }

    if ((dp = opendir(name)) != NULL) {
	/* Scan through all entries in the directory. */
	while ((ep = readdir(dp)) != NULL) {
	    p = ep->d_name;
	    if (!CTYPE(isdigit, p[0]) || strspn(p, "0123456789") != strlen(p)
	     || (j = atol(p)) == 0)
		continue;
	    if (lomark == 0 || j < lomark)
		lomark = j;
	    if (j > himark)
		himark = j;
	}
	(void)closedir(dp);
    }
    if (lomark == 0 || lomark - 1 > himark)
	lomark = himark + 1;

    /* Reset marks if computed them and didn't find any articles. */
    if (ComputeMarks && lomark == 1 && himark == 0) {
	himark = oldhimark;
	lomark = oldlomark;
    }
    /* Turn the directory name back into a newsgroup name. */
    for (p = name; *p; p++)
	if (*p == '/')
	    *p = '.';
    if (printf("%s %010ld %010ld %s\n",
	    name, himark, lomark, rest) == EOF
     || fflush(stdout) == EOF
     || ferror(stdout)) {
	(void)fprintf(stderr, "Error writing %s entry, %s\n",
		name, strerror(errno));
	return FALSE;
    }
    return TRUE;
}


/*
**  See if a line is too long to be a newsgroup name, return TRUE if so.
*/
STATIC BOOL
TooLong(buff, i)
    char		*buff;
    int			i;
{
    register char	*p;

    if ((p = strchr(buff, '\n')) == NULL) {
	(void)fprintf(stderr, "Line %d is too long:  \"%.40s\"...\n",
		i, buff);
	return TRUE;
    }
    *p = '\0';
    if (p - buff > SMBUF) {
	(void)fprintf(stderr, "Group line %d is too long: \"%.40s\"...\n",
		i, buff);
	return TRUE;
    }
    return FALSE;
}


/*
**  Renumber the active file based on the old active file.
*/
STATIC BOOL
RebuildFromOld(ComputeMarks)
    BOOL		ComputeMarks;
{
    register FILE	*F;
    register char	*p;
    register int	i;
    register BOOL	Ok;
    char		buff[BUFSIZ];
    STRING		rest;
    long		lomark;
    long		himark;
    char		*save1;
    char		*save2;

    /* Open the file. */
    if ((F = fopen(ACTIVE, "r")) == NULL) {
	(void)fprintf(stderr, "Can't open \"%s\", %s\n",
		ACTIVE, strerror(errno));
	exit(1);
    }

    /* Process each entry. */
    for (i = 1, Ok = TRUE; fgets(buff, sizeof buff, F) != NULL; i++) {
	if (TooLong(buff, i)) {
	    Ok = FALSE;
	    continue;
	}

	/* Set default fields. */
	lomark = 0;
	himark = 0;
	rest = "y";

	/* Try to parse the other fields. */
	if ((p = strchr(buff, ' ')) != NULL) {
	    *p++ = '\0';
	    save1 = p;
	    if ((p = strchr(p, ' ')) != NULL) {
		*p++ = '\0';
		save2 = p;
		if ((p = strchr(p, ' ')) != NULL) {
		    *p++ = '\0';
		    rest = p;
		    lomark = atol(save2);
		    himark = atol(save1);
		}
	    }
	}

	if (!MakeEntry(buff, rest, himark, lomark, ComputeMarks)) {
	    Ok = FALSE;
	    break;
	}
    }

    (void)fclose(F);
    return Ok;
}


STATIC BOOL
RebuildFromFind()
{
    register int	i;
    register char	*p;
    register FILE	*F;
    register BOOL	Ok;
    char		buff[BUFSIZ];

    /* Start getting a list of the directories. */
#if	defined(DO_HAVE_SYMLINK)
    F = popen("exec find . -follow -type d -print", "r");
#else
    F = popen("exec find . -type d -print", "r");
#endif	/* defined(DO_HAVE_SYMLINK) */
    if (F == NULL) {
	(void)fprintf(stderr, "Can't start find, %s\n", strerror(errno));
	exit(1);
    }

    /* Loop over all input. */
    for (i = 1, Ok = TRUE; fgets(buff, sizeof buff, F) != NULL; i++) {
	if (TooLong(buff, i)) {
	    Ok = FALSE;
	    continue;
	}

	/* Skip leading "./" and some known-to-be-bad directories. */
	p = buff[0] == '.' && buff[1] == '/' ? &buff[2] : buff;
	if (EQ(p, "lost+found") || strchr(p, '.') != NULL)
	    continue;
	if (!MakeEntry(p, "y", 0L, 0L, FALSE)) {
	    Ok = FALSE;
	    break;
	}
    }

    /* Clean up. */
    i = pclose(F) >> 8;
    if (i) {
	(void)fprintf(stderr, "Find exited with status %d\n", i);
	Ok = FALSE;
    }
    return Ok;
}


/*
**  Print a usage message and exit.
*/
STATIC NORETURN
Usage()
{
    (void)fprintf(stderr, "Usage: makeactive [-o [-m] ] >output\n");
    exit(1);
    /* NOTREACHED */
}


int
main(ac, av)
    int			ac;
    char		*av[];
{
    BOOL		Ok;
    register int	i;
    BOOL		OldFile;
    BOOL		ComputeMarks;

    /* Set defaults. */
    OldFile = FALSE;
    ComputeMarks = FALSE;
    (void)umask(NEWSUMASK);

    /* Parse JCL. */
    while ((i = getopt(ac, av, "mo")) != EOF)
	switch (i) {
	default:
	    Usage();
	    /* NOTREACHED */
	case 'm':
	    ComputeMarks = TRUE;
	    break;
	case 'o':
	    OldFile = TRUE;
	    break;
	}
    ac -= optind;
    av += optind;
    if (ac || (ComputeMarks && !OldFile))
	Usage();

    /* Go to where the articles are. */
    if (chdir(_PATH_SPOOL) < 0) {
	(void)fprintf(stderr, "Can't change to spool directory, %s\n",
		strerror(errno));
	exit(1);
    }

    if (OldFile)
	Ok = RebuildFromOld(ComputeMarks);
    else
	Ok = RebuildFromFind();

    if (fflush(stdout) || ferror(stdout)) {
	(void)fprintf(stderr, "Can't flush stdout, %s\n", strerror(errno));
	Ok = FALSE;
    }

    exit(Ok ? 0 : 1);
    /* NOTREACHED */
}
