/* GNUPLOT - variable.c */

/*[
 * Copyright 1999, 2004   Lars Hecking
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the complete modified source code.  Modifications are to
 * be distributed as patches to the released version.  Permission to
 * distribute binaries produced by compiling modified sources is granted,
 * provided you
 *   1. distribute the corresponding source modifications from the
 *    released version in the form of a patch file along with the binaries,
 *   2. add special version identification to distinguish your version
 *    in addition to the base release version number,
 *   3. provide your name and address as the primary contact for the
 *    support of your modified version, and
 *   4. retain our contact information in regard to use of the base
 *    software.
 * Permission to distribute the released version of the source code along
 * with corresponding source modifications in the form of a patch file is
 * granted with same provisions 2 through 4 for binary distributions.
 *
 * This software is provided "as is" without express or implied warranty
 * to the extent permitted by applicable law.
]*/

/* The Death of Global Variables - part one. */
#include <time.h>
#include <string.h>

#include "variable.h"

#include "alloc.h"
#include "command.h"
#include "util.h"

#define PATHSEP_TO_NUL(arg)			\
do {						\
    char *s = arg;				\
    while ((s = strchr(s, PATHSEP)) != NULL)	\
	*s++ = NUL;				\
} while (0)

#define PRINT_PATHLIST(start, limit)		\
do {						\
    char *s = start;				\
						\
    while (s < limit) {				\
	fprintf(stderr, "\"%s\" ", s);		\
	s += strlen(s) + 1;			\
    }						\
    fputc('\n',stderr);				\
} while (0)

/*
 * char *loadpath_handler (int, char *)
 *
 */
char *
loadpath_handler(int action, char *path)
{
    /* loadpath variable
     * the path elements are '\0' separated (!)
     * this way, reading out loadpath is very
     * easy to implement */
    static char *loadpath;
    /* index pointer, end of loadpath,
     * env section of loadpath, current limit, in that order */
    static char *p, *last, *envptr, *limit;

    switch (action) {
    case ACTION_CLEAR:
	/* Clear loadpath, fall through to init */
	FPRINTF((stderr, "Clear loadpath\n"));
	free(loadpath);
	loadpath = p = last = NULL;
	/* HBB 20000726: 'limit' has to be initialized to NULL, too! */
	limit = NULL;
    case ACTION_INIT:
	/* Init loadpath from environment */
	FPRINTF((stderr, "Init loadpath from environment\n"));
	assert(loadpath == NULL);
	if (!loadpath)
	{
	    char *envlib = getenv("GNUPLOT_LIB");
	    if (envlib) {
		int len = strlen(envlib);
		loadpath = gp_strdup(envlib);
		/* point to end of loadpath */
		last = loadpath + len;
		/* convert all PATHSEPs to \0 */
		PATHSEP_TO_NUL(loadpath);
	    }			/* else: NULL = empty */
	}			/* else: already initialised; int_warn (?) */
	/* point to env portion of loadpath */
	envptr = loadpath;
	break;
    case ACTION_SET:
	/* set the loadpath */
	FPRINTF((stderr, "Set loadpath\n"));
	if (path && *path != NUL) {
	    /* length of env portion */
	    size_t elen = last - envptr;
	    size_t plen = strlen(path);
	    if (loadpath && envptr) {
		/* we are prepending a path name; because
		 * realloc() preserves only the contents up
		 * to the minimum of old and new size, we move
		 * the part to be preserved to the beginning
		 * of the string; use memmove() because strings
		 * may overlap */
		memmove(loadpath, envptr, elen + 1);
	    }
	    loadpath = (char *)gp_realloc(loadpath, elen + 1 + plen + 1, "expand loadpath");
	    /* now move env part back to the end to make space for
	     * the new path */
	    memmove(loadpath + plen + 1, loadpath, elen + 1);
	    strcpy(loadpath, path);
	    /* separate new path(s) and env path(s) */
	    loadpath[plen] = PATHSEP;
	    /* adjust pointer to env part and last */
	    envptr = &loadpath[plen+1];
	    last = envptr + elen;
	    PATHSEP_TO_NUL(loadpath);
	}			/* else: NULL = empty */
	break;
    case ACTION_SHOW:
	/* print the current, full loadpath */
	FPRINTF((stderr, "Show loadpath\n"));
	if (loadpath) {
	    fputs("\tloadpath is ", stderr);
	    PRINT_PATHLIST(loadpath, envptr);
	    if (envptr) {
		/* env part */
		fputs("\tsystem loadpath is ", stderr);
		PRINT_PATHLIST(envptr, last);
	    }
	} else
	    fputs("\tloadpath is empty\n", stderr);
	break;
    case ACTION_SAVE:
	/* we don't save the load path taken from the
	 * environment, so don't go beyond envptr when
	 * extracting the path elements
	 */
	limit = envptr;
    case ACTION_GET:
	/* subsequent calls to get_loadpath() return all
	 * elements of the loadpath until exhausted
	 */
	FPRINTF((stderr, "Get loadpath\n"));
	if (!loadpath)
	    return NULL;
	if (!p) {
	    /* init section */
	    p = loadpath;
	    if (!limit)
		limit = last;
	} else {
	    /* skip over '\0' */
	    p += strlen(p) + 1;
	}
	if (p >= limit) 
	    limit = p = NULL;
	return p;
	break;
    case ACTION_NULL:
	/* just return */
    default:
	break;
    }

    /* should always be ignored - points to the
     * first path in the list */
    return loadpath;

}



