/*
 * Copyright (c) 1998,1999,2000  Kazushi (Jam) Marukawa
 * All rights of my changes are reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice in the documentation and/or other materials provided with
 *    the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* $Orig-Id: util.c,v 1.22 1997/07/23 18:35:18 agulbra Exp $ */
/*

Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
22646949.

Use, modification and distribution is allowed without limitation,
warranty, or liability of any kind. */

/*
This code is derived from only leafnode+ by using same structure
of Cornelius's leafnode to prepare for merging with Cornelius's code.
*/

#ifdef SOCKS
#include <socks.h>
#endif

#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <netdb.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>

#include "leafnode.h"

struct newsgroup * active;

/* insert or update a newsgroup in the global list */
void insertgroup(const char* name,
		 unsigned long first, unsigned long last,
		 const char* desc, unsigned long server)
{
    struct newsgroup** a;
    int c;

    if (!desc)
	desc = "";

    a = &active;
    while(a) {
	if (*a) {
	    c = strcmp((*a)->name, name);
	    if (c < 0) {
		a = &((*a)->left);
	    } else if (c > 0) {
		a = &((*a)->right);
	    } else {
		if (first >= 0)
		    (*a)->first = first;
		if (last >= 0)
		    (*a)->last = last;
		if (server >= 0)
		    (*a)->server = server;
		if (desc && *desc) {
		    /* This leaks memory if the new desc > old one, but I
		       cannot see a way round this... */
		    if (strlen((*a)->desc) >= strlen(desc))
			strcpy((*a)->desc, desc);
		    else
			(*a)->desc = strdup(desc);
		}
		return;
	    }
	} else {
	    *a = (struct newsgroup *)
		 critmalloc(sizeof(struct newsgroup) + 2 +
			    strlen(name) + strlen(desc),
			    "Building newsgroups info list");
	    if (!*a)
		return;
	    (*a)->left = (*a)->right = NULL;
	    (*a)->first = first;
	    (*a)->last = last;
	    (*a)->server = server;
	    (*a)->alive = 0;
	    (*a)->newarticles = 0;
	    (*a)->name = (char*)(1 + *a);
	    strcpy((*a)->name, name);
	    (*a)->desc = strlen((*a)->name) + 1 + ((*a)->name);
	    strcpy((*a)->desc, desc);
	    return;
	}
    }
}

/* find a group by name */
struct newsgroup* findgroup(const char* name)
{
    struct newsgroup* a;
    int c;

    a = active;
    while (a) {
	c = strcmp(a->name, name);
	if (c < 0)
	    a = a->left;
	else if (c > 0)
	    a = a->right;
	else
	    return a;
    }
    return a;
}

static void freeactive(struct newsgroup* g)
{
    if (g) {
	freeactive(g->right);
	freeactive(g->left);
	free((char*)g);
    }
}

static int lockfd = -1;

void lockactive(void)
{
    const char* s = getinfofname();
    if ((lockfd = open(s, O_RDWR | O_CREAT, S_IWGRP|S_IWOTH)) < 0) {
	mysyslog(LOG_ERR, "can't open %s for lock: %s", s, strerror(errno));
	exit(2);
    }
#ifdef USE_LOCKF
# ifdef NOWAIT_LOCK
    if (lockf(lockfd, F_TLOCK, (off_t)0))
# else
    if (lockf(lockfd, F_LOCK, (off_t)0))
# endif
#else
# ifdef NOWAIT_LOCK
    if (flock(lockfd, LOCK_EX | LOCK_NB))
# else
    if (flock(lockfd, LOCK_EX))
# endif
#endif
    {
	mysyslog(LOG_ERR, "can't lock %s: %s", s, strerror(errno));
	exit(2);
    }
    /* keep file open to hold a lock */
}

void unlockactive(void)
{
    if (lockfd >= 0) {
	close(lockfd);
	lockfd = -1;
    }
}

/* this uses a nifty technique for building a binary tree from a
   sorted list... the basic observation is that if you count from one
   end, with 1 being the rightmost node, then the number of nodes
   between N and the leaf nodes is equal to the number of zero bits
   at the small end of N.  this tree probably doesn't make it obvious:

   1 2 3 4 5 6 7

         1
     1   0   1
   1 0 1 0 1 0 1
   | | | | | | + no zeroes: leaf node
   | | | | | +-- one zero: one step up
   | | | | +---- no zeroes: leaf node
   | | | +------ two zeroes: two steps up
   | | +-------- no zeroes: leaf node
   | +---------- one zero: one step up
   +------------ no zeroes: leaf node

   this can be used to build a smallest-height tree, and with a bit more
   care, a _perfectly_ balanced tree */

