
/*
 *  ALIAS.C
 *
 *  (C) Copyright 1989-1990 by Matthew Dillon,  All Rights Reserved.
 *
 *  Interpret UULIB:Aliases file.  To save space we do not load
 *  the entire file, just sections on demand.
 *
 *  #	= comment
 *  id: name [, name...]
 *
 *  name is a user name, path, or |filter, or quoted name.  Example,
 *  "|more" or "|rnews"
 */

#include <proto/all.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>

#include "log.h"

#define HASHSIZE    256
#define HASHMASK    (HASHSIZE-1)

#define HF_TERM     0x01    /*	terminator name 	*/
#define HF_ALIAS    0x02    /*	alias			*/
#define HF_LOADED   0x04    /*	def loaded		*/

typedef struct Hash {
    struct Hash *Next;
    short   NumAlias;	/*  # of aliases	*/
    short   Flags;
    char    *Name;	/*  aliased user name	*/
    union {
	struct Hash    **Alias;    /*  list of aliases	   */
	long	Offset;     /*	offset into file    */
    } u;
} Hash;

static Hash    *HashTab[HASHSIZE];
static char    Tmp[256];

void
LoadAliases()
{
    FILE *fi = fopen("UULIB:Aliases", "r");
    short i;
    short j;
    short k;
    short line = 0;
    long newoffset = 0;
    long offset;
    Hash *h;
    char *buf = Tmp;

    if (fi == NULL) {
	ulog(-1, "Can't open UULIB:Aliases");
	return;
    }
    while (fgets(buf, 256, fi)) {
	offset = newoffset;
	newoffset = ftell(fi);
	++line;
	for (i = 0; buf[i] == ' ' || buf[i] == 9; ++i);
	if (buf[i] == '#' || buf[i] == '\n')
	    continue;
	for (j = i; buf[j] && buf[j] != ':'; ++j);
	if (buf[j] == 0) {
	    ulog(-1, "No Colon UULIB:Aliases line %d", line);
	    continue;
	}
	buf[j] = 0;

	k = HashFunc(buf + i);
	h = malloc(sizeof(Hash));
	h->Next = HashTab[k];
	h->NumAlias = 0;
	h->Flags = HF_ALIAS;
	h->Name = malloc(strlen(buf+i) + 1);
	h->u.Offset = offset + j + 1;
	strcpy(h->Name, buf + i);

	HashTab[k] = h;
    }
    fclose(fi);
}

static
Hash *
FindHashObject(name)
char *name;
{
    short k = HashFunc(name);
    Hash *h;

    for (h = HashTab[k]; h; h = h->Next) {
	if (strcmp(name, h->Name) == 0)
	    return(h);
    }
    return(NULL);
}

static
void
LoadHashObject(hash)
Hash *hash;
{
    FILE *fi = fopen("UULIB:Aliases", "r");
    char *buf = Tmp;
    short i, j;
    short c;
    short numalloc = 4;
    Hash **hv = malloc(sizeof(Hash *) * 4);
    Hash *h;

    if (fi == NULL) {
	ulog(-1, "Can't open UULIB:Aliases");
	return;
    }

    hash->Flags |= HF_LOADED;
    fseek(fi, hash->u.Offset, 0);
    while (fgets(buf, 256, fi)) {
	i = 0;
	c = 'x';

	for (;;) {
	    while (buf[i] == ' ' || buf[i] == 9)
		++i;
	    if (buf[i] == 0 || buf[i] == '\n')
		break;

	    for (j = i; buf[j] != '\n' && buf[j] != ' ' && buf[j] != 9 && buf[j] != ','; ++j) {
		if (buf[j] == '\"') {
		    i = j + 1;
		    for (++j; buf[j] != '\n' && buf[j] != '\"'; ++j);
		    break;
		}
	    }
	    c = buf[j];
	    buf[j] = 0;

	    if ((h = FindHashObject(buf + i)) == NULL) {
		short k = HashFunc(buf + i);

		h = malloc(sizeof(Hash));
		h->Next = HashTab[k];
		h->NumAlias = 0;
		h->Flags = HF_TERM;
		h->Name = malloc(strlen(buf + i) + 1);
		h->u.Alias = NULL;
		strcpy(h->Name, buf + i);

		HashTab[k] = h;
	    }

	    if (hash->NumAlias == numalloc) {
		Hash **hvo = hv;
		short add = 4;

		hv = malloc(sizeof(Hash *) * (numalloc + add));
		movmem((char *)hvo, (char *)hv, sizeof(Hash *) * numalloc);
		numalloc += add;
	    }
	    hv[hash->NumAlias++] = h;

	    i = j + 1;
	    if (c == '\"')
		c = buf[i++];
	    if (c == '\n' || c == 0)
		i = j;
	}
	if (c != ',')
	    break;
    }
    hash->u.Alias = hv;
}

void
UserAliasList(user, callback)
char *user;
void (*callback)();
{
    short i;
    Hash *hash = FindHashObject(user);
    static short stack;

    if (++stack == 32) {
	ulog(-1, "UULIB:Aliases recursion near user %s", user);
	--stack;
	return;
    }

    if (hash && (hash->Flags & HF_TERM) == 0) {
	if ((hash->Flags & HF_LOADED) == 0)
	    LoadHashObject(hash);
	for (i = 0; i < hash->NumAlias; ++i) {
	    Hash *h = hash->u.Alias[i];
	    UserAliasList(h->Name, callback);
	}
    } else {
	(*callback)(user);
    }
    --stack;
}

static int
HashFunc(str)
char *str;
{
    unsigned long v = 0x14FBA5C3;
    while (*str) {
	v = (v << 5) ^ (*str & 0x1F) ^ (v >> 27);
	++str;
    }
    return((int)(v & HASHMASK));
}

