/*
 * common.c
 * Routines used throughout client and server.
 *
 * Copyright (C) Leni Mayo 2001-2002
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License, version 2, as
 *	published by the Free Software Foundation.
 *	
 *	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.
 */

#include <string.h>
#include <stdlib.h>
#include <stdarg.h> 
#include <unistd.h>
#include <ctype.h>
#include "common.h"

/*
 * sprintfTo(AsStringForEach *p, char *fmt, ...)
 *
 * This is a safe sprintf to a buffer.
 *
 * The sprintf man page is a bit unclear, but it looks as if
 * depending on the version of glibc, the return value of snprintf can either
 * be -1, or:
 *   "the number of characters (excluding the trailing '\0') which
 *	  would have been written to the final string if enough space
 *    had been available.)"
 * This function caters for both eventualities.
 */
int sprintfTo(SafeBuf *p, char *fmt, ...)
{
	va_list ap;
	int written;

	va_start(ap, fmt); 
	written = vsnprintf(p->buf, p->bufsiz, fmt, ap);
	
	if (written > 0)
	{
		p->buf    += written;
		p->bufsiz -= written;
	}
	else if (written == -1)
	{
		p->bufsiz = 0;
	}

	return written;
}


void parseEnvironment(int *pDebugLevel, const char **ppszLogName)
{
	char *pszValue;

	pszValue = getenv("SEREL_DEBUG_LEVEL");

	if (pszValue)
	{
		*pDebugLevel=atoi(pszValue);
	}

	pszValue = getenv("SEREL_LOG_NAME");

	if (pszValue)
	{
		*ppszLogName=pszValue;
	}
}

const char * getClientServerPipePath(char *buf, int bufsiz)
{
	uid_t euid = geteuid();

	if (euid == 0)
	{
		snprintf(buf, bufsiz, "/dev/serel-pipe");
	}
	else
	{
		snprintf(buf, bufsiz, "/tmp/serel-pipe.%d", euid);
	}

	return buf;
}

void callbackFreeValue(void * key, void * value, void * pUserData)
{
	free(value);
}

