tzsetup.c revision 30999
169783Smsmith/*
269783Smsmith * Copyright 1996 Massachusetts Institute of Technology
369783Smsmith *
469783Smsmith * Permission to use, copy, modify, and distribute this software and
569783Smsmith * its documentation for any purpose and without fee is hereby
669783Smsmith * granted, provided that both the above copyright notice and this
769783Smsmith * permission notice appear in all copies, that both the above
869783Smsmith * copyright notice and this permission notice appear in all
969783Smsmith * supporting documentation, and that the name of M.I.T. not be used
1069783Smsmith * in advertising or publicity pertaining to distribution of the
1169783Smsmith * software without specific, written prior permission.  M.I.T. makes
1269783Smsmith * no representations about the suitability of this software for any
1369783Smsmith * purpose.  It is provided "as is" without express or implied
1469783Smsmith * warranty.
1569783Smsmith *
1669783Smsmith * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1769783Smsmith * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
1869783Smsmith * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1969783Smsmith * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2069783Smsmith * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2169783Smsmith * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2269783Smsmith * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2369783Smsmith * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2469783Smsmith * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2569783Smsmith * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2669783Smsmith * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2769783Smsmith * SUCH DAMAGE.
2869783Smsmith */
2969783Smsmith
3069783Smsmith/*
31119418Sobrien * Second attempt at a `tzmenu' program, using the separate description
32119418Sobrien * files provided in newer tzdata releases.
33119418Sobrien */
3469783Smsmith
3569783Smsmith#ifndef lint
3669783Smsmithstatic const char rcsid[] =
3769783Smsmith	"$Id: tzsetup.c,v 1.8 1997/10/27 07:49:47 charnier Exp $";
3869783Smsmith#endif /* not lint */
3969783Smsmith
4069783Smsmith#include <sys/types.h>
41129876Sphk#include <dialog.h>
4269783Smsmith#include <err.h>
43107546Simp#include <errno.h>
44107546Simp#include <stdio.h>
45106844Smdodd#include <stdlib.h>
4669783Smsmith#include <string.h>
4769783Smsmith#include <unistd.h>
4869783Smsmith
49119285Simp#include <sys/fcntl.h>
50119285Simp#include <sys/queue.h>
51119285Simp#include <sys/stat.h>
5269783Smsmith
5369783Smsmith#include "paths.h"
5469783Smsmith
55200341Sjkimstatic int reallydoit = 1;
56200341Sjkim
57200341Sjkimstatic int continent_country_menu(dialogMenuItem *);
58200341Sjkimstatic int set_zone_multi(dialogMenuItem *);
59200341Sjkimstatic int set_zone_whole_country(dialogMenuItem *);
60200341Sjkimstatic int set_zone_menu(dialogMenuItem *);
61200341Sjkim
62200341Sjkimstruct continent {
63200341Sjkim	dialogMenuItem *menu;
6469783Smsmith	int nitems;
65200341Sjkim	int ch;
66200341Sjkim	int sc;
6769783Smsmith};
6869783Smsmith
6969783Smsmithstatic struct continent africa, america, antarctica, arctic, asia, atlantic;
7069783Smsmithstatic struct continent australia, europe, indian, pacific;
7169783Smsmith
72145661Simpstatic struct continent_names {
7369783Smsmith	char *name;
74200341Sjkim	struct continent *continent;
75200341Sjkim} continent_names[] = {
7669783Smsmith	{ "Africa", &africa }, { "America", &america },
7769783Smsmith	{ "Antarctica", &antarctica }, { "Arctic", &arctic },
7869783Smsmith	{ "Asia", &asia },
7969783Smsmith	{ "Atlantic", &atlantic }, { "Australia", &australia },
8069783Smsmith	{ "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific }
8169783Smsmith};
8269783Smsmith
8369783Smsmithstatic dialogMenuItem continents[] = {
8469783Smsmith	{ "1", "Africa", 0, continent_country_menu, 0, &africa },
8569783Smsmith	{ "2", "America -- North and South", 0, continent_country_menu, 0,
8669783Smsmith		  &america },
8769783Smsmith	{ "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
8869783Smsmith	{ "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
8969783Smsmith	{ "5", "Asia", 0, continent_country_menu, 0, &asia },
9069783Smsmith	{ "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
9169783Smsmith	{ "7", "Australia", 0, continent_country_menu, 0, &australia },
9269783Smsmith	{ "8", "Europe", 0, continent_country_menu, 0, &europe },
93164264Sjhb	{ "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
94164264Sjhb	{ "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
95164264Sjhb};
96164264Sjhb#define NCONTINENTS ((sizeof continents)/(sizeof continents[0]))
97169221Sjhb#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
9869783Smsmith
9969783Smsmithstatic int
10069783Smsmithcontinent_country_menu(dialogMenuItem *continent)
10169783Smsmith{
102154079Sjhb	int rv;
10369783Smsmith	struct continent *contp = continent->data;
104154079Sjhb	char title[256];
10569783Smsmith	int isocean = OCEANP(continent - continents);
10669783Smsmith	int menulen;
10769783Smsmith
108163805Simp	/* Short cut -- if there's only one country, don't post a menu. */
109163805Simp	if (contp->nitems == 1) {
110163805Simp		return set_zone_menu(&contp->menu[0]);
111163805Simp	}
112163805Simp
113163805Simp	/* It's amazing how much good grammar really matters... */
114163805Simp	if (!isocean)
115163805Simp		snprintf(title, sizeof title, "Countries in %s",
116163805Simp			 continent->title);
117163805Simp	else
118163805Simp		snprintf(title, sizeof title, "Islands and groups in the %s",
119163805Simp			 continent->title);
120163805Simp
121163805Simp	menulen = contp->nitems < 16 ? contp->nitems : 16;
122163805Simp	rv = dialog_menu(title, (isocean ? "Select an island or group"
123163805Simp                                 : "Select a country"), -1, -1, menulen,
124163805Simp			 -contp->nitems, contp->menu, 0, &contp->ch,
125163805Simp			 &contp->sc);
126163805Simp	if (rv == 0)
127163805Simp		return DITEM_LEAVE_MENU;
128163805Simp	return DITEM_RECREATE;
129163805Simp}
130163805Simp
131163805Simpstatic struct continent *
132163805Simpfind_continent(const char *name)
133163805Simp{
134163805Simp	int i;
135200341Sjkim
136200341Sjkim	for (i = 0; i < NCONTINENTS; i++) {
137200341Sjkim		if (strcmp(name, continent_names[i].name) == 0)
138200341Sjkim			return continent_names[i].continent;
139200341Sjkim	}
140200341Sjkim	return 0;
141200341Sjkim}
142200341Sjkim
143200341Sjkimstruct country {
144200341Sjkim	char *name;
145200341Sjkim	char *tlc;
146200341Sjkim	int nzones;
147200341Sjkim	char *filename;		/* use iff nzones < 0 */
148200341Sjkim	struct continent *continent; /* use iff nzones < 0 */
149200341Sjkim	TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */
150200341Sjkim	dialogMenuItem *submenu; /* use iff nzones > 0 */
151200341Sjkim};
152200341Sjkim
153200341Sjkimstruct zone {
154200341Sjkim	TAILQ_ENTRY(zone) link;
155200341Sjkim	char *descr;
156200341Sjkim	char *filename;
157200341Sjkim	struct continent *continent;
158200341Sjkim};
159200341Sjkim
160200341Sjkim/*
161200341Sjkim * This is the easiest organization... we use ISO 3166 country codes,
162200341Sjkim * of the two-letter variety, so we just size this array to suit.
163200341Sjkim * Beats worrying about dynamic allocation.
164200341Sjkim */
165200341Sjkim#define NCOUNTRIES	(26*26)
166200341Sjkimstatic struct country countries[NCOUNTRIES];
167200341Sjkim#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
168200341Sjkim
169200341Sjkim/*
170200341Sjkim * Read the ISO 3166 country code database in _PATH_ISO3166
171200341Sjkim * (/usr/share/misc/iso3166).  On error, exit via err(3).
172200341Sjkim */
173200341Sjkimstatic void
174200341Sjkimread_iso3166_table(void)
175200341Sjkim{
176200341Sjkim	FILE *fp;
177200341Sjkim	char *s, *t, *name;
178200341Sjkim	size_t len;
179200341Sjkim	int lineno;
180200341Sjkim	struct country *cp;
181200341Sjkim
182200341Sjkim	fp = fopen(_PATH_ISO3166, "r");
183200341Sjkim	if (!fp)
184200341Sjkim		err(1, _PATH_ISO3166);
185200341Sjkim	lineno = 0;
186200341Sjkim
187200341Sjkim	while ((s = fgetln(fp, &len)) != 0) {
188200341Sjkim		lineno++;
189200341Sjkim		if (s[len - 1] != '\n')
190200341Sjkim			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
191200341Sjkim		s[len - 1] = '\0';
192200341Sjkim		if (s[0] == '#' || strspn(s, " \t") == len - 1)
193200341Sjkim			continue;
194200341Sjkim
195200341Sjkim		/* Isolate the two-letter code. */
196200341Sjkim		t = strsep(&s, "\t");
197200341Sjkim		if (t == 0 || strlen(t) != 2)
198200341Sjkim			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
199200341Sjkim		if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
200200341Sjkim			errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
201200341Sjkim			     lineno, t);
202200341Sjkim
203200341Sjkim		/* Now skip past the three-letter and numeric codes. */
204200341Sjkim		name = strsep(&s, "\t"); /* 3-let */
205200341Sjkim		if (name == 0 || strlen(name) != 3)
206200341Sjkim			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
207200341Sjkim		name = strsep(&s, "\t"); /* numeric */
208200341Sjkim		if (name == 0 || strlen(name) != 3)
209200341Sjkim			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
210200341Sjkim
211200341Sjkim		name = s;
212200341Sjkim
213200341Sjkim		cp = &countries[CODE2INT(t)];
214200341Sjkim		if (cp->name)
215200341Sjkim			errx(1, _PATH_ISO3166
216200341Sjkim			     ":%d: country code `%s' multiply defined: %s",
217200341Sjkim			     lineno, t, cp->name);
218200341Sjkim		cp->name = strdup(name);
219200341Sjkim		cp->tlc = strdup(t);
220200341Sjkim	}
221200341Sjkim
222200341Sjkim	fclose(fp);
223200341Sjkim}
224200341Sjkim
225200341Sjkimstatic void
226200341Sjkimadd_zone_to_country(int lineno, const char *tlc, const char *descr,
227200341Sjkim		    const char *file, struct continent *cont)
228200341Sjkim{
229200341Sjkim	struct zone *zp;
230200341Sjkim	struct country *cp;
231200341Sjkim
232200341Sjkim	if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
233200341Sjkim		errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
234200341Sjkim		     lineno, tlc);
235200341Sjkim
236200341Sjkim	cp = &countries[CODE2INT(tlc)];
237200341Sjkim	if (cp->name == 0)
238200341Sjkim		errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
239200341Sjkim		     lineno, tlc);
240200341Sjkim
241200341Sjkim	if (descr) {
242200341Sjkim		if (cp->nzones < 0)
243200341Sjkim			errx(1, _PATH_ZONETAB
244200341Sjkim			     ":%d: conflicting zone definition", lineno);
245200341Sjkim
246200341Sjkim		zp = malloc(sizeof *zp);
247200341Sjkim		if (zp == 0)
248200341Sjkim			err(1, "malloc(%lu)", (unsigned long)sizeof *zp);
249200341Sjkim
250200341Sjkim		if (cp->nzones == 0)
251200341Sjkim			TAILQ_INIT(&cp->zones);
252200341Sjkim
253200341Sjkim		zp->descr = strdup(descr);
254200341Sjkim		zp->filename = strdup(file);
255200341Sjkim		zp->continent = cont;
256200341Sjkim		TAILQ_INSERT_TAIL(&cp->zones, zp, link);
257200341Sjkim		cp->nzones++;
258200341Sjkim	} else {
259200341Sjkim		if (cp->nzones > 0)
260200341Sjkim			errx(1, _PATH_ZONETAB
261200341Sjkim			     ":%d: zone must have description", lineno);
262200341Sjkim		if (cp->nzones < 0)
263200341Sjkim			errx(1, _PATH_ZONETAB
264200341Sjkim			     ":%d: zone multiply defined", lineno);
265200341Sjkim		cp->nzones = -1;
266200341Sjkim		cp->filename = strdup(file);
267200341Sjkim		cp->continent = cont;
268200341Sjkim	}
269200341Sjkim}
270200341Sjkim
271200341Sjkim/*
272200341Sjkim * This comparison function intentionally sorts all of the null-named
273200341Sjkim * ``countries''---i.e., the codes that don't correspond to a real
274200341Sjkim * country---to the end.  Everything else is lexical by country name.
275200341Sjkim */
276200341Sjkimstatic int
277200341Sjkimcompare_countries(const void *xa, const void *xb)
278200341Sjkim{
279200341Sjkim	const struct country *a = xa, *b = xb;
280200341Sjkim
281200341Sjkim	if (a->name == 0 && b->name == 0)
282200341Sjkim		return 0;
28369783Smsmith	if (a->name == 0 && b->name != 0)
28469783Smsmith		return 1;
28569783Smsmith	if (b->name == 0)
28669783Smsmith		return -1;
28769783Smsmith
28869783Smsmith	return strcmp(a->name, b->name);
28969783Smsmith}
29069783Smsmith
29169783Smsmith/*
29269783Smsmith * This must be done AFTER all zone descriptions are read, since it breaks
29369783Smsmith * CODE2INT().
29469783Smsmith */
29569783Smsmithstatic void
296102441Sjhbsort_countries(void)
297102441Sjhb{
29869783Smsmith	qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries);
29969783Smsmith}
300181789Simp
301181789Simpstatic void
30269783Smsmithread_zones(void)
30369783Smsmith{
30469783Smsmith	FILE *fp;
30569783Smsmith	char *line;
30669908Smsmith	size_t len;
30769908Smsmith	int lineno;
30869908Smsmith	char *tlc, *coord, *file, *descr, *p;
309200341Sjkim	char contbuf[16];
310200341Sjkim	struct continent *cont;
311200341Sjkim
31269783Smsmith	fp = fopen(_PATH_ZONETAB, "r");
31369908Smsmith	if (!fp)
314181789Simp		err(1, _PATH_ZONETAB);
315181789Simp	lineno = 0;
316181789Simp
317181789Simp	while ((line = fgetln(fp, &len)) != 0) {
318181789Simp		lineno++;
319182706Simp		if (line[len - 1] != '\n')
320181789Simp			errx(1, _PATH_ZONETAB ":%d: invalid format", lineno);
321182706Simp		line[len - 1] = '\0';
322181789Simp		if (line[0] == '#')
323182706Simp			continue;
324181789Simp
325182706Simp		tlc = strsep(&line, "\t");
326181789Simp		if (strlen(tlc) != 2)
327181789Simp			errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'",
32869908Smsmith			     lineno, tlc);
32969908Smsmith		coord = strsep(&line, "\t");
33069908Smsmith		file = strsep(&line, "\t");
331124365Simp		p = strchr(file, '/');
33269908Smsmith		if (p == 0)
333119266Simp			errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'",
33469908Smsmith			     lineno, file);
33569908Smsmith		contbuf[0] = '\0';
33669908Smsmith		strncat(contbuf, file, p - file);
33769908Smsmith		cont = find_continent(contbuf);
33869908Smsmith		if (!cont)
33969908Smsmith			errx(1, _PATH_ZONETAB ":%d: invalid region `%s'",
340124365Simp			     lineno, contbuf);
34169908Smsmith
342124365Simp		descr = (line && *line) ? line : 0;
343124365Simp
344124365Simp		add_zone_to_country(lineno, tlc, descr, file, cont);
345124365Simp	}
346124365Simp	fclose(fp);
347124365Simp}
348124365Simp
349124365Simpstatic void
350124365Simpmake_menus(void)
351124365Simp{
352124365Simp	struct country *cp;
35369908Smsmith	struct zone *zp, *zp2;
354149521Sjkim	struct continent *cont;
355149521Sjkim	dialogMenuItem *dmi;
356149521Sjkim	int i;
357149521Sjkim
358149521Sjkim	/*
359149521Sjkim	 * First, count up all the countries in each continent/ocean.
360157949Sjkim	 * Be careful to count those countries which have multiple zones
361149521Sjkim	 * only once for each.  NB: some countries are in multiple
362157949Sjkim	 * continents/oceans.
363157949Sjkim	 */
364149521Sjkim	for (cp = countries; cp->name; cp++) {
365157949Sjkim		if (cp->nzones == 0)
366157949Sjkim			continue;
367157949Sjkim		if (cp->nzones < 0) {
368157949Sjkim			cp->continent->nitems++;
369157949Sjkim		} else {
370157949Sjkim			for (zp = cp->zones.tqh_first; zp;
371157949Sjkim			     zp = zp->link.tqe_next) {
372157949Sjkim				cont = zp->continent;
373157949Sjkim				for (zp2 = cp->zones.tqh_first;
374149521Sjkim				     zp2->continent != cont;
375149521Sjkim				     zp2 = zp2->link.tqe_next)
376149521Sjkim					;
377149521Sjkim				if (zp2 == zp)
378149521Sjkim					zp->continent->nitems++;
379149521Sjkim			}
38069908Smsmith		}
38169908Smsmith	}
382165995Sjhb
383165995Sjhb	/*
384165995Sjhb	 * Now allocate memory for the country menus.  We set
385124365Simp	 * nitems back to zero so that we can use it for counting
386124365Simp	 * again when we actually build the menus.
387124365Simp	 */
388124365Simp	for (i = 0; i < NCONTINENTS; i++) {
389124365Simp		continent_names[i].continent->menu =
390124365Simp			malloc(sizeof(dialogMenuItem) *
391124365Simp			       continent_names[i].continent->nitems);
392124365Simp		if (continent_names[i].continent->menu == 0)
393124365Simp			err(1, "malloc for continent menu");
394168157Sjhb		continent_names[i].continent->nitems = 0;
395124365Simp	}
396124365Simp
39769783Smsmith	/*
398172394Smarius	 * Now that memory is allocated, create the menu items for
39969783Smsmith	 * each continent.  For multiple-zone countries, also create
40069783Smsmith	 * the country's zone submenu.
40169783Smsmith	 */
402163805Simp	for (cp = countries; cp->name; cp++) {
403163805Simp		if (cp->nzones == 0)
404163805Simp			continue;
405163805Simp		if (cp->nzones < 0) {
406163805Simp			dmi = &cp->continent->menu[cp->continent->nitems];
407163805Simp			memset(dmi, 0, sizeof *dmi);
408163805Simp			asprintf(&dmi->prompt, "%d",
409163805Simp				 ++cp->continent->nitems);
410124365Simp			dmi->title = cp->name;
411124365Simp			dmi->checked = 0;
41269783Smsmith			dmi->fire = set_zone_whole_country;
41369783Smsmith			dmi->selected = 0;
41469783Smsmith			dmi->data = cp;
41569783Smsmith		} else {
416181798Simp			cp->submenu = malloc(cp->nzones * sizeof *dmi);
417181798Simp			if (cp->submenu == 0)
418181798Simp				err(1, "malloc for submenu");
419181798Simp			cp->nzones = 0;
420181798Simp			for (zp = cp->zones.tqh_first; zp;
421181798Simp			     zp = zp->link.tqe_next) {
42269783Smsmith				cont = zp->continent;
42369783Smsmith				dmi = &cp->submenu[cp->nzones];
424181798Simp				memset(dmi, 0, sizeof *dmi);
425181798Simp				asprintf(&dmi->prompt, "%d",
42669783Smsmith					 ++cp->nzones);
427102441Sjhb				dmi->title = zp->descr;
42869783Smsmith				dmi->checked = 0;
429103042Sjhb				dmi->fire = set_zone_multi;
430102441Sjhb				dmi->selected = 0;
431102441Sjhb				dmi->data = zp;
432102441Sjhb
433102441Sjhb				for (zp2 = cp->zones.tqh_first;
434102441Sjhb				     zp2->continent != cont;
435102441Sjhb				     zp2 = zp2->link.tqe_next)
436102441Sjhb					;
43769783Smsmith				if (zp2 != zp)
438103016Sjhb					continue;
43969783Smsmith
44069783Smsmith				dmi = &cont->menu[cont->nitems];
441181798Simp				memset(dmi, 0, sizeof *dmi);
44269783Smsmith				asprintf(&dmi->prompt, "%d", ++cont->nitems);
44369783Smsmith				dmi->title = cp->name;
44469783Smsmith				dmi->checked = 0;
44569783Smsmith				dmi->fire = set_zone_menu;
44669783Smsmith				dmi->selected = 0;
447102441Sjhb				dmi->data = cp;
448200341Sjkim			}
449200341Sjkim		}
450200341Sjkim	}
451200341Sjkim}
452200341Sjkim
453200341Sjkimstatic int
454200341Sjkimset_zone_menu(dialogMenuItem *dmi)
455200341Sjkim{
456200341Sjkim	int rv;
457200341Sjkim	char buf[256];
458200341Sjkim	struct country *cp = dmi->data;
459200341Sjkim	int menulen;
460200341Sjkim
461200341Sjkim	snprintf(buf, sizeof buf, "%s Time Zones", cp->name);
462200341Sjkim	menulen = cp->nzones < 16 ? cp->nzones : 16;
463200341Sjkim	rv = dialog_menu(buf, "Select a zone which observes the same time as "
464200341Sjkim			 "your locality.", -1, -1, menulen, -cp->nzones,
465200341Sjkim			 cp->submenu, 0, 0, 0);
466200341Sjkim	if (rv != 0)
467200341Sjkim		return DITEM_RECREATE;
468200341Sjkim	return DITEM_LEAVE_MENU;
469200341Sjkim}
470200341Sjkim
471200341Sjkimstatic int
472200341Sjkiminstall_zone_file(const char *filename)
473200341Sjkim{
474200341Sjkim	struct stat sb;
475200341Sjkim	int fd1, fd2;
476200341Sjkim	int copymode;
477200341Sjkim	char *msg;
478200341Sjkim	ssize_t len;
479200341Sjkim	char buf[1024];
480200341Sjkim
481200341Sjkim	if (lstat(_PATH_LOCALTIME, &sb) < 0)
482200341Sjkim		/* Nothing there yet... */
48369783Smsmith		copymode = 1;
48469783Smsmith	else if(S_ISLNK(sb.st_mode))
48569783Smsmith		copymode = 0;
48669783Smsmith	else
48769783Smsmith		copymode = 1;
488172394Smarius
489172394Smarius#ifdef VERBOSE
490172394Smarius	if (copymode)
49169783Smsmith		asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename);
49269783Smsmith	else
49369783Smsmith		asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME
49469783Smsmith			 " to %s", filename);
49569783Smsmith
49669783Smsmith	dialog_notify(msg);
49769783Smsmith	free(msg);
498102441Sjhb#endif
49969783Smsmith
50069783Smsmith	if (reallydoit) {
50169783Smsmith		if (copymode) {
50269783Smsmith			fd1 = open(filename, O_RDONLY, 0);
50369783Smsmith			if (fd1 < 0) {
504172394Smarius				asprintf(&msg, "Could not open %s: %s",
505172394Smarius					 filename, strerror(errno));
50669783Smsmith				dialog_mesgbox("Error", msg, 8, 72);
50769783Smsmith				free(msg);
508172394Smarius				return DITEM_FAILURE | DITEM_RECREATE;
50969783Smsmith			}
51069783Smsmith
51169783Smsmith			unlink(_PATH_LOCALTIME);
51269783Smsmith			fd2 = open(_PATH_LOCALTIME,
51369783Smsmith				   O_CREAT | O_EXCL | O_WRONLY,
51469783Smsmith				   0444);
51569783Smsmith			if (fd2 < 0) {
51669783Smsmith				asprintf(&msg, "Could not open "
517102441Sjhb					 _PATH_LOCALTIME ": %s",
51869783Smsmith					 strerror(errno));
519142051Simp				dialog_mesgbox("Error", msg, 8, 72);
52069783Smsmith				free(msg);
521124365Simp				return DITEM_FAILURE | DITEM_RECREATE;
522164130Sjhb			}
523124365Simp
52469783Smsmith			while ((len = read(fd1, buf, sizeof buf)) > 0)
52569783Smsmith				len = write(fd2, buf, len);
52669783Smsmith
52769783Smsmith			if (len == -1) {
528164130Sjhb				asprintf(&msg, "Error copying %s to "
529164130Sjhb					 _PATH_LOCALTIME ": %s",
530164130Sjhb					 strerror(errno));
531164130Sjhb				dialog_mesgbox("Error", msg, 8, 72);
532164130Sjhb				free(msg);
533164130Sjhb				/* Better to leave none than a corrupt one. */
53469783Smsmith				unlink(_PATH_LOCALTIME);
53569783Smsmith				return DITEM_FAILURE | DITEM_RECREATE;
536107546Simp			}
537124365Simp			close(fd1);
538124365Simp			close(fd2);
539124365Simp		} else {
540145652Smarcel			if (access(filename, R_OK) != 0) {
541145652Smarcel				asprintf(&msg, "Cannot access %s: %s",
542145652Smarcel					 filename, strerror(errno));
543145652Smarcel				dialog_mesgbox("Error", msg, 8, 72);
544145652Smarcel				free(msg);
545145652Smarcel				return DITEM_FAILURE | DITEM_RECREATE;
546145652Smarcel			}
547145652Smarcel			unlink(_PATH_LOCALTIME);
548124365Simp			if (symlink(filename, _PATH_LOCALTIME) < 0) {
549124365Simp				asprintf(&msg, "Cannot create symbolic link "
550124365Simp					 _PATH_LOCALTIME " to %s: %s",
551124365Simp					 filename, strerror(errno));
552124365Simp				dialog_mesgbox("Error", msg, 8, 72);
553124365Simp				free(msg);
554142051Simp				return DITEM_FAILURE | DITEM_RECREATE;
555142051Simp			}
556124365Simp		}
557106844Smdodd	}
558124365Simp
559189844Simp#ifdef VERBOSE
560189792Simp	if (copymode)
561189792Simp		asprintf(&msg, "Copied timezone file from %s to "
562189792Simp			 _PATH_LOCALTIME, filename);
563189792Simp	else
564189792Simp		asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME
565189792Simp			 " to %s", filename);
566189844Simp
567106844Smdodd	dialog_mesgbox("Done", msg, 8, 72);
568124365Simp	free(msg);
569142051Simp#endif
570142051Simp	return DITEM_LEAVE_MENU;
571124365Simp}
572124365Simp
573124365Simpstatic int
574124365Simpconfirm_zone(const char *filename)
575124365Simp{
576164130Sjhb	char *msg;
577124365Simp	struct tm *tm;
578164130Sjhb	time_t t = time(0);
579124365Simp	int rv;
580124365Simp
581124365Simp	setenv("TZ", filename, 1);
582142051Simp	tzset();
583164130Sjhb	tm = localtime(&t);
584164130Sjhb
585124365Simp	asprintf(&msg, "Does the abbreviation `%s' look reasonable?",
58669783Smsmith		 tm->tm_zone);
58769783Smsmith	rv = !dialog_yesno("Confirmation", msg, 4, 72);
588107546Simp	free(msg);
589107546Simp	return rv;
590124365Simp}
591107546Simp
592124365Simpstatic int
593145652Smarcelset_zone_multi(dialogMenuItem *dmi)
594145652Smarcel{
595145652Smarcel	char *fn;
596145652Smarcel	struct zone *zp = dmi->data;
597145652Smarcel	int rv;
598145652Smarcel
599145652Smarcel	if (!confirm_zone(zp->filename))
600145652Smarcel		return DITEM_FAILURE | DITEM_RECREATE;
601124365Simp
602124365Simp	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename);
603124365Simp	rv = install_zone_file(fn);
604124365Simp	free(fn);
605124365Simp	return rv;
606124365Simp}
607124365Simp
608124365Simpstatic int
609124365Simpset_zone_whole_country(dialogMenuItem *dmi)
610124365Simp{
611124365Simp	char *fn;
612124365Simp	struct country *cp = dmi->data;
613124365Simp	int rv;
614124365Simp
615124365Simp	if (!confirm_zone(cp->filename))
616124365Simp		return DITEM_FAILURE | DITEM_RECREATE;
617124365Simp
618124365Simp	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename);
619124365Simp	rv = install_zone_file(fn);
620124365Simp	free(fn);
621124365Simp	return rv;
622124365Simp}
623107546Simp
624107546Simpstatic void
625124365Simpusage()
626189844Simp{
627124365Simp	fprintf(stderr, "usage: tzsetup [-n]\n");
628189792Simp	exit(1);
629189792Simp}
630124365Simp
631124365Simpint
632189792Simpmain(int argc, char **argv)
633189792Simp{
634124365Simp	int c;
635189844Simp
636106844Smdodd	while ((c = getopt(argc, argv, "n")) != -1) {
637124365Simp		switch(c) {
638142051Simp		case 'n':
639142051Simp			reallydoit = 0;
640124365Simp			break;
641124365Simp
642124365Simp		default:
643124365Simp			usage();
644124365Simp		}
645124365Simp	}
646164130Sjhb
647163805Simp	if (optind != argc)
648164130Sjhb		usage();
649163805Simp
650163805Simp	read_iso3166_table();
651124365Simp	read_zones();
652124365Simp	sort_countries();
653124365Simp	make_menus();
654164130Sjhb
655142051Simp	init_dialog();
656164130Sjhb	if (!dialog_yesno("Select local or UTC (Greenwich Mean Time) clock",
657124365Simp			  "Is this machine's CMOS clock set to UTC?  If it is set to local time,\n"
65869908Smsmith			  "please choose NO here!", 7, 72))
65969783Smsmith		if (reallydoit)
660124365Simp			system("rm -f /etc/wall_cmos_clock");
66169783Smsmith	else
662124365Simp		if (reallydoit)
663124365Simp			system("touch /etc/wall_cmos_clock");
664124365Simp	dialog_clear_norefresh();
665142051Simp	dialog_menu("Time Zone Selector", "Select a region", -1, -1,
666142051Simp		    NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL);
66769783Smsmith	dialog_clear();
66869783Smsmith	end_dialog();
66969783Smsmith	return 0;
67069783Smsmith}
67169783Smsmith
672102441Sjhb