struct path_table {
    const char *dir;
};

/* Yet, no special font paths for these operating systems:
 * MSDOS, ATARI, AMIGA, MTOS, NeXT, ultrix, VMS, _IBMR2, alliant
 *
 * Environmental variables are written as $(name).
 * Commands are written as $`command`.
 */

#if defined(OS2) && !defined(FONTPATHSET)
#  define FONTPATHSET
static const struct path_table fontpath_tbl[] =
{
    { "$(BOOTDIR)/PSFONTS" },
    /* X11 */
    { "$(X11ROOT)/X11R6/lib/X11/fonts/Type1" },
    { NULL }
};
#endif

#if defined(_Windows) && !defined(FONTPATHSET)
#  define FONTPATHSET
static const struct path_table fontpath_tbl[] =
{
    { "$(windir)\\fonts" },
    /* Ghostscript */
    { "c:\\gs\\fonts" },
    /* X11 */
    { "$(CYGWIN_ROOT)\\usr\\X11R6\\lib\\X11\\fonts\\Type1" },
#ifdef HAVE_KPSEXPAND
    /* fpTeX */
    { "$`kpsewhich -expand-path=$HOMETEXMF`\\fonts\\type1!" },
    { "$`kpsewhich -expand-path=$TEXMFLOCAL`\\fonts\\type1!" },
    { "$`kpsewhich -expand-path=$TEXMFMAIN`\\fonts\\type1!" },
    { "$`kpsewhich -expand-path=$TEXMFDIST`\\fonts\\type1!" },
#endif
    { NULL }
};
#endif

#if defined(_Macintosh) && !defined(FONTPATHSET)
#  define FONTPATHSET
static const struct path_table fontpath_tbl[] =
{
    { "/System/Library/Fonts!" },
    { "/Library/Fonts!" },
    { "$(HOME)/Library/Fonts!" },
    { NULL }
};
#endif

#if defined(VMS) && !defined(FONTPATHSET)
#  define FONTPATHSET
static const struct path_table fontpath_tbl[] =
{
    { "SYS$COMMON:[SYSFONT]!" },
    { NULL }
};
#endif

