/*
 *      $Filename: expandalias $
 *      $Revision: 1.11 $
 *      $Date: 1994/03/13 18:24:34 $
 *
 *      Copyright (C) 1993 by Peter Simons <simons@peti.GUN.de>
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License as
 *      published by the Free Software Foundation; either version 2 of
 *      the License, or (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *      General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *      $Id: expandalias.c,v 1.11 1994/03/13 18:24:34 simons Exp simons $
 *
 */

/**************************************************************************
 *                                                                        *
 * Sektion: Macros, Definitions, Includes, Structures                     *
 *                                                                        *
 **************************************************************************/

/************************************* Includes ***********/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>

#include <libraries/netsupport.h>
#include <proto/netsupport.h>

#include "protos.h"

/************************************* Defines ************/
#define MAX_RECEIPIENTS 1024    /*
                                 * max. number of receipients
                                 */

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

#define HF_TERM     0x01        /* terminator name */
#define HF_ALIAS    0x02        /* alias */
#define HF_LOADED   0x04        /* def loaded */
#define HF_NOEXPAND 0x08        /* do not expand alias */

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;

/************************************* Prototypes *********/
static void callBack(char *, long,  int);
static Hash *FindHashObject(const char *);
static void LoadHashObject(Hash *);
static int HashFunc(const char *);

/************************************* global Variables ***/
static const char __RCSId[] = "$Id: expandalias.c,v 1.11 1994/03/13 18:24:34 simons Exp simons $";

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


/**************************************************************************
 *                                                                        *
 * Sektion: Unterprogramme                                                *
 *                                                                        *
 **************************************************************************/

char **ExpandAliases(char *receipients[])
{
        char **new_receipients2;

        if (*receipients == NULL)
                return NULL;

        if ((new_receipients = malloc(sizeof(char *[MAX_RECEIPIENTS]))) == NULL)
                 return NULL;

        new_receipients2 = new_receipients;
        LoadAliases();
        while (*receipients != NULL) {
                UserAliasList(*receipients, (int (*)(char *, long, int)) callBack, 0L, 1);
                receipients++;
        }
        *new_receipients = NULL;
        return new_receipients2;
}

static void callBack(char *name, long dummy, int show)
{
        switch (name[0]) {
        case '|':
        case '>':
        case '<':
                break;
        case '\\':
                ++name;
        default:
                *new_receipients = name;
                new_receipients++;
                break;
        }
}

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

        if (fi == NULL) {
                MakeLogEntry(PRGNAME, MLE_FATAL_ERROR, "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) {
                        MakeLogEntry(PRGNAME, MLE_FATAL_ERROR, "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);
                if (buf[j + 1] == ':') {
                        h->Flags |= HF_NOEXPAND;
                        ++j;
                }
                h->u.Offset = offset + j + 1;
                strcpy(h->Name, buf + i);

                HashTab[k] = h;

                /*
                 *  if trailing comma, list continues onto next line
                 */

                for (;;) {
                        for (++j; buf[j]; ++j) ;
                        while (buf[j - 1] == ' ' || buf[j - 1] == 9 || buf[j - 1] == '\n')
                                --j;
                        if (buf[j - 1] != ',')
                                break;
                        if (fgets(buf, 256, fi) == NULL)
                                break;
                        newoffset = ftell(fi);
                        j = 0;
                }
        }
        fclose(fi);
}

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

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

static void LoadHashObject(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) {
                MakeLogEntry(PRGNAME, MLE_FATAL_ERROR, "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 (i = 0; buf[i] == ' ' || buf[i] == 9; ++i) ;
                if (buf[i] == '#')
                        continue;

                for (;;) {
                        while (buf[i] == ' ' || buf[i] == 9)
                                ++i;
                        if (buf[i] == 0 || buf[i] == '\n' || buf[i] == '#')
                                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;

                        /*
                         *  skip remaining junk before comma
                         */

                        while (c && c != '\n' && c != ',')
                                c = buf[++j];

                        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;

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

int AliasExists(const char *user)
{
        if (FindHashObject(user))
                return (1);
        return (0);
}

/*
 *  UserAliasList returns whether the 'user' should be displayed in the
 *  To: field of the letter.  Normally it isn't, but if an alias is
 *  specified to NOT expand on the To: field then the alias name itself
 *  IS put on the To: field.
 *
 *  showto is passed from an upper level.  If set, the callback specifies
 *  expansion (unless overriden by the alias), else the callback specifies
 *  no expansion.
 *
 *  In the case where a high level alias is expanded but a low level alias
 *  is not, the callback function is called for the low level alias with
 *  a showto of -1, indicating that it should be placed on the To: list
 *  WITHOUT being placed on the actual send-to list (because its expansion
 *  is passed normally)
 */

int UserAliasList(const char *user,
                  int (*callback) (const char *, long, int),
                  long arg,
                  int showto)
{
        short i;
        Hash *hash = FindHashObject(user);
        static short stack;

        if (++stack == 32) {
                MakeLogEntry(PRGNAME, MLE_FATAL_ERROR, "UULib:Aliases recursion near user %s.", user);
                --stack;
                return 0;
        }

        if (hash) {
                if ((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];
                                int r;

                                if (showto)
                                        r = UserAliasList(h->Name, callback, arg, !(hash->Flags & HF_NOEXPAND));
                                else
                                        r = UserAliasList(h->Name, callback, arg, 0);
                                --stack;
                                if (r && showto && !(hash->Flags & HF_NOEXPAND))
                                        (*callback) (h->Name, arg, -1);
                        }
                }
                else {
                        if (showto)
                                (*callback) (user, arg, !(hash->Flags & HF_NOEXPAND));
                        else
                                (*callback) (user, arg, 0);
                }
        }
        else {
                (*callback) (user, arg, showto);
        }
        --stack;
        if (hash && (hash->Flags & HF_NOEXPAND))
                return (1);
        return (0);
}

static int HashFunc(const char *str)
{
        unsigned long v = 0x14FBA5C3;

        while (*str) {
                v = (v << 5) ^ (*str & 0x1F) ^ (v >> 27);
                ++str;
        }
        return ((int) (v & HASHMASK));
}
