Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/Java/Openjdk/src/java.base/unix/native/libjava/   (Sun/Oracle ©)  Datei vom 13.11.2022 mit Größe 15 kB image not shown  

Quelle  TimeZone_md.c   Sprache: C

 
/*
 * Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <time.h>
#include <limits.h>
#include <errno.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>

#include "jvm.h"
#include "TimeZone_md.h"
#include "path_util.h"

static char *isFileIdentical(char* buf, size_t size, char *pathname);

#define SKIP_SPACE(p)   while (*p == ' ' || *p == '\t') p++;

#define RESTARTABLE(_cmd, _result) do { \
  do { \
    _result = _cmd; \
  } while((_result == -1) && (errno == EINTR)); \
while(0)

#define fileopen        fopen
#define filegets        fgets
#define fileclose       fclose

#if defined(_ALLBSD_SOURCE)
#define stat64 stat
#define lstat64 lstat
#define fstat64 fstat
#endif

#if defined(__linux__) || defined(_ALLBSD_SOURCE)
static const char *ETC_TIMEZONE_FILE = "/etc/timezone";
static const char *ZONEINFO_DIR = "/usr/share/zoneinfo";
static const char *DEFAULT_ZONEINFO_FILE = "/etc/localtime";
#else
static const char *SYS_INIT_FILE = "/etc/default/init";
static const char *ZONEINFO_DIR = "/usr/share/lib/zoneinfo";
static const char *DEFAULT_ZONEINFO_FILE = "/usr/share/lib/zoneinfo/localtime";
#endif /* defined(__linux__) || defined(_ALLBSD_SOURCE) */

static const char popularZones[][4] = {"UTC""GMT"};

#if defined(_AIX)
static const char *ETC_ENVIRONMENT_FILE = "/etc/environment";
#endif

#if defined(__linux__) || defined(MACOSX)

/*
 * remove repeated path separators ('/') in the given 'path'.
 */

static void
removeDuplicateSlashes(char *path)
{
    char *left = path;
    char *right = path;
    char *end = path + strlen(path);

    for (; right < end; right++) {
        // Skip sequence of multiple path-separators.
        while (*right == '/' && *(right + 1) == '/') {
            right++;
        }

        while (*right != '\0' && !(*right == '/' && *(right + 1) == '/')) {
            *left++ = *right++;
        }

        if (*right == '\0') {
            *left = '\0';
            break;
        }
    }
}

/*
 * Returns a pointer to the zone ID portion of the given zoneinfo file
 * name, or NULL if the given string doesn't contain "zoneinfo/".
 */

static char *
getZoneName(char *str)
{
    static const char *zidir = "zoneinfo/";

    char *pos = strstr((const char *)str, zidir);
    if (pos == NULL) {
        return NULL;
    }
    return pos + strlen(zidir);
}

/*
 * Returns a path name created from the given 'dir' and 'name' under
 * UNIX. This function allocates memory for the pathname calling
 * malloc(). NULL is returned if malloc() fails.
 */

static char *
getPathName(const char *dir, const char *name) {
    char *path;

    path = (char *) malloc(strlen(dir) + strlen(name) + 2);
    if (path == NULL) {
        return NULL;
    }
    return strcat(strcat(strcpy(path, dir), "/"), name);
}

/*
 * Scans the specified directory and its subdirectories to find a
 * zoneinfo file which has the same content as /etc/localtime on Linux
 * or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'.
 * If file is symbolic link, then the contents it points to are in buf.
 * Returns a zone ID if found, otherwise, NULL is returned.
 */