/* Fallback: Should work for unix */
#ifndef FONTPATHSET
static const struct path_table fontpath_tbl[] =
{
#ifdef HAVE_KPSEXPAND
    /* teTeX or TeXLive */
    { "$`kpsexpand '$HOMETEXMF'`/fonts/type1!" },
    { "$`kpsexpand '$TEXMFLOCAL'`/fonts/type1!" },
    { "$`kpsexpand '$TEXMFMAIN'`/fonts/type1!" },
    { "$`kpsexpand '$TEXMFDIST'`/fonts/type1!" },
#endif
    /* Linux paths */
    { "/usr/X11R6/lib/X11/fonts/Type1" },
    { "/usr/X11R6/lib/X11/fonts/truetype" },
    /* HP-UX */
    { "/usr/lib/X11/fonts!"},
    /* Ghostscript */
    { "/usr/share/ghostscript/fonts" },
    { "/usr/local/share/ghostscript/fonts" },
    { NULL }
};
#endif

#undef FONTPATHSET

static TBOOLEAN fontpath_init_done = FALSE;

/*
 * char *fontpath_handler (int, char *)
 *
 */
char *
fontpath_handler(int action, char *path)
{
    /* fontpath variable
     * the path elements are '\0' separated (!)
     * this way, reading out fontpath is very
     * easy to implement */
    static char *fontpath;
    /* index pointer, end of fontpath,
     * env section of fontpath, current limit, in that order */
    static char *p, *last, *envptr, *limit;

    if (!fontpath_init_done) {
	fontpath_init_done = TRUE;
	init_fontpath();
    }

    switch (action) {
    case ACTION_CLEAR:
	/* Clear fontpath, fall through to init */
	FPRINTF((stderr, "Clear fontpath\n"));
	free(fontpath);
	fontpath = p = last = NULL;
	/* HBB 20000726: 'limit' has to be initialized to NULL, too! */
	limit = NULL;
    case ACTION_INIT:
	/* Init fontpath from environment */
	FPRINTF((stderr, "Init fontpath from environment\n"));
	assert(fontpath == NULL);
	if (!fontpath)
	{
	    char *envlib = getenv("GNUPLOT_FONTPATH");
	    if (envlib) {
		/* get paths from environment */
		int len = strlen(envlib);
		fontpath = gp_strdup(envlib);
		/* point to end of fontpath */
		last = fontpath + len;
		/* convert all PATHSEPs to \0 */
		PATHSEP_TO_NUL(fontpath);
	    }
#if defined(HAVE_DIRENT_H) || defined(_Windows)
	    else {
		/* set hardcoded paths */
		const struct path_table *curr_fontpath = fontpath_tbl;

		while (curr_fontpath->dir) {
		    char *currdir = NULL;
		    char *envbeg = NULL;
#  if defined(PIPES)
		    char *cmdbeg = NULL;
#  endif
		    TBOOLEAN subdirs = FALSE;

		    currdir = gp_strdup( curr_fontpath->dir );

		    while ( (envbeg=strstr(currdir, "$("))
#  if defined(PIPES)
			    || (cmdbeg=strstr( currdir, "$`" ))
#  endif
			    ) {
			/* Read environment variables */
			if (envbeg) {
			    char *tmpdir = NULL;
			    char *envend = NULL, *envval = NULL;
			    unsigned int envlen;
			    envend = strchr(envbeg+2,')');
			    envend[0] = '\0';
			    envval = getenv(envbeg+2);
			    envend[0] = ')';
			    envlen = envval ? strlen(envval) : 0;
			    tmpdir = (char *)gp_alloc(strlen(currdir)+envlen
					      +envbeg-envend+1,
					      "expand fontpath");
			    strncpy(tmpdir,currdir,envbeg-currdir);
			    if (envval)
				strcpy(tmpdir+(envbeg-currdir),envval);
			    strcpy(tmpdir+(envbeg-currdir+envlen), envend+1);

			    free(currdir);
			    currdir = tmpdir;
			}
#  if defined(PIPES)
			/* Read environment variables */
			else if (cmdbeg) {
			    char *tmpdir = NULL;
			    char *envend = NULL;
			    char envval[256];
			    unsigned int envlen;
			    FILE *fcmd;
			    envend = strchr(cmdbeg+2,'`');
			    envend[0] = '\0';
			    fcmd = popen(cmdbeg+2,"r");
			    if (fcmd) {
				fgets(envval,255,fcmd);
				if (envval[strlen(envval)-1]=='\n')
				    envval[strlen(envval)-1]='\0';
				pclose(fcmd);
			    }
			    envend[0] = '`';
			    envlen = strlen(envval);
			    tmpdir = gp_alloc(strlen(currdir)+envlen
					      +cmdbeg-envend+1,
					      "expand fontpath");
			    strncpy(tmpdir,currdir,cmdbeg-currdir);
			    if (envval)
				strcpy(tmpdir+(cmdbeg-currdir),envval);
			    strcpy(tmpdir+(cmdbeg-currdir+envlen), envend+1);

			    free(currdir);
			    currdir = tmpdir;
			}
#  endif
		    }

		    if ( currdir[strlen(currdir)-1] == '!' ) {
			/* search subdirectories */
			/* delete ! from directory name */
			currdir[strlen(currdir)-1] = '\0';
			subdirs = TRUE;
		    }

		    if ( existdir( currdir ) ) {
			size_t plen;
			if ( subdirs )
			    /* add ! to directory name again */
			    currdir[strlen(currdir)] = '!';
			plen = strlen(currdir);
			if (fontpath) {
			    size_t elen = strlen(fontpath);
			    fontpath = (char *)gp_realloc(fontpath,
						  elen + 1 + plen + 1,
						  "expand fontpath");
			    last = fontpath+elen;
			    *last = PATHSEP;
			    ++last;
			    *last = '\0';
			} else {
			    fontpath = (char *)gp_alloc(plen + 1,
						"expand fontpath");
			    last = fontpath;
			}

			strcpy(last, currdir );
			last += plen;
		    }
		    curr_fontpath++;
		    if (currdir) {
			free(currdir);
			currdir = NULL;
		    }
		}
		/* convert all PATHSEPs to \0 */
		if (fontpath)
		    PATHSEP_TO_NUL(fontpath);
	    }
#endif /* HAVE_DIRENT_H */

	}			/* else: already initialised; int_warn (?) */
	/* point to env portion of fontpath */
	envptr = fontpath;
	break;
    case ACTION_SET:
	/* set the fontpath */
	FPRINTF((stderr, "Set fontpath\n"));
	if (path && *path != NUL) {
	    /* length of env portion */
	    size_t elen = last - envptr;
	    size_t plen = strlen(path);
	    if (fontpath && envptr) {
		/* we are prepending a path name; because
		 * realloc() preserves only the contents up
		 * to the minimum of old and new size, we move
		 * the part to be preserved to the beginning
		 * of the string; use memmove() because strings
		 * may overlap */
		memmove(fontpath, envptr, elen + 1);
	    }
	    fontpath = (char *)gp_realloc(fontpath, elen + 1 + plen + 1, "expand fontpath");
	    /* now move env part back to the end to make space for
	     * the new path */
	    memmove(fontpath + plen + 1, fontpath, elen + 1);
	    strcpy(fontpath, path);
	    /* separate new path(s) and env path(s) */
	    fontpath[plen] = PATHSEP;
	    /* adjust pointer to env part and last */
	    envptr = &fontpath[plen+1];
	    last = envptr + elen;
	    PATHSEP_TO_NUL(fontpath);
	}			/* else: NULL = empty */
	break;
    case ACTION_SHOW:
	/* print the current, full fontpath */
	FPRINTF((stderr, "Show fontpath\n"));
	if (fontpath) {
	    fputs("\tfontpath is ", stderr);
	    PRINT_PATHLIST(fontpath, envptr);
	    if (envptr) {
		/* env part */
		fputs("\tsystem fontpath is ", stderr);
		PRINT_PATHLIST(envptr, last);
	    }
	} else
	    fputs("\tfontpath is empty\n", stderr);
	break;
    case ACTION_SAVE:
	/* we don't save the font path taken from the
	 * environment, so don't go beyond envptr when
	 * extracting the path elements
	 */
	limit = envptr;
    case ACTION_GET:
	/* subsequent calls to get_fontpath() return all
	 * elements of the fontpath until exhausted
	 */
	FPRINTF((stderr, "Get fontpath\n"));
	if (!fontpath)
	    return NULL;
	if (!p) {
	    /* init section */
	    p = fontpath;
	    if (!limit)
		limit = last;
	} else {
	    /* skip over '\0' */
	    p += strlen(p) + 1;
	}
	if (p >= limit)
	    limit = p = NULL;
	return p;
    case ACTION_NULL:
	/* just return */
    default:
	break;
    }

    /* should always be ignored - points to the
     * first path in the list */
    return fontpath;

}

