/*  $Revision: 1.4 $
**
**  Read a C news "sys" file and split it up into a set of INN
**  newsfeeds entries.  Also works with B news.
**
**  Once done, edit all files that have HELP or all in them.
**  Review all files, anyway.
*/
#include <stdio.h>
#include <sys/types.h>
#include "configdata.h"
#include "clibrary.h"
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include "nntp.h"
#include "libinn.h"
#include "macros.h"

#define TEMPFILE	":tmp"
static char		**Groups;


/*
**  Fill in the Groups array with the names of all active newsgroups.
*/
static void
ReadActive(act)
    char	*act;
{
    FILE	*F;
    int		i;
    char	buff[BUFSIZ];
    char	*p;

    /* Open file, count lines. */
    if ((F = fopen(act, "r")) == NULL) {
	perror(act);
	exit(1);
    }
    for (i = 0; fgets(buff, sizeof buff, F) != NULL; i++)
	continue;
    Groups = NEW(char*, i + 2);

    /* Fill in each word. */
    rewind(F);
    for (i = 0; fgets(buff, sizeof buff, F) != NULL; i++) {
	if ((p = strchr(buff, ' ')) != NULL)
	    *p = '\0';
	Groups[i] = COPY(buff);
    }
    Groups[i] = NULL;
    (void)fclose(F);
}


/*
**  Read in the sys file and turn it into an array of strings, one
**  per continued line.
*/
char **
ReadSys(sys)
    char		*sys;
{
    register char	*p;
    register char	*to;
    register char	*site;
    register int	i;
    char		*data;
    char		**strings;

    /* Read in the file, get rough count. */
    if ((data = ReadInFile(sys, (struct stat *)NULL)) == NULL) {
	perror(sys);
	exit(1);
    }
    for (p = data, i = 0; (p = strchr(p, '\n')) != NULL; p++, i++)
	continue;

    /* Scan the file, glue all multi-line entries. */
    for (strings = NEW(char*, i + 1), i = 0, to = p = data; *p; ) {
	for (site = to; *p; ) {
	    if (*p == '\n') {
		p++;
		*to = '\0';
		break;
	    }
	    if (*p == '\\' && p[1] == '\n')
		while (*++p && CTYPE(isspace, *p))
		    continue;
	    else
		*to++ = *p++;
	}
	*to++ = '\0';
	if (*site == '\0')
	    continue;
	strings[i++] = COPY(site);
    }
    strings[i] = NULL;
    DISPOSE(data);
    return strings;
}


/*
**  Is this the name of a top-level group?  We want a simple name, "foo",
**  and should find a "foo." in the group list.
*/
static BOOL
Toplevel(p)
    char	*p;
{
    char	**gp;
    char	*g;
    int		i;

    if (strchr(p, '.') != NULL)
	return FALSE;
    for (i = strlen(p) - 1, gp = Groups; (g = *gp++) != NULL; )
	if (EQn(p, g, i) && g[i + 1] == '.')
	    return TRUE;
    return FALSE;
}


/*
**  Do we have a name that's a prefix for more then one newsgroup?
**  For "foo.bar", we must find more then one "foo.bar" or "foo.bar."
*/
static BOOL
GroupPrefix(p)
    char	*p;
{
    char	**gp;
    char	*g;
    int		count;
    int		i;

    if (strchr(p, '.') == NULL)
	return FALSE;
    for (i = strlen(p), count = 0, gp = Groups; (g = *gp++) != NULL; )
	if (EQ(p, g) || (EQn(p, g, i) && g[i] == '.'))
	    count++;
    return count > 1;
}


