/*
 * LIB/INCLUDE.C
 *
 * (c)Copyright 2002, Joe Greco and sol.net Network Services.
 *    All Rights Reserved.  Refer to the COPYRIGHT file in the
 *    base directory of this distribution for specific rights 
 *    granted.
 */

#include "defs.h"

Prototype FILE *iopen(const char *path, const char *mode);
Prototype int iclose(FILE *stream);
Prototype char *igets(char *str, int size, FILE *stream);
Prototype int istat(int fd, struct stat *st);





/*
 * These functions are designed to (very!) loosely replace
 * fopen/fclose/fgets, interpreting the named file as a 
 * include-directive preprocessor.
 *
 * Only one instance at a time is supported.
 */

#define	MAXLEVELS	4
#define	MAXFILES	1024

int inc_initialized = 0;
int inc_isopen = 0;

FILE *inc_fps[MAXLEVELS];
int inc_nfps = 0;
int inc_prog[MAXLEVELS];

char *inc_files[MAXFILES];
int inc_nfiles = 0;





/*
 * The return from this isn't a usable FILE *.  It's just
 * NULL for failure or non-NULL for success.
 */

FILE *
iopen(const char *path, const char *mode)
{
	FILE *rval;
	int i;

	if (! inc_initialized) {
		inc_initialized++;
		bzero((char *)inc_fps, sizeof(inc_fps));
		bzero((char *)inc_prog, sizeof(inc_prog));
		bzero((char *)inc_files, sizeof(inc_files));
	}


	/*
	 * Only allow one instance
	 */

	if (inc_isopen) {
		logit(LOG_ERR, "iopen failed; already open");
		return(NULL);
	}


	/*
	 * Clear the cached filenames being used by istat
	 */

	for (i = 0; i < inc_nfiles; i++) {
		zfreeStr(&SysMemPool, &inc_files[i]);
		inc_files[i] = NULL;
	}
	inc_nfiles = 0;


	/*
	 * Open
	 */

	if (*path == '|') {
		path++;
		if (! ((rval = popen(path, mode)))) {
			return(rval);
		}
		inc_fps[inc_nfps] = rval;
		inc_prog[inc_nfps] = 1;
		inc_nfps++;
	} else {
		if (! ((rval = fopen(path, mode)))) {
			return(rval);
		}
		inc_fps[inc_nfps] = rval;
		inc_nfps++;
		inc_files[inc_nfiles] = zallocStr(&SysMemPool, path);
		inc_nfiles++;
	}

	inc_isopen++;
	return(rval);
}





int
iclose(FILE *stream)
{
	int rval = 0;
	int i;

	for (i = 0; i < inc_nfps; i++) {
		if (inc_prog[i]) {
			rval += pclose(inc_fps[i]);
		} else {
			rval += fclose(inc_fps[i]);
		}
		inc_fps[i] = NULL;
		inc_prog[i] = 0;
	}
	inc_nfps = 0;

	inc_isopen = 0;
	return(rval);
}





/*
 * This might not work right if the user supplied
 * buffer is too small.  Not an issue here.
 */

char *
igets(char *str, int size, FILE *stream)
{
	char *rval, *fname, *ptr;
	FILE *fp;

	rval = fgets(str, size, inc_fps[inc_nfps - 1]);

	if (! rval) {

		/* We got an EOF or error */

		if (inc_nfps == 1) {

			/*
			 * We're on the primary file
			 * so we just return
			 */

			return(rval);
		}

		/* We step back to the previous file */

		inc_nfps--;
		if (inc_prog[inc_nfps]) {
			rval += pclose(inc_fps[inc_nfps]);
		} else {
			rval += fclose(inc_fps[inc_nfps]);
		}
		inc_fps[inc_nfps] = NULL;
		inc_prog[inc_nfps] = 0;

		/* 
		 * And now we recurse into igets to read
		 * "the next line"
		 */
		
		return(igets(str, size, stream));
	}

	/* We got a line */

	if (strncmp(str, "%include", 8)) {

		/* And it's not an include directive */

		return(rval);
	}

	if (inc_nfps >= MAXLEVELS)
	{
		logit(LOG_ERR, "too many %%include levels");
		return(rval);
	}

	/* Fetch out the filename */

	if (! (fname = strchr(str, '"'))) {
		logit(LOG_ERR, "no open quote in %%include");
		return(rval);
	}
	fname++;
	if (! (ptr = strchr(fname, '"'))) {
		logit(LOG_ERR, "no close quote in %%include");
		return(rval);
	}
	*ptr = '\0';

	/* Do the open */

	if (*fname == '|') {
		fname++;
		if (! ((fp = popen(fname, "r")))) {
			logit(LOG_ERR, "error popening %%include: %s", fname);
			*ptr = '"';
			return(rval);
		}
		inc_fps[inc_nfps] = fp;
		inc_prog[inc_nfps] = 1;
		inc_nfps++;
	} else {
		if (! ((fp = fopen(fname, "r")))) {
			logit(LOG_ERR, "error fopening %%include: %s", fname);
			*ptr = '"';
			return(rval);
		}
		inc_fps[inc_nfps] = fp;
		inc_nfps++;
		if (inc_nfiles < MAXFILES) {
			inc_files[inc_nfiles] = zallocStr(&SysMemPool, fname);
			inc_nfiles++;
		}
	}

	/* 
	 * And now we recurse into igets to read
	 * "the next line"
	 */

	return(igets(str, size, stream));
}





int
istat(int fd, struct stat *st)
{
	int rval = 0;
	struct stat sb;
	int i;

	/*
	 * Pick the newest stat time.
	 */

	bzero((char *)st, sizeof(struct stat));

	for (i = 0; i < inc_nfiles; i++) {
		if (stat(inc_files[i], &sb) < 0) {
			logit(LOG_ERR, "error stating %%include: %s", inc_files[i]);
			rval = -1;
		} else {
			if (sb.st_mtime > st->st_mtime) {
				bcopy((char *)&sb, (char *)st, sizeof(sb));
			}
		}
	}
	return(rval);
}


syntax highlighted by Code2HTML, v. 0.9.1