/* not set or shown directly, but controlled by 'set locale'
 * defined in national.h
 */

char full_month_names[12][32] =
{ FMON01, FMON02, FMON03, FMON04, FMON05, FMON06, FMON07, FMON08, FMON09, FMON10, FMON11, FMON12 };
char abbrev_month_names[12][8] =
{ AMON01, AMON02, AMON03, AMON04, AMON05, AMON06, AMON07, AMON08, AMON09, AMON10, AMON11, AMON12 };

char full_day_names[7][32] =
{ FDAY0, FDAY1, FDAY2, FDAY3, FDAY4, FDAY5, FDAY6 };
char abbrev_day_names[7][8] =
{ ADAY0, ADAY1, ADAY2, ADAY3, ADAY4, ADAY5, ADAY6 };

char *
locale_handler(int action, char *newlocale)
{
    static char *current_locale = NULL;
    struct tm tm;
    int i;

    switch(action) {
    case ACTION_CLEAR:
    case ACTION_INIT:
	if (current_locale) free(current_locale);
	current_locale = gp_strdup(INITIAL_LOCALE);
	break;
    case ACTION_SET:
/* FIXME: configure test for setlocale() */
#ifdef HAVE_LOCALE_H
	if (setlocale(LC_TIME, newlocale)) {
	    current_locale = gp_realloc(current_locale, strlen(newlocale) + 1, "locale");
	    strcpy(current_locale, newlocale);
	}
	else
	    int_error(c_token, "Locale not available");

	/* we can do a *lot* better than this ; eg use system functions
	 * where available; create values on first use, etc
	 */
	memset(&tm, 0, sizeof(struct tm));
	for (i = 0; i < 7; ++i) {
	    tm.tm_wday = i;		/* hope this enough */
	    strftime(full_day_names[i], sizeof(full_day_names[i]), "%A", &tm);
	    strftime(abbrev_day_names[i], sizeof(abbrev_day_names[i]), "%a", &tm);
	}
	for (i = 0; i < 12; ++i) {
	    tm.tm_mon = i;		/* hope this enough */
	    strftime(full_month_names[i], sizeof(full_month_names[i]), "%B", &tm);
	    strftime(abbrev_month_names[i], sizeof(abbrev_month_names[i]), "%b", &tm);
	}
#else
	current_locale = (char *)gp_realloc(current_locale, strlen(newlocale) + 1, "locale");
	strcpy(current_locale, newlocale);
#endif /* HAVE_LOCALE_H */
	break;
    case ACTION_SHOW:
#ifdef HAVE_LOCALE_H
	fprintf(stderr, "\tLC_CTYPE is %s\n", setlocale(LC_CTYPE,NULL));
	fprintf(stderr, "\tLC_TIME is %s\n", setlocale(LC_TIME,NULL));
#else
	fprintf(stderr, "\tlocale is \"%s\"\n", current_locale);
#endif
	break;
    case ACTION_SAVE:
    case ACTION_GET:
    case ACTION_NULL:
    default:
	/* just return */
	break;
    }

    return current_locale;
}