void printVersion(FILE *fp, const char *pszPackage, const char *pszVersion)
{
	fprintf(fp, "%s version %s\n", pszPackage, pszVersion);
	fprintf(fp, "Copyright (C) 2001-2002 Leni Mayo.\n");
	fprintf(fp, "This program may be freely redistributed under the terms of the GNU\n");
	fprintf(fp, "General Public License, version 2.  See the source code for details.\n");
	fprintf(fp, "\n");
#ifdef TAKENOUT
	fprintf(fp, "THIS SOFTWARE COMES WITH NO WARRANTY - NOT EVEN\n");
	fprintf(fp, "MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.\n");
	/*	I like the next version better. */
#endif
	fprintf(fp, "This program is distributed in the hope that it will be useful, but\n");
	fprintf(fp, "WITHOUT ANY WARRANTY, to the extent permitted by law; without even the\n");
	fprintf(fp, "implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
}

/*
 * getArgv0()
 * Handle gracefully the bizzare event that argv or argv[0] is NULL
 * Returns:
 *	argv[0] if present, otherwise a default
 */
const char * getArgv0(char *argv[], const char *pszDefault)
{
	const char *psz;

	if (argv && argv[0])
	{
		 psz = argv[0];
	}
	else
	{
		psz = pszDefault;
	}

	return psz;
}

/*
 * void copyFpToFp(FILE *fpDst, FILE *fpSrc)
 *	Copy everything in one stream to another.
 */

void copyFpToFp(FILE *fpDst, FILE *fpSrc)
{
	int cBytes;
	char buf[1000];

	assert(sizeof(char) == 1);

	while (!feof(fpSrc) && !ferror(fpSrc) && !ferror(fpDst))
	{
		cBytes = fread(buf, 1, sizeof(buf) - 1, fpSrc);
		fwrite(buf, cBytes, 1, fpDst);
	}
}

/*
 * int createAndOpenTmpFile()
 *	Put the name of a temporary file into filename[]
 *	and make sure that it can be opened.
 * Returns:
 *	a file descriptor if the filename is usable, -1 otherwise.
 *
 */
int createAndOpenTmpFile(char filename[], int len)
{
	int fd = -1;
	const char *pszTemplate = "/tmp/XXXXXX";

	assert(len > 0);

	filename[0] = '\0';

	if (len > (strlen(pszTemplate) + 1))
	{
		strcpy(filename, pszTemplate);

		fd = mkstemp(filename);
	}

	return fd;
}

FILE * createAndOpenTmpFp(char filename[], int len, const char *mode)
{
	int fd;
	FILE *fp = NULL;

    fd = createAndOpenTmpFile(filename, len);

	if (fd != -1)
	{
		fp = fdopen(fd, mode);
	}

	return fp;
}

const char *argvCodeAsString(ArgvCode code)
{
	static const char *table[eArgvCodeLast];
	static int initialised = FALSE;

	if (!initialised)
	{
		table[eArgvCodeSereld]   = "sereld";
		table[eArgvSerelhistory] = "serelhistory";

		initialised = TRUE;
	}
	
	assert(code < eArgvCodeLast);

	return table[code];
}

/*
 * Notes:
 *	- this routine permutes the input string
 *  - the resulting list contains pointers into the input string
 */
GSList * splitStringIntoList(char *psz, ISDELIM fn)
{
	int i;
	int len = psz ? strlen(psz) : 0;
	GSList *pListReturn = NULL;

	/* Turn delimeter characters into '\0';
	*/
	for (i = 0; i < len; i++)
	{
		if (fn(psz[i]))
		{
			psz[i] = '\0';
		}
	}

	/* shove the now null-terminated strings into the list
	*/
	i = 0;

	while (i < len)
	{
		if (psz[i] != '\0')
		{
			pListReturn = g_slist_append(pListReturn, &psz[i]);

			i += strlen(&psz[i]);

		}

		i++;
	}

	return pListReturn;
}

int isWhitespace(char ch)
{
	return isspace(ch);
}

/*
 * This is an crappy implementation of a crappy idea, but
 * it works, and it's free of memory corruption or leaks.
 * Feel free to reimplement it, so long as it retains these properties!
 * leni - Tue Apr  9 08:22:13 EST 2002
 */
char * stringFromFp(FILE *fp)
{
	GSList *pList = NULL;
	GSList *pElement;
	char buf[1000];
	int cBytes;
	int total = 0;
	char *pReturn = NULL;
	static const int upperlimit=20000; /* just to maintain sanity */

	assert(sizeof(char) == 1);

	while (!feof(fp) && !ferror(fp) && total < upperlimit)
	{
		cBytes = fread(buf, 1, sizeof(buf) - 1, fp);
		buf[cBytes] = '\0';
		total += cBytes + 1;

		pList = g_slist_append(pList, strdup(buf));
	}

	if (total >= upperlimit)
	{
		fprintf(stderr, "Warning: input too large (> %d bytes) - truncating.\n", upperlimit);
	}

	pReturn = malloc(total);

	pReturn[0] = '\0';

	for (pElement = pList; pElement != NULL; pElement = pElement->next)
	{
		strcat(pReturn, pElement->data);
	}

	for (pElement = pList; pElement != NULL; pElement = pElement->next)
	{
		free(pElement->data);
	}

	g_slist_free(pList);

	return pReturn;
}

/*
 * These command line options are common to the daemon and it's clients
 */
void printCommonCmdlineOptions(FILE *fp)
{
	fprintf(fp, "   -d --debuglevel num    Set verbosity of log output to num\n");
	fprintf(fp, "   -h --help              Print usage\n");
	fprintf(fp, "   -l --log log           Output logging to path or syslog facility \"log\"\n");
	fprintf(fp, "   -a --log-append        When logging to a file, append (default)\n");
	fprintf(fp, "   -t --log-truncate      When logging to a file, truncate first\n");
	fprintf(fp, "   -v --version           Print version\n");
}
