#ifndef lint
static const char RCSid[] = "$Id: paths.c,v 2.5 2016/03/06 01:13:17 schorsch Exp $";
#endif
/*
 * Find a writeable tempfile directory.
 * Create unique filenames therein, and possibly open the file.
 *
 */

#include "copyright.h"

#include "paths.h"


#define TEMPFILE_TEMPLATE "rtXXXXXX"
static const char *defaultpaths[] = DEFAULT_TEMPDIRS;


/* Return a writeable directory for temporary files */
/* If s is NULL, we return a static string */
char *
temp_directory(char *s, size_t len)
{
	static char td[PATH_MAX]; /* remember */
	char * ts = NULL;
	int i = 0;
	
	if (td[0] != '\0') { /* we already have one */
		if (s == NULL) { return td; }
		strncpy(s, td, len);
		s[len-1] = '\0';
		return s;
	}
	/* check where TMP and TEMP point to */
	ts = getenv("TMP");
	if (ts != NULL && access(ts, W_OK) == 0) {
		strncpy(td, ts, sizeof(td));
		td[sizeof(td)-1] = '\0';
	}
	if (ts == NULL) {
		ts = getenv("TEMP");
		if (ts != NULL && access(ts, W_OK) == 0) {
			strncpy(td, ts, sizeof(td));
			td[sizeof(td)-1] = '\0';
		}
	}
	/* check the platform specific default paths in order */
	while (ts == NULL) {
		if (defaultpaths[i] == NULL) {
			break;
		}
		if (access(defaultpaths[i], W_OK) == 0) {
			ts = strncpy(td, defaultpaths[i], sizeof(td));
			td[sizeof(td)-1] = '\0';
			break;
		}
		i++;
	}
	/* we found something */
	if (ts != NULL) {
		if (s == NULL) { return td; }
		strncpy(s, ts, len);
		s[len-1] = '\0';
		return s;
	}
	return NULL;
}


/* Concatenate two strings, leaving exactly one DIRSEP in between */
char *
append_filepath(char *s1, char *s2, size_t len)
{
	size_t siz;
	char *s;

	siz = strlen(s1);
	if (siz > 0) {
		/* XXX siz > len is an error */
		while (siz > 1 && ISDIRSEP(s1[siz-1])) {
			s1[siz-1] = '\0';
			siz--;
		}
		if (siz+1 <= len) {
			s1[siz] = DIRSEP;
			siz++;
		}
	} else if (len >= 2) { /* first path empty */
		s1[0] = CURDIR;
		s1[1] = DIRSEP;
		siz = 2;
	} else {
		return NULL;
	}
	while (ISDIRSEP(s2[0])) {
		s2++;
	}
	s = strncat(s1, s2, len-siz);
	return s;
}


/* Do the actual work for tempfiles, except for the uniquification */
static char *
prepare_tmpname(char *s, size_t len, char *templ)
{
	static char lp[PATH_MAX] = "\0";
	char *ts = NULL;

	if (s == NULL) { /* return our static string */
		s = lp;
		len = sizeof(lp);
	}

	ts = temp_directory(s, len);
	if (ts == NULL) { return NULL; }
	ts = append_filepath(ts, templ != NULL ? templ : TEMPFILE_TEMPLATE, len);
	return ts;
}


/* Compose a *currently* unique name within a temporary directory */
/* If s is NULL, we return a static string */
/* If templ is NULL, we take our default template */
/* WARNING: On Windows, there's a maximum of 27 unique names within
            one process for the same template. */
char *
temp_filename(char *s, size_t len, char *templ)
{
	char *ts = NULL;

	ts = prepare_tmpname(s, len, templ);
	if (ts == NULL) { return NULL; }
	return mktemp(ts);
}


/* Open a unique temp file in a safe way (not safe on Windows) */
/* If s is NULL, we use a static string the caller won't learn about */
/* If templ is NULL, we take our default template */
/* This one is supposed to protect against race conditions on unix */
/* WARNING: On Windows, there's no protection against race conditions */
/* WARNING: On Windows, there's a maximum of 27 unique names within
            one process for the same template. */
int
temp_fd(char *s, size_t len, char *templ)
{
	char *ts = NULL;

	ts = prepare_tmpname(s, len, templ);
	if (ts == NULL) return -1;
#if defined(_WIN32) || defined(_WIN64)
	ts = mktemp(ts);
	if (ts == NULL) return -1;
	return open(ts, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
#else
	return mkstemp(ts);
#endif
}

/* As above, but returns a file pointer instead of a descriptor */
FILE *
temp_fp(char *s, size_t len, char *templ)
{
	int fd = temp_fd(s, len, templ);
	if (fd < 0) return NULL;
	return fdopen(fd, "w+");
}


#ifdef TEST_MODULE
int main()
{
	static char p[PATH_MAX] = "\0";
	char * pp, *qq = NULL;
	pp = temp_directory(p, sizeof(p));
	printf("%s\n", pp);

	qq = temp_filename(pp, sizeof(p), "//something/else_XXXXXX");
	printf("%s\n", qq);

	qq = temp_filename(pp, sizeof(p), NULL);
	printf("%s\n", qq);
}
#endif

