Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / icu / source / tools / tzcode / zdump.c
1 static char     elsieid[] = "@(#)zdump.c        8.8";
2
3 /*
4 ** This code has been made independent of the rest of the time
5 ** conversion package to increase confidence in the verification it provides.
6 ** You can use this code to help in verifying other implementations.
7 */
8
9 /*
10  * ICU note: Mr. Arthur David Olson (olsona@dc37a.nci.nih.gov) stated that
11  * "zdump.c is indeed in the public domain" in e-mail on Feb 22, 2007.
12  * This version of zdump.c is modified by ICU team to change output format
13  * and some additional options.
14  */
15
16
17 #include "stdio.h"      /* for stdout, stderr, perror */
18 #include "string.h"     /* for strcpy */
19 #include "sys/types.h"  /* for time_t */
20 #include "time.h"       /* for struct tm */
21 #include "stdlib.h"     /* for exit, malloc, atoi */
22 #include "float.h"      /* for FLT_MAX and DBL_MAX */
23 #include "ctype.h"      /* for isalpha et al. */
24
25 /* Enable extensions and modifications for ICU. */
26 #define ICU
27
28 #ifdef ICU
29 #include "dirent.h"
30 #endif
31
32 #ifndef isascii
33 #define isascii(x) 1
34 #endif /* !defined isascii */
35
36 #ifndef ZDUMP_LO_YEAR
37 #define ZDUMP_LO_YEAR   (-500)
38 #endif /* !defined ZDUMP_LO_YEAR */
39
40 #ifndef ZDUMP_HI_YEAR
41 #define ZDUMP_HI_YEAR   2500
42 #endif /* !defined ZDUMP_HI_YEAR */
43
44 #ifndef MAX_STRING_LENGTH
45 #define MAX_STRING_LENGTH       1024
46 #endif /* !defined MAX_STRING_LENGTH */
47
48 #ifndef TRUE
49 #define TRUE            1
50 #endif /* !defined TRUE */
51
52 #ifndef FALSE
53 #define FALSE           0
54 #endif /* !defined FALSE */
55
56 #ifndef EXIT_SUCCESS
57 #define EXIT_SUCCESS    0
58 #endif /* !defined EXIT_SUCCESS */
59
60 #ifndef EXIT_FAILURE
61 #define EXIT_FAILURE    1
62 #endif /* !defined EXIT_FAILURE */
63
64 #ifndef SECSPERMIN
65 #define SECSPERMIN      60
66 #endif /* !defined SECSPERMIN */
67
68 #ifndef MINSPERHOUR
69 #define MINSPERHOUR     60
70 #endif /* !defined MINSPERHOUR */
71
72 #ifndef SECSPERHOUR
73 #define SECSPERHOUR     (SECSPERMIN * MINSPERHOUR)
74 #endif /* !defined SECSPERHOUR */
75
76 #ifndef HOURSPERDAY
77 #define HOURSPERDAY     24
78 #endif /* !defined HOURSPERDAY */
79
80 #ifndef EPOCH_YEAR
81 #define EPOCH_YEAR      1970
82 #endif /* !defined EPOCH_YEAR */
83
84 #ifndef TM_YEAR_BASE
85 #define TM_YEAR_BASE    1900
86 #endif /* !defined TM_YEAR_BASE */
87
88 #ifndef DAYSPERNYEAR
89 #define DAYSPERNYEAR    365
90 #endif /* !defined DAYSPERNYEAR */
91
92 #ifndef isleap
93 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
94 #endif /* !defined isleap */
95
96 #ifndef isleap_sum
97 /*
98 ** See tzfile.h for details on isleap_sum.
99 */
100 #define isleap_sum(a, b)        isleap((a) % 400 + (b) % 400)
101 #endif /* !defined isleap_sum */
102
103 #define SECSPERDAY      ((long) SECSPERHOUR * HOURSPERDAY)
104 #define SECSPERNYEAR    (SECSPERDAY * DAYSPERNYEAR)
105 #define SECSPERLYEAR    (SECSPERNYEAR + SECSPERDAY)
106
107 #ifndef HAVE_GETTEXT
108 #define HAVE_GETTEXT 0
109 #endif
110 #if HAVE_GETTEXT
111 #include "locale.h"     /* for setlocale */
112 #include "libintl.h"
113 #endif /* HAVE_GETTEXT */
114
115 #ifndef GNUC_or_lint
116 #ifdef lint
117 #define GNUC_or_lint
118 #else /* !defined lint */
119 #ifdef __GNUC__
120 #define GNUC_or_lint
121 #endif /* defined __GNUC__ */
122 #endif /* !defined lint */
123 #endif /* !defined GNUC_or_lint */
124
125 #ifndef INITIALIZE
126 #ifdef GNUC_or_lint
127 #define INITIALIZE(x)   ((x) = 0)
128 #else /* !defined GNUC_or_lint */
129 #define INITIALIZE(x)
130 #endif /* !defined GNUC_or_lint */
131 #endif /* !defined INITIALIZE */
132
133 /*
134 ** For the benefit of GNU folk...
135 ** `_(MSGID)' uses the current locale's message library string for MSGID.
136 ** The default is to use gettext if available, and use MSGID otherwise.
137 */
138
139 #ifndef _
140 #if HAVE_GETTEXT
141 #define _(msgid) gettext(msgid)
142 #else /* !HAVE_GETTEXT */
143 #define _(msgid) msgid
144 #endif /* !HAVE_GETTEXT */
145 #endif /* !defined _ */
146
147 #ifndef TZ_DOMAIN
148 #define TZ_DOMAIN "tz"
149 #endif /* !defined TZ_DOMAIN */
150
151 extern char **  environ;
152 extern int      getopt(int argc, char * const argv[],
153                         const char * options);
154 extern char *   optarg;
155 extern int      optind;
156 extern char *   tzname[2];
157
158 static time_t   absolute_min_time;
159 static time_t   absolute_max_time;
160 static size_t   longest;
161 static char *   progname;
162 static int      warned;
163
164 static char *   abbr(struct tm * tmp);
165 static void     abbrok(const char * abbrp, const char * zone);
166 static long     delta(struct tm * newp, struct tm * oldp);
167 static void     dumptime(const struct tm * tmp);
168 static time_t   hunt(char * name, time_t lot, time_t    hit);
169 static void     setabsolutes(void);
170 static void     show(char * zone, time_t t, int v);
171 static const char *     tformat(void);
172 static time_t   yeartot(long y);
173 #ifdef ICU
174 typedef struct listentry {
175         char *          name;
176         struct listentry *      next;
177 } listentry;
178
179 static time_t   huntICU(char * name, time_t lot, time_t hit, FILE *fp);
180 static void     dumptimeICU(FILE * fp, time_t t);
181 static void     showICU(FILE * fp, char * zone, time_t t1, time_t t2);
182 static int      getall(struct listentry ** namelist);
183 static void getzones(char * basedir, char * subdir, struct listentry ** last, int * count);
184 #endif
185
186 #ifndef TYPECHECK
187 #define my_localtime    localtime
188 #else /* !defined TYPECHECK */
189 static struct tm *
190 my_localtime(tp)
191 time_t *        tp;
192 {
193         register struct tm *    tmp;
194
195         tmp = localtime(tp);
196         if (tp != NULL && tmp != NULL) {
197                 struct tm       tm;
198                 register time_t t;
199
200                 tm = *tmp;
201                 t = mktime(&tm);
202                 if (t - *tp >= 1 || *tp - t >= 1) {
203                         (void) fflush(stdout);
204                         (void) fprintf(stderr, "\n%s: ", progname);
205                         (void) fprintf(stderr, tformat(), *tp);
206                         (void) fprintf(stderr, " ->");
207                         (void) fprintf(stderr, " year=%d", tmp->tm_year);
208                         (void) fprintf(stderr, " mon=%d", tmp->tm_mon);
209                         (void) fprintf(stderr, " mday=%d", tmp->tm_mday);
210                         (void) fprintf(stderr, " hour=%d", tmp->tm_hour);
211                         (void) fprintf(stderr, " min=%d", tmp->tm_min);
212                         (void) fprintf(stderr, " sec=%d", tmp->tm_sec);
213                         (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
214                         (void) fprintf(stderr, " -> ");
215                         (void) fprintf(stderr, tformat(), t);
216                         (void) fprintf(stderr, "\n");
217                 }
218         }
219         return tmp;
220 }
221 #endif /* !defined TYPECHECK */
222
223 static void
224 abbrok(abbrp, zone)
225 const char * const      abbrp;
226 const char * const      zone;
227 {
228         register const char *   cp;
229         register char *         wp;
230
231         if (warned)
232                 return;
233         cp = abbrp;
234         wp = NULL;
235         while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
236                 ++cp;
237         if (cp - abbrp == 0)
238                 wp = _("lacks alphabetic at start");
239         else if (cp - abbrp < 3)
240                 wp = _("has fewer than 3 alphabetics");
241         else if (cp - abbrp > 6)
242                 wp = _("has more than 6 alphabetics");
243         if (wp == NULL && (*cp == '+' || *cp == '-')) {
244                 ++cp;
245                 if (isascii((unsigned char) *cp) &&
246                         isdigit((unsigned char) *cp))
247                                 if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
248                                         ++cp;
249                 if (*cp != '\0')
250                         wp = _("differs from POSIX standard");
251         }
252         if (wp == NULL)
253                 return;
254         (void) fflush(stdout);
255         (void) fprintf(stderr,
256                 _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
257                 progname, zone, abbrp, wp);
258         warned = TRUE;
259 }
260
261 static void
262 usage(const char *progname, FILE *stream, int status)
263 {
264         (void) fprintf(stream,
265 _("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -c [loyear,]hiyear ] zonename ...\n\
266 \n\
267 Report bugs to tz@elsie.nci.nih.gov.\n"),
268                        progname, progname);
269         exit(status);
270 }
271
272 int
273 main(argc, argv)
274 int     argc;
275 char *  argv[];
276 {
277         register int            i;
278         register int            c;
279         register int            vflag;
280         register char *         cutarg;
281         register long           cutloyear = ZDUMP_LO_YEAR;
282         register long           cuthiyear = ZDUMP_HI_YEAR;
283         register time_t         cutlotime;
284         register time_t         cuthitime;
285         register char **        fakeenv;
286         time_t                  now;
287         time_t                  t;
288         time_t                  newt;
289         struct tm               tm;
290         struct tm               newtm;
291         register struct tm *    tmp;
292         register struct tm *    newtmp;
293 #ifdef ICU
294         int             nextopt;
295         char *  dirarg;
296         int             aflag;
297         int             iflag;
298         listentry *     namelist = NULL;
299         FILE *  fp = stdout;
300 #endif
301
302         INITIALIZE(cutlotime);
303         INITIALIZE(cuthitime);
304 #if HAVE_GETTEXT
305         (void) setlocale(LC_ALL, "");
306 #ifdef TZ_DOMAINDIR
307         (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
308 #endif /* defined TEXTDOMAINDIR */
309         (void) textdomain(TZ_DOMAIN);
310 #endif /* HAVE_GETTEXT */
311         progname = argv[0];
312         for (i = 1; i < argc; ++i)
313                 if (strcmp(argv[i], "--version") == 0) {
314                         (void) printf("%s\n", elsieid);
315                         exit(EXIT_SUCCESS);
316                 } else if (strcmp(argv[i], "--help") == 0) {
317                         usage(progname, stdout, EXIT_SUCCESS);
318                 }
319         vflag = 0;
320         cutarg = NULL;
321 #ifdef ICU
322         aflag = 0;
323         iflag = 0;
324         dirarg = NULL;
325         nextopt = 1;
326         while(nextopt) {
327                 c = getopt(argc, argv, "ac:d:iv");
328                 switch(c) {
329                 case 'a':
330                         aflag = 1;
331                         break;
332                 case 'c':
333                         cutarg = optarg;
334                         break;
335                 case 'd':
336                         dirarg = optarg;
337                         break;
338                 case 'i':
339                         iflag = 1;
340                         break;
341                 case 'v':
342                         vflag = 1;
343                         break;
344                 default:
345                         nextopt = 0;
346                         break;
347                 }
348         }
349         if ((c != EOF && c != -1) ||
350                 (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
351                         (void) fprintf(stderr,
352                                 _("%s: usage is %s [ --version ] [ -a ] [ -v ] [ -i ] [ -c [loyear,]hiyear ] [ -d dir ] [ zonename ... ]\n"),
353                                 progname, progname);
354                         exit(EXIT_FAILURE);
355         }
356
357         if (dirarg != NULL) {
358                 DIR *   dp;
359                 /* create the output directory */
360                 mkdir(dirarg, 0777);
361                 if ((dp = opendir(dirarg)) == NULL) {
362                         fprintf(stderr, "cannot create the target directory");
363                         exit(EXIT_FAILURE);
364                 }
365                 closedir(dp);
366         }
367 #else
368         while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
369                 if (c == 'v')
370                         vflag = 1;
371                 else    cutarg = optarg;
372         if ((c != EOF && c != -1) ||
373                 (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
374                         usage(progname, stderr, EXIT_FAILURE);
375         }
376 #endif
377         if (vflag) {
378                 if (cutarg != NULL) {
379                         long    lo;
380                         long    hi;
381                         char    dummy;
382
383                         if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
384                                 cuthiyear = hi;
385                         } else if (sscanf(cutarg, "%ld,%ld%c",
386                                 &lo, &hi, &dummy) == 2) {
387                                         cutloyear = lo;
388                                         cuthiyear = hi;
389                         } else {
390 (void) fprintf(stderr, _("%s: wild -c argument %s\n"),
391                                         progname, cutarg);
392                                 exit(EXIT_FAILURE);
393                         }
394                 }
395                 setabsolutes();
396                 cutlotime = yeartot(cutloyear);
397                 cuthitime = yeartot(cuthiyear);
398         }
399
400 #ifdef ICU
401         if (aflag) {
402                 /* get all available zones */
403                 char ** fakeargv;
404                 int i;
405                 int count;
406
407                 count = getall(&namelist);
408
409                 fakeargv = (char **) malloc((size_t) (argc + count) * sizeof *argv);
410                 /*
411                 if ((fakeargv = (char **) malloc((size_t) (argc + count) * sizeof *argv)) == NULL) {
412                         exit(EXIT_FAILURE);
413                 }
414                 */
415                 for (i = 0; i < argc; i++) {
416                         fakeargv[i] = argv[i];
417                 }
418                 for (i = 0; i < count; i++) {
419                         fakeargv[i + argc] = namelist->name;
420                         namelist = namelist->next;
421                 }
422                 argv = fakeargv;
423                 argc += count;
424         }
425 #endif
426         (void) time(&now);
427         longest = 0;
428         for (i = optind; i < argc; ++i)
429                 if (strlen(argv[i]) > longest)
430                         longest = strlen(argv[i]);
431         {
432                 register int    from;
433                 register int    to;
434
435                 for (i = 0; environ[i] != NULL; ++i)
436                         continue;
437                 fakeenv = (char **) malloc((size_t) ((i + 2) *
438                         sizeof *fakeenv));
439                 if (fakeenv == NULL ||
440                         (fakeenv[0] = (char *) malloc(longest + 4)) == NULL) {
441                                         (void) perror(progname);
442                                         exit(EXIT_FAILURE);
443                 }
444                 to = 0;
445                 (void) strcpy(fakeenv[to++], "TZ=");
446                 for (from = 0; environ[from] != NULL; ++from)
447                         if (strncmp(environ[from], "TZ=", 3) != 0)
448                                 fakeenv[to++] = environ[from];
449                 fakeenv[to] = NULL;
450                 environ = fakeenv;
451         }
452         for (i = optind; i < argc; ++i) {
453                 static char     buf[MAX_STRING_LENGTH];
454
455                 (void) strcpy(&fakeenv[0][3], argv[i]);
456                 if (!vflag) {
457                         show(argv[i], now, FALSE);
458                         continue;
459                 }
460 #ifdef ICU
461                 fp = NULL;
462                 if (iflag) {
463                         if (dirarg == NULL) {
464                                 /* we want to display a zone name here */
465                                 if (i != optind) {
466                                         printf("\n");
467                                 }
468                                 printf("ZONE: %s\n", argv[i]);
469                         } else {
470                                 int             zstart;
471                                 char    path[FILENAME_MAX + 1];
472                                 strcpy(path, dirarg);
473                                 strcat(path, "/");
474                                 zstart = strlen(path);
475                                 strcat(path, argv[i]);
476                                 /* replace '/' with '-' */
477                                 while(path[++zstart] != 0) {
478                                         if (path[zstart] == '/') {
479                                                 path[zstart] = '-';
480                                         }
481                                 }
482                                 if ((fp = fopen(path, "w")) == NULL) {
483                                         fprintf(stderr, "cannot create output file %s\n", path);
484                                         exit(EXIT_FAILURE);
485                                 }
486                         }
487                 }
488 #endif
489                 warned = FALSE;
490                 t = absolute_min_time;
491 #ifdef ICU
492                 /* skip displaying info for the lowest time, which is actually not
493                  * a transition when -i option is set */
494                 if (!iflag) {
495 #endif
496                 show(argv[i], t, TRUE);
497                 t += SECSPERHOUR * HOURSPERDAY;
498                 show(argv[i], t, TRUE);
499 #ifdef ICU
500                 }
501 #endif
502                 if (t < cutlotime)
503                         t = cutlotime;
504                 tmp = my_localtime(&t);
505                 if (tmp != NULL) {
506                         tm = *tmp;
507                         (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
508                 }
509                 for ( ; ; ) {
510                         if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
511                                 break;
512                         newt = t + SECSPERHOUR * 12;
513                         newtmp = localtime(&newt);
514                         if (newtmp != NULL)
515                                 newtm = *newtmp;
516 #ifdef ICU
517                         if (iflag) {
518                                 /* We  do not want to capture transitions just for
519                                  * abbreviated zone name changes */
520                                 if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
521                                         (delta(&newtm, &tm) != (newt - t) ||
522                                         newtm.tm_isdst != tm.tm_isdst)) {
523                                                 newt = huntICU(argv[i], t, newt, fp);
524                                                 newtmp = localtime(&newt);
525                                                 if (newtmp != NULL) {
526                                                         newtm = *newtmp;
527                                                         (void) strncpy(buf,
528                                                                 abbr(&newtm),
529                                                                 (sizeof buf) - 1);
530                                                 }
531                                 }
532                         } else {
533 #endif
534                         if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
535                                 (delta(&newtm, &tm) != (newt - t) ||
536                                 newtm.tm_isdst != tm.tm_isdst ||
537                                 strcmp(abbr(&newtm), buf) != 0)) {
538                                         newt = hunt(argv[i], t, newt);
539                                         newtmp = localtime(&newt);
540                                         if (newtmp != NULL) {
541                                                 newtm = *newtmp;
542                                                 (void) strncpy(buf,
543                                                         abbr(&newtm),
544                                                         (sizeof buf) - 1);
545                                         }
546                         }
547 #ifdef ICU
548                         }
549 #endif
550                         t = newt;
551                         tm = newtm;
552                         tmp = newtmp;
553                 }
554 #ifdef ICU
555                 if (!iflag) {
556                 /* skip displaying info for the highest time, which is actually not
557                  * a transition when -i option is used*/
558 #endif
559                 t = absolute_max_time;
560                 t -= SECSPERHOUR * HOURSPERDAY;
561                 show(argv[i], t, TRUE);
562                 t += SECSPERHOUR * HOURSPERDAY;
563                 show(argv[i], t, TRUE);
564
565 #ifdef ICU
566                 }
567                 /* close file */
568                 if (fp != NULL) {
569                         fclose(fp);
570                 }
571 #endif
572         }
573         if (fflush(stdout) || ferror(stdout)) {
574                 (void) fprintf(stderr, "%s: ", progname);
575                 (void) perror(_("Error writing to standard output"));
576                 exit(EXIT_FAILURE);
577         }
578 #ifdef ICU
579         if (aflag) {
580                 struct listentry *      entry = namelist;
581                 struct listentry *      next;
582                 while (entry != NULL) {
583                         free(entry->name);
584                         next = entry->next;
585                         free(entry);
586                         entry = next;
587                 }
588         }
589 #endif
590         exit(EXIT_SUCCESS);
591         /* If exit fails to exit... */
592         return EXIT_FAILURE;
593 }
594
595 static void
596 setabsolutes(void)
597 {
598         if (0.5 == (time_t) 0.5) {
599                 /*
600                 ** time_t is floating.
601                 */
602                 if (sizeof (time_t) == sizeof (float)) {
603                         absolute_min_time = (time_t) -FLT_MAX;
604                         absolute_max_time = (time_t) FLT_MAX;
605                 } else if (sizeof (time_t) == sizeof (double)) {
606                         absolute_min_time = (time_t) -DBL_MAX;
607                         absolute_max_time = (time_t) DBL_MAX;
608                 } else {
609                         (void) fprintf(stderr,
610 _("%s: use of -v on system with floating time_t other than float or double\n"),
611                                 progname);
612                         exit(EXIT_FAILURE);
613                 }
614         } else if (0 > (time_t) -1) {
615                 /*
616                 ** time_t is signed.  Assume overflow wraps around.
617                 */
618                 time_t t = 0;
619                 time_t t1 = 1;
620
621                 while (t < t1) {
622                         t = t1;
623                         t1 = 2 * t1 + 1;
624                 }
625
626                 absolute_max_time = t;
627                 t = -t;
628                 absolute_min_time = t - 1;
629                 if (t < absolute_min_time)
630                         absolute_min_time = t;
631         } else {
632                 /*
633                 ** time_t is unsigned.
634                 */
635                 absolute_min_time = 0;
636                 absolute_max_time = absolute_min_time - 1;
637         }
638 }
639
640 static time_t
641 yeartot(y)
642 const long      y;
643 {
644         register long   myy;
645         register long   seconds;
646         register time_t t;
647
648         myy = EPOCH_YEAR;
649         t = 0;
650         while (myy != y) {
651                 if (myy < y) {
652                         seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
653                         ++myy;
654                         if (t > absolute_max_time - seconds) {
655                                 t = absolute_max_time;
656                                 break;
657                         }
658                         t += seconds;
659                 } else {
660                         --myy;
661                         seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
662                         if (t < absolute_min_time + seconds) {
663                                 t = absolute_min_time;
664                                 break;
665                         }
666                         t -= seconds;
667                 }
668         }
669         return t;
670 }
671
672 static time_t
673 hunt(char *name, time_t lot, time_t hit)
674 {
675         time_t                  t;
676         long                    diff;
677         struct tm               lotm;
678         register struct tm *    lotmp;
679         struct tm               tm;
680         register struct tm *    tmp;
681         char                    loab[MAX_STRING_LENGTH];
682
683         lotmp = my_localtime(&lot);
684         if (lotmp != NULL) {
685                 lotm = *lotmp;
686                 (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
687         }
688         for ( ; ; ) {
689                 diff = (long) (hit - lot);
690                 if (diff < 2)
691                         break;
692                 t = lot;
693                 t += diff / 2;
694                 if (t <= lot)
695                         ++t;
696                 else if (t >= hit)
697                         --t;
698                 tmp = my_localtime(&t);
699                 if (tmp != NULL)
700                         tm = *tmp;
701                 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
702                         (delta(&tm, &lotm) == (t - lot) &&
703                         tm.tm_isdst == lotm.tm_isdst &&
704                         strcmp(abbr(&tm), loab) == 0)) {
705                                 lot = t;
706                                 lotm = tm;
707                                 lotmp = tmp;
708                 } else  hit = t;
709         }
710         show(name, lot, TRUE);
711         show(name, hit, TRUE);
712         return hit;
713 }
714
715 /*
716 ** Thanks to Paul Eggert for logic used in delta.
717 */
718
719 static long
720 delta(newp, oldp)
721 struct tm *     newp;
722 struct tm *     oldp;
723 {
724         register long   result;
725         register int    tmy;
726
727         if (newp->tm_year < oldp->tm_year)
728                 return -delta(oldp, newp);
729         result = 0;
730         for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
731                 result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
732         result += newp->tm_yday - oldp->tm_yday;
733         result *= HOURSPERDAY;
734         result += newp->tm_hour - oldp->tm_hour;
735         result *= MINSPERHOUR;
736         result += newp->tm_min - oldp->tm_min;
737         result *= SECSPERMIN;
738         result += newp->tm_sec - oldp->tm_sec;
739         return result;
740 }
741
742 static void
743 show(char *zone, time_t t, int v)
744 {
745         register struct tm *    tmp;
746
747         (void) printf("%-*s  ", (int) longest, zone);
748         if (v) {
749                 tmp = gmtime(&t);
750                 if (tmp == NULL) {
751                         (void) printf(tformat(), t);
752                 } else {
753                         dumptime(tmp);
754                         (void) printf(" UTC");
755                 }
756                 (void) printf(" = ");
757         }
758         tmp = my_localtime(&t);
759         dumptime(tmp);
760         if (tmp != NULL) {
761                 if (*abbr(tmp) != '\0')
762                         (void) printf(" %s", abbr(tmp));
763                 if (v) {
764                         (void) printf(" isdst=%d", tmp->tm_isdst);
765 #ifdef TM_GMTOFF
766                         (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
767 #endif /* defined TM_GMTOFF */
768                 }
769         }
770         (void) printf("\n");
771         if (tmp != NULL && *abbr(tmp) != '\0')
772                 abbrok(abbr(tmp), zone);
773 }
774
775 static char *
776 abbr(tmp)
777 struct tm *     tmp;
778 {
779         register char * result;
780         static char     nada;
781
782         if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
783                 return &nada;
784         result = tzname[tmp->tm_isdst];
785         return (result == NULL) ? &nada : result;
786 }
787
788 /*
789 ** The code below can fail on certain theoretical systems;
790 ** it works on all known real-world systems as of 2004-12-30.
791 */
792
793 static const char *
794 tformat(void)
795 {
796         if (0.5 == (time_t) 0.5) {      /* floating */
797                 if (sizeof (time_t) > sizeof (double))
798                         return "%Lg";
799                 return "%g";
800         }
801         if (0 > (time_t) -1) {          /* signed */
802                 if (sizeof (time_t) > sizeof (long))
803                         return "%lld";
804                 if (sizeof (time_t) > sizeof (int))
805                         return "%ld";
806                 return "%d";
807         }
808         if (sizeof (time_t) > sizeof (unsigned long))
809                 return "%llu";
810         if (sizeof (time_t) > sizeof (unsigned int))
811                 return "%lu";
812         return "%u";
813 }
814
815 static void
816 dumptime(timeptr)
817 register const struct tm *      timeptr;
818 {
819         static const char       wday_name[][3] = {
820                 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
821         };
822         static const char       mon_name[][3] = {
823                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
824                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
825         };
826         register const char *   wn;
827         register const char *   mn;
828         register int            lead;
829         register int            trail;
830
831         if (timeptr == NULL) {
832                 (void) printf("NULL");
833                 return;
834         }
835         /*
836         ** The packaged versions of localtime and gmtime never put out-of-range
837         ** values in tm_wday or tm_mon, but since this code might be compiled
838         ** with other (perhaps experimental) versions, paranoia is in order.
839         */
840         if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
841                 (int) (sizeof wday_name / sizeof wday_name[0]))
842                         wn = "???";
843         else            wn = wday_name[timeptr->tm_wday];
844         if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
845                 (int) (sizeof mon_name / sizeof mon_name[0]))
846                         mn = "???";
847         else            mn = mon_name[timeptr->tm_mon];
848         (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
849                 wn, mn,
850                 timeptr->tm_mday, timeptr->tm_hour,
851                 timeptr->tm_min, timeptr->tm_sec);
852 #define DIVISOR 10
853         trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
854         lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
855                 trail / DIVISOR;
856         trail %= DIVISOR;
857         if (trail < 0 && lead > 0) {
858                 trail += DIVISOR;
859                 --lead;
860         } else if (lead < 0 && trail > 0) {
861                 trail -= DIVISOR;
862                 ++lead;
863         }
864         if (lead == 0)
865                 (void) printf("%d", trail);
866         else    (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
867 }
868
869 #ifdef ICU
870 static time_t
871 huntICU(char *name, time_t lot, time_t hit, FILE * fp)
872 {
873         time_t                  t;
874         long                    diff;
875         struct tm               lotm;
876         register struct tm *    lotmp;
877         struct tm               tm;
878         register struct tm *    tmp;
879         char                    loab[MAX_STRING_LENGTH];
880
881         lotmp = my_localtime(&lot);
882         if (lotmp != NULL) {
883                 lotm = *lotmp;
884                 (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
885         }
886         for ( ; ; ) {
887                 diff = (long) (hit - lot);
888                 if (diff < 2)
889                         break;
890                 t = lot;
891                 t += diff / 2;
892                 if (t <= lot)
893                         ++t;
894                 else if (t >= hit)
895                         --t;
896                 tmp = my_localtime(&t);
897                 if (tmp != NULL)
898                         tm = *tmp;
899                 /* We  do not want to capture transitions just for
900                  * abbreviated zone name changes */
901                 if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
902                         (delta(&tm, &lotm) == (t - lot) &&
903                         tm.tm_isdst == lotm.tm_isdst)) {
904                                 lot = t;
905                                 lotm = tm;
906                                 lotmp = tmp;
907                 } else  hit = t;
908         }
909         showICU(fp, name, lot, hit);
910         return hit;
911 }
912
913 static void showICU(FILE * fp, char *zone, time_t t1, time_t t2)
914 {
915         if (fp == NULL) {
916                 fp = stdout;
917         }
918         dumptimeICU(fp, t1);
919         fprintf(fp, " > ");
920         dumptimeICU(fp, t2);
921         fprintf(fp, "\n");
922 }
923
924 static void dumptimeICU(FILE * fp, time_t t)
925 {
926         static const char       wday_name[][3] = {
927                 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
928         };
929         struct tm gmt;
930         struct tm loc;
931         register int            lead;
932         register int            trail;
933         long    offset;
934         long    hour, min, sec;
935
936         loc = *my_localtime(&t);
937
938         trail = loc.tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
939         lead = loc.tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + trail / DIVISOR;
940         trail %= DIVISOR;
941         if (trail < 0 && lead > 0) {
942                 trail += DIVISOR;
943                 --lead;
944         } else if (lead < 0 && trail > 0) {
945                 trail -= DIVISOR;
946                 ++lead;
947         }
948
949         fprintf(fp, "%04d-%02d-%02d", lead * DIVISOR + trail, loc.tm_mon + 1, loc.tm_mday);
950         fprintf(fp, " %.3s ", wday_name[loc.tm_wday]);
951         fprintf(fp, "%02d:%02d:%02d", loc.tm_hour, loc.tm_min, loc.tm_sec);
952
953         gmt = *gmtime(&t);
954         offset = delta(&loc, &gmt);
955         if (offset < 0) {
956                 offset = -offset;
957                 fprintf(fp, "-");
958         } else {
959                 fprintf(fp, "+");
960         }
961
962         sec = offset % 60;
963         offset = (offset - sec) / 60;
964         min = offset % 60;
965         hour = offset / 60;
966         
967         fprintf(fp, "%02d", hour);
968         fprintf(fp, "%02d", min);
969         fprintf(fp, "%02d", sec);
970         fprintf(fp, "[DST=%d]", loc.tm_isdst);
971 }
972
973 static int getall(struct listentry ** namelist) {
974         int count = 0;
975         struct listentry dummyentry;
976         struct listentry *      last = &dummyentry;
977
978         getzones(TZDIR, NULL, &last, &count);
979         if (count > 0) {
980                 *namelist = dummyentry.next;
981         }
982
983         return count;
984 }
985
986 static void getzones(char * basedir, char * relpath, struct listentry ** last, int * count) {
987         char    path[FILENAME_MAX + 1];
988         struct  dirent *        dir;
989         DIR *   dp;
990
991         strcpy(path, basedir);
992         if (relpath != NULL) {
993                 strcat(path, "/");
994                 strcat(path, relpath);
995         }
996
997         if ((dp = opendir(path)) == NULL) {
998                 /* file */
999                 if (strstr(relpath, ".tab") == NULL && strcmp(relpath, "Etc/Unknown") != 0) {
1000                         char *          pzonename;
1001                         listentry *     pentry;
1002
1003                         if ((pzonename = malloc(strlen(relpath) + 1)) == NULL) {
1004                                 exit(EXIT_FAILURE);
1005                         }
1006                         strcpy(pzonename, relpath);
1007                 
1008                         if ((pentry = malloc(sizeof(listentry))) == NULL) {
1009                                 exit(EXIT_FAILURE);
1010                         }
1011
1012                         pentry->name = pzonename;
1013                         pentry->next = NULL;
1014                         (*last)->next = pentry;
1015                         *last = pentry;
1016                         (*count)++;
1017                 }
1018         } else {
1019                 /* directory */
1020                 while ((dir = readdir(dp)) != NULL) {
1021                         char    subpath[FILENAME_MAX + 1];
1022
1023                         if (strcmp(dir->d_name, ".") == 0
1024                                 || strcmp(dir->d_name, "..") == 0) {
1025                                 continue;
1026                         }
1027                         if (relpath != NULL) {
1028                                 strcpy(subpath, relpath);
1029                                 strcat(subpath, "/");
1030                                 strcat(subpath, dir->d_name);
1031                         } else {
1032                                 strcpy(subpath, dir->d_name);
1033                         }
1034                         getzones(basedir, subpath, last, count);
1035                 }
1036                 closedir(dp);
1037         }
1038 }
1039 #endif