static char *
findZoneinfoFile(char *buf, size_t size, const char *dir)
{
    DIR *dirp = NULL;
    struct dirent *dp = NULL;
    char *pathname = NULL;
    char *tz = NULL;

    if (strcmp(dir, ZONEINFO_DIR) == 0) {
        /* fast path for 1st iteration */
        for (unsigned int i = 0; i < sizeof (popularZones) / sizeof (popularZones[0]); i++) {
            pathname = getPathName(dir, popularZones[i]);
            if (pathname == NULL) {
                continue;
            }
            tz = isFileIdentical(buf, size, pathname);
            free((void *) pathname);
            pathname = NULL;
            if (tz != NULL) {
                return tz;
            }
        }
    }

    dirp = opendir(dir);
    if (dirp == NULL) {
        return NULL;
    }

    while ((dp = readdir(dirp)) != NULL) {
        /*
         * Skip '.' and '..' (and possibly other .* files)
         */

        if (dp->d_name[0] == '.') {
            continue;
        }

        /*
         * Skip "ROC", "posixrules", and "localtime".
         */

        if ((strcmp(dp->d_name, "ROC") == 0)
            || (strcmp(dp->d_name, "posixrules") == 0)
            || (strcmp(dp->d_name, "localtime") == 0)) {
            continue;
        }

        pathname = getPathName(dir, dp->d_name);
        if (pathname == NULL) {
            break;
        }

        tz = isFileIdentical(buf, size, pathname);
        free((void *) pathname);
        pathname = NULL;
        if (tz != NULL) {
           break;
        }
    }

    if (dirp != NULL) {
        (void) closedir(dirp);
    }
    return tz;
}

/*
 * Checks if the file pointed to by pathname matches
 * the data contents in buf.
 * Returns a representation of the timezone file name
 * if file match is found, otherwise NULL.
 */

static char *
isFileIdentical(char *buf, size_t size, char *pathname)
{
    char *possibleMatch = NULL;
    struct stat64 statbuf;
    char *dbuf = NULL;
    int fd = -1;
    int res;

    RESTARTABLE(stat64(pathname, &statbuf), res);
    if (res == -1) {
        return NULL;
    }

    if (S_ISDIR(statbuf.st_mode)) {
        possibleMatch  = findZoneinfoFile(buf, size, pathname);
    } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) {
        dbuf = (char *) malloc(size);
        if (dbuf == NULL) {
            return NULL;
        }
        RESTARTABLE(open(pathname, O_RDONLY), fd);
        if (fd == -1) {
            goto freedata;
        }
        RESTARTABLE(read(fd, dbuf, size), res);
        if (res != (ssize_t) size) {
            goto freedata;
        }
        if (memcmp(buf, dbuf, size) == 0) {
            possibleMatch = getZoneName(pathname);
            if (possibleMatch != NULL) {
                possibleMatch = strdup(possibleMatch);
            }
        }
        freedata:
        free((void *) dbuf);
        (void) close(fd);
    }
    return possibleMatch;
}

/*
 * Performs Linux specific mapping and returns a zone ID
 * if found. Otherwise, NULL is returned.
 */