/*
**  Step through the old subscription list, try to update each one in
**  turn.
*/
static void
DoSub(F, p)
    register FILE	*F;
    char		*p;
{
    register char	*s;
    register int	len;
    register BOOL	SawBang;
    register BOOL	SawAll;

    if ((s = strtok(p, ",")) == NULL)
	return;

    (void)fprintf(F, "!*");
    len = 8 + 1 + 2;
    do {
	/* These are distributions, not newsgroups. */
	if (EQ(s, "world") || EQ(s, "na") || EQ(s, "usa") || EQ(s, "inet")
	 || EQ(s, "mod") || EQ(s, "net")
	 || EQ(s, "local")
	)
	    continue;

#if	defined(DO_MERGE_TO_GROUPS)
	if (EQ(s, "!to") || EQn(s, "to.", 3))
	    continue;
#endif	/* defined(DO_MERGE_TO_GROUPS) */

	(void)putc(',', F);
	len++;

	if (len + strlen(s) + 3 > 72) {
	    (void)fprintf(F,"\\\n\t    ");
	    len = 12;
	}

	SawBang = *s == '!';
	if (SawBang) {
	    (void)putc('!', F);
	    len++;
	    s++;
	}

	SawAll = EQ(s, "all");
	if (SawAll)
	    s = SawBang ? "*" : "*,!control";
	len += strlen(s);
	(void)fprintf(F, "%s", s);

	if (SawAll)
	    ;
	else if (
	    EQ(s, "comp") || EQ(s, "misc") || EQ(s, "news") || EQ(s, "rec")
	 || EQ(s, "sci") || EQ(s, "soc") || EQ(s, "talk")

	 || EQ(s, "alt") || EQ(s, "bionet") || EQ(s, "bit") || EQ(s, "biz")
	 || EQ(s, "clari") || EQ(s, "ddn") || EQ(s, "gnu") || EQ(s, "ieee")
	 || EQ(s, "k12") || EQ(s, "pubnet") || EQ(s, "trial") || EQ(s, "u3b")
	 || EQ(s, "vmsnet")

	 || EQ(s, "ba") || EQ(s, "ca") || EQ(s, "dc") || EQ(s, "ne")
	 || EQ(s, "ny") || EQ(s, "tx")

	 || EQ(s, "info") || EQ(s, "mail") || EQ(s, "opinions")
	 || EQ(s, "uunet")

	 || Toplevel(s)) {
	    (void)fprintf(F, ".*");
	    len += 2;
	}
	else if (GroupPrefix(s)) {
	    (void)putc('*', F);
	    len++;
	}
    } while ((s = strtok((char *)NULL, ",")) != NULL);
}


int
main(ac, av)
    int		 ac;
    char	*av[];
{
    FILE	*F;
    FILE	*out;
    char	**sites;
    char	*f2;
    char	*f3;
    char	*f4;
    char	*p;
    char	*q;
    char	*site;
    char	buff[256];
    char	*act;
    char	*dir;
    char	*sys;
    int		i;

    /* Set defaults. */
    act = "/usr/local/lib/newslib/active";
    sys = "sys";
    dir = "feeds";
    while ((i = getopt(ac, av, "a:s:d:")) != EOF)
    switch (i) {
    default:
	exit(1);
	/* NOTREACHED */
    case 'a':	act = optarg;	break;
    case 'd':	dir = optarg;	break;
    case 's':	sys = optarg;	break;
    }

    sites = ReadSys(sys);
    ReadActive(act);
    if (mkdir(dir, 0777) < 0 && errno != EEXIST)
	perror(dir), exit(1);
    if (chdir(dir) < 0)
	perror("chdir"), exit(1);
    for ( ; ; ) {
	/* Get next non-comment ilne. */
	if ((p = *sites++) == NULL)
	    break;
	for (F = fopen(TEMPFILE, "w"); p && *p == '#'; p = *sites++)
	    (void)fprintf(F, "%s\n", p);
	if (p == NULL) {
	    (void)fclose(F);
	    break;
	}
	site = COPY(p);
	if ((f2 = strchr(site, ':')) == NULL)
	    f2 = "HELP";
	else
	    *f2++ = '\0';
	if ((f3 = strchr(f2, ':')) == NULL)
	    f3 = "HELP";
	else
	    *f3++ = '\0';
	if ((f4 = strchr(f3, ':')) == NULL)
	    f4 = "HELP";
	else
	    *f4++ = '\0';

	/* Write the fields. */
	(void)fprintf(F, "%s\\\n", site);
	(void)fprintf(F, "\t:");
	DoSub(F, f2);
	(void)fprintf(F, "\\\n");
	if (EQ(f3, "n"))
	    (void)fprintf(F, "\t:Tf,Wnm\\\n", f3);
	else
	    (void)fprintf(F, "\t:HELP%s\\\n", f3);
	(void)fprintf(F, "\t:%s\n", f4);
	if (ferror(F) || fclose(F) == EOF)
	    perror(TEMPFILE), exit(1);

	DISPOSE(site);

	/* Find the sitename. */
	for (q = p; *q && *q != '/' && *q != ':'; q++)
	    continue;
	*q = '\0';

	/* Append temp file to site file. */
	if ((F = fopen(TEMPFILE, "r")) == NULL)
	    perror(TEMPFILE), exit(1);
	if ((out = xfopena(p)) == NULL)
	    perror(p), exit(1);
	while ((i = fread(buff, 1, sizeof buff, F)) > 0)
	    if (fwrite(buff, 1, i, out) != i)
		perror(p), exit(1);
	(void)fclose(F);
	if (fclose(out) == EOF)
	    perror(p), exit(1);

	if (unlink(TEMPFILE) < 0)
	    perror("can't unlink temp file");
    }

    exit(0);
    /* NOTREACHED */
}
