/* 
 * aliases: the interface to /etc/aliases
 *
 * Copyright (c) 1998 David L Parsons 
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by David L Parsons (orc@pell.chi.il.us).  My name may not be used
 * to endorse or promote products derived from this software without
 * specific prior written permission.  THIS SOFTWARE IS PROVIDED
 * ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.
 */
static const char rcsid[] = "$Id: s.Aliases.cc 1.6 01/03/11 17:58:55-08:00 orc@downbelow.pell.portland.or.us $";

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <ctype.h>
#include <string.h>
}

#include "Aliases.H"
#include "util.H"

/*
 * load up aliases.cf, or suitable defaults
 */
Aliases::Aliases():KAarray()
{
    Refresh();
    changed(0);
} /* Aliases::Aliases */

/*
 * if anything has changed, write it to disk.
 * 
 * TODO: file locking, dirty file checking.
 */
int
Aliases::commit()
{
    FILE *cf;
    pid_t child;

    if (changed()) {
	if (backup(ALIASES) != 0)
	    return EOF;
	if ((cf = fopen(ALIASES, "w")) == (FILE*)0)
	    return EOF;
	make_header(cf, 0);
	print(cf);
	fclose(cf);
	KAarray::changed(0);

	if ((child = fork()) == 0) {
	    close(0);
	    close(1);
	    close(2);
	    open("/dev/null", O_RDONLY);	/* kludge: assume fds open starting at 0 */
	    open("/dev/null", O_WRONLY);
	    open("/dev/null", O_WRONLY);
	    execl("/usr/bin/newaliases", "newaliases", 0);
	    exit(0);
	}
	else if (fork > 0) {
	    int status;
	    wait(&status);
	}
	else	/* we couldn't fork.  Return nonzero, in case the caller
		     * cares */
	    return 1;
    }
    return 0;
} /* Aliases::commit */


/*
 * has the disk configuration file been touched since we last loaded it in?
 */
int
Aliases::dirty()
{
    return 0;
}

/*
 * Refresh() the configuration from the disk image 
 */
int
Aliases::Refresh()
{
    FILE *cf;

    if ((cf = fopen(ALIASES, "r")) != (FILE*)0) {
	KAarray::Erase();		/* never merge configurations */
	KAarray::autoappend(1);
	read(cf);
	KAarray::changed(0);
	fclose(cf);
	return 0;
    }
    return EOF;
} /* Aliases::Refresh */


/*
 * read() aliases from the given file descriptor.
 */
int
Aliases::read(FILE *in)
{
    register int c;
    char bfr[200];
    int bsiz = 0;
    KA* currentalias = 0;
    int atboln=1;		/* at beginning of line */
    int needtoken = 1;		/* looking for a token */

    while (( c = getc(in)) != EOF) {

	if (atboln) {
	    if (c == '#') {
		/* comment; eat this line */
		while (( c = getc(in)) != EOF && c != '\n')
		    ;
		if (c == EOF)
		    goto falloffend;
		else
		    continue;
	    }
	    else {
		/* possible continuation line? */
		needtoken = !isspace(c);
	    }
	    atboln = 0;
	}
	if (isspace(c) || c == (needtoken ? ':' : ',')) {
	    atboln = (c == '\n');
	    bfr[bsiz] = 0;
	    if (bsiz > 0) {
		if (needtoken) {
		    currentalias = &create(bfr);
		    needtoken = 0;
		}
		else if (currentalias)
		    *currentalias += bfr;
	    }
	    bsiz = 0;
	}
	else if (c == '"') {
	    /* consume quoted strings */
	    while ((c = getc(in)) != EOF && c != '"') {
		if (c == '\n') {
		    while ((c = getc(in)) != EOF && isspace(c))
			;
		    if (c == EOF)
			goto falloffend;
		}
		bfr[bsiz++] = c;
	    }
	    if (c == EOF)
		goto falloffend;
	}
	else
	    bfr[bsiz++] = c;
    }
falloffend:
    return count();
} /* Aliases::read */


/*
 * print() writes the contents of /etc/aliases out to the given FILE*
 */
int
Aliases::print(FILE *out)
{
    unsigned int i, j;

    for (i=0; i<count(); i++)
	if (_list[i]._used) {
	    KA& val = *(_list[i]._content);

	    if (val.count() > 0) {
		fprintf(out, "%s", _list[i]._key);
		for (j=0; j<val.count(); j++) {
		    fprintf(out,"%s", (j==0)  ? ": " : ",");
		    if (strchr(val[j], ' ') || strchr(val[j], '\t') || strchr(val[j], ','))
			fprintf(out, "\"%s\"", val[j]);
		    else
			fprintf(out, "%s", val[j]);
		}
		fputc('\n', out);
	    }
	}
    return ferror(out);
} /* Aliases::print */