static char *
getPlatformTimeZoneID()
{
    struct stat64 statbuf;
    char *tz = NULL;
    FILE *fp;
    int fd;
    char *buf;
    size_t size;
    int res;

#if defined(__linux__)
    /*
     * Try reading the /etc/timezone file for Debian distros. There's
     * no spec of the file format available. This parsing assumes that
     * there's one line of an Olson tzid followed by a '\n', no
     * leading or trailing spaces, no comments.
     */

    if ((fp = fopen(ETC_TIMEZONE_FILE, "r")) != NULL) {
        char line[256];

        if (fgets(line, sizeof(line), fp) != NULL) {
            char *p = strchr(line, '\n');
            if (p != NULL) {
                *p = '\0';
            }
            if (strlen(line) > 0) {
                tz = strdup(line);
            }
        }
        (void) fclose(fp);
        if (tz != NULL) {
            return tz;
        }
    }
#endif /* defined(__linux__) */

    /*
     * Next, try /etc/localtime to find the zone ID.
     */

    RESTARTABLE(lstat64(DEFAULT_ZONEINFO_FILE, &statbuf), res);
    if (res == -1) {
        return NULL;
    }

    /*
     * If it's a symlink, get the link name and its zone ID part. (The
     * older versions of timeconfig created a symlink as described in
     * the Red Hat man page. It was changed in 1999 to create a copy
     * of a zoneinfo file. It's no longer possible to get the zone ID
     * from /etc/localtime.)
     */

    if (S_ISLNK(statbuf.st_mode)) {
        char linkbuf[PATH_MAX+1];
        int len;

        if ((len = readlink(DEFAULT_ZONEINFO_FILE, linkbuf, sizeof(linkbuf)-1)) == -1) {
            jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n",
                        DEFAULT_ZONEINFO_FILE);
            return NULL;
        }
        linkbuf[len] = '\0';
        removeDuplicateSlashes(linkbuf);
        collapse(linkbuf);
        tz = getZoneName(linkbuf);
        if (tz != NULL) {
            tz = strdup(tz);
            return tz;
        }
    }

    /*
     * If it's a regular file, we need to find out the same zoneinfo file
     * that has been copied as /etc/localtime.
     * If initial symbolic link resolution failed, we should treat target
     * file as a regular file.
     */

    RESTARTABLE(open(DEFAULT_ZONEINFO_FILE, O_RDONLY), fd);
    if (fd == -1) {
        return NULL;
    }

    RESTARTABLE(fstat64(fd, &statbuf), res);
    if (res == -1) {
        (void) close(fd);
        return NULL;
    }
    size = (size_t) statbuf.st_size;
    buf = (char *) malloc(size);
    if (buf == NULL) {
        (void) close(fd);
        return NULL;
    }

    RESTARTABLE(read(fd, buf, size), res);
    if (res != (ssize_t) size) {
        (void) close(fd);
        free((void *) buf);
        return NULL;
    }
    (void) close(fd);

    tz = findZoneinfoFile(buf, size, ZONEINFO_DIR);
    free((void *) buf);
    return tz;
}

#elif defined(_AIX)

static char *
getPlatformTimeZoneID()
{
    FILE *fp;
    char *tz = NULL;
    char *tz_key = "TZ=";
    char line[256];
    size_t tz_key_len = strlen(tz_key);

    if ((fp = fopen(ETC_ENVIRONMENT_FILE, "r")) != NULL) {
        while (fgets(line, sizeof(line), fp) != NULL) {
            char *p = strchr(line, '\n');
            if (p != NULL) {
                *p = '\0';
            }
            if (0 == strncmp(line, tz_key, tz_key_len)) {
                tz = strdup(line + tz_key_len);
                break;
            }
        }
        (void) fclose(fp);
    }

    return tz;
}

static char *
mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) {
    FILE *tzmapf;
    char mapfilename[PATH_MAX + 1];
    char line[256];
    int linecount = 0;
    char *tz_buf = NULL;
    char *temp_tz = NULL;
    char *javatz = NULL;
    size_t tz_len = 0;

    /* On AIX, the TZ environment variable may end with a comma
     * followed by modifier fields until early AIX6.1.
     * This restriction has been removed from AIX7. */


    tz_buf = strdup(tz);
    tz_len = strlen(tz_buf);

    /* Open tzmappings file, with buffer overrun check */
    if ((strlen(java_home_dir) + 15) > PATH_MAX) {
        jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir);
        goto tzerr;
    }
    strcpy(mapfilename, java_home_dir);
    strcat(mapfilename, "/lib/tzmappings");
    if ((tzmapf = fopen(mapfilename, "r")) == NULL) {
        jio_fprintf(stderr, "can't open %s\n", mapfilename);
        goto tzerr;
    }

    while (fgets(line, sizeof(line), tzmapf) != NULL) {
        char *p = line;
        char *sol = line;
        char *java;
        int result;

        linecount++;
        /*
         * Skip comments and blank lines
         */

        if (*p == '#' || *p == '\n') {
            continue;
        }

        /*
         * Get the first field, platform zone ID
         */

        while (*p != '\0' && *p != '\t') {
            p++;
        }
        if (*p == '\0') {
            /* mapping table is broken! */
            jio_fprintf(stderr, "tzmappings: Illegal format at near line %d.\n", linecount);
            break;
        }

        *p++ = '\0';
        if ((result = strncmp(tz_buf, sol, tz_len)) == 0) {
            /*
             * If this is the current platform zone ID,
             * take the Java time zone ID (2nd field).
             */

            java = p;
            while (*p != '\0' && *p != '\n') {
                p++;
            }

            if (*p == '\0') {
                /* mapping table is broken! */
                jio_fprintf(stderr, "tzmappings: Illegal format at line %d.\n", linecount);
                break;
            }

            *p = '\0';
            javatz = strdup(java);
            break;
        } else if (result < 0) {
            break;
        }
    }
    (void) fclose(tzmapf);

