4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
33 #include <sys/param.h>
34 #include <sys/types.h>
41 #include <libzoneinfo.h>
43 #define DEFINIT "/etc/default/init"
44 #define ZONEINFOTABDIR "/usr/share/lib/zoneinfo/tab/"
45 #define CONTINENT_TAB ZONEINFOTABDIR "continent.tab"
46 #define COUNTRY_TAB ZONEINFOTABDIR "country.tab"
47 #define ZONE_SUN_TAB ZONEINFOTABDIR "zone_sun.tab"
51 #define WHITESPACE "\t "
52 #define WHITESPACE_NL "\t \n"
53 #define DIGITS "0123456789"
56 #define CCLEN 2 /* country code length */
58 #define GMT_MAX (12*60*60) /* The maximum GMT offset */
59 #define GMT_MIN (-13*60*60) /* The minimum GMT offset */
60 #define GMT_FMT_Q "<GMT%c%d>%c%d"
61 #define GMT_FMT_Q_LEN (11) /* "<GMT+dd>+dd" - maximum 11 chars */
62 #define GMT0_FMT "GMT0" /* backwards compatibility name */
63 #define GMT_FMT_ZONE ":Etc/GMT%c%d" /* ":Etc/GMT+dd" */
64 #define GMT_FMT_ZONE_LEN (11) /* ":Etc/GMT+dd" - maximum 11 chars */
66 #define TZ_FMT "TZ=%s\n" /* format TZ entry init file */
67 #define TZ_FMT_Q "TZ=\"%s\"\n" /* format quoted TZ entry init file */
69 #define COORD_FMTLEN1 (sizeof ("+DDMM+DDDMM") - 1)
70 #define COORD_FMTLEN2 (sizeof ("+DDMMSS+DDDMMSS") - 1)
71 #define COORD_FMT1 (1) /* flag for format 1 */
72 #define COORD_FMT2 (2) /* flag for format 2 */
73 #define COORD_DLEN_LAT (2) /* length of DD for latitude */
74 #define COORD_DLEN_LONG (3) /* length of DDD for longtitude */
75 #define COORD_MLEN (2) /* length of MM */
76 #define COORD_SLEN (2) /* length of SS */
78 #define TRAILER "/XXXXXX"
79 #define TR_LEN (sizeof (TRAILER) -1)
81 /* Internal Declarations */
82 static char *skipwhite(char *);
83 static int skipline(char *);
84 static int trav_link(char **);
85 static void remove_component(char *);
86 static void strip_quotes(char *, char *);
87 static int compar(struct tz_country
*, struct tz_country
*);
88 static int get_coord(struct tz_timezone
*, char *, size_t);
89 static int _tz_match(const char *, const char *);
90 static char *_conv_gmt_zoneinfo(int);
91 static char *_conv_gmt_posix(int);
94 * get_tz_continents() reads the continent.tab file, and
95 * returns a list of continents.
98 get_tz_continents(struct tz_continent
**cont
)
102 char *lp
; /* line pointer */
103 char *lptr
, *ptr
; /* temp pointer */
104 struct tz_continent
*head
= NULL
, *lcp
, *prev
= NULL
;
105 int sav_errno
= 0, ncount
, status
;
108 /* open continents file */
109 if ((fp
= fopen(CONTINENT_TAB
, "r")) == NULL
) {
110 /* fopen() sets errno */
113 /* read and count continents */
115 /*CONSTANTCONDITION*/
117 if (fgets(buff
, sizeof (buff
), fp
) == NULL
) {
119 /* fgets() sets errno */
125 /* Skip comments or blank/whitespace lines */
126 if ((status
= skipline(buff
)) != 0) {
135 /* Get continent name */
136 lp
= skipwhite(&buff
[0]);
137 if ((len
= strcspn(lp
, WHITESPACE
)) > _TZBUFLEN
-1) {
138 sav_errno
= ENAMETOOLONG
;
142 /* create continent struct */
143 if ((lcp
= (struct tz_continent
*)
144 calloc(1, sizeof (struct tz_continent
))) == NULL
) {
149 (void) strncpy(lcp
->ctnt_name
, lp
, len
);
150 lcp
->ctnt_name
[len
] = '\0';
152 /* Get continent description */
153 lp
= skipwhite(lp
+ len
);
154 len
= strcspn(lp
, NEWLINE
);
155 if ((ptr
= malloc(len
+ 1)) == NULL
) {
156 (void) free_tz_continents(lcp
);
161 (void) strncpy(ptr
, lp
, len
);
163 lcp
->ctnt_id_desc
= ptr
;
165 /* Get localized continent description */
166 lptr
= dgettext(TEXT_DOMAIN
, lcp
->ctnt_id_desc
);
167 if ((ptr
= strdup(lptr
)) == NULL
) {
168 (void) free_tz_continents(lcp
);
173 lcp
->ctnt_display_desc
= ptr
;
178 prev
->ctnt_next
= lcp
;
186 (void) free_tz_continents(head
);
197 * get_tz_countries() finds the list of countries from the zone_sun.tab
198 * file, for the input continent, and retrieves the country
199 * names from the country.tab file. It also retrieves the localized
200 * country names. The returned list of countries is sorted by the
201 * countries' localized name fields.
204 get_tz_countries(struct tz_country
**country
, struct tz_continent
*cont
)
206 FILE *fp_zone
, *fp_cc
;
207 char buff
[BUFFLEN
], ccbuf
[_CCBUFLEN
], *ptr
;
208 char *lp
, *lptr
, *lp_coord
, *lp_cc
, *lp_tz
; /* line pointer */
209 struct tz_country
*head
= NULL
, *prev
= NULL
, *next
, *cp
, *cp2
;
210 int sav_errno
= 0, ncount
, i
;
212 size_t len
, len_coord
, len_ctnt
;
214 if (cont
->ctnt_name
== NULL
) {
218 len_ctnt
= strlen(cont
->ctnt_name
);
221 /* open zone_sun.tab and country.tab files */
222 if ((fp_zone
= fopen(ZONE_SUN_TAB
, "r")) == NULL
) {
223 /* fopen() sets errno */
226 if ((fp_cc
= fopen(COUNTRY_TAB
, "r")) == NULL
) {
227 /* fopen() sets errno */
228 (void) fclose(fp_zone
);
232 /* read timezones to match continents, and get countries */
234 /*CONSTANTCONDITION*/
236 if (fgets(buff
, sizeof (buff
), fp_zone
) == NULL
) {
237 if (feof(fp_zone
) == 0) {
238 /* fgets() error - errno set */
244 /* Skip comments or blank/whitespace lines */
245 if ((status
= skipline(buff
)) != 0) {
255 * If country matches previously *matched* country, skip
256 * entry, since zone.tab is alphabetized by country code
257 * (It should be a *matched* country, because the same country
258 * can be in different continents.)
260 /* Get country code */
261 lp_cc
= skipwhite(&buff
[0]);
262 if (strcspn(lp_cc
, WHITESPACE
) != CCLEN
) {
267 /* Check country code cache; skip if already found */
268 if (strncmp(ccbuf
, lp_cc
, CCLEN
) == 0) {
271 /* Get coordinates */
272 lp_coord
= skipwhite(lp_cc
+ CCLEN
);
273 if (((len_coord
= strcspn(lp_coord
, WHITESPACE
)) !=
275 (len_coord
!= COORD_FMTLEN2
)) {
281 /* Get timezone name (Skip timezone description) */
282 lp_tz
= skipwhite(lp_coord
+ len_coord
);
283 if ((len
= strcspn(lp_tz
, SLASH
)) == 0) {
288 /* If continents match, allocate a country struct */
289 if ((len
== len_ctnt
) &&
290 (strncmp(cont
->ctnt_name
, lp_tz
, len
) == 0)) {
291 if ((cp
= (struct tz_country
*)
292 calloc(1, sizeof (struct tz_country
))) == NULL
) {
297 /* Copy and save country code (len already checked) */
298 (void) strncpy(cp
->ctry_code
, lp_cc
, CCLEN
);
299 cp
->ctry_code
[CCLEN
] = '\0';
300 (void) strncpy(ccbuf
, lp_cc
, CCLEN
);
303 /* Create linked list */
307 prev
->ctry_next
= cp
;
317 /* Get country name from country.tab; get localized country name */
318 /* Read country list, match country codes to process entry */
320 /*CONSTANTCONDITION*/
322 if (fgets(buff
, sizeof (buff
), fp_cc
) == NULL
) {
323 if (feof(fp_cc
) == 0) {
324 /* fgets() sets errno */
330 /* Skip comments or blank/whitespace lines */
331 if ((status
= skipline(buff
)) != 0) {
340 /* Match country codes */
341 if ((len
= strcspn(buff
, WHITESPACE
)) != CCLEN
) {
346 if ((cmp
= strncmp(cp
->ctry_code
, buff
, CCLEN
)) == 0) {
347 /* Get country description, and localized desc. */
348 /* Skip to country description */
350 if ((len
= strspn(lp
, WHITESPACE
)) == 0) {
355 lp
+= len
; /* lp points to country desc. */
356 len
= strcspn(lp
, NEWLINE
);
357 if ((ptr
= calloc(len
+ 1, 1)) == NULL
) {
362 (void) strncpy(ptr
, lp
, len
);
364 cp
->ctry_id_desc
= ptr
;
366 /* Get localized country description */
367 lptr
= dgettext(TEXT_DOMAIN
, ptr
);
368 if ((ptr
= strdup(lptr
)) == NULL
) {
373 cp
->ctry_display_desc
= ptr
;
374 } else if (cmp
> 0) {
375 /* Keep searching country.tab */
378 /* Not found - should not happen */
383 if (cp
->ctry_next
== NULL
) {
384 /* done with countries list */
391 /* Now sort the list by ctry_display_desc field */
392 if ((ncount
!= -1) &&
393 ((cp2
= calloc(ncount
, sizeof (struct tz_country
))) != NULL
)) {
395 * First copy list to a static array for qsort() to use.
396 * Use the cnt_next field to point back to original structure.
399 for (i
= 0; i
< ncount
; i
++) {
400 next
= cp
->ctry_next
;
402 (void) memcpy(&cp2
[i
], cp
, sizeof (struct tz_country
));
406 /* Next, call qsort() using strcoll() to order */
407 qsort(cp2
, ncount
, sizeof (struct tz_country
),
408 (int (*)(const void *, const void *))compar
);
410 /* Rearrange the country list according to qsort order */
411 head
= cp2
->ctry_next
; /* ctry_next is pointer to orig struct */
413 for (i
= 0; i
< ncount
; i
++) {
415 cp
= cp2
[i
].ctry_next
;
416 prev
->ctry_next
= cp
;
418 cp
->ctry_next
= NULL
;
420 /* Last, free the static buffer */
429 (void) fclose(fp_zone
);
430 (void) fclose(fp_cc
);
432 /* free the linked list */
434 (void) free_tz_countries(head
);
444 * get_timezones_by_country() finds the list of timezones from the
445 * zone_sun.tab file, for the input country.
448 get_timezones_by_country(struct tz_timezone
**tmzone
,
449 struct tz_country
*country
)
451 FILE *fp_zone
; /* zone.tab */
452 int match
= 0, ncount
= 0, sav_errno
= 0, status
;
454 char *lp_cc
, *lp_tz
, *lp_otz
, *lp_coord
, *lp_tzdesc
, *ptr
, *lptr
;
455 size_t len_tz
, len_otz
, len_coord
, len_tzdesc
;
456 struct tz_timezone
*head
= NULL
, *prev
= NULL
, *tp
;
458 /* open zone.tab file */
459 if ((fp_zone
= fopen(ZONE_SUN_TAB
, "r")) == NULL
)
462 /* Read through zone.tab until countries match */
463 /*CONSTANTCONDITION*/
465 if (fgets(buff
, sizeof (buff
), fp_zone
) == NULL
) {
469 /* fgets() sets errno */
475 /* Skip comments or blank/whitespace lines */
476 if ((status
= skipline(buff
)) != 0) {
486 * Find country entries, or detect if no country matches.
488 lp_cc
= skipwhite(&buff
[0]);
489 if (strcspn(lp_cc
, WHITESPACE
) != CCLEN
) {
494 if (strncmp(country
->ctry_code
, lp_cc
, CCLEN
) == 0) {
497 /* Get coordinates */
498 lp_coord
= skipwhite(lp_cc
+ CCLEN
);
499 if (((len_coord
= strcspn(lp_coord
, WHITESPACE
)) !=
501 (len_coord
!= COORD_FMTLEN2
)) {
506 /* Get Olson timezone name */
507 lp_otz
= skipwhite(lp_coord
+ len_coord
);
508 len_otz
= strcspn(lp_otz
, WHITESPACE
);
510 /* Get Solaris compatible timezone name */
511 lp_tz
= skipwhite(lp_otz
+ len_otz
);
512 len_tz
= strcspn(lp_tz
, WHITESPACE_NL
);
513 if (*(lp_tz
+ len_tz
- 1) == '\n') {
514 /* No timezone description */
519 /* Get timezone description */
520 lp_tzdesc
= skipwhite(lp_tz
+
522 len_tzdesc
= strcspn(lp_tzdesc
,
526 * Check tz name lengths. This check assumes the
527 * tz_oname and tz_name fields are the same size.
528 * (since tz_name may be written with lp_otz, if
531 if ((len_otz
> _TZBUFLEN
- 1) ||
532 (len_tz
> _TZBUFLEN
- 1)) {
533 sav_errno
= ENAMETOOLONG
;
537 /* Create timezone struct */
538 if ((tp
= (struct tz_timezone
*)
539 calloc(1, sizeof (struct tz_timezone
))) ==
546 * Copy the timezone names - use the Solaris
547 * compatible timezone name if one exists,
548 * otherwise use the current Olson timezone
551 (void) strncpy(tp
->tz_oname
, lp_otz
, len_otz
);
552 tp
->tz_oname
[len_otz
] = '\0';
553 if (strncmp("-", lp_tz
, len_tz
) == 0) {
557 /* If name has numeric digits, prefix ':' */
558 if (strcspn(lp_tz
, DIGITS
) < len_tz
) {
559 if (len_tz
> _TZBUFLEN
- 2) {
561 sav_errno
= ENAMETOOLONG
;
565 tp
->tz_name
[0] = ':';
566 (void) strncpy(tp
->tz_name
+ 1, lp_tz
, len_tz
);
567 tp
->tz_name
[len_tz
+ 1] = '\0';
569 (void) strncpy(tp
->tz_name
, lp_tz
, len_tz
);
570 tp
->tz_name
[len_tz
] = '\0';
572 /* Process timezone description, if one exists */
573 if ((lp_tzdesc
!= NULL
) && (*lp_tzdesc
!= '\n')) {
574 if ((ptr
= calloc(1, len_tzdesc
+ 1))
578 (void) free_timezones(tp
);
581 (void) strncpy(ptr
, lp_tzdesc
, len_tzdesc
);
582 *(ptr
+ len_tzdesc
) = '\0';
583 tp
->tz_id_desc
= ptr
;
585 /* Get localized country description */
586 lptr
= dgettext(TEXT_DOMAIN
, ptr
);
587 if ((ptr
= strdup(lptr
)) == NULL
) {
590 (void) free_timezones(tp
);
593 tp
->tz_display_desc
= ptr
;
596 tp
->tz_id_desc
= NULL
;
597 tp
->tz_display_desc
= NULL
;
599 /* Get coordinate information */
600 if (get_coord(tp
, lp_coord
, len_coord
) == -1) {
603 (void) free_timezones(tp
);
606 /* Store timezone struct in a linked list */
617 * At this point, since zone_sun.tab is ordered,
618 * if we've already found timezone entries for
619 * the input country, then we've found all of
620 * the desired timezone entries (since we will
621 * be past that country's section in
622 * zone_sun.tab), and we are done.
630 (void) fclose(fp_zone
);
633 (void) free_timezones(head
);
643 free_tz_continents(struct tz_continent
*cont
)
645 struct tz_continent
*cptr
, *cprev
;
648 while (cptr
!= NULL
) {
649 if (cptr
->ctnt_id_desc
!= NULL
)
650 free(cptr
->ctnt_id_desc
);
651 if (cptr
->ctnt_display_desc
!= NULL
)
652 free(cptr
->ctnt_display_desc
);
654 cptr
= cptr
->ctnt_next
;
661 free_tz_countries(struct tz_country
*country
)
663 struct tz_country
*cptr
, *cprev
;
666 while (cptr
!= NULL
) {
667 if (cptr
->ctry_id_desc
!= NULL
)
668 free(cptr
->ctry_id_desc
);
669 if (cptr
->ctry_display_desc
!= NULL
)
670 free(cptr
->ctry_display_desc
);
672 cptr
= cptr
->ctry_next
;
679 free_timezones(struct tz_timezone
*timezone
)
681 struct tz_timezone
*tzptr
, *tzprev
;
684 while (tzptr
!= NULL
) {
685 if (tzptr
->tz_id_desc
!= NULL
)
686 free(tzptr
->tz_id_desc
);
687 if (tzptr
->tz_display_desc
!= NULL
)
688 free(tzptr
->tz_display_desc
);
690 tzptr
= tzptr
->tz_next
;
697 * conv_gmt() returns a GMT-offset style timezone
698 * If flag = 0, return Quoted POSIX timezone like: <GMT+8>+8
699 * If flag = 1, return zoneinfo timezone like: :Etc/GMT+8
702 conv_gmt(int seconds
, int flag
)
707 if ((seconds
< _GMT_MIN
) || (seconds
> _GMT_MAX
)) {
711 hour
= (seconds
/ 60) / 60;
714 cp
= _conv_gmt_posix(hour
);
715 } else if (flag
== 1) {
716 cp
= _conv_gmt_zoneinfo(hour
);
725 _conv_gmt_posix(int hour
)
731 if ((cp
= strdup(GMT0_FMT
)) == NULL
) {
738 /* make hour positive for snprintf() */
743 if ((cp
= malloc(GMT_FMT_Q_LEN
+ 1)) == NULL
) {
747 (void) snprintf(cp
, GMT_FMT_Q_LEN
+ 1, GMT_FMT_Q
,
748 xsign
, hour
, xsign
, hour
);
754 _conv_gmt_zoneinfo(int hour
)
761 /* make hour positive for snprintf() */
766 if ((cp
= malloc(GMT_FMT_ZONE_LEN
+ 1)) == NULL
) {
770 (void) snprintf(cp
, GMT_FMT_ZONE_LEN
+ 1, GMT_FMT_ZONE
,
775 /* Regular expression for POSIX GMT-offset timezone */
776 #define _GMT_EXPR "(" _GMT_EXPR_U "|" _GMT_EXPR_Q ")"
777 #define _GMT_EXPR_U "^[gG][mM][tT][-+]?[0-2]?[0-9]$"
778 #define _GMT_EXPR_Q "^<[gG][mM][tT][-+]?[0-2]?[0-9]>[-+]?[0-2]?[0-9]$"
781 * Regular expression for quoted POSIX timezone.
783 /* Avoid alphabetic ranges (eg, a-z) due to effect of LC_COLLATE */
784 #define _ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
785 #define _NUM "0123456789" /* for safe */
786 #define _STD_Q_ELM "[-+" _ALPHA _NUM "]"
787 #define _STD_Q "<" _STD_Q_ELM _STD_Q_ELM _STD_Q_ELM "+>"
789 /* Regular expression for unquoted POSIX timezone */
790 #define _STD_U_ELM_1 "[^-+,<" _NUM "]"
791 #define _STD_U_ELM "[^-+," _NUM "]"
792 #define _STD_U _STD_U_ELM_1 _STD_U_ELM _STD_U_ELM "+"
794 /* Regular expression for POSIX timezone */
795 #define _STD "(" _STD_U "|" _STD_Q ")"
797 #define _OFFSET "[-+]?" _TIME
798 #define _START "(" _DATEJ "|" _DATEn "|" _DATEM ")"
799 #define _DATEJ "J(([0-2]?[0-9]?[0-9])|3[0-5][0-9]|36[0-5])"
800 #define _DATEn "(([0-2]?[0-9]?[0-9])|3[0-5][0-9]|36[0-5])"
801 #define _DATEM "M([0-9]|10|11|12)\\.[1-5]\\.[0-6]"
803 #define _TIME _HH "(:" _MM "(:" _SS ")?" ")?"
804 #define _HH "(([0-1]?[0-9])|20|21|22|23|24)"
805 #define _MM "[0-5]?[0-9]"
807 #define _POSIX_EXPR "^" _STD _OFFSET "(" _DST "(" _OFFSET ")?" \
808 "(," _START "(/" _TIME ")?" \
809 "," _END "(/" _TIME ")?" ")?" ")?" "$"
811 #define LEN_TZDIR (sizeof (TZDIR) - 1)
814 * isvalid_tz() checks if timezone is a valid POSIX or zoneinfo
815 * timezone, depending on the value of flag. For flag = _VTZ_INSTALL,
816 * isvalid_tz() behaves according to the behavior of Solaris Install
817 * in Solaris 9 and earlier, where timezones under /usr/share/lib/zoneinfo
818 * were validated. isvalid_tz() has a special check for GMT+-* timezones
819 * because Solaris Install validated /usr/share/lib/zoneinfo/GMT+-*.
820 * However, when /usr/share/lib/zoneinfo/GMT+-* are EOF'd, that check
823 * isvalid_tz() returns 1 if a valid timezone is detected.
826 isvalid_tz(char *timezone
, char *root
, int flag
)
828 char path
[MAXPATHLEN
];
829 char buf
[sizeof (struct tzhead
)];
832 if ((timezone
== NULL
) || (*timezone
== '\0')) {
836 /* First check if timezone is a valid POSIX timezone */
840 * Special check for POSIX GMT timezone.
841 * If no match, check for zoneinfo timezone below
843 if (_tz_match(_GMT_EXPR
, timezone
) == 0) {
844 /* Valid GMT timezone */
849 /* Check for generic POSIX timezone */
850 if (_tz_match(_POSIX_EXPR
, timezone
) == 0) {
851 /* Valid POSIX timezone */
854 /* Invalid POSIX timezone */
857 /* Check for generic POSIX timezone */
858 if (_tz_match(_POSIX_EXPR
, timezone
) == 0) {
859 /* Valid POSIX timezone */
870 * Check for valid zoneinfo timezone -
871 * open zoneinfo file and check for magic number
874 /* skip prepended ':' if one exists */
875 if (*timezone
== ':') {
878 /* Construct full zoneinfo pathname */
879 if ((root
!= NULL
) && (*root
!= '\0')) {
880 ret
= snprintf(path
, sizeof (path
),
881 "%s%s/%s", root
, TZDIR
, timezone
);
882 if (ret
>= sizeof (path
)) {
887 ret
= snprintf(path
, sizeof (path
),
888 "%s/%s", TZDIR
, timezone
);
889 if (ret
>= sizeof (path
)) {
894 if ((fid
= open(path
, O_RDONLY
)) == -1) {
897 if (read(fid
, buf
, sizeof (struct tzhead
)) !=
898 sizeof (struct tzhead
)) {
902 if (strncmp(buf
, TZ_MAGIC
, sizeof (TZ_MAGIC
) - 1) != 0) {
906 if (close(fid
) == -1) {
909 /* Valid zoneinfo timezone */
916 _tz_match(const char *expr
, const char *string
)
919 regmatch_t pmatch
[N_MATCH
];
922 ret
= regcomp(®
, expr
, REG_EXTENDED
);
927 ret
= regexec((const regex_t
*)®
, string
, N_MATCH
, pmatch
, 0);
930 printf("OK matched - %s\n", string
);
936 printf("NOT matched - %s\n", string
);
943 get_system_tz(char *root
)
949 char fname
[MAXPATHLEN
];
951 if ((ret
= snprintf(fname
, sizeof (fname
), "%s/%s", root
, DEFINIT
)) >=
953 errno
= ENAMETOOLONG
;
955 } else if (ret
< 0) {
958 if ((ifp
= fopen(fname
, "r")) == NULL
)
960 while (fgets(buff
, sizeof (buff
), ifp
) != NULL
) {
961 if (strncmp(buff
, "TZ=", 3) == 0) {
964 if ((sp
= strchr(p
, ';')) != NULL
) {
966 } else if ((sp
= strchr(p
, '\n')) != NULL
) {
969 if (strpbrk(p
, "\"'") != NULL
) {
981 /* Either reached EOF with no TZ= entry, or got fgets() error */
983 if (feof(ifp
) != 0) {
984 /* No "TZ=" entry found */
993 set_system_tz(char *tz
, char *root
)
995 FILE *ifp
, *ofp
; /* Input & output files */
996 char *tmpdir
, *tmp
; /* Temp file name and location */
998 int replaced
= 0, ret
, serrno
;
1001 char fname
[MAXPATHLEN
];
1005 if (tz
== NULL
|| root
== NULL
)
1008 if (strchr(tz
, '<')) {
1014 if ((ret
= snprintf(fname
, sizeof (fname
), "%s/%s", root
, DEFINIT
)) >=
1016 errno
= ENAMETOOLONG
;
1018 } else if (ret
< 0) {
1023 * Generate temporary file name to use. We make sure it's in the same
1024 * directory as the db we're processing so that we can use rename to
1025 * do the replace later. Otherwise we run the risk of being on the
1026 * wrong filesystem and having rename() fail for that reason.
1029 if (trav_link(&tdb
) == -1)
1031 if ((tmpdir
= strdup(tdb
)) == NULL
) {
1035 remove_component(tmpdir
);
1036 if ((len
= strlen(tmpdir
)) == 0) {
1037 (void) strcpy(tmpdir
, ".");
1041 if ((tmp
= malloc(len
+ TR_LEN
+ 1)) == NULL
) {
1046 (void) strcpy(tmp
, tmpdir
);
1047 (void) strcpy(tmp
+ len
, TRAILER
);
1049 if ((fd
= mkstemp(tmp
)) == -1) {
1053 if ((ofp
= fdopen(fd
, "w")) == NULL
) {
1061 /* Preserve permissions of current file if it exists */
1062 if (stat(tdb
, &sb
) == 0) {
1063 if (fchmod(fileno(ofp
), sb
.st_mode
) == -1) {
1071 if (fchown(fileno(ofp
), sb
.st_uid
, sb
.st_gid
) == -1) {
1079 } else if (errno
!= ENOENT
) {
1088 if ((ifp
= fopen(fname
, "r+")) != NULL
) {
1089 while (fgets(buff
, sizeof (buff
), ifp
) != NULL
) {
1090 if (!replaced
&& (strncmp(buff
, "TZ=", 3) == 0)) {
1091 ret
= snprintf(buff
, sizeof (buff
), tzfmt
,
1093 if ((ret
>= sizeof (buff
)) || (ret
< 0)) {
1094 if (ret
>= sizeof (buff
))
1105 if (fputs(buff
, ofp
) == EOF
) {
1117 } else if (errno
!= ENOENT
) {
1127 * no $(ROOT)/etc/default/init found, or
1128 * no "TZ=" entry found in the init file.
1131 (fprintf(ofp
, tzfmt
, tz
) == EOF
)) {
1140 if (fsync(fileno(ofp
))) {
1149 if (rename(tmp
, tdb
) != 0) {
1162 * Function to traverse a symlink path to find the real file at the end of
1166 trav_link(char **path
)
1168 static char newpath
[MAXPATHLEN
];
1169 char lastpath
[MAXPATHLEN
];
1173 (void) strcpy(lastpath
, *path
);
1174 while ((len
= readlink(*path
, newpath
, sizeof (newpath
))) != -1) {
1175 newpath
[len
] = '\0';
1176 if (newpath
[0] != '/') {
1177 if ((tp
= strdup(newpath
)) == NULL
) {
1181 remove_component(lastpath
);
1182 ret
= snprintf(newpath
, sizeof (newpath
),
1183 "%s/%s", lastpath
, tp
);
1185 if ((ret
>= sizeof (newpath
)) || (ret
< 0))
1188 (void) strcpy(lastpath
, newpath
);
1193 * ENOENT or EINVAL is the normal exit case of the above loop.
1195 if ((errno
== ENOENT
) || (errno
== EINVAL
))
1202 remove_component(char *path
)
1206 p
= strrchr(path
, '/'); /* find last '/' */
1208 *path
= '\0'; /* set path to null str */
1210 *p
= '\0'; /* zap it */
1215 * get_coord() fills in the tz_coord structure of the tz_timezone
1216 * struct. It returns 0 on success, or -1 on error.
1217 * The format of p_coord is:
1219 * Latitude and longitude of the zone's principal location
1220 * in ISO 6709 sign-degrees-minutes-seconds format,
1221 * either +-DDMM+-DDDMM or +-DDMMSS+-DDDMMSS,
1222 * first latitude (+ is north), then longitude (+ is east).
1225 get_coord(struct tz_timezone
*tp
, char *p_coord
, size_t len_coord
)
1227 int i
, fmt_flag
, nchar
;
1228 int *signp
, *degp
, *minp
, *secp
;
1229 struct tz_coord
*tcp
;
1230 char buff
[512], *endp
;
1232 tcp
= &(tp
->tz_coord
);
1234 /* Figure out which format to use */
1235 if (len_coord
== COORD_FMTLEN1
) {
1236 /* "+-DDMM+-DDDMM" */
1237 fmt_flag
= COORD_FMT1
;
1238 } else if (len_coord
== COORD_FMTLEN2
) {
1239 /* "+-DDMMSS+-DDDMMSS" */
1240 fmt_flag
= COORD_FMT2
;
1246 * First time through, get values for latitude;
1247 * second time through, get values for longitude.
1249 for (i
= 0; i
< 2; i
++) {
1250 /* Set up pointers */
1253 nchar
= COORD_DLEN_LAT
;
1254 signp
= (int *)&(tcp
->lat_sign
);
1255 degp
= (int *)&(tcp
->lat_degree
);
1256 minp
= (int *)&(tcp
->lat_minute
);
1257 secp
= (int *)&(tcp
->lat_second
);
1260 nchar
= COORD_DLEN_LONG
;
1261 signp
= (int *)&(tcp
->long_sign
);
1262 degp
= (int *)&tcp
->long_degree
;
1263 minp
= (int *)&tcp
->long_minute
;
1264 secp
= (int *)&tcp
->long_second
;
1266 /* Get latitude/logitude sign */
1267 if (*p_coord
== '+') {
1269 } else if (*p_coord
== '-') {
1276 /* Get DD latitude, or DDD longitude */
1277 (void) strncpy(buff
, p_coord
, nchar
);
1280 *degp
= (int)strtol(buff
, &endp
, 10);
1281 if ((endp
!= &buff
[nchar
]) || ((*degp
== 0) && (errno
!= 0)))
1285 /* Get MM latitude/longitude */
1286 (void) strncpy(buff
, p_coord
, COORD_MLEN
);
1287 buff
[COORD_MLEN
] = '\0';
1289 *minp
= (int)strtol(buff
, &endp
, 10);
1290 if ((endp
!= &buff
[COORD_MLEN
]) ||
1291 ((*degp
== 0) && (errno
!= 0)))
1293 p_coord
+= COORD_MLEN
;
1295 /* If FMT2, then get SS latitude/longitude */
1296 if (fmt_flag
== COORD_FMT2
) {
1297 (void) strncpy(buff
, p_coord
, COORD_SLEN
);
1298 buff
[COORD_SLEN
] = '\0';
1300 *secp
= (int)strtol(buff
, &endp
, 10);
1301 if ((endp
!= &buff
[COORD_SLEN
]) ||
1302 ((*degp
== 0) && (errno
!= 0)))
1304 p_coord
+= COORD_SLEN
;
1315 while (*cp
&& ((*cp
== ' ') || (*cp
== '\t'))) {
1323 * skipline() checks if the line begins with a comment
1324 * comment character anywhere in the line, or if the
1325 * line is only whitespace.
1326 * skipline() also checks if the line read is too long to
1327 * fit in the buffer.
1328 * skipline() returns 1 if the line can be skipped, -1 if
1329 * the line read is too long, and 0 if the line should not be skipped.
1332 skipline(char *line
)
1337 if (line
[len
- 1] != '\n')
1339 if (line
[0] == '#' || line
[0] == '\0' ||
1340 (len
= strspn(line
, " \t\n")) == strlen(line
) ||
1341 strchr(line
, '#') == line
+ len
)
1349 * strip_quotes -- strip double (") or single (') quotes
1352 strip_quotes(char *from
, char *to
)
1354 char *strip_ptr
= NULL
;
1356 while (*from
!= '\0') {
1357 if ((*from
== '"') || (*from
== '\'')) {
1358 if (strip_ptr
== NULL
)
1361 if (strip_ptr
!= NULL
) {
1371 if (strip_ptr
!= NULL
) {
1379 * Compare function used by get_tz_countries() - uses strcoll()
1380 * for locale-sensitive comparison for the localized country names.
1383 compar(struct tz_country
*p1
, struct tz_country
*p2
)
1387 ret
= strcoll(p1
->ctry_display_desc
, p2
->ctry_display_desc
);