tzsetup.c revision 30999
1141240Snjl/*
2167905Snjl * Copyright 1996 Massachusetts Institute of Technology
3141240Snjl *
4141240Snjl * Permission to use, copy, modify, and distribute this software and
5141240Snjl * its documentation for any purpose and without fee is hereby
6141240Snjl * granted, provided that both the above copyright notice and this
7141240Snjl * permission notice appear in all copies, that both the above
8141240Snjl * copyright notice and this permission notice appear in all
9141240Snjl * supporting documentation, and that the name of M.I.T. not be used
10141240Snjl * in advertising or publicity pertaining to distribution of the
11141240Snjl * software without specific, written prior permission.  M.I.T. makes
12141240Snjl * no representations about the suitability of this software for any
13141240Snjl * purpose.  It is provided "as is" without express or implied
14141240Snjl * warranty.
15141240Snjl *
16141240Snjl * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17141240Snjl * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18141240Snjl * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19141240Snjl * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20141240Snjl * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21141240Snjl * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22141240Snjl * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23141240Snjl * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24141240Snjl * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25141240Snjl * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26141240Snjl * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27141240Snjl * SUCH DAMAGE.
28141240Snjl */
29141240Snjl
30141240Snjl/*
31141240Snjl * Second attempt at a `tzmenu' program, using the separate description
32141240Snjl * files provided in newer tzdata releases.
33141240Snjl */
34141240Snjl
35142603Snjl#ifndef lint
36141240Snjlstatic const char rcsid[] =
37141240Snjl	"$Id: tzsetup.c,v 1.8 1997/10/27 07:49:47 charnier Exp $";
38141240Snjl#endif /* not lint */
39141240Snjl
40173204Snjl#include <sys/types.h>
41141240Snjl#include <dialog.h>
42173204Snjl#include <err.h>
43141240Snjl#include <errno.h>
44141240Snjl#include <stdio.h>
45142603Snjl#include <stdlib.h>
46141814Snjl#include <string.h>
47167905Snjl#include <unistd.h>
48141240Snjl
49141240Snjl#include <sys/fcntl.h>
50141240Snjl#include <sys/queue.h>
51141240Snjl#include <sys/stat.h>
52141240Snjl
53141240Snjl#include "paths.h"
54141240Snjl
55141240Snjlstatic int reallydoit = 1;
56141240Snjl
57141240Snjlstatic int continent_country_menu(dialogMenuItem *);
58142395Snjlstatic int set_zone_multi(dialogMenuItem *);
59141240Snjlstatic int set_zone_whole_country(dialogMenuItem *);
60142395Snjlstatic int set_zone_menu(dialogMenuItem *);
61141240Snjl
62150847Sumestruct continent {
63150847Sume	dialogMenuItem *menu;
64150847Sume	int nitems;
65150847Sume	int ch;
66150847Sume	int sc;
67150847Sume};
68141240Snjl
69142603Snjlstatic struct continent africa, america, antarctica, arctic, asia, atlantic;
70141240Snjlstatic struct continent australia, europe, indian, pacific;
71141923Snjl
72150847Sumestatic struct continent_names {
73141923Snjl	char *name;
74141413Snjl	struct continent *continent;
75141945Snjl} continent_names[] = {
76141240Snjl	{ "Africa", &africa }, { "America", &america },
77141240Snjl	{ "Antarctica", &antarctica }, { "Arctic", &arctic },
78167905Snjl	{ "Asia", &asia },
79141240Snjl	{ "Atlantic", &atlantic }, { "Australia", &australia },
80141240Snjl	{ "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific }
81141240Snjl};
82141240Snjl
83141240Snjlstatic dialogMenuItem continents[] = {
84141240Snjl	{ "1", "Africa", 0, continent_country_menu, 0, &africa },
85141240Snjl	{ "2", "America -- North and South", 0, continent_country_menu, 0,
86141240Snjl		  &america },
87141240Snjl	{ "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
88141240Snjl	{ "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
89142603Snjl	{ "5", "Asia", 0, continent_country_menu, 0, &asia },
90142603Snjl	{ "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
91142603Snjl	{ "7", "Australia", 0, continent_country_menu, 0, &australia },
92142603Snjl	{ "8", "Europe", 0, continent_country_menu, 0, &europe },
93142603Snjl	{ "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
94144876Snjl	{ "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
95144876Snjl};
96144876Snjl#define NCONTINENTS ((sizeof continents)/(sizeof continents[0]))
97144876Snjl#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
98144876Snjl
99141240Snjlstatic int
100167905Snjlcontinent_country_menu(dialogMenuItem *continent)
101141240Snjl{
102141240Snjl	int rv;
103141240Snjl	struct continent *contp = continent->data;
104141240Snjl	char title[256];
105141240Snjl	int isocean = OCEANP(continent - continents);
106141240Snjl	int menulen;
107141413Snjl
108141240Snjl	/* Short cut -- if there's only one country, don't post a menu. */
109141413Snjl	if (contp->nitems == 1) {
110141413Snjl		return set_zone_menu(&contp->menu[0]);
111141413Snjl	}
112141413Snjl
113141240Snjl	/* It's amazing how much good grammar really matters... */
114141240Snjl	if (!isocean)
115142114Snjl		snprintf(title, sizeof title, "Countries in %s",
116141240Snjl			 continent->title);
117141240Snjl	else
118141240Snjl		snprintf(title, sizeof title, "Islands and groups in the %s",
119141240Snjl			 continent->title);
120141240Snjl
121141240Snjl	menulen = contp->nitems < 16 ? contp->nitems : 16;
122141240Snjl	rv = dialog_menu(title, (isocean ? "Select an island or group"
123141240Snjl                                 : "Select a country"), -1, -1, menulen,
124141240Snjl			 -contp->nitems, contp->menu, 0, &contp->ch,
125141240Snjl			 &contp->sc);
126141240Snjl	if (rv == 0)
127141240Snjl		return DITEM_LEAVE_MENU;
128141240Snjl	return DITEM_RECREATE;
129141240Snjl}
130141240Snjl
131141240Snjlstatic struct continent *
132141240Snjlfind_continent(const char *name)
133142590Snjl{
134144876Snjl	int i;
135142590Snjl
136144876Snjl	for (i = 0; i < NCONTINENTS; i++) {
137142590Snjl		if (strcmp(name, continent_names[i].name) == 0)
138142590Snjl			return continent_names[i].continent;
139142590Snjl	}
140144876Snjl	return 0;
141144876Snjl}
142142590Snjl
143141240Snjlstruct country {
144141240Snjl	char *name;
145141240Snjl	char *tlc;
146141240Snjl	int nzones;
147141240Snjl	char *filename;		/* use iff nzones < 0 */
148141240Snjl	struct continent *continent; /* use iff nzones < 0 */
149141240Snjl	TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */
150144876Snjl	dialogMenuItem *submenu; /* use iff nzones > 0 */
151141240Snjl};
152141240Snjl
153141240Snjlstruct zone {
154141240Snjl	TAILQ_ENTRY(zone) link;
155141240Snjl	char *descr;
156142603Snjl	char *filename;
157141240Snjl	struct continent *continent;
158150847Sume};
159141945Snjl
160141240Snjl/*
161141240Snjl * This is the easiest organization... we use ISO 3166 country codes,
162141240Snjl * of the two-letter variety, so we just size this array to suit.
163141240Snjl * Beats worrying about dynamic allocation.
164141240Snjl */
165141240Snjl#define NCOUNTRIES	(26*26)
166141240Snjlstatic struct country countries[NCOUNTRIES];
167141240Snjl#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
168141240Snjl
169141240Snjl/*
170144876Snjl * Read the ISO 3166 country code database in _PATH_ISO3166
171144876Snjl * (/usr/share/misc/iso3166).  On error, exit via err(3).
172141240Snjl */
173141240Snjlstatic void
174141240Snjlread_iso3166_table(void)
175141240Snjl{
176141240Snjl	FILE *fp;
177141240Snjl	char *s, *t, *name;
178141240Snjl	size_t len;
179141240Snjl	int lineno;
180141240Snjl	struct country *cp;
181167905Snjl
182167905Snjl	fp = fopen(_PATH_ISO3166, "r");
183167905Snjl	if (!fp)
184167905Snjl		err(1, _PATH_ISO3166);
185167905Snjl	lineno = 0;
186167905Snjl
187167905Snjl	while ((s = fgetln(fp, &len)) != 0) {
188141240Snjl		lineno++;
189141240Snjl		if (s[len - 1] != '\n')
190141240Snjl			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
191167905Snjl		s[len - 1] = '\0';
192167905Snjl		if (s[0] == '#' || strspn(s, " \t") == len - 1)
193167905Snjl			continue;
194167905Snjl
195167905Snjl		/* Isolate the two-letter code. */
196167905Snjl		t = strsep(&s, "\t");
197167905Snjl		if (t == 0 || strlen(t) != 2)
198167905Snjl			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
199141240Snjl		if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
200141240Snjl			errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
201141240Snjl			     lineno, t);
202141240Snjl
203150847Sume		/* Now skip past the three-letter and numeric codes. */
204141240Snjl		name = strsep(&s, "\t"); /* 3-let */
205141240Snjl		if (name == 0 || strlen(name) != 3)
206144876Snjl			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
207141240Snjl		name = strsep(&s, "\t"); /* numeric */
208141240Snjl		if (name == 0 || strlen(name) != 3)
209141240Snjl			errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
210150847Sume
211150847Sume		name = s;
212150847Sume
213150847Sume		cp = &countries[CODE2INT(t)];
214150847Sume		if (cp->name)
215141240Snjl			errx(1, _PATH_ISO3166
216141240Snjl			     ":%d: country code `%s' multiply defined: %s",
217144876Snjl			     lineno, t, cp->name);
218144876Snjl		cp->name = strdup(name);
219144876Snjl		cp->tlc = strdup(t);
220141240Snjl	}
221141240Snjl
222141240Snjl	fclose(fp);
223141240Snjl}
224141240Snjl
225141240Snjlstatic void
226141240Snjladd_zone_to_country(int lineno, const char *tlc, const char *descr,
227141240Snjl		    const char *file, struct continent *cont)
228141240Snjl{
229150847Sume	struct zone *zp;
230141814Snjl	struct country *cp;
231171898Snjl
232141240Snjl	if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
233141240Snjl		errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
234142603Snjl		     lineno, tlc);
235142603Snjl
236150847Sume	cp = &countries[CODE2INT(tlc)];
237141240Snjl	if (cp->name == 0)
238167905Snjl		errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
239167905Snjl		     lineno, tlc);
240167905Snjl
241167905Snjl	if (descr) {
242167905Snjl		if (cp->nzones < 0)
243156228Smnag			errx(1, _PATH_ZONETAB
244141814Snjl			     ":%d: conflicting zone definition", lineno);
245150847Sume
246150847Sume		zp = malloc(sizeof *zp);
247173204Snjl		if (zp == 0)
248141923Snjl			err(1, "malloc(%lu)", (unsigned long)sizeof *zp);
249173204Snjl
250173204Snjl		if (cp->nzones == 0)
251173204Snjl			TAILQ_INIT(&cp->zones);
252173204Snjl
253173204Snjl		zp->descr = strdup(descr);
254173204Snjl		zp->filename = strdup(file);
255173204Snjl		zp->continent = cont;
256173204Snjl		TAILQ_INSERT_TAIL(&cp->zones, zp, link);
257173204Snjl		cp->nzones++;
258173204Snjl	} else {
259173204Snjl		if (cp->nzones > 0)
260173204Snjl			errx(1, _PATH_ZONETAB
261173204Snjl			     ":%d: zone must have description", lineno);
262173204Snjl		if (cp->nzones < 0)
263150847Sume			errx(1, _PATH_ZONETAB
264150847Sume			     ":%d: zone multiply defined", lineno);
265150847Sume		cp->nzones = -1;
266150847Sume		cp->filename = strdup(file);
267150847Sume		cp->continent = cont;
268150847Sume	}
269150847Sume}
270150847Sume
271150847Sume/*
272150847Sume * This comparison function intentionally sorts all of the null-named
273150847Sume * ``countries''---i.e., the codes that don't correspond to a real
274141923Snjl * country---to the end.  Everything else is lexical by country name.
275141923Snjl */
276141923Snjlstatic int
277141923Snjlcompare_countries(const void *xa, const void *xb)
278150847Sume{
279150847Sume	const struct country *a = xa, *b = xb;
280144876Snjl
281142603Snjl	if (a->name == 0 && b->name == 0)
282142603Snjl		return 0;
283142603Snjl	if (a->name == 0 && b->name != 0)
284150847Sume		return 1;
285150847Sume	if (b->name == 0)
286150847Sume		return -1;
287150847Sume
288142603Snjl	return strcmp(a->name, b->name);
289141923Snjl}
290142590Snjl
291148972Snjl/*
292144876Snjl * This must be done AFTER all zone descriptions are read, since it breaks
293144876Snjl * CODE2INT().
294142603Snjl */
295142603Snjlstatic void
296142603Snjlsort_countries(void)
297142590Snjl{
298141240Snjl	qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries);
299144876Snjl}
300144876Snjl
301144876Snjlstatic void
302149239Sumeread_zones(void)
303144876Snjl{
304141240Snjl	FILE *fp;
305141240Snjl	char *line;
306141240Snjl	size_t len;
307141240Snjl	int lineno;
308141240Snjl	char *tlc, *coord, *file, *descr, *p;
309141240Snjl	char contbuf[16];
310141240Snjl	struct continent *cont;
311141240Snjl
312141943Snjl	fp = fopen(_PATH_ZONETAB, "r");
313171898Snjl	if (!fp)
314141943Snjl		err(1, _PATH_ZONETAB);
315171898Snjl	lineno = 0;
316171898Snjl
317171898Snjl	while ((line = fgetln(fp, &len)) != 0) {
318144876Snjl		lineno++;
319144876Snjl		if (line[len - 1] != '\n')
320141240Snjl			errx(1, _PATH_ZONETAB ":%d: invalid format", lineno);
321171898Snjl		line[len - 1] = '\0';
322171898Snjl		if (line[0] == '#')
323171898Snjl			continue;
324141240Snjl
325141240Snjl		tlc = strsep(&line, "\t");
326141240Snjl		if (strlen(tlc) != 2)
327141240Snjl			errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'",
328141240Snjl			     lineno, tlc);
329141413Snjl		coord = strsep(&line, "\t");
330141413Snjl		file = strsep(&line, "\t");
331141413Snjl		p = strchr(file, '/');
332141413Snjl		if (p == 0)
333141413Snjl			errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'",
334141413Snjl			     lineno, file);
335141413Snjl		contbuf[0] = '\0';
336141943Snjl		strncat(contbuf, file, p - file);
337171898Snjl		cont = find_continent(contbuf);
338141943Snjl		if (!cont)
339171898Snjl			errx(1, _PATH_ZONETAB ":%d: invalid region `%s'",
340171898Snjl			     lineno, contbuf);
341171898Snjl
342144876Snjl		descr = (line && *line) ? line : 0;
343144876Snjl
344141413Snjl		add_zone_to_country(lineno, tlc, descr, file, cont);
345171898Snjl	}
346171898Snjl	fclose(fp);
347171898Snjl}
348141413Snjl
349141413Snjlstatic void
350141413Snjlmake_menus(void)
351141413Snjl{
352141413Snjl	struct country *cp;
353141240Snjl	struct zone *zp, *zp2;
354149239Sume	struct continent *cont;
355141923Snjl	dialogMenuItem *dmi;
356141923Snjl	int i;
357150847Sume
358141923Snjl	/*
359141923Snjl	 * First, count up all the countries in each continent/ocean.
360150847Sume	 * Be careful to count those countries which have multiple zones
361144876Snjl	 * only once for each.  NB: some countries are in multiple
362144876Snjl	 * continents/oceans.
363150847Sume	 */
364150847Sume	for (cp = countries; cp->name; cp++) {
365150847Sume		if (cp->nzones == 0)
366150847Sume			continue;
367150847Sume		if (cp->nzones < 0) {
368150847Sume			cp->continent->nitems++;
369150847Sume		} else {
370150847Sume			for (zp = cp->zones.tqh_first; zp;
371141923Snjl			     zp = zp->link.tqe_next) {
372141240Snjl				cont = zp->continent;
373141923Snjl				for (zp2 = cp->zones.tqh_first;
374141240Snjl				     zp2->continent != cont;
375150847Sume				     zp2 = zp2->link.tqe_next)
376150847Sume					;
377150847Sume				if (zp2 == zp)
378150847Sume					zp->continent->nitems++;
379150847Sume			}
380150847Sume		}
381150847Sume	}
382150847Sume
383141240Snjl	/*
384142603Snjl	 * Now allocate memory for the country menus.  We set
385167905Snjl	 * nitems back to zero so that we can use it for counting
386167905Snjl	 * again when we actually build the menus.
387167905Snjl	 */
388167905Snjl	for (i = 0; i < NCONTINENTS; i++) {
389167905Snjl		continent_names[i].continent->menu =
390167905Snjl			malloc(sizeof(dialogMenuItem) *
391142603Snjl			       continent_names[i].continent->nitems);
392141240Snjl		if (continent_names[i].continent->menu == 0)
393167905Snjl			err(1, "malloc for continent menu");
394141240Snjl		continent_names[i].continent->nitems = 0;
395141240Snjl	}
396141240Snjl
397141240Snjl	/*
398141240Snjl	 * Now that memory is allocated, create the menu items for
399141240Snjl	 * each continent.  For multiple-zone countries, also create
400141240Snjl	 * the country's zone submenu.
401141240Snjl	 */
402141240Snjl	for (cp = countries; cp->name; cp++) {
403141240Snjl		if (cp->nzones == 0)
404141240Snjl			continue;
405171896Snjl		if (cp->nzones < 0) {
406141240Snjl			dmi = &cp->continent->menu[cp->continent->nitems];
407141240Snjl			memset(dmi, 0, sizeof *dmi);
408141240Snjl			asprintf(&dmi->prompt, "%d",
409142603Snjl				 ++cp->continent->nitems);
410141240Snjl			dmi->title = cp->name;
411141240Snjl			dmi->checked = 0;
412141240Snjl			dmi->fire = set_zone_whole_country;
413142603Snjl			dmi->selected = 0;
414142603Snjl			dmi->data = cp;
415144876Snjl		} else {
416144876Snjl			cp->submenu = malloc(cp->nzones * sizeof *dmi);
417141240Snjl			if (cp->submenu == 0)
418144876Snjl				err(1, "malloc for submenu");
419142603Snjl			cp->nzones = 0;
420141240Snjl			for (zp = cp->zones.tqh_first; zp;
421141240Snjl			     zp = zp->link.tqe_next) {
422141240Snjl				cont = zp->continent;
423141240Snjl				dmi = &cp->submenu[cp->nzones];
424141240Snjl				memset(dmi, 0, sizeof *dmi);
425141240Snjl				asprintf(&dmi->prompt, "%d",
426141240Snjl					 ++cp->nzones);
427141240Snjl				dmi->title = zp->descr;
428141240Snjl				dmi->checked = 0;
429141240Snjl				dmi->fire = set_zone_multi;
430141240Snjl				dmi->selected = 0;
431142395Snjl				dmi->data = zp;
432142395Snjl
433142395Snjl				for (zp2 = cp->zones.tqh_first;
434142603Snjl				     zp2->continent != cont;
435142603Snjl				     zp2 = zp2->link.tqe_next)
436142395Snjl					;
437141240Snjl				if (zp2 != zp)
438142603Snjl					continue;
439142603Snjl
440142603Snjl				dmi = &cont->menu[cont->nitems];
441142603Snjl				memset(dmi, 0, sizeof *dmi);
442142603Snjl				asprintf(&dmi->prompt, "%d", ++cont->nitems);
443142603Snjl				dmi->title = cp->name;
444142603Snjl				dmi->checked = 0;
445142603Snjl				dmi->fire = set_zone_menu;
446142603Snjl				dmi->selected = 0;
447142603Snjl				dmi->data = cp;
448142603Snjl			}
449142603Snjl		}
450142603Snjl	}
451142603Snjl}
452171896Snjl
453171896Snjlstatic int
454141240Snjlset_zone_menu(dialogMenuItem *dmi)
455171896Snjl{
456141240Snjl	int rv;
457141240Snjl	char buf[256];
458141240Snjl	struct country *cp = dmi->data;
459141413Snjl	int menulen;
460141240Snjl
461141240Snjl	snprintf(buf, sizeof buf, "%s Time Zones", cp->name);
462141240Snjl	menulen = cp->nzones < 16 ? cp->nzones : 16;
463141240Snjl	rv = dialog_menu(buf, "Select a zone which observes the same time as "
464141240Snjl			 "your locality.", -1, -1, menulen, -cp->nzones,
465141240Snjl			 cp->submenu, 0, 0, 0);
466144876Snjl	if (rv != 0)
467144876Snjl		return DITEM_RECREATE;
468141240Snjl	return DITEM_LEAVE_MENU;
469144876Snjl}
470141240Snjl
471141240Snjlstatic int
472141240Snjlinstall_zone_file(const char *filename)
473141240Snjl{
474141240Snjl	struct stat sb;
475141240Snjl	int fd1, fd2;
476141240Snjl	int copymode;
477141240Snjl	char *msg;
478141240Snjl	ssize_t len;
479141240Snjl	char buf[1024];
480141240Snjl
481141240Snjl	if (lstat(_PATH_LOCALTIME, &sb) < 0)
482141240Snjl		/* Nothing there yet... */
483141240Snjl		copymode = 1;
484141240Snjl	else if(S_ISLNK(sb.st_mode))
485144876Snjl		copymode = 0;
486141240Snjl	else
487141240Snjl		copymode = 1;
488141240Snjl
489141240Snjl#ifdef VERBOSE
490141240Snjl	if (copymode)
491142603Snjl		asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename);
492142603Snjl	else
493142603Snjl		asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME
494142603Snjl			 " to %s", filename);
495141240Snjl
496141240Snjl	dialog_notify(msg);
497142603Snjl	free(msg);
498141240Snjl#endif
499141240Snjl
500141240Snjl	if (reallydoit) {
501141240Snjl		if (copymode) {
502141240Snjl			fd1 = open(filename, O_RDONLY, 0);
503141413Snjl			if (fd1 < 0) {
504141240Snjl				asprintf(&msg, "Could not open %s: %s",
505141240Snjl					 filename, strerror(errno));
506141240Snjl				dialog_mesgbox("Error", msg, 8, 72);
507141240Snjl				free(msg);
508141240Snjl				return DITEM_FAILURE | DITEM_RECREATE;
509141240Snjl			}
510141413Snjl
511141240Snjl			unlink(_PATH_LOCALTIME);
512141240Snjl			fd2 = open(_PATH_LOCALTIME,
513141240Snjl				   O_CREAT | O_EXCL | O_WRONLY,
514141240Snjl				   0444);
515141240Snjl			if (fd2 < 0) {
516141240Snjl				asprintf(&msg, "Could not open "
517141240Snjl					 _PATH_LOCALTIME ": %s",
518141240Snjl					 strerror(errno));
519141240Snjl				dialog_mesgbox("Error", msg, 8, 72);
520141240Snjl				free(msg);
521141240Snjl				return DITEM_FAILURE | DITEM_RECREATE;
522141240Snjl			}
523141240Snjl
524141240Snjl			while ((len = read(fd1, buf, sizeof buf)) > 0)
525141240Snjl				len = write(fd2, buf, len);
526141240Snjl
527141240Snjl			if (len == -1) {
528142603Snjl				asprintf(&msg, "Error copying %s to "
529141240Snjl					 _PATH_LOCALTIME ": %s",
530141824Snjl					 strerror(errno));
531141240Snjl				dialog_mesgbox("Error", msg, 8, 72);
532141240Snjl				free(msg);
533141824Snjl				/* Better to leave none than a corrupt one. */
534141824Snjl				unlink(_PATH_LOCALTIME);
535141824Snjl				return DITEM_FAILURE | DITEM_RECREATE;
536141824Snjl			}
537141824Snjl			close(fd1);
538142032Snjl			close(fd2);
539144876Snjl		} else {
540144876Snjl			if (access(filename, R_OK) != 0) {
541144876Snjl				asprintf(&msg, "Cannot access %s: %s",
542144876Snjl					 filename, strerror(errno));
543144876Snjl				dialog_mesgbox("Error", msg, 8, 72);
544142032Snjl				free(msg);
545144876Snjl				return DITEM_FAILURE | DITEM_RECREATE;
546141240Snjl			}
547142032Snjl			unlink(_PATH_LOCALTIME);
548142032Snjl			if (symlink(filename, _PATH_LOCALTIME) < 0) {
549141240Snjl				asprintf(&msg, "Cannot create symbolic link "
550141413Snjl					 _PATH_LOCALTIME " to %s: %s",
551141824Snjl					 filename, strerror(errno));
552141814Snjl				dialog_mesgbox("Error", msg, 8, 72);
553141413Snjl				free(msg);
554141413Snjl				return DITEM_FAILURE | DITEM_RECREATE;
555141413Snjl			}
556141413Snjl		}
557144876Snjl	}
558141413Snjl
559141413Snjl#ifdef VERBOSE
560141413Snjl	if (copymode)
561141413Snjl		asprintf(&msg, "Copied timezone file from %s to "
562141413Snjl			 _PATH_LOCALTIME, filename);
563141413Snjl	else
564141413Snjl		asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME
565141413Snjl			 " to %s", filename);
566141413Snjl
567141413Snjl	dialog_mesgbox("Done", msg, 8, 72);
568141413Snjl	free(msg);
569141413Snjl#endif
570141240Snjl	return DITEM_LEAVE_MENU;
571141240Snjl}
572141240Snjl
573141240Snjlstatic int
574141945Snjlconfirm_zone(const char *filename)
575141945Snjl{
576141945Snjl	char *msg;
577141945Snjl	struct tm *tm;
578141945Snjl	time_t t = time(0);
579141945Snjl	int rv;
580141945Snjl
581141240Snjl	setenv("TZ", filename, 1);
582141945Snjl	tzset();
583141945Snjl	tm = localtime(&t);
584141945Snjl
585141945Snjl	asprintf(&msg, "Does the abbreviation `%s' look reasonable?",
586141240Snjl		 tm->tm_zone);
587141945Snjl	rv = !dialog_yesno("Confirmation", msg, 4, 72);
588141945Snjl	free(msg);
589141945Snjl	return rv;
590141413Snjl}
591141240Snjl
592141240Snjlstatic int
593141240Snjlset_zone_multi(dialogMenuItem *dmi)
594141240Snjl{
595141413Snjl	char *fn;
596141413Snjl	struct zone *zp = dmi->data;
597141413Snjl	int rv;
598141413Snjl
599141413Snjl	if (!confirm_zone(zp->filename))
600141413Snjl		return DITEM_FAILURE | DITEM_RECREATE;
601141413Snjl
602141413Snjl	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename);
603141413Snjl	rv = install_zone_file(fn);
604141413Snjl	free(fn);
605141413Snjl	return rv;
606141413Snjl}
607141240Snjl
608141240Snjlstatic int
609142590Snjlset_zone_whole_country(dialogMenuItem *dmi)
610148972Snjl{
611142590Snjl	char *fn;
612142590Snjl	struct country *cp = dmi->data;
613142590Snjl	int rv;
614142590Snjl
615141240Snjl	if (!confirm_zone(cp->filename))
616141240Snjl		return DITEM_FAILURE | DITEM_RECREATE;
617141240Snjl
618141413Snjl	asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename);
619141240Snjl	rv = install_zone_file(fn);
620141240Snjl	free(fn);
621141240Snjl	return rv;
622141240Snjl}
623141240Snjl
624141240Snjlstatic void
625141240Snjlusage()
626141240Snjl{
627142603Snjl	fprintf(stderr, "usage: tzsetup [-n]\n");
628142603Snjl	exit(1);
629142603Snjl}
630141413Snjl
631141413Snjlint
632141413Snjlmain(int argc, char **argv)
633141413Snjl{
634141240Snjl	int c;
635141240Snjl
636141240Snjl	while ((c = getopt(argc, argv, "n")) != -1) {
637141240Snjl		switch(c) {
638141240Snjl		case 'n':
639141240Snjl			reallydoit = 0;
640141240Snjl			break;
641141240Snjl
642141240Snjl		default:
643141240Snjl			usage();
644141413Snjl		}
645141240Snjl	}
646141240Snjl
647141413Snjl	if (optind != argc)
648141240Snjl		usage();
649141240Snjl
650141240Snjl	read_iso3166_table();
651142603Snjl	read_zones();
652142603Snjl	sort_countries();
653141413Snjl	make_menus();
654141240Snjl
655141240Snjl	init_dialog();
656141240Snjl	if (!dialog_yesno("Select local or UTC (Greenwich Mean Time) clock",
657141240Snjl			  "Is this machine's CMOS clock set to UTC?  If it is set to local time,\n"
658141240Snjl			  "please choose NO here!", 7, 72))
659141413Snjl		if (reallydoit)
660141413Snjl			system("rm -f /etc/wall_cmos_clock");
661141413Snjl	else
662141240Snjl		if (reallydoit)
663141240Snjl			system("touch /etc/wall_cmos_clock");
664144876Snjl	dialog_clear_norefresh();
665144876Snjl	dialog_menu("Time Zone Selector", "Select a region", -1, -1,
666141240Snjl		    NCONTINENTS, -NCONTINENTS, continents, 0, NULL, NULL);
667141240Snjl	dialog_clear();
668141240Snjl	end_dialog();
669141240Snjl	return 0;
670141240Snjl}
671141413Snjl
672144876Snjl