tzerr:
    if (tz_buf != NULL ) {
        free((void *) tz_buf);
    }

    if (javatz == NULL) {
        return getGMTOffsetID();
    }

    return javatz;
}

#endif /* defined(_AIX) */

/*
 * findJavaTZ_md() maps platform time zone ID to Java time zone ID
 * using <java_home>/lib/tzmappings. If the TZ value is not found, it
 * tries some libc implementation dependent mappings. If it still
 * can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm
 * form.
 */

/*ARGSUSED1*/
char *
findJavaTZ_md(const char *java_home_dir)
{
    char *tz;
    char *javatz = NULL;
    char *freetz = NULL;

    tz = getenv("TZ");

    if (tz == NULL || *tz == '\0') {
        tz = getPlatformTimeZoneID();
        freetz = tz;
    }

    if (tz != NULL) {
        /* Ignore preceding ':' */
        if (*tz == ':') {
            tz++;
        }
#if defined(__linux__)
        /* Ignore "posix/" prefix on Linux. */
        if (strncmp(tz, "posix/", 6) == 0) {
            tz += 6;
        }
#endif

#if defined(_AIX)
        /* On AIX do the platform to Java mapping. */
        javatz = mapPlatformToJavaTimezone(java_home_dir, tz);
        if (freetz != NULL) {
            free((void *) freetz);
        }
#else
        if (freetz == NULL) {
            /* strdup if we are still working on getenv result. */
            javatz = strdup(tz);
        } else if (freetz != tz) {
            /* strdup and free the old buffer, if we moved the pointer. */
            javatz = strdup(tz);
            free((void *) freetz);
        } else {
            /* we are good if we already work on a freshly allocated buffer. */
            javatz = tz;
        }
#endif
    }

    return javatz;
}

/**
 * Returns a GMT-offset-based zone ID. (e.g., "GMT-08:00")
 */

char *
getGMTOffsetID()
{
    char buf[32];
    char offset[6];
    struct tm localtm;
    time_t clock = time(NULL);
    if (localtime_r(&clock, &localtm) == NULL) {
        return strdup("GMT");
    }

#if defined(MACOSX)
    time_t gmt_offset;
    gmt_offset = (time_t)localtm.tm_gmtoff;
    if (gmt_offset == 0) {
        return strdup("GMT");
    }
#else
    struct tm gmt;
    if (gmtime_r(&clock, &gmt) == NULL) {
        return strdup("GMT");
    }

    if(localtm.tm_hour == gmt.tm_hour && localtm.tm_min == gmt.tm_min) {
        return strdup("GMT");
    }
#endif

#if defined(_AIX)
    // strftime() with "%z" does not return ISO 8601 format by AIX default.
    // XPG_SUS_ENV=ON environment variable is required.
    // But Hotspot does not support XPG_SUS_ENV=ON.
    // Ignore daylight saving settings to calculate current time difference
    localtm.tm_isdst = 0;
    int gmt_off = (int)(difftime(mktime(&localtm), mktime(&gmt)) / 60.0);
    sprintf(buf, (const char *)"GMT%c%02.2d:%02.2d",
            gmt_off < 0 ? '-' : '+' , abs(gmt_off / 60), gmt_off % 60);
#else
    if (strftime(offset, 6, "%z", &localtm) != 5) {
        return strdup("GMT");
    }

    sprintf(buf, (const char *)"GMT%c%c%c:%c%c", offset[0], offset[1], offset[2],
        offset[3], offset[4]);
#endif
    return strdup(buf);
}

94%


¤ Dauer der Verarbeitung: 0.34 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.