/*
 * timezone: set the current timezone.
 *
 * Copyright (c) 1997,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.timezone.c 1.11 00/05/21 16:33:42-00:00 orc $";

/*
 * timezone: set up your timezone
 *
 * Timezone uses a modified file-selector arrangement for picking
 * the timezone.  It's two menus -- a REGION menu on the left, that
 * contains the names of all the directories found in the timezone
 * folder, and a ZONES menu that contains the names of all the files
 * found in the currently selected folder.
 */
#include <ndialog.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <malloc.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>

#include "buttons.h"

#define TZFILE		"/var/lib/zoneinfo/localtime"
#define TZPREFIX	"/usr/share/zoneinfo"

static LIA zonemenu;
static int initialized = 0;

static char tzfile[sizeof TZPREFIX + 200];

/*
 * chk_zone() asks the user if they want to set the timezone, then sets it
 * and exits
 */
static int
chk_zone(ndObject obj, ndDisplay display)
{
    ListItem* list;
    int dir, file;
    int rc;
    char tz[200];

    getIndexedMenuSelection(obj, &dir, &file);

    sprintf(tzfile, TZPREFIX "/%s", LIAlist(zonemenu)[dir].item);
    if (LIAlist(zonemenu)[dir].id) {
	strcat(tzfile, "/");
	strcat(tzfile, LIAlist(LIAlist(zonemenu)[dir].id)[file].item);
    }

#if STAGE2
    return -1;
#else
    sprintf(tz, "Set timezone to %s", tzfile+sizeof TZPREFIX);

    if (dialog_yesno(0, tz, -1, -1) == 0) {
        unlink(TZFILE);
        if (symlink(tzfile, TZFILE) == 0)
	    return -1;
	else
            Error("When writing new timezone: %s", strerror(errno));
    }
#endif
    return 0;
} /* chk_zone */


/*
 * sortzoneftn() is used to sort the various zonefiles[] Lists.
 */
static int
sortzoneftn(ListItem *a, ListItem *b)
{
    return strcmp(a->item, b->item);
} /* sortzoneftn */


/*
 * loadzones() slurps up all the files beginning with an uppercase
 * letter that are in TZPREFIX and any directories under TZPREFIX.
 *
 * This is horribly ugly and kludgy code that is badly documented. BEWARE.
 */
static void
loadzones()
{
    DIR* zones;
    DIR* SubZ;
    struct dirent* zone;
    struct dirent* sz;
    struct stat st;
    char bfr[1000];
    int x;
    int thiszone;
    LIA submenu;

    if (initialized)
	return;
    initialized = 1;

    zonemenu = newLIA(0,0);


    /* walk TZPREFIX, looking for files or directories that start with
     * an uppercase letter.
     */
    if ((zones = opendir(TZPREFIX)) != 0) {
	while ((zone = readdir(zones)) != 0) {
	    if (isupper(zone->d_name[0])) {
		sprintf(bfr, TZPREFIX "/%s", zone->d_name);
		if (stat(bfr, &st) != 0)
		    continue;

		thiszone = LIAcount(zonemenu);
		addToLIA(zonemenu, 0, zone->d_name, 0);

		if (S_ISDIR(st.st_mode)) {
		    /* If we find a directory, we scan it for files beginning
		     * with an uppercase letter.
		     */
		    if ((SubZ = opendir(bfr)) != 0) {
			submenu = newLIA(0,0);

			while ((sz = readdir(SubZ)) != 0) {
			    if (!isupper(sz->d_name[0]))
				continue;
			    sprintf(bfr, TZPREFIX "/%s/%s",
					    zone->d_name,
					    sz->d_name);
			    if (stat(bfr, &st) == 0 && S_ISREG(st.st_mode))
				addToLIA(submenu, 0, sz->d_name, 0);
			}
			closedir(SubZ);

			if (LIAcount(submenu) > 0) {
			    qsort(LIAlist(submenu),
				  LIAcount(submenu),
				  sizeof(ListItem),
				  sortzoneftn);
			    LIAlist(zonemenu)[thiszone].id = submenu;
			}
			else
			    deleteLIA(submenu);
		    }
		}
	    }
	}
	qsort(LIAlist(zonemenu),
	      LIAcount(zonemenu),
	      sizeof(ListItem),
	      sortzoneftn);
	closedir(zones);
    }
} /* loadzones */


#if STAGE2
/*
 * initialize the timezone structures
 */
void
tzinit()
{
    loadzones();
}

/*
 * set the timezone
 */
void
tzcommit()
{
    unlink(TZFILE);
    if (symlink(tzfile, TZFILE) != 0)
	Error("When writing new timezone: %s", strerror(errno));
}

/*
 * display the timezone
 */
void
tzinventory(char *i)
{
    strcat(i, "Timezone: ");
    strcat(i, tzfile[0] ? &tzfile[sizeof TZPREFIX] : "[not set]");
    strcat(i, "\n");
}

#define doit tzmenu
#endif


/*
 * doit() is main() for an administration tool.
 */
int
doit()
{
    int x, y;
    char oldtz[200];
    char *p, *q;
    int rc;
    ndObject chain;
    int dir=0, file=0;
    ndObject menu;

#if STAGE2 == 0
    /* grab a list of timezones */
    loadzones();
#endif

    if (LIAcount(zonemenu) == 0) {
	Error("The timezone directory " TZPREFIX " is\n"
	      "empty or not readable.");
#if STAGE2 == 0
	return 1;
#else
	return NEXT_BUTTON;
#endif
    }

    /* find the current timezone */
    memset(oldtz, 0, sizeof oldtz);

    if (readlink(TZFILE, oldtz, sizeof oldtz) > 0) {
	p = oldtz + strlen(TZPREFIX) + 1;
	if ((q=strchr(p, '/')) != 0)
	    *q++ = 0;
    }
    else
	p = q = 0;

    for (x=0; x<LIAcount(zonemenu); x++)
	if (p && strcmp(LIAlist(zonemenu)[x].item, p) == 0) {
	    if (q) {
		LIA submenu = LIAlist(zonemenu)[x].id;

		for (y = 0; y < LIAcount(submenu); y++)
		    if (strcmp(LIAlist(submenu)[y].item, q) == 0) {
			dir = x;
			file = y;
			goto matched;
		    }
	    }
	    else {
		dir = x;
		file = 0;
		break;
	    }
	}
matched: ;

    menu = newIndexedMenu(0, 1, 46, 12, -1, zonemenu, 0,
			       chk_zone, "timezone.html");
    setIndexedMenuSelection(menu, dir, file);

#if STAGE2
    chain = ObjChain(menu, newCancelButton(0, "BACK", 0, "timezone.html"));
#else
    chain = ObjChain(menu, newCancelButton(0, "CANCEL", 0, "timezone.html"));
#endif

    rc = MENU(chain, -1, -1, "Set Timezone", "Pick a city or country inside \n"
					     "your timezone", 0);

    deleteObjChain(chain);

#if STAGE2
    return (rc == MENU_OK) ? NEXT_BUTTON : BACK_BUTTON;
#else
    return (rc == MENU_OK) ? 0 : 1;
#endif
}
