jdk/src/solaris/native/java/util/TimeZone_md.c
changeset 2 90ce3da70b43
child 3740 efb34ff1cef4
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 1999-2004 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 #include <stdlib.h>
       
    27 #include <stdio.h>
       
    28 #include <strings.h>
       
    29 #include <time.h>
       
    30 #include <limits.h>
       
    31 #include <errno.h>
       
    32 #include <stddef.h>
       
    33 
       
    34 #ifdef __linux__
       
    35 #include <string.h>
       
    36 #include <dirent.h>
       
    37 #include <sys/stat.h>
       
    38 #include <sys/types.h>
       
    39 #include <unistd.h>
       
    40 #endif
       
    41 
       
    42 #include "jvm.h"
       
    43 
       
    44 #define SKIP_SPACE(p)   while (*p == ' ' || *p == '\t') p++;
       
    45 
       
    46 #if !defined(__solaris__) || defined(__sparcv9) || defined(amd64)
       
    47 #define fileopen        fopen
       
    48 #define filegets        fgets
       
    49 #define fileclose       fclose
       
    50 #endif
       
    51 
       
    52 #ifdef __linux__
       
    53 
       
    54 static const char *sysconfig_clock_file = "/etc/sysconfig/clock";
       
    55 static const char *zoneinfo_dir = "/usr/share/zoneinfo";
       
    56 static const char *defailt_zoneinfo_file = "/etc/localtime";
       
    57 
       
    58 /*
       
    59  * Returns a point to the zone ID portion of the given zoneinfo file
       
    60  * name.
       
    61  */
       
    62 static char *
       
    63 getZoneName(char *str)
       
    64 {
       
    65     static const char *zidir = "zoneinfo/";
       
    66 
       
    67     char * pos = strstr((const char *)str, zidir);
       
    68     if (pos == NULL) {
       
    69         return NULL;
       
    70     }
       
    71     return pos + strlen(zidir);
       
    72 }
       
    73 
       
    74 /*
       
    75  * Returns a path name created from the given 'dir' and 'name' under
       
    76  * UNIX. This function allocates memory for the pathname calling
       
    77  * malloc().
       
    78  */
       
    79 static char *
       
    80 getPathName(const char *dir, const char *name) {
       
    81     char *path;
       
    82 
       
    83     path = (char *) malloc(strlen(dir) + strlen(name) + 2);
       
    84     if (path == NULL) {
       
    85         return NULL;
       
    86     }
       
    87     return strcat(strcat(strcpy(path, dir), "/"), name);
       
    88 }
       
    89 
       
    90 /*
       
    91  * Scans the specified directory and its subdirectories to find a
       
    92  * zoneinfo file which has the same content as /etc/localtime given in
       
    93  * 'buf'. Returns a zone ID if found, otherwise, NULL is returned.
       
    94  */
       
    95 static char *
       
    96 findZoneinfoFile(char *buf, size_t size, const char *dir)
       
    97 {
       
    98     DIR *dirp = NULL;
       
    99     struct stat statbuf;
       
   100     union {
       
   101         struct dirent d;
       
   102         char b[offsetof (struct dirent, d_name) + NAME_MAX + 1];
       
   103     } entry;
       
   104     struct dirent *dp;
       
   105     char *pathname = NULL;
       
   106     int fd = -1;
       
   107     char *dbuf = NULL;
       
   108     char *tz = NULL;
       
   109 
       
   110     dirp = opendir(dir);
       
   111     if (dirp == NULL) {
       
   112         return NULL;
       
   113     }
       
   114 
       
   115     while (readdir_r(dirp, &entry.d, &dp) == 0 && dp != NULL) {
       
   116         /*
       
   117          * Skip '.' and '..' (and possibly other .* files)
       
   118          */
       
   119         if (dp->d_name[0] == '.') {
       
   120             continue;
       
   121         }
       
   122 
       
   123         /*
       
   124          * Skip "ROC", "posixrules", and "localtime" since Java doesn't
       
   125          * support them.
       
   126          */
       
   127         if ((strcmp(dp->d_name, "ROC") == 0)
       
   128             || (strcmp(dp->d_name, "posixrules") == 0)
       
   129             || (strcmp(dp->d_name, "localtime") == 0)) {
       
   130             continue;
       
   131         }
       
   132 
       
   133         pathname = getPathName(dir, dp->d_name);
       
   134         if (pathname == NULL) {
       
   135             break;
       
   136         }
       
   137         if (stat(pathname, &statbuf) == -1) {
       
   138             break;
       
   139         }
       
   140 
       
   141         if (S_ISDIR(statbuf.st_mode)) {
       
   142             tz = findZoneinfoFile(buf, size, pathname);
       
   143             if (tz != NULL) {
       
   144                 break;
       
   145             }
       
   146         } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) {
       
   147             dbuf = (char *) malloc(size);
       
   148             if (dbuf == NULL) {
       
   149                 break;
       
   150             }
       
   151             if ((fd = open(pathname, O_RDONLY)) == -1) {
       
   152                 fd = 0;
       
   153                 break;
       
   154             }
       
   155             if (read(fd, dbuf, size) != (ssize_t) size) {
       
   156                 break;
       
   157             }
       
   158             if (memcmp(buf, dbuf, size) == 0) {
       
   159                 tz = getZoneName(pathname);
       
   160                 if (tz != NULL) {
       
   161                     tz = strdup(tz);
       
   162                 }
       
   163                 break;
       
   164             }
       
   165             free((void *) dbuf);
       
   166             dbuf = NULL;
       
   167             (void) close(fd);
       
   168             fd = 0;
       
   169         }
       
   170         free((void *) pathname);
       
   171         pathname = NULL;
       
   172     }
       
   173 
       
   174     if (dirp != NULL) {
       
   175         (void) closedir(dirp);
       
   176     }
       
   177     if (pathname != NULL) {
       
   178         free((void *) pathname);
       
   179     }
       
   180     if (fd != 0) {
       
   181         (void) close(fd);
       
   182     }
       
   183     if (dbuf != NULL) {
       
   184         free((void *) dbuf);
       
   185     }
       
   186     return tz;
       
   187 }
       
   188 
       
   189 /*
       
   190  * Performs libc implementation specific mapping and returns a zone ID
       
   191  * if found. Otherwise, NULL is returned.
       
   192  */
       
   193 static char *
       
   194 getPlatformTimeZoneID()
       
   195 {
       
   196     struct stat statbuf;
       
   197     char *tz = NULL;
       
   198     FILE *fp;
       
   199     int fd;
       
   200     char *buf;
       
   201     size_t size;
       
   202 
       
   203     /*
       
   204      * First, try the ZONE entry in /etc/sysconfig/clock. However, the
       
   205      * ZONE entry is not set up after initial Red Hat Linux
       
   206      * installation. In case that /etc/localtime is set up without
       
   207      * using timeconfig, there might be inconsistency between
       
   208      * /etc/localtime and the ZONE entry. The inconsistency between
       
   209      * timeconfig and linuxconf is reported as a bug in the Red Hat
       
   210      * web page as of May 1, 2000.
       
   211      */
       
   212     if ((fp = fopen(sysconfig_clock_file, "r")) != NULL) {
       
   213         char line[256];
       
   214 
       
   215         while (fgets(line, sizeof(line), fp) != NULL) {
       
   216             char *p = line;
       
   217             char *s;
       
   218 
       
   219             SKIP_SPACE(p);
       
   220             if (*p != 'Z') {
       
   221                 continue;
       
   222             }
       
   223             if (strncmp(p, "ZONE=\"", 6) == 0) {
       
   224                 p += 6;
       
   225             } else {
       
   226                 /*
       
   227                  * In case we need to parse it token by token.
       
   228                  */
       
   229                 if (strncmp(p, "ZONE", 4) != 0) {
       
   230                     continue;
       
   231                 }
       
   232                 p += 4;
       
   233                 SKIP_SPACE(p);
       
   234                 if (*p++ != '=') {
       
   235                     break;
       
   236                 }
       
   237                 SKIP_SPACE(p);
       
   238                 if (*p++ != '"') {
       
   239                     break;
       
   240                 }
       
   241             }
       
   242             for (s = p; *s && *s != '"'; s++)
       
   243                 ;
       
   244             if (*s != '"') {
       
   245                 /* this ZONE entry is broken. */
       
   246                 break;
       
   247             }
       
   248             *s = '\0';
       
   249             tz = strdup(p);
       
   250             break;
       
   251         }
       
   252         (void) fclose(fp);
       
   253         if (tz != NULL) {
       
   254             return tz;
       
   255         }
       
   256     }
       
   257 
       
   258     /*
       
   259      * Next, try /etc/localtime to find the zone ID.
       
   260      */
       
   261     if (lstat(defailt_zoneinfo_file, &statbuf) == -1) {
       
   262         return NULL;
       
   263     }
       
   264 
       
   265     /*
       
   266      * If it's a symlink, get the link name and its zone ID part. (The
       
   267      * older versions of timeconfig created a symlink as described in
       
   268      * the Red Hat man page. It was changed in 1999 to create a copy
       
   269      * of a zoneinfo file. It's no longer possible to get the zone ID
       
   270      * from /etc/localtime.)
       
   271      */
       
   272     if (S_ISLNK(statbuf.st_mode)) {
       
   273         char linkbuf[PATH_MAX+1];
       
   274         int len;
       
   275 
       
   276         if ((len = readlink(defailt_zoneinfo_file, linkbuf, sizeof(linkbuf)-1)) == -1) {
       
   277             jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n",
       
   278                         defailt_zoneinfo_file);
       
   279             return NULL;
       
   280         }
       
   281         linkbuf[len] = '\0';
       
   282         tz = getZoneName(linkbuf);
       
   283         if (tz != NULL) {
       
   284             tz = strdup(tz);
       
   285         }
       
   286         return tz;
       
   287     }
       
   288 
       
   289     /*
       
   290      * If it's a regular file, we need to find out the same zoneinfo file
       
   291      * that has been copied as /etc/localtime.
       
   292      */
       
   293     size = (size_t) statbuf.st_size;
       
   294     buf = (char *) malloc(size);
       
   295     if (buf == NULL) {
       
   296         return NULL;
       
   297     }
       
   298     if ((fd = open(defailt_zoneinfo_file, O_RDONLY)) == -1) {
       
   299         free((void *) buf);
       
   300         return NULL;
       
   301     }
       
   302 
       
   303     if (read(fd, buf, size) != (ssize_t) size) {
       
   304         (void) close(fd);
       
   305         free((void *) buf);
       
   306         return NULL;
       
   307     }
       
   308     (void) close(fd);
       
   309 
       
   310     tz = findZoneinfoFile(buf, size, zoneinfo_dir);
       
   311     free((void *) buf);
       
   312     return tz;
       
   313 }
       
   314 #else
       
   315 #ifdef __solaris__
       
   316 #if !defined(__sparcv9) && !defined(amd64)
       
   317 
       
   318 /*
       
   319  * Those file* functions mimic the UNIX stream io functions. This is
       
   320  * because of the limitation of the number of open files on Solaris
       
   321  * (32-bit mode only) due to the System V ABI.
       
   322  */
       
   323 
       
   324 #define BUFFER_SIZE     4096
       
   325 
       
   326 static struct iobuffer {
       
   327     int     magic;      /* -1 to distinguish from the real FILE */
       
   328     int     fd;         /* file descriptor */
       
   329     char    *buffer;    /* pointer to buffer */
       
   330     char    *ptr;       /* current read pointer */
       
   331     char    *endptr;    /* end pointer */
       
   332 };
       
   333 
       
   334 static int
       
   335 fileclose(FILE *stream)
       
   336 {
       
   337     struct iobuffer *iop = (struct iobuffer *) stream;
       
   338 
       
   339     if (iop->magic != -1) {
       
   340         return fclose(stream);
       
   341     }
       
   342 
       
   343     if (iop == NULL) {
       
   344         return 0;
       
   345     }
       
   346     close(iop->fd);
       
   347     free((void *)iop->buffer);
       
   348     free((void *)iop);
       
   349     return 0;
       
   350 }
       
   351 
       
   352 static FILE *
       
   353 fileopen(const char *fname, const char *fmode)
       
   354 {
       
   355     FILE *fp;
       
   356     int fd;
       
   357     struct iobuffer *iop;
       
   358 
       
   359     if ((fp = fopen(fname, fmode)) != NULL) {
       
   360         return fp;
       
   361     }
       
   362 
       
   363     /*
       
   364      * It assumes read open.
       
   365      */
       
   366     if ((fd = open(fname, O_RDONLY)) == -1) {
       
   367         return NULL;
       
   368     }
       
   369 
       
   370     /*
       
   371      * Allocate struct iobuffer and its buffer
       
   372      */
       
   373     iop = malloc(sizeof(struct iobuffer));
       
   374     if (iop == NULL) {
       
   375         (void) close(fd);
       
   376         errno = ENOMEM;
       
   377         return NULL;
       
   378     }
       
   379     iop->magic = -1;
       
   380     iop->fd = fd;
       
   381     iop->buffer = malloc(BUFFER_SIZE);
       
   382     if (iop->buffer == NULL) {
       
   383         (void) close(fd);
       
   384         free((void *) iop);
       
   385         errno = ENOMEM;
       
   386         return NULL;
       
   387     }
       
   388     iop->ptr = iop->buffer;
       
   389     iop->endptr = iop->buffer;
       
   390     return (FILE *)iop;
       
   391 }
       
   392 
       
   393 /*
       
   394  * This implementation assumes that n is large enough and the line
       
   395  * separator is '\n'.
       
   396  */
       
   397 static char *
       
   398 filegets(char *s, int n, FILE *stream)
       
   399 {
       
   400     struct iobuffer *iop = (struct iobuffer *) stream;
       
   401     char *p;
       
   402 
       
   403     if (iop->magic != -1) {
       
   404         return fgets(s, n, stream);
       
   405     }
       
   406 
       
   407     p = s;
       
   408     for (;;) {
       
   409         char c;
       
   410 
       
   411         if (iop->ptr == iop->endptr) {
       
   412             ssize_t len;
       
   413 
       
   414             if ((len = read(iop->fd, (void *)iop->buffer, BUFFER_SIZE)) == -1) {
       
   415                 return NULL;
       
   416             }
       
   417             if (len == 0) {
       
   418                 *p = 0;
       
   419                 if (s == p) {
       
   420                     return NULL;
       
   421                 }
       
   422                 return s;
       
   423             }
       
   424             iop->ptr = iop->buffer;
       
   425             iop->endptr = iop->buffer + len;
       
   426         }
       
   427         c = *iop->ptr++;
       
   428         *p++ = c;
       
   429         if ((p - s) == (n - 1)) {
       
   430             *p = 0;
       
   431             return s;
       
   432         }
       
   433         if (c == '\n') {
       
   434             *p = 0;
       
   435             return s;
       
   436         }
       
   437     }
       
   438     /*NOTREACHED*/
       
   439 }
       
   440 #endif /* not __sparcv9 */
       
   441 
       
   442 static const char *sys_init_file = "/etc/default/init";
       
   443 
       
   444 /*
       
   445  * Performs libc implementation dependent mapping. Returns a zone ID
       
   446  * if found. Otherwise, NULL is returned.  Solaris libc looks up
       
   447  * "/etc/default/init" to get a default TZ value if TZ is not defined
       
   448  * as an environment variable.
       
   449  */
       
   450 static char *
       
   451 getPlatformTimeZoneID()
       
   452 {
       
   453     char *tz = NULL;
       
   454     FILE *fp;
       
   455 
       
   456     /*
       
   457      * Try the TZ entry in /etc/default/init.
       
   458      */
       
   459     if ((fp = fileopen(sys_init_file, "r")) != NULL) {
       
   460         char line[256];
       
   461         char quote = '\0';
       
   462 
       
   463         while (filegets(line, sizeof(line), fp) != NULL) {
       
   464             char *p = line;
       
   465             char *s;
       
   466             char c;
       
   467 
       
   468             /* quick check for comment lines */
       
   469             if (*p == '#') {
       
   470                 continue;
       
   471             }
       
   472             if (strncmp(p, "TZ=", 3) == 0) {
       
   473                 p += 3;
       
   474                 SKIP_SPACE(p);
       
   475                 c = *p;
       
   476                 if (c == '"' || c == '\'') {
       
   477                     quote = c;
       
   478                     p++;
       
   479                 }
       
   480 
       
   481                 /*
       
   482                  * PSARC/2001/383: quoted string support
       
   483                  */
       
   484                 for (s = p; (c = *s) != '\0' && c != '\n'; s++) {
       
   485                     /* No '\\' is supported here. */
       
   486                     if (c == quote) {
       
   487                         quote = '\0';
       
   488                         break;
       
   489                     }
       
   490                     if (c == ' ' && quote == '\0') {
       
   491                         break;
       
   492                     }
       
   493                 }
       
   494                 if (quote != '\0') {
       
   495                     jio_fprintf(stderr, "ZoneInfo: unterminated time zone name in /etc/TIMEZONE\n");
       
   496                 }
       
   497                 *s = '\0';
       
   498                 tz = strdup(p);
       
   499                 break;
       
   500             }
       
   501         }
       
   502         (void) fileclose(fp);
       
   503     }
       
   504     return tz;
       
   505 }
       
   506 
       
   507 #endif
       
   508 #endif
       
   509 
       
   510 /*
       
   511  * findJavaTZ_md() maps platform time zone ID to Java time zone ID
       
   512  * using <java_home>/lib/tzmappings. If the TZ value is not found, it
       
   513  * trys some libc implementation dependent mappings. If it still
       
   514  * can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm
       
   515  * form. `country', which can be null, is not used for UNIX platforms.
       
   516  */
       
   517 /*ARGSUSED1*/
       
   518 char *
       
   519 findJavaTZ_md(const char *java_home_dir, const char *country)
       
   520 {
       
   521     char *tz;
       
   522     char *javatz = NULL;
       
   523     char *freetz = NULL;
       
   524 
       
   525     tz = getenv("TZ");
       
   526 
       
   527 #ifdef __linux__
       
   528     if (tz == NULL) {
       
   529 #else
       
   530 #ifdef __solaris__
       
   531     if (tz == NULL || *tz == '\0') {
       
   532 #endif
       
   533 #endif
       
   534         tz = getPlatformTimeZoneID();
       
   535         freetz = tz;
       
   536     }
       
   537 
       
   538     if (tz != NULL) {
       
   539         if (*tz == ':') {
       
   540             tz++;
       
   541         }
       
   542 #ifdef __linux__
       
   543         /*
       
   544          * Ignore "posix/" prefix.
       
   545          */
       
   546         if (strncmp(tz, "posix/", 6) == 0) {
       
   547             tz += 6;
       
   548         }
       
   549 #endif
       
   550         javatz = strdup(tz);
       
   551         if (freetz != NULL) {
       
   552             free((void *) freetz);
       
   553         }
       
   554     }
       
   555     return javatz;
       
   556 }
       
   557 
       
   558 /**
       
   559  * Returns a GMT-offset-based time zone ID. (e.g., "GMT-08:00")
       
   560  */
       
   561 char *
       
   562 getGMTOffsetID()
       
   563 {
       
   564     time_t offset;
       
   565     char sign, buf[16];
       
   566 
       
   567     if (timezone == 0) {
       
   568         return strdup("GMT");
       
   569     }
       
   570 
       
   571     /* Note that the time offset direction is opposite. */
       
   572     if (timezone > 0) {
       
   573         offset = timezone;
       
   574         sign = '-';
       
   575     } else {
       
   576         offset = -timezone;
       
   577         sign = '+';
       
   578     }
       
   579     sprintf(buf, (const char *)"GMT%c%02d:%02d",
       
   580             sign, (int)(offset/3600), (int)((offset%3600)/60));
       
   581     return strdup(buf);
       
   582 }