void readactive(void)
{
    char* p;
    char* q;
    unsigned long first, last, server;
    int fd;
    struct newsgroup* g;
    struct stat st;

    int line;
    struct newsgroup* levels[32]; /* theoretically finite size :) */
    int l;

    static char* stuff;
    const char* s = getinfofname();

    if (stuff) {
	freeactive(active);
	active = NULL;
	free((char*)stuff);
	stuff = NULL;
    }

    fd = open(s, O_RDONLY);
    if (stat(s, &st) < 0) {
	mysyslog(LOG_ERR, "can't stat %s: %s", s, strerror(errno));
	return;
    }
    stuff = critmalloc(st.st_size + 1, "Reading group info");
    if ((fd = open(s, O_RDONLY)) < 0 ||
	read(fd, stuff, st.st_size) < st.st_size) {
	mysyslog(LOG_ERR, "can't open/read %s: %s", s, strerror(errno));
	return;
    } else {
	close(fd);
	stuff[st.st_size] = '\0'; /* 0-terminate string */
    }

    line = 1;
    for(l = 0; l < 32; levels[l++] = NULL)
	;

    p = stuff;
    while (p && *p) {
	q = p;
	p = strchr(p, ' ');
	if (p && *p) {
	    *p++ = '\0';
	    if (*q) {
		last = p ? strtoul(p, &p, 10) : 0;
		first = p ? strtoul(p, &p, 10) : 0;
		server = p ? strtoul(p, &p, 10) : 0;
		p = skipspaces(p);
		if (first < 1)
		    first = 1;

		g = (struct newsgroup *)critmalloc(sizeof(struct newsgroup),
						   "reading groupinfo");
		g->right = g->left = NULL;
		g->first = first;
		g->last = last;
		g->server = server;
		g->alive = 0;
		g->newarticles = 0;
		g->name = q;
		g->desc = p;

		for (l = 0; (line & (1 << l)) == 0; l++)
		    ;
		if (l) {
		    g->right = levels[l - 1];
		}
		if (active == g->right)
		    active = g;
		if (l < 31 && levels[l + 1] && !levels[l + 1]->left)
		    levels[l + 1]->left = g;
		levels[l] = g;

		p = strchr(p, '\n');
		if (p)
		    *p++ = '\0';
		line++;
	    } else {
		p = strchr(p, '\n');
		if (p)
		    p++;
	    }
	}
    }
    g = NULL;
    for (l = 0; l < 31; l++) {
	if (levels[l]) {
	    if (g && levels[l] &&
		levels[l]->left == NULL && levels[l]->right != g) {
		levels[l]->left = g;
		g = NULL;
	    }
	    if (!levels[l + 1] ||
		(levels[l] != levels[l + 1]->left &&
		 levels[l] != levels[l + 1]->right)) {
		if (g)
		    mysyslog(LOG_ERR, "2+5=2");
		g = levels[l];
	    }
	}
    }
}

static void helpwriteactive(struct newsgroup* g, FILE* f)
{
    if (g) {
	helpwriteactive(g->right, f);
	fprintf(f, "%s %lu %lu %lu %s\n", g->name, g->last, g->first, 0LU,
		g->desc && *(g->desc) ? g->desc : "-x-");
	helpwriteactive(g->left, f);
    }
}

void writeactive(void)
{
    const char* s;
    FILE* a;
    char c[PATH_MAX];

    s = getinfonewfname();
    a = fopen(s, "w");
    if (!a)
	return;
    helpwriteactive(active, a);
    fflush(a);
    if (ferror(a)) {
	fclose(a);
	mysyslog(LOG_ERR, "%s has error while writing: %s", s, strerror(errno));
	return;
    }
    fclose(a);
    strcpy(c, s);
    s = getinfofname();
    rename(c, s);
}

#if 0
/* Cornelius's code has fakeactive here */
/*
 * fake an active file if the groupinfo file is corrupted
 */
void fakeactive(void)
{
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1