2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <string.h>
2N/A#include <unistd.h>
2N/A#include <sys/param.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <tzfile.h>
2N/A#include <fcntl.h>
2N/A#include <regex.h>
2N/A#include <errno.h>
2N/A#include <libintl.h>
2N/A#include <libzoneinfo.h>
2N/A#include <libscf.h>
2N/A
2N/A#define DEFINIT "/etc/default/init"
2N/A#define SMF_TIMEZONE_FMRI "svc:/system/timezone:default"
2N/A#define ZONEINFOTABDIR "/usr/share/lib/zoneinfo/tab/"
2N/A#define CONTINENT_TAB ZONEINFOTABDIR "continent.tab"
2N/A#define COUNTRY_TAB ZONEINFOTABDIR "country.tab"
2N/A#define ZONE_SUN_TAB ZONEINFOTABDIR "zone_sun.tab"
2N/A
2N/A#define NEWLINE "\n"
2N/A#define SLASH "/"
2N/A#define WHITESPACE "\t "
2N/A#define WHITESPACE_NL "\t \n"
2N/A#define DIGITS "0123456789"
2N/A#define BUFFLEN 1024
2N/A
2N/A#define CCLEN 2 /* country code length */
2N/A
2N/A#define GMT_MAX (12*60*60) /* The maximum GMT offset */
2N/A#define GMT_MIN (-13*60*60) /* The minimum GMT offset */
2N/A#define GMT_FMT_Q "<GMT%c%d>%c%d"
2N/A#define GMT_FMT_Q_LEN (11) /* "<GMT+dd>+dd" - maximum 11 chars */
2N/A#define GMT0_FMT "GMT0" /* backwards compatibility name */
2N/A#define GMT_FMT_ZONE ":Etc/GMT%c%d" /* ":Etc/GMT+dd" */
2N/A#define GMT_FMT_ZONE_LEN (11) /* ":Etc/GMT+dd" - maximum 11 chars */
2N/A
2N/A#define TZ_FMT "TZ=%s\n" /* format TZ entry init file */
2N/A#define TZ_FMT_Q "TZ=\"%s\"\n" /* format quoted TZ entry init file */
2N/A
2N/A#define COORD_FMTLEN1 (sizeof ("+DDMM+DDDMM") - 1)
2N/A#define COORD_FMTLEN2 (sizeof ("+DDMMSS+DDDMMSS") - 1)
2N/A#define COORD_FMT1 (1) /* flag for format 1 */
2N/A#define COORD_FMT2 (2) /* flag for format 2 */
2N/A#define COORD_DLEN_LAT (2) /* length of DD for latitude */
2N/A#define COORD_DLEN_LONG (3) /* length of DDD for longtitude */
2N/A#define COORD_MLEN (2) /* length of MM */
2N/A#define COORD_SLEN (2) /* length of SS */
2N/A
2N/A#define TRAILER "/XXXXXX"
2N/A#define TR_LEN (sizeof (TRAILER) -1)
2N/A
2N/A/* Internal Declarations */
2N/Astatic char *skipwhite(char *);
2N/Astatic int skipline(char *);
2N/Astatic int trav_link(char **);
2N/Astatic void remove_component(char *);
2N/Astatic void strip_quotes(char *, char *);
2N/Astatic int compar(struct tz_country *, struct tz_country *);
2N/Astatic int get_coord(struct tz_timezone *, char *, size_t);
2N/Astatic int _tz_match(const char *, const char *);
2N/Astatic char *_conv_gmt_zoneinfo(int);
2N/Astatic char *_conv_gmt_posix(int);
2N/A
2N/A/*
2N/A * get_tz_continents() reads the continent.tab file, and
2N/A * returns a list of continents.
2N/A */
2N/Aint
2N/Aget_tz_continents(struct tz_continent **cont)
2N/A{
2N/A FILE *fp;
2N/A char buff[BUFFLEN];
2N/A char *lp; /* line pointer */
2N/A char *lptr, *ptr; /* temp pointer */
2N/A struct tz_continent *head = NULL, *lcp, *prev = NULL;
2N/A int sav_errno = 0, ncount, status;
2N/A size_t len;
2N/A
2N/A /* open continents file */
2N/A if ((fp = fopen(CONTINENT_TAB, "r")) == NULL) {
2N/A /* fopen() sets errno */
2N/A return (-1);
2N/A }
2N/A /* read and count continents */
2N/A ncount = 0;
2N/A /*CONSTANTCONDITION*/
2N/A while (1) {
2N/A if (fgets(buff, sizeof (buff), fp) == NULL) {
2N/A if (feof(fp) == 0) {
2N/A /* fgets() sets errno */
2N/A sav_errno = errno;
2N/A ncount = -1;
2N/A }
2N/A break;
2N/A }
2N/A /* Skip comments or blank/whitespace lines */
2N/A if ((status = skipline(buff)) != 0) {
2N/A if (status == 1)
2N/A continue;
2N/A else {
2N/A sav_errno = EINVAL;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A }
2N/A /* Get continent name */
2N/A lp = skipwhite(&buff[0]);
2N/A if ((len = strcspn(lp, WHITESPACE)) > _TZBUFLEN -1) {
2N/A sav_errno = ENAMETOOLONG;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A /* create continent struct */
2N/A if ((lcp = (struct tz_continent *)
2N/A calloc(1, sizeof (struct tz_continent))) == NULL) {
2N/A sav_errno = ENOMEM;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A (void) strncpy(lcp->ctnt_name, lp, len);
2N/A lcp->ctnt_name[len] = '\0';
2N/A
2N/A /* Get continent description */
2N/A lp = skipwhite(lp + len);
2N/A len = strcspn(lp, NEWLINE);
2N/A if ((ptr = malloc(len + 1)) == NULL) {
2N/A (void) free_tz_continents(lcp);
2N/A sav_errno = ENOMEM;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A (void) strncpy(ptr, lp, len);
2N/A *(ptr + len) = '\0';
2N/A lcp->ctnt_id_desc = ptr;
2N/A
2N/A /* Get localized continent description */
2N/A lptr = dgettext(TEXT_DOMAIN, lcp->ctnt_id_desc);
2N/A if ((ptr = strdup(lptr)) == NULL) {
2N/A (void) free_tz_continents(lcp);
2N/A sav_errno = ENOMEM;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A lcp->ctnt_display_desc = ptr;
2N/A
2N/A if (head == NULL) {
2N/A head = lcp;
2N/A } else {
2N/A prev->ctnt_next = lcp;
2N/A }
2N/A prev = lcp;
2N/A ncount++;
2N/A }
2N/A (void) fclose(fp);
2N/A if (ncount == -1) {
2N/A if (head != NULL) {
2N/A (void) free_tz_continents(head);
2N/A }
2N/A if (sav_errno)
2N/A errno = sav_errno;
2N/A } else {
2N/A *cont = head;
2N/A }
2N/A return (ncount);
2N/A}
2N/A
2N/A/*
2N/A * get_tz_countries() finds the list of countries from the zone_sun.tab
2N/A * file, for the input continent, and retrieves the country
2N/A * names from the country.tab file. It also retrieves the localized
2N/A * country names. The returned list of countries is sorted by the
2N/A * countries' localized name fields.
2N/A */
2N/Aint
2N/Aget_tz_countries(struct tz_country **country, struct tz_continent *cont)
2N/A{
2N/A FILE *fp_zone, *fp_cc;
2N/A char buff[BUFFLEN], ccbuf[_CCBUFLEN], *ptr;
2N/A char *lp, *lptr, *lp_coord, *lp_cc, *lp_tz; /* line pointer */
2N/A struct tz_country *head = NULL, *prev = NULL, *next, *cp, *cp2;
2N/A int sav_errno = 0, ncount, i;
2N/A int cmp, status;
2N/A size_t len, len_coord, len_ctnt;
2N/A
2N/A if (cont->ctnt_name == NULL) {
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A len_ctnt = strlen(cont->ctnt_name);
2N/A ccbuf[0] = '\0';
2N/A
2N/A /* open zone_sun.tab and country.tab files */
2N/A if ((fp_zone = fopen(ZONE_SUN_TAB, "r")) == NULL) {
2N/A /* fopen() sets errno */
2N/A return (-1);
2N/A }
2N/A if ((fp_cc = fopen(COUNTRY_TAB, "r")) == NULL) {
2N/A /* fopen() sets errno */
2N/A (void) fclose(fp_zone);
2N/A return (-1);
2N/A }
2N/A
2N/A /* read timezones to match continents, and get countries */
2N/A ncount = 0;
2N/A /*CONSTANTCONDITION*/
2N/A while (1) {
2N/A if (fgets(buff, sizeof (buff), fp_zone) == NULL) {
2N/A if (feof(fp_zone) == 0) {
2N/A /* fgets() error - errno set */
2N/A sav_errno = errno;
2N/A ncount = -1;
2N/A }
2N/A break;
2N/A }
2N/A /* Skip comments or blank/whitespace lines */
2N/A if ((status = skipline(buff)) != 0) {
2N/A if (status == 1)
2N/A continue;
2N/A else {
2N/A sav_errno = EINVAL;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A }
2N/A /*
2N/A * If country matches previously *matched* country, skip
2N/A * entry, since zone.tab is alphabetized by country code
2N/A * (It should be a *matched* country, because the same country
2N/A * can be in different continents.)
2N/A */
2N/A /* Get country code */
2N/A lp_cc = skipwhite(&buff[0]);
2N/A if (strcspn(lp_cc, WHITESPACE) != CCLEN) {
2N/A ncount = -1;
2N/A sav_errno = EINVAL;
2N/A break;
2N/A }
2N/A /* Check country code cache; skip if already found */
2N/A if (strncmp(ccbuf, lp_cc, CCLEN) == 0) {
2N/A continue;
2N/A }
2N/A /* Get coordinates */
2N/A lp_coord = skipwhite(lp_cc + CCLEN);
2N/A if (((len_coord = strcspn(lp_coord, WHITESPACE)) !=
2N/A COORD_FMTLEN1) &&
2N/A (len_coord != COORD_FMTLEN2)) {
2N/A ncount = -1;
2N/A sav_errno = EINVAL;
2N/A break;
2N/A }
2N/A
2N/A /* Get timezone name (Skip timezone description) */
2N/A lp_tz = skipwhite(lp_coord + len_coord);
2N/A if ((len = strcspn(lp_tz, SLASH)) == 0) {
2N/A ncount = -1;
2N/A sav_errno = EINVAL;
2N/A break;
2N/A }
2N/A /* If continents match, allocate a country struct */
2N/A if ((len == len_ctnt) &&
2N/A (strncmp(cont->ctnt_name, lp_tz, len) == 0)) {
2N/A if ((cp = (struct tz_country *)
2N/A calloc(1, sizeof (struct tz_country))) == NULL) {
2N/A sav_errno = ENOMEM;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A /* Copy and save country code (len already checked) */
2N/A (void) strncpy(cp->ctry_code, lp_cc, CCLEN);
2N/A cp->ctry_code[CCLEN] = '\0';
2N/A (void) strncpy(ccbuf, lp_cc, CCLEN);
2N/A ccbuf[CCLEN] = '\0';
2N/A
2N/A /* Create linked list */
2N/A if (head == NULL) {
2N/A head = cp;
2N/A } else {
2N/A prev->ctry_next = cp;
2N/A };
2N/A prev = cp;
2N/A ncount++;
2N/A }
2N/A } /* while */
2N/A
2N/A if (ncount == -1)
2N/A goto error;
2N/A
2N/A /* Get country name from country.tab; get localized country name */
2N/A /* Read country list, match country codes to process entry */
2N/A cp = head;
2N/A /*CONSTANTCONDITION*/
2N/A while (1) {
2N/A if (fgets(buff, sizeof (buff), fp_cc) == NULL) {
2N/A if (feof(fp_cc) == 0) {
2N/A /* fgets() sets errno */
2N/A ncount = -1;
2N/A sav_errno = errno;
2N/A }
2N/A break;
2N/A }
2N/A /* Skip comments or blank/whitespace lines */
2N/A if ((status = skipline(buff)) != 0) {
2N/A if (status == 1)
2N/A continue;
2N/A else {
2N/A sav_errno = EINVAL;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A }
2N/A /* Match country codes */
2N/A if ((len = strcspn(buff, WHITESPACE)) != CCLEN) {
2N/A sav_errno = EINVAL;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A if ((cmp = strncmp(cp->ctry_code, buff, CCLEN)) == 0) {
2N/A /* Get country description, and localized desc. */
2N/A /* Skip to country description */
2N/A lp = &buff[CCLEN];
2N/A if ((len = strspn(lp, WHITESPACE)) == 0) {
2N/A sav_errno = EINVAL;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A lp += len; /* lp points to country desc. */
2N/A len = strcspn(lp, NEWLINE);
2N/A if ((ptr = calloc(len + 1, 1)) == NULL) {
2N/A ncount = -1;
2N/A errno = ENOMEM;
2N/A break;
2N/A }
2N/A (void) strncpy(ptr, lp, len);
2N/A *(ptr + len) = '\0';
2N/A cp->ctry_id_desc = ptr;
2N/A
2N/A /* Get localized country description */
2N/A lptr = dgettext(TEXT_DOMAIN, ptr);
2N/A if ((ptr = strdup(lptr)) == NULL) {
2N/A ncount = -1;
2N/A errno = ENOMEM;
2N/A break;
2N/A }
2N/A cp->ctry_display_desc = ptr;
2N/A } else if (cmp > 0) {
2N/A /* Keep searching country.tab */
2N/A continue;
2N/A } else {
2N/A /* Not found - should not happen */
2N/A ncount = -1;
2N/A errno = EILSEQ;
2N/A break;
2N/A }
2N/A if (cp->ctry_next == NULL) {
2N/A /* done with countries list */
2N/A break;
2N/A } else {
2N/A cp = cp->ctry_next;
2N/A }
2N/A } /* while */
2N/A
2N/A /* Now sort the list by ctry_display_desc field */
2N/A if ((ncount != -1) &&
2N/A ((cp2 = calloc(ncount, sizeof (struct tz_country))) != NULL)) {
2N/A /*
2N/A * First copy list to a static array for qsort() to use.
2N/A * Use the cnt_next field to point back to original structure.
2N/A */
2N/A cp = head;
2N/A for (i = 0; i < ncount; i++) {
2N/A next = cp->ctry_next;
2N/A cp->ctry_next = cp;
2N/A (void) memcpy(&cp2[i], cp, sizeof (struct tz_country));
2N/A cp = next;
2N/A }
2N/A
2N/A /* Next, call qsort() using strcoll() to order */
2N/A qsort(cp2, ncount, sizeof (struct tz_country),
2N/A (int (*)(const void *, const void *))compar);
2N/A
2N/A /* Rearrange the country list according to qsort order */
2N/A head = cp2->ctry_next; /* ctry_next is pointer to orig struct */
2N/A cp = head;
2N/A for (i = 0; i < ncount; i++) {
2N/A prev = cp;
2N/A cp = cp2[i].ctry_next;
2N/A prev->ctry_next = cp;
2N/A }
2N/A cp->ctry_next = NULL;
2N/A
2N/A /* Last, free the static buffer */
2N/A free(cp2);
2N/A
2N/A } else {
2N/A if (ncount != -1)
2N/A ncount = -1;
2N/A }
2N/A
2N/Aerror:
2N/A (void) fclose(fp_zone);
2N/A (void) fclose(fp_cc);
2N/A if (ncount == -1) {
2N/A /* free the linked list */
2N/A if (head != NULL)
2N/A (void) free_tz_countries(head);
2N/A if (sav_errno)
2N/A errno = sav_errno;
2N/A } else {
2N/A *country = head;
2N/A }
2N/A return (ncount);
2N/A}
2N/A
2N/A/*
2N/A * get_timezones_by_country() finds the list of timezones from the
2N/A * zone_sun.tab file, for the input country.
2N/A */
2N/Aint
2N/Aget_timezones_by_country(struct tz_timezone **tmzone,
2N/A struct tz_country *country)
2N/A{
2N/A FILE *fp_zone; /* zone.tab */
2N/A int match = 0, ncount = 0, sav_errno = 0, status;
2N/A char buff[1024];
2N/A char *lp_cc, *lp_tz, *lp_otz, *lp_coord, *lp_tzdesc, *ptr, *lptr;
2N/A size_t len_tz, len_otz, len_coord, len_tzdesc;
2N/A struct tz_timezone *head = NULL, *prev = NULL, *tp;
2N/A
2N/A /* open zone.tab file */
2N/A if ((fp_zone = fopen(ZONE_SUN_TAB, "r")) == NULL)
2N/A return (-1);
2N/A
2N/A /* Read through zone.tab until countries match */
2N/A /*CONSTANTCONDITION*/
2N/A while (1) {
2N/A if (fgets(buff, sizeof (buff), fp_zone) == NULL) {
2N/A if (feof(fp_zone)) {
2N/A break;
2N/A } else {
2N/A /* fgets() sets errno */
2N/A ncount = -1;
2N/A sav_errno = errno;
2N/A break;
2N/A }
2N/A }
2N/A /* Skip comments or blank/whitespace lines */
2N/A if ((status = skipline(buff)) != 0) {
2N/A if (status == 1)
2N/A continue;
2N/A else {
2N/A sav_errno = EINVAL;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A }
2N/A /*
2N/A * Find country entries, or detect if no country matches.
2N/A */
2N/A lp_cc = skipwhite(&buff[0]);
2N/A if (strcspn(lp_cc, WHITESPACE) != CCLEN) {
2N/A sav_errno = EINVAL;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A if (strncmp(country->ctry_code, lp_cc, CCLEN) == 0) {
2N/A match = 1;
2N/A
2N/A /* Get coordinates */
2N/A lp_coord = skipwhite(lp_cc + CCLEN);
2N/A if (((len_coord = strcspn(lp_coord, WHITESPACE)) !=
2N/A COORD_FMTLEN1) &&
2N/A (len_coord != COORD_FMTLEN2)) {
2N/A ncount = -1;
2N/A sav_errno = EINVAL;
2N/A break;
2N/A }
2N/A /* Get Olson timezone name */
2N/A lp_otz = skipwhite(lp_coord + len_coord);
2N/A len_otz = strcspn(lp_otz, WHITESPACE);
2N/A
2N/A /* Get Solaris compatible timezone name */
2N/A lp_tz = skipwhite(lp_otz + len_otz);
2N/A len_tz = strcspn(lp_tz, WHITESPACE_NL);
2N/A if (*(lp_tz + len_tz - 1) == '\n') {
2N/A /* No timezone description */
2N/A len_tz--;
2N/A lp_tzdesc = NULL;
2N/A len_tzdesc = 0;
2N/A } else {
2N/A /* Get timezone description */
2N/A lp_tzdesc = skipwhite(lp_tz +
2N/A len_tz);
2N/A len_tzdesc = strcspn(lp_tzdesc,
2N/A NEWLINE);
2N/A }
2N/A /*
2N/A * Check tz name lengths. This check assumes the
2N/A * tz_oname and tz_name fields are the same size.
2N/A * (since tz_name may be written with lp_otz, if
2N/A * lp_tz is "-".)
2N/A */
2N/A if ((len_otz > _TZBUFLEN - 1) ||
2N/A (len_tz > _TZBUFLEN - 1)) {
2N/A sav_errno = ENAMETOOLONG;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A /* Create timezone struct */
2N/A if ((tp = (struct tz_timezone *)
2N/A calloc(1, sizeof (struct tz_timezone))) ==
2N/A NULL) {
2N/A sav_errno = ENOMEM;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A /*
2N/A * Copy the timezone names - use the Solaris
2N/A * compatible timezone name if one exists,
2N/A * otherwise use the current Olson timezone
2N/A * name.
2N/A */
2N/A (void) strncpy(tp->tz_oname, lp_otz, len_otz);
2N/A tp->tz_oname[len_otz] = '\0';
2N/A if (strncmp("-", lp_tz, len_tz) == 0) {
2N/A lp_tz = lp_otz;
2N/A len_tz = len_otz;
2N/A }
2N/A /* If name has numeric digits, prefix ':' */
2N/A if (strcspn(lp_tz, DIGITS) < len_tz) {
2N/A if (len_tz > _TZBUFLEN - 2) {
2N/A free(tp);
2N/A sav_errno = ENAMETOOLONG;
2N/A ncount = -1;
2N/A break;
2N/A }
2N/A tp->tz_name[0] = ':';
2N/A (void) strncpy(tp->tz_name + 1, lp_tz, len_tz);
2N/A tp->tz_name[len_tz + 1] = '\0';
2N/A } else {
2N/A (void) strncpy(tp->tz_name, lp_tz, len_tz);
2N/A tp->tz_name[len_tz] = '\0';
2N/A }
2N/A /* Process timezone description, if one exists */
2N/A if ((lp_tzdesc != NULL) && (*lp_tzdesc != '\n')) {
2N/A if ((ptr = calloc(1, len_tzdesc + 1))
2N/A == NULL) {
2N/A sav_errno = ENOMEM;
2N/A ncount = -1;
2N/A (void) free_timezones(tp);
2N/A break;
2N/A }
2N/A (void) strncpy(ptr, lp_tzdesc, len_tzdesc);
2N/A *(ptr + len_tzdesc) = '\0';
2N/A tp->tz_id_desc = ptr;
2N/A
2N/A /* Get localized country description */
2N/A lptr = dgettext(TEXT_DOMAIN, ptr);
2N/A if ((ptr = strdup(lptr)) == NULL) {
2N/A sav_errno = ENOMEM;
2N/A ncount = -1;
2N/A (void) free_timezones(tp);
2N/A break;
2N/A }
2N/A tp->tz_display_desc = ptr;
2N/A
2N/A } else {
2N/A tp->tz_id_desc = NULL;
2N/A tp->tz_display_desc = NULL;
2N/A }
2N/A /* Get coordinate information */
2N/A if (get_coord(tp, lp_coord, len_coord) == -1) {
2N/A sav_errno = EILSEQ;
2N/A ncount = -1;
2N/A (void) free_timezones(tp);
2N/A break;
2N/A }
2N/A /* Store timezone struct in a linked list */
2N/A if (head == NULL) {
2N/A head = tp;
2N/A } else {
2N/A prev->tz_next = tp;
2N/A }
2N/A prev = tp;
2N/A ncount++;
2N/A } else {
2N/A if (match == 1) {
2N/A /*
2N/A * At this point, since zone_sun.tab is ordered,
2N/A * if we've already found timezone entries for
2N/A * the input country, then we've found all of
2N/A * the desired timezone entries (since we will
2N/A * be past that country's section in
2N/A * zone_sun.tab), and we are done.
2N/A */
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* Finish up */
2N/A (void) fclose(fp_zone);
2N/A if (ncount == -1) {
2N/A if (head != NULL)
2N/A (void) free_timezones(head);
2N/A if (sav_errno)
2N/A errno = sav_errno;
2N/A } else {
2N/A *tmzone = head;
2N/A }
2N/A return (ncount);
2N/A}
2N/A
2N/Aint
2N/Afree_tz_continents(struct tz_continent *cont)
2N/A{
2N/A struct tz_continent *cptr, *cprev;
2N/A
2N/A cptr = cont;
2N/A while (cptr != NULL) {
2N/A if (cptr->ctnt_id_desc != NULL)
2N/A free(cptr->ctnt_id_desc);
2N/A if (cptr->ctnt_display_desc != NULL)
2N/A free(cptr->ctnt_display_desc);
2N/A cprev = cptr;
2N/A cptr = cptr->ctnt_next;
2N/A free(cprev);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Afree_tz_countries(struct tz_country *country)
2N/A{
2N/A struct tz_country *cptr, *cprev;
2N/A
2N/A cptr = country;
2N/A while (cptr != NULL) {
2N/A if (cptr->ctry_id_desc != NULL)
2N/A free(cptr->ctry_id_desc);
2N/A if (cptr->ctry_display_desc != NULL)
2N/A free(cptr->ctry_display_desc);
2N/A cprev = cptr;
2N/A cptr = cptr->ctry_next;
2N/A free(cprev);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Afree_timezones(struct tz_timezone *timezone)
2N/A{
2N/A struct tz_timezone *tzptr, *tzprev;
2N/A
2N/A tzptr = timezone;
2N/A while (tzptr != NULL) {
2N/A if (tzptr->tz_id_desc != NULL)
2N/A free(tzptr->tz_id_desc);
2N/A if (tzptr->tz_display_desc != NULL)
2N/A free(tzptr->tz_display_desc);
2N/A tzprev = tzptr;
2N/A tzptr = tzptr->tz_next;
2N/A free(tzprev);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * conv_gmt() returns a GMT-offset style timezone
2N/A * If flag = 0, return Quoted POSIX timezone like: <GMT+8>+8
2N/A * If flag = 1, return zoneinfo timezone like: :Etc/GMT+8
2N/A */
2N/Achar *
2N/Aconv_gmt(int seconds, int flag)
2N/A{
2N/A int hour;
2N/A char *cp;
2N/A
2N/A if ((seconds < _GMT_MIN) || (seconds > _GMT_MAX)) {
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A hour = (seconds / 60) / 60;
2N/A
2N/A if (flag == 0) {
2N/A cp = _conv_gmt_posix(hour);
2N/A } else if (flag == 1) {
2N/A cp = _conv_gmt_zoneinfo(hour);
2N/A } else {
2N/A errno = EINVAL;
2N/A return (NULL);
2N/A }
2N/A return (cp);
2N/A}
2N/A
2N/Astatic char *
2N/A_conv_gmt_posix(int hour)
2N/A{
2N/A char *cp;
2N/A char xsign;
2N/A
2N/A if (hour == 0) {
2N/A if ((cp = strdup(GMT0_FMT)) == NULL) {
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A } else {
2N/A if (hour < 0) {
2N/A xsign = '-';
2N/A /* make hour positive for snprintf() */
2N/A hour = -hour;
2N/A } else {
2N/A xsign = '+';
2N/A }
2N/A if ((cp = malloc(GMT_FMT_Q_LEN + 1)) == NULL) {
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A (void) snprintf(cp, GMT_FMT_Q_LEN + 1, GMT_FMT_Q,
2N/A xsign, hour, xsign, hour);
2N/A }
2N/A return (cp);
2N/A}
2N/A
2N/Astatic char *
2N/A_conv_gmt_zoneinfo(int hour)
2N/A{
2N/A char *cp;
2N/A char xsign;
2N/A
2N/A if (hour < 0) {
2N/A xsign = '-';
2N/A /* make hour positive for snprintf() */
2N/A hour = -hour;
2N/A } else {
2N/A xsign = '+';
2N/A }
2N/A if ((cp = malloc(GMT_FMT_ZONE_LEN + 1)) == NULL) {
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A (void) snprintf(cp, GMT_FMT_ZONE_LEN + 1, GMT_FMT_ZONE,
2N/A xsign, hour);
2N/A return (cp);
2N/A}
2N/A
2N/A/* Regular expression for POSIX GMT-offset timezone */
2N/A#define _GMT_EXPR "(" _GMT_EXPR_U "|" _GMT_EXPR_Q ")"
2N/A#define _GMT_EXPR_U "^[gG][mM][tT][-+]?[0-2]?[0-9]$"
2N/A#define _GMT_EXPR_Q "^<[gG][mM][tT][-+]?[0-2]?[0-9]>[-+]?[0-2]?[0-9]$"
2N/A
2N/A/*
2N/A * Regular expression for quoted POSIX timezone.
2N/A */
2N/A/* Avoid alphabetic ranges (eg, a-z) due to effect of LC_COLLATE */
2N/A#define _ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
2N/A#define _NUM "0123456789" /* for safe */
2N/A#define _STD_Q_ELM "[-+" _ALPHA _NUM "]"
2N/A#define _STD_Q "<" _STD_Q_ELM _STD_Q_ELM _STD_Q_ELM "+>"
2N/A
2N/A/* Regular expression for unquoted POSIX timezone */
2N/A#define _STD_U_ELM_1 "[^-+,<" _NUM "]"
2N/A#define _STD_U_ELM "[^-+," _NUM "]"
2N/A#define _STD_U _STD_U_ELM_1 _STD_U_ELM _STD_U_ELM "+"
2N/A
2N/A/* Regular expression for POSIX timezone */
2N/A#define _STD "(" _STD_U "|" _STD_Q ")"
2N/A#define _DST _STD
2N/A#define _OFFSET "[-+]?" _TIME
2N/A#define _START "(" _DATEJ "|" _DATEn "|" _DATEM ")"
2N/A#define _DATEJ "J(([0-2]?[0-9]?[0-9])|3[0-5][0-9]|36[0-5])"
2N/A#define _DATEn "(([0-2]?[0-9]?[0-9])|3[0-5][0-9]|36[0-5])"
2N/A#define _DATEM "M([0-9]|10|11|12)\\.[1-5]\\.[0-6]"
2N/A#define _END _START
2N/A#define _TIME _HH "(:" _MM "(:" _SS ")?" ")?"
2N/A#define _HH "(([0-1]?[0-9])|20|21|22|23|24)"
2N/A#define _MM "[0-5]?[0-9]"
2N/A#define _SS _MM
2N/A#define _POSIX_EXPR "^" _STD _OFFSET "(" _DST "(" _OFFSET ")?" \
2N/A "(," _START "(/" _TIME ")?" \
2N/A "," _END "(/" _TIME ")?" ")?" ")?" "$"
2N/A
2N/A#define LEN_TZDIR (sizeof (TZDIR) - 1)
2N/A
2N/A/*
2N/A * isvalid_tz() checks if timezone is a valid POSIX or zoneinfo
2N/A * timezone, depending on the value of flag. For flag = _VTZ_INSTALL,
2N/A * isvalid_tz() behaves according to the behavior of Solaris Install
2N/A * in Solaris 9 and earlier, where timezones under /usr/share/lib/zoneinfo
2N/A * were validated. isvalid_tz() has a special check for GMT+-* timezones
2N/A * because Solaris Install validated /usr/share/lib/zoneinfo/GMT+-*.
2N/A * However, when /usr/share/lib/zoneinfo/GMT+-* are EOF'd, that check
2N/A * no longer works.
2N/A *
2N/A * isvalid_tz() returns 1 if a valid timezone is detected.
2N/A */
2N/Aint
2N/Aisvalid_tz(char *timezone, char *root, int flag)
2N/A{
2N/A char path[MAXPATHLEN];
2N/A char buf[sizeof (struct tzhead)];
2N/A int fid, ret;
2N/A
2N/A if ((timezone == NULL) || (*timezone == '\0')) {
2N/A return (0);
2N/A }
2N/A
2N/A /* First check if timezone is a valid POSIX timezone */
2N/A switch (flag) {
2N/A case _VTZ_INSTALL:
2N/A /*
2N/A * Special check for POSIX GMT timezone.
2N/A * If no match, check for zoneinfo timezone below
2N/A */
2N/A if (_tz_match(_GMT_EXPR, timezone) == 0) {
2N/A /* Valid GMT timezone */
2N/A return (1);
2N/A }
2N/A break;
2N/A case _VTZ_POSIX:
2N/A /* Check for generic POSIX timezone */
2N/A if (_tz_match(_POSIX_EXPR, timezone) == 0) {
2N/A /* Valid POSIX timezone */
2N/A return (1);
2N/A }
2N/A /* Invalid POSIX timezone */
2N/A return (0);
2N/A case _VTZ_ALL:
2N/A /* Check for generic POSIX timezone */
2N/A if (_tz_match(_POSIX_EXPR, timezone) == 0) {
2N/A /* Valid POSIX timezone */
2N/A return (1);
2N/A }
2N/A break;
2N/A case _VTZ_ZONEINFO:
2N/A break;
2N/A default:
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * Check for valid zoneinfo timezone -
2N/A * open zoneinfo file and check for magic number
2N/A */
2N/A
2N/A /* skip prepended ':' if one exists */
2N/A if (*timezone == ':') {
2N/A timezone++;
2N/A }
2N/A /* Construct full zoneinfo pathname */
2N/A if ((root != NULL) && (*root != '\0')) {
2N/A ret = snprintf(path, sizeof (path),
2N/A "%s%s/%s", root, TZDIR, timezone);
2N/A if (ret >= sizeof (path)) {
2N/A /* too long */
2N/A return (0);
2N/A }
2N/A } else {
2N/A ret = snprintf(path, sizeof (path),
2N/A "%s/%s", TZDIR, timezone);
2N/A if (ret >= sizeof (path)) {
2N/A /* too long */
2N/A return (0);
2N/A }
2N/A }
2N/A if ((fid = open(path, O_RDONLY)) == -1) {
2N/A return (0);
2N/A }
2N/A if (read(fid, buf, sizeof (struct tzhead)) !=
2N/A sizeof (struct tzhead)) {
2N/A (void) close(fid);
2N/A return (0);
2N/A }
2N/A if (strncmp(buf, TZ_MAGIC, sizeof (TZ_MAGIC) - 1) != 0) {
2N/A (void) close(fid);
2N/A return (0);
2N/A }
2N/A if (close(fid) == -1) {
2N/A return (0);
2N/A }
2N/A /* Valid zoneinfo timezone */
2N/A return (1);
2N/A}
2N/A
2N/A#define N_MATCH 1
2N/A
2N/Aint
2N/A_tz_match(const char *expr, const char *string)
2N/A{
2N/A regex_t reg;
2N/A regmatch_t pmatch[N_MATCH];
2N/A int ret;
2N/A
2N/A ret = regcomp(&reg, expr, REG_EXTENDED);
2N/A if (ret != 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A ret = regexec((const regex_t *)&reg, string, N_MATCH, pmatch, 0);
2N/A if (ret == 0) {
2N/A#ifdef DEBUG
2N/A printf("OK matched - %s\n", string);
2N/A#endif
2N/A regfree(&reg);
2N/A return (0);
2N/A }
2N/A#ifdef DEBUG
2N/A printf("NOT matched - %s\n", string);
2N/A#endif
2N/A regfree(&reg);
2N/A return (-1);
2N/A}
2N/A
2N/Achar *
2N/Aget_system_tz(char *root)
2N/A{
2N/A FILE *ifp;
2N/A char buff[512];
2N/A int serrno, ret;
2N/A char *sp, *ptr, *p;
2N/A char fname[MAXPATHLEN];
2N/A
2N/A if ((ret = snprintf(fname, sizeof (fname), "%s/%s", root, DEFINIT)) >=
2N/A sizeof (fname)) {
2N/A errno = ENAMETOOLONG;
2N/A return (NULL);
2N/A } else if (ret < 0) {
2N/A return (NULL);
2N/A }
2N/A if ((ifp = fopen(fname, "r")) == NULL)
2N/A return (NULL);
2N/A while (fgets(buff, sizeof (buff), ifp) != NULL) {
2N/A if (strncmp(buff, "TZ=", 3) == 0) {
2N/A (void) fclose(ifp);
2N/A p = &buff[3];
2N/A if ((sp = strchr(p, ';')) != NULL) {
2N/A *sp = '\0';
2N/A } else if ((sp = strchr(p, '\n')) != NULL) {
2N/A *sp = '\0';
2N/A }
2N/A if (strpbrk(p, "\"'") != NULL) {
2N/A strip_quotes(p, p);
2N/A }
2N/A ptr = strdup(p);
2N/A if (ptr == NULL) {
2N/A errno = ENOMEM;
2N/A return (NULL);
2N/A }
2N/A return (ptr);
2N/A }
2N/A }
2N/A
2N/A /* Either reached EOF with no TZ= entry, or got fgets() error */
2N/A serrno = errno;
2N/A if (feof(ifp) != 0) {
2N/A /* No "TZ=" entry found */
2N/A serrno = EINVAL;
2N/A }
2N/A (void) fclose(ifp);
2N/A errno = serrno;
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Set timezone/localtime property in
2N/A * timezone SMF service based on input.
2N/A *
2N/A * Returns 0 on success or -1 on failure.
2N/A */
2N/Astatic int
2N/Aset_smf_tz(char *tz)
2N/A{
2N/A scf_handle_t *h = NULL;
2N/A scf_instance_t *inst = NULL;
2N/A scf_propertygroup_t *pg = NULL;
2N/A scf_property_t *prop = NULL;
2N/A scf_value_t *val = NULL;
2N/A scf_transaction_t *tx = NULL;
2N/A scf_transaction_entry_t *ent = NULL;
2N/A int err = -1;
2N/A
2N/A if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
2N/A scf_handle_bind(h) != 0 ||
2N/A (inst = scf_instance_create(h)) == NULL ||
2N/A (pg = scf_pg_create(h)) == NULL ||
2N/A (prop = scf_property_create(h)) == NULL ||
2N/A (val = scf_value_create(h)) == NULL ||
2N/A scf_value_set_astring(val, tz) != 0 ||
2N/A (tx = scf_transaction_create(h)) == NULL ||
2N/A (ent = scf_entry_create(h)) == NULL) {
2N/A goto cleanup;
2N/A }
2N/A if (scf_handle_decode_fmri(h, SMF_TIMEZONE_FMRI, NULL, NULL, inst,
2N/A NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0 ||
2N/A scf_instance_get_pg_composed(inst, NULL, "timezone", pg) != 0) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (scf_transaction_start(tx, pg) == -1 ||
2N/A scf_transaction_property_change(tx, ent, "localtime",
2N/A SCF_TYPE_ASTRING) == -1 ||
2N/A scf_entry_add_value(ent, val) != 0) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (scf_transaction_commit(tx) == 1 &&
2N/A (smf_refresh_instance(SMF_TIMEZONE_FMRI) == 0)) {
2N/A err = 0;
2N/A }
2N/A
2N/Acleanup:
2N/A if (h) {
2N/A (void) scf_handle_unbind(h);
2N/A scf_handle_destroy(h);
2N/A }
2N/A scf_value_destroy(val);
2N/A scf_property_destroy(prop);
2N/A scf_pg_destroy(pg);
2N/A scf_instance_destroy(inst);
2N/A scf_entry_destroy(ent);
2N/A scf_transaction_destroy(tx);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/Aint
2N/Aset_system_tz(char *tz, char *root)
2N/A{
2N/A FILE *ifp, *ofp; /* Input & output files */
2N/A char *tmpdir, *tmp; /* Temp file name and location */
2N/A char buff[1024];
2N/A int replaced = 0, ret, serrno;
2N/A char *tdb;
2N/A struct stat sb;
2N/A char fname[MAXPATHLEN];
2N/A const char *tzfmt;
2N/A int len, fd;
2N/A
2N/A if (tz == NULL || root == NULL)
2N/A return (-1);
2N/A
2N/A /*
2N/A * If root is '/', then set the tz in SMF service
2N/A */
2N/A if (root == "/")
2N/A return (set_smf_tz(tz));
2N/A
2N/A /*
2N/A * If it is alternate root, then set
2N/A * the tz in /etc/default/init file
2N/A */
2N/A if (strchr(tz, '<')) {
2N/A tzfmt = TZ_FMT_Q;
2N/A } else {
2N/A tzfmt = TZ_FMT;
2N/A }
2N/A
2N/A if ((ret = snprintf(fname, sizeof (fname), "%s/%s", root, DEFINIT)) >=
2N/A sizeof (fname)) {
2N/A errno = ENAMETOOLONG;
2N/A return (-1);
2N/A } else if (ret < 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Generate temporary file name to use. We make sure it's in the same
2N/A * directory as the db we're processing so that we can use rename to
2N/A * do the replace later. Otherwise we run the risk of being on the
2N/A * wrong filesystem and having rename() fail for that reason.
2N/A */
2N/A tdb = fname;
2N/A if (trav_link(&tdb) == -1)
2N/A return (-1);
2N/A if ((tmpdir = strdup(tdb)) == NULL) {
2N/A errno = ENOMEM;
2N/A return (-1);
2N/A }
2N/A remove_component(tmpdir);
2N/A if ((len = strlen(tmpdir)) == 0) {
2N/A (void) strcpy(tmpdir, ".");
2N/A len = 1;
2N/A }
2N/A
2N/A if ((tmp = malloc(len + TR_LEN + 1)) == NULL) {
2N/A free(tmpdir);
2N/A errno = ENOMEM;
2N/A return (-1);
2N/A }
2N/A (void) strcpy(tmp, tmpdir);
2N/A (void) strcpy(tmp + len, TRAILER);
2N/A free(tmpdir);
2N/A if ((fd = mkstemp(tmp)) == -1) {
2N/A free(tmp);
2N/A return (-1);
2N/A }
2N/A if ((ofp = fdopen(fd, "w")) == NULL) {
2N/A serrno = errno;
2N/A (void) close(fd);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A
2N/A /* Preserve permissions of current file if it exists */
2N/A if (stat(tdb, &sb) == 0) {
2N/A if (fchmod(fileno(ofp), sb.st_mode) == -1) {
2N/A serrno = errno;
2N/A (void) fclose(ofp);
2N/A (void) unlink(tmp);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A if (fchown(fileno(ofp), sb.st_uid, sb.st_gid) == -1) {
2N/A serrno = errno;
2N/A (void) fclose(ofp);
2N/A (void) unlink(tmp);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A } else if (errno != ENOENT) {
2N/A serrno = errno;
2N/A (void) fclose(ofp);
2N/A (void) unlink(tmp);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A
2N/A if ((ifp = fopen(fname, "r+")) != NULL) {
2N/A while (fgets(buff, sizeof (buff), ifp) != NULL) {
2N/A if (!replaced && (strncmp(buff, "TZ=", 3) == 0)) {
2N/A ret = snprintf(buff, sizeof (buff), tzfmt, tz);
2N/A if ((ret >= sizeof (buff)) || (ret < 0)) {
2N/A if (ret >= sizeof (buff))
2N/A serrno = EINVAL;
2N/A (void) fclose(ofp);
2N/A (void) fclose(ifp);
2N/A (void) unlink(tmp);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A replaced = 1;
2N/A }
2N/A if (fputs(buff, ofp) == EOF) {
2N/A serrno = errno;
2N/A (void) fclose(ofp);
2N/A (void) fclose(ifp);
2N/A (void) unlink(tmp);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A }
2N/A (void) fclose(ifp);
2N/A
2N/A } else if (errno != ENOENT) {
2N/A serrno = errno;
2N/A (void) fclose(ofp);
2N/A (void) unlink(tmp);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * no $(ROOT)/etc/default/init found, or
2N/A * no "TZ=" entry found in the init file.
2N/A */
2N/A if (!replaced &&
2N/A (fprintf(ofp, tzfmt, tz) == EOF)) {
2N/A serrno = errno;
2N/A (void) fclose(ofp);
2N/A (void) unlink(tmp);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A
2N/A if (fsync(fileno(ofp))) {
2N/A serrno = errno;
2N/A (void) unlink(tmp);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A }
2N/A
2N/A (void) fclose(ofp);
2N/A if (rename(tmp, tdb) != 0) {
2N/A serrno = errno;
2N/A (void) unlink(tmp);
2N/A free(tmp);
2N/A errno = serrno;
2N/A return (-1);
2N/A } else {
2N/A free(tmp);
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Function to traverse a symlink path to find the real file at the end of
2N/A * the rainbow.
2N/A */
2N/Aint
2N/Atrav_link(char **path)
2N/A{
2N/A static char newpath[MAXPATHLEN];
2N/A char lastpath[MAXPATHLEN];
2N/A int len, ret;
2N/A char *tp;
2N/A
2N/A (void) strcpy(lastpath, *path);
2N/A while ((len = readlink(*path, newpath, sizeof (newpath))) != -1) {
2N/A newpath[len] = '\0';
2N/A if (newpath[0] != '/') {
2N/A if ((tp = strdup(newpath)) == NULL) {
2N/A errno = ENOMEM;
2N/A return (-1);
2N/A }
2N/A remove_component(lastpath);
2N/A ret = snprintf(newpath, sizeof (newpath),
2N/A "%s/%s", lastpath, tp);
2N/A free(tp);
2N/A if ((ret >= sizeof (newpath)) || (ret < 0))
2N/A return (-1);
2N/A }
2N/A (void) strcpy(lastpath, newpath);
2N/A *path = newpath;
2N/A }
2N/A
2N/A /*
2N/A * ENOENT or EINVAL is the normal exit case of the above loop.
2N/A */
2N/A if ((errno == ENOENT) || (errno == EINVAL))
2N/A return (0);
2N/A else
2N/A return (-1);
2N/A}
2N/A
2N/Avoid
2N/Aremove_component(char *path)
2N/A{
2N/A char *p;
2N/A
2N/A p = strrchr(path, '/'); /* find last '/' */
2N/A if (p == NULL) {
2N/A *path = '\0'; /* set path to null str */
2N/A } else {
2N/A *p = '\0'; /* zap it */
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * get_coord() fills in the tz_coord structure of the tz_timezone
2N/A * struct. It returns 0 on success, or -1 on error.
2N/A * The format of p_coord is:
2N/A *
2N/A * Latitude and longitude of the zone's principal location
2N/A * in ISO 6709 sign-degrees-minutes-seconds format,
2N/A * either +-DDMM+-DDDMM or +-DDMMSS+-DDDMMSS,
2N/A * first latitude (+ is north), then longitude (+ is east).
2N/A */
2N/Astatic int
2N/Aget_coord(struct tz_timezone *tp, char *p_coord, size_t len_coord)
2N/A{
2N/A int i, fmt_flag, nchar;
2N/A int *signp, *degp, *minp, *secp;
2N/A struct tz_coord *tcp;
2N/A char buff[512], *endp;
2N/A
2N/A tcp = &(tp->tz_coord);
2N/A
2N/A /* Figure out which format to use */
2N/A if (len_coord == COORD_FMTLEN1) {
2N/A /* "+-DDMM+-DDDMM" */
2N/A fmt_flag = COORD_FMT1;
2N/A } else if (len_coord == COORD_FMTLEN2) {
2N/A /* "+-DDMMSS+-DDDMMSS" */
2N/A fmt_flag = COORD_FMT2;
2N/A } else {
2N/A /* error */
2N/A return (-1);
2N/A }
2N/A /*
2N/A * First time through, get values for latitude;
2N/A * second time through, get values for longitude.
2N/A */
2N/A for (i = 0; i < 2; i++) {
2N/A /* Set up pointers */
2N/A if (i == 0) {
2N/A /* Do latitude */
2N/A nchar = COORD_DLEN_LAT;
2N/A signp = (int *)&(tcp->lat_sign);
2N/A degp = (int *)&(tcp->lat_degree);
2N/A minp = (int *)&(tcp->lat_minute);
2N/A secp = (int *)&(tcp->lat_second);
2N/A } else {
2N/A /* Do longitude */
2N/A nchar = COORD_DLEN_LONG;
2N/A signp = (int *)&(tcp->long_sign);
2N/A degp = (int *)&tcp->long_degree;
2N/A minp = (int *)&tcp->long_minute;
2N/A secp = (int *)&tcp->long_second;
2N/A }
2N/A /* Get latitude/logitude sign */
2N/A if (*p_coord == '+') {
2N/A *signp = 1;
2N/A } else if (*p_coord == '-') {
2N/A *signp = -1;
2N/A } else {
2N/A return (-1);
2N/A }
2N/A p_coord++;
2N/A
2N/A /* Get DD latitude, or DDD longitude */
2N/A (void) strncpy(buff, p_coord, nchar);
2N/A buff[nchar] = '\0';
2N/A errno = 0;
2N/A *degp = (int)strtol(buff, &endp, 10);
2N/A if ((endp != &buff[nchar]) || ((*degp == 0) && (errno != 0)))
2N/A return (-1);
2N/A p_coord += nchar;
2N/A
2N/A /* Get MM latitude/longitude */
2N/A (void) strncpy(buff, p_coord, COORD_MLEN);
2N/A buff[COORD_MLEN] = '\0';
2N/A errno = 0;
2N/A *minp = (int)strtol(buff, &endp, 10);
2N/A if ((endp != &buff[COORD_MLEN]) ||
2N/A ((*degp == 0) && (errno != 0)))
2N/A return (-1);
2N/A p_coord += COORD_MLEN;
2N/A
2N/A /* If FMT2, then get SS latitude/longitude */
2N/A if (fmt_flag == COORD_FMT2) {
2N/A (void) strncpy(buff, p_coord, COORD_SLEN);
2N/A buff[COORD_SLEN] = '\0';
2N/A errno = 0;
2N/A *secp = (int)strtol(buff, &endp, 10);
2N/A if ((endp != &buff[COORD_SLEN]) ||
2N/A ((*degp == 0) && (errno != 0)))
2N/A return (-1);
2N/A p_coord += COORD_SLEN;
2N/A } else {
2N/A *secp = 0;
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Astatic char *
2N/Askipwhite(char *cp)
2N/A{
2N/A while (*cp && ((*cp == ' ') || (*cp == '\t'))) {
2N/A cp++;
2N/A }
2N/A
2N/A return (cp);
2N/A}
2N/A
2N/A/*
2N/A * skipline() checks if the line begins with a comment
2N/A * comment character anywhere in the line, or if the
2N/A * line is only whitespace.
2N/A * skipline() also checks if the line read is too long to
2N/A * fit in the buffer.
2N/A * skipline() returns 1 if the line can be skipped, -1 if
2N/A * the line read is too long, and 0 if the line should not be skipped.
2N/A */
2N/Astatic int
2N/Askipline(char *line)
2N/A{
2N/A size_t len;
2N/A
2N/A len = strlen(line);
2N/A if (line[len - 1] != '\n')
2N/A return (-1);
2N/A if (line[0] == '#' || line[0] == '\0' ||
2N/A (len = strspn(line, " \t\n")) == strlen(line) ||
2N/A strchr(line, '#') == line + len)
2N/A
2N/A return (1);
2N/A else
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * strip_quotes -- strip double (") or single (') quotes
2N/A */
2N/Astatic void
2N/Astrip_quotes(char *from, char *to)
2N/A{
2N/A char *strip_ptr = NULL;
2N/A
2N/A while (*from != '\0') {
2N/A if ((*from == '"') || (*from == '\'')) {
2N/A if (strip_ptr == NULL)
2N/A strip_ptr = to;
2N/A } else {
2N/A if (strip_ptr != NULL) {
2N/A *strip_ptr = *from;
2N/A strip_ptr++;
2N/A } else {
2N/A *to = *from;
2N/A to++;
2N/A }
2N/A }
2N/A from++;
2N/A }
2N/A if (strip_ptr != NULL) {
2N/A *strip_ptr = '\0';
2N/A } else {
2N/A *to = '\0';
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Compare function used by get_tz_countries() - uses strcoll()
2N/A * for locale-sensitive comparison for the localized country names.
2N/A */
2N/Astatic int
2N/Acompar(struct tz_country *p1, struct tz_country *p2)
2N/A{
2N/A int ret;
2N/A
2N/A ret = strcoll(p1->ctry_display_desc, p2->ctry_display_desc);
2N/A return (ret);
2N/A}