mirror of
				https://git.proxmox.com/git/mirror_edk2
				synced 2025-10-30 05:08:29 +00:00 
			
		
		
		
	 0c1992fbcc
			
		
	
	
		0c1992fbcc
		
	
	
	
	
		
			
			Include/ctype.h:             Function declaration and Macro definition of isDirSep
Include/unistd.h:            Declarations added from EfiSysCall.h
Include/utime.h:             New file.  For the Posix utime() function.
Include/sys/_ctype.h:        Update character class bit maps.
Include/sys/EfiSysCall.h:    Move declarations to unistd.h
Include/sys/fcntl.h:         Improve comments.  Add UEFI-specific macros.
Include/sys/filio.h:         Remove declarations for unsupported file ioctls.
Include/sys/stat.h:          Fix flags.  Add macros and declarations.
Include/sys/time.h:          Add declarations for new functions Tm2Efi() and Time2Efi().
Include/sys/types.h:         Use EFI-specific instead of BSD-specific definitions for typedefs.
Include/sys/unistd.h:        Delete inappropriate content.  Guard macro definitions.
LibC/Locale/setlocale.c
LibC/Stdio/{fdopen.c, findfp.c, fopen.c, freopen.c, gettemp.c, makebuf.c, mktemp.c,
            remove.c, stdio.c, tempnam.c, tmpfile.c, tmpnam.c}
LibC/Time/{itimer.c, ZoneProc.c}
LibC/Uefi/SysCalls.c
LibC/Uefi/Devices/Console/daConsole.c
LibC/Uefi/Devices/UefiShell/daShell.c
PosixLib/Gen/readdir.c
                             Include unistd.h instead of EfiSysCall.h
LibC/Ctype/CClass.c:         Character classification function implementation for isDirSep.
LibC/Ctype/iCtype.c:         Update character classification and case conversion tables.
LibC/Time/TimeEfi.c:         Improve comments.  Implement new functions Tm2Efi() and Time2Efi().
LibC/Uefi/StubFunctions.c:   Add missing include.  Cosmetic changes to declarations.
LibC/Uefi/SysCalls.c:        Add support function for utime().
LibC/Uefi/Uefi.inf:          Add LibGen library class dependency.
LibC/Uefi/Xform.c:           Enhance Omode2EFI().
LibC/Uefi/Devices/UefiShell/daShell.c:    Enhance da_ShellMkdir.  Implement da_ShellIoctl to set file times.
PosixLib/Gen/access.c:       New file.  Implement the access() function.
PosixLib/Gen/dirname.c:      Enhance to use isDirSep and differentiate between the device, path, and filename components of UEFI Shell-style paths.
PosixLib/Gen/utime.c:        New file.  Implement the utime() function.
PosixLib/Gen/LibGen.inf:     Change MODULE_TYPE.  Add new files.
Signed-off-by: darylm503
Reviewed-by: geekboy15a
Reviewed-by: jljusten
Reviewed-by: Rahul Khana
Reviewed-by: leegrosenbaum
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12800 6f19259b-4bc3-4df7-8a09-765794883524
		
	
			
		
			
				
	
	
		
			829 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			829 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|     Time Zone processing.
 | |
| 
 | |
|   Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
 | |
|   This program and the accompanying materials are licensed and made available under
 | |
|   the terms and conditions of the BSD License that accompanies this distribution.
 | |
|   The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php.
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
|   Portions derived from the NIH time zone package file, localtime.c,
 | |
|   which contains the following notice:
 | |
| 
 | |
|     This file is in the public domain, so clarified as of
 | |
|     1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
 | |
| 
 | |
|   NetBSD: localtime.c,v 1.39 2006/03/22 14:01:30 christos Exp
 | |
| **/
 | |
| #include  <LibConfig.h>
 | |
| 
 | |
| #include  <ctype.h>
 | |
| #include  <fcntl.h>
 | |
| #include  <stdio.h>
 | |
| #include  <stdlib.h>
 | |
| #include  <string.h>
 | |
| #include  <time.h>
 | |
| #include  <unistd.h>
 | |
| #include  "tzfile.h"
 | |
| #include  "TimeVals.h"
 | |
| 
 | |
| #ifndef WILDABBR
 | |
| /*
 | |
| ** Someone might make incorrect use of a time zone abbreviation:
 | |
| **  1.  They might reference tzname[0] before calling tzset (explicitly
 | |
| **    or implicitly).
 | |
| **  2.  They might reference tzname[1] before calling tzset (explicitly
 | |
| **    or implicitly).
 | |
| **  3.  They might reference tzname[1] after setting to a time zone
 | |
| **    in which Daylight Saving Time is never observed.
 | |
| **  4.  They might reference tzname[0] after setting to a time zone
 | |
| **    in which Standard Time is never observed.
 | |
| **  5.  They might reference tm.TM_ZONE after calling offtime.
 | |
| ** What's best to do in the above cases is open to debate;
 | |
| ** for now, we just set things up so that in any of the five cases
 | |
| ** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
 | |
| ** string "tzname[0] used before set", and similarly for the other cases.
 | |
| ** And another:  initialize tzname[0] to "ERA", with an explanation in the
 | |
| ** manual page of what this "time zone abbreviation" means (doing this so
 | |
| ** that tzname[0] has the "normal" length of three characters).
 | |
| */
 | |
| #define WILDABBR  "   "
 | |
| #endif /* !defined WILDABBR */
 | |
| 
 | |
| const char wildabbr[9]  = "WILDABBR";
 | |
| const char gmt[4]       = "GMT";
 | |
| 
 | |
| struct state * lclptr = NULL;
 | |
| struct state * gmtptr = NULL;
 | |
| 
 | |
| #ifndef TZ_STRLEN_MAX
 | |
| #define TZ_STRLEN_MAX 255
 | |
| #endif /* !defined TZ_STRLEN_MAX */
 | |
| 
 | |
| static char   lcl_TZname[TZ_STRLEN_MAX + 1];
 | |
| static int    lcl_is_set = 0;
 | |
| //static int    gmt_is_set = 0;
 | |
| 
 | |
| char *   tzname[2] = {
 | |
|   (char *)__UNCONST(wildabbr),
 | |
|   (char *)__UNCONST(wildabbr)
 | |
| };
 | |
| 
 | |
| long int    timezone = 0;
 | |
| int         daylight = 0;
 | |
| 
 | |
| #ifndef NO_ZONEINFO_FILES
 | |
| /** Get first 4 characters of codep as a 32-bit integer.
 | |
| 
 | |
|     The first character of codep becomes the MSB of the resultant integer.
 | |
| **/
 | |
| static INT32
 | |
| detzcode(const char * const codep)
 | |
| {
 | |
|   register INT32 result;
 | |
| 
 | |
|   /*
 | |
|   ** The first character must be sign extended on systems with >32bit
 | |
|   ** longs.  This was solved differently in the master tzcode sources
 | |
|   ** (the fix first appeared in tzcode95c.tar.gz).  But I believe
 | |
|   ** that this implementation is superior.
 | |
|   */
 | |
| #define SIGN_EXTEND_CHAR(x) ((signed char) x)
 | |
| 
 | |
|   result = (SIGN_EXTEND_CHAR(codep[0]) << 24) \
 | |
|     | (codep[1] & 0xff) << 16 \
 | |
|     | (codep[2] & 0xff) << 8
 | |
|     | (codep[3] & 0xff);
 | |
|   return result;
 | |
| }
 | |
| #endif  /* NO_ZONEINFO_FILES */
 | |
| 
 | |
| static void
 | |
| settzname (void)
 | |
| {
 | |
|   register struct state * const sp = lclptr;
 | |
|   register int      i;
 | |
| 
 | |
|   tzname[0] = (char *)__UNCONST(wildabbr);
 | |
|   tzname[1] = (char *)__UNCONST(wildabbr);
 | |
|   daylight = 0;
 | |
|   timezone = 0;
 | |
|   if (sp == NULL) {
 | |
|     tzname[0] = tzname[1] = (char *)__UNCONST(gmt);
 | |
|     return;
 | |
|   }
 | |
|   for (i = 0; i < sp->typecnt; ++i) {
 | |
|     register const struct ttinfo * const  ttisp = &sp->ttis[i];
 | |
| 
 | |
|     tzname[ttisp->tt_isdst] =
 | |
|       &sp->chars[ttisp->tt_abbrind];
 | |
|     if (ttisp->tt_isdst)
 | |
|       daylight = 1;
 | |
|     if (i == 0 || !ttisp->tt_isdst)
 | |
|       timezone = -(ttisp->tt_gmtoff);
 | |
|   }
 | |
|   /*
 | |
|   ** And to get the latest zone names into tzname. . .
 | |
|   */
 | |
|   for (i = 0; i < sp->timecnt; ++i) {
 | |
|     register const struct ttinfo * const  ttisp =
 | |
|       &sp->ttis[ sp->types[i] ];
 | |
| 
 | |
|     tzname[ttisp->tt_isdst] =
 | |
|       &sp->chars[ttisp->tt_abbrind];
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*
 | |
| ** Given a pointer into a time zone string, scan until a character that is not
 | |
| ** a valid character in a zone name is found.  Return a pointer to that
 | |
| ** character.
 | |
| */
 | |
| static const char *
 | |
| getzname(register const char *strp)
 | |
| {
 | |
|   register char c;
 | |
| 
 | |
|   while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
 | |
|          c != '+')
 | |
|     ++strp;
 | |
|   return strp;
 | |
| }
 | |
| 
 | |
| /*
 | |
| ** Given a pointer into a time zone string, extract a number from that string.
 | |
| ** Check that the number is within a specified range; if it is not, return
 | |
| ** NULL.
 | |
| ** Otherwise, return a pointer to the first character not part of the number.
 | |
| */
 | |
| static const char *
 | |
| getnum(
 | |
|   register const char  *strp,
 | |
|   int           * const nump,
 | |
|   const int             min,
 | |
|   const int             max
 | |
|   )
 | |
| {
 | |
|   register char c;
 | |
|   register int  num;
 | |
| 
 | |
|   if (strp == NULL || !is_digit(c = *strp))
 | |
|     return NULL;
 | |
|   num = 0;
 | |
|   do {
 | |
|     num = num * 10 + (c - '0');
 | |
|     if (num > max)
 | |
|       return NULL;  /* illegal value */
 | |
|     c = *++strp;
 | |
|   } while (is_digit(c));
 | |
|   if (num < min)
 | |
|     return NULL;    /* illegal value */
 | |
|   *nump = num;
 | |
|   return strp;
 | |
| }
 | |
| 
 | |
| /*
 | |
| ** Given a pointer into a time zone string, extract a number of seconds,
 | |
| ** in hh[:mm[:ss]] form, from the string.
 | |
| ** If any error occurs, return NULL.
 | |
| ** Otherwise, return a pointer to the first character not part of the number
 | |
| ** of seconds.
 | |
| */
 | |
| static const char *
 | |
| getsecs(
 | |
|   register const char  *strp,
 | |
|   LONG32          * const secsp
 | |
|   )
 | |
| {
 | |
|   int num;
 | |
| 
 | |
|   /*
 | |
|   ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
 | |
|   ** "M10.4.6/26", which does not conform to Posix,
 | |
|   ** but which specifies the equivalent of
 | |
|   ** ``02:00 on the first Sunday on or after 23 Oct''.
 | |
|   */
 | |
|   strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
 | |
|   if (strp == NULL)
 | |
|     return NULL;
 | |
|   *secsp = (long)(num * SECSPERHOUR);
 | |
|   if (*strp == ':') {
 | |
|     ++strp;
 | |
|     strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
 | |
|     if (strp == NULL)
 | |
|       return NULL;
 | |
|     *secsp += num * SECSPERMIN;
 | |
|     if (*strp == ':') {
 | |
|       ++strp;
 | |
|       /* `SECSPERMIN' allows for leap seconds.  */
 | |
|       strp = getnum(strp, &num, 0, SECSPERMIN);
 | |
|       if (strp == NULL)
 | |
|         return NULL;
 | |
|       *secsp += num;
 | |
|     }
 | |
|   }
 | |
|   return strp;
 | |
| }
 | |
| 
 | |
| /*
 | |
| ** Given a pointer into a time zone string, extract an offset, in
 | |
| ** [+-]hh[:mm[:ss]] form, from the string.
 | |
| ** If any error occurs, return NULL.
 | |
| ** Otherwise, return a pointer to the first character not part of the time.
 | |
| */
 | |
| static const char *
 | |
| getoffset(
 | |
|   register const char  *strp,
 | |
|   LONG32          * const offsetp
 | |
|   )
 | |
| {
 | |
|   register int  neg = 0;
 | |
| 
 | |
|   if (*strp == '-') {
 | |
|     neg = 1;
 | |
|     ++strp;
 | |
|   } else if (*strp == '+')
 | |
|     ++strp;
 | |
|   strp = getsecs(strp, offsetp);
 | |
|   if (strp == NULL)
 | |
|     return NULL;    /* illegal time */
 | |
|   if (neg)
 | |
|     *offsetp = -*offsetp;
 | |
|   return strp;
 | |
| }
 | |
| 
 | |
| /*
 | |
| ** Given a pointer into a time zone string, extract a rule in the form
 | |
| ** date[/time].  See POSIX section 8 for the format of "date" and "time".
 | |
| ** If a valid rule is not found, return NULL.
 | |
| ** Otherwise, return a pointer to the first character not part of the rule.
 | |
| */
 | |
| static const char *
 | |
| getrule(
 | |
|   const char *strp,
 | |
|   register struct rule * const rulep
 | |
|   )
 | |
| {
 | |
|   if (*strp == 'J') {
 | |
|     /*
 | |
|     ** Julian day.
 | |
|     */
 | |
|     rulep->r_type = JULIAN_DAY;
 | |
|     ++strp;
 | |
|     strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
 | |
|   } else if (*strp == 'M') {
 | |
|     /*
 | |
|     ** Month, week, day.
 | |
|     */
 | |
|     rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
 | |
|     ++strp;
 | |
|     strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
 | |
|     if (strp == NULL)
 | |
|       return NULL;
 | |
|     if (*strp++ != '.')
 | |
|       return NULL;
 | |
|     strp = getnum(strp, &rulep->r_week, 1, 5);
 | |
|     if (strp == NULL)
 | |
|       return NULL;
 | |
|     if (*strp++ != '.')
 | |
|       return NULL;
 | |
|     strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
 | |
|   } else if (is_digit(*strp)) {
 | |
|     /*
 | |
|     ** Day of year.
 | |
|     */
 | |
|     rulep->r_type = DAY_OF_YEAR;
 | |
|     strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
 | |
|   } else  return NULL;    /* invalid format */
 | |
|   if (strp == NULL)
 | |
|     return NULL;
 | |
|   if (*strp == '/') {
 | |
|     /*
 | |
|     ** Time specified.
 | |
|     */
 | |
|     ++strp;
 | |
|     strp = getsecs(strp, &rulep->r_time);
 | |
|   } else  rulep->r_time = 2 * SECSPERHOUR;  /* default = 2:00:00 */
 | |
|   return strp;
 | |
| }
 | |
| 
 | |
| static int
 | |
| tzload(register const char *name, register struct state * const sp)
 | |
| {
 | |
| #ifndef NO_ZONEINFO_FILES
 | |
|   register const char * p;
 | |
|   register int    i;
 | |
|   register int    fid;
 | |
| 
 | |
|   if (name == NULL && (name = TZDEFAULT) == NULL)
 | |
|     return -1;
 | |
| 
 | |
|   {
 | |
|     register int  doaccess;
 | |
|     /*
 | |
|     ** Section 4.9.1 of the C standard says that
 | |
|     ** "FILENAME_MAX expands to an integral constant expression
 | |
|     ** that is the size needed for an array of char large enough
 | |
|     ** to hold the longest file name string that the implementation
 | |
|     ** guarantees can be opened."
 | |
|     */
 | |
|     char    fullname[FILENAME_MAX + 1];
 | |
| 
 | |
|     if (name[0] == ':')
 | |
|       ++name;
 | |
|     doaccess = name[0] == '/';
 | |
|     if (!doaccess) {
 | |
|       if ((p = TZDIR) == NULL)
 | |
|         return -1;
 | |
|       if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
 | |
|         return -1;
 | |
|       (void) strcpy(fullname, p); /* XXX strcpy is safe */
 | |
|       (void) strcat(fullname, "/"); /* XXX strcat is safe */
 | |
|       (void) strcat(fullname, name);  /* XXX strcat is safe */
 | |
|       /*
 | |
|       ** Set doaccess if '.' (as in "../") shows up in name.
 | |
|       */
 | |
|       if (strchr(name, '.') != NULL)
 | |
|         doaccess = TRUE;
 | |
|       name = fullname;
 | |
|     }
 | |
|     if (doaccess && access(name, R_OK) != 0)
 | |
|       return -1;
 | |
|     /*
 | |
|     * XXX potential security problem here if user of a set-id
 | |
|     * program has set TZ (which is passed in as name) here,
 | |
|     * and uses a race condition trick to defeat the access(2)
 | |
|     * above.
 | |
|     */
 | |
|     if ((fid = open(name, OPEN_MODE)) == -1)
 | |
|       return -1;
 | |
|   }
 | |
|   {
 | |
|     struct tzhead * tzhp;
 | |
|     union {
 | |
|       struct tzhead tzhead;
 | |
|       char    buf[sizeof *sp + sizeof *tzhp];
 | |
|     } u;
 | |
|     int   ttisstdcnt;
 | |
|     int   ttisgmtcnt;
 | |
| 
 | |
|     i = read(fid, u.buf, sizeof u.buf);
 | |
|     if (close(fid) != 0)
 | |
|       return -1;
 | |
|     ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
 | |
|     ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
 | |
|     sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
 | |
|     sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
 | |
|     sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
 | |
|     sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
 | |
|     p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
 | |
|     if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
 | |
|         sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
 | |
|         sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
 | |
|         sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
 | |
|         (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
 | |
|         (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
 | |
|       return -1;
 | |
|     if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */
 | |
|         sp->timecnt +     /* types */
 | |
|         sp->typecnt * (4 + 2) +   /* ttinfos */
 | |
|         sp->charcnt +     /* chars */
 | |
|         sp->leapcnt * (4 + 4) +   /* lsinfos */
 | |
|         ttisstdcnt +      /* ttisstds */
 | |
|         ttisgmtcnt)     /* ttisgmts */
 | |
|       return -1;
 | |
|     for (i = 0; i < sp->timecnt; ++i) {
 | |
|       sp->ats[i] = detzcode(p);
 | |
|       p += 4;
 | |
|     }
 | |
|     for (i = 0; i < sp->timecnt; ++i) {
 | |
|       sp->types[i] = (unsigned char) *p++;
 | |
|       if (sp->types[i] >= sp->typecnt)
 | |
|         return -1;
 | |
|     }
 | |
|     for (i = 0; i < sp->typecnt; ++i) {
 | |
|       register struct ttinfo *  ttisp;
 | |
| 
 | |
|       ttisp = &sp->ttis[i];
 | |
|       ttisp->tt_gmtoff = detzcode(p);
 | |
|       p += 4;
 | |
|       ttisp->tt_isdst = (unsigned char) *p++;
 | |
|       if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
 | |
|         return -1;
 | |
|       ttisp->tt_abbrind = (unsigned char) *p++;
 | |
|       if (ttisp->tt_abbrind < 0 ||
 | |
|           ttisp->tt_abbrind > sp->charcnt)
 | |
|         return -1;
 | |
|     }
 | |
|     for (i = 0; i < sp->charcnt; ++i)
 | |
|       sp->chars[i] = *p++;
 | |
|     sp->chars[i] = '\0';  /* ensure '\0' at end */
 | |
|     for (i = 0; i < sp->leapcnt; ++i) {
 | |
|       register struct lsinfo *  lsisp;
 | |
| 
 | |
|       lsisp = &sp->lsis[i];
 | |
|       lsisp->ls_trans = detzcode(p);
 | |
|       p += 4;
 | |
|       lsisp->ls_corr = detzcode(p);
 | |
|       p += 4;
 | |
|     }
 | |
|     for (i = 0; i < sp->typecnt; ++i) {
 | |
|       register struct ttinfo *  ttisp;
 | |
| 
 | |
|       ttisp = &sp->ttis[i];
 | |
|       if (ttisstdcnt == 0)
 | |
|         ttisp->tt_ttisstd = FALSE;
 | |
|       else {
 | |
|         ttisp->tt_ttisstd = *p++;
 | |
|         if (ttisp->tt_ttisstd != TRUE &&
 | |
|             ttisp->tt_ttisstd != FALSE)
 | |
|           return -1;
 | |
|       }
 | |
|     }
 | |
|     for (i = 0; i < sp->typecnt; ++i) {
 | |
|       register struct ttinfo *  ttisp;
 | |
| 
 | |
|       ttisp = &sp->ttis[i];
 | |
|       if (ttisgmtcnt == 0)
 | |
|         ttisp->tt_ttisgmt = FALSE;
 | |
|       else {
 | |
|         ttisp->tt_ttisgmt = *p++;
 | |
|         if (ttisp->tt_ttisgmt != TRUE &&
 | |
|             ttisp->tt_ttisgmt != FALSE)
 | |
|           return -1;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return 0;
 | |
| #else   /* ! NO_ZONEINFO_FILES */
 | |
|   return -1;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /*
 | |
| ** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
 | |
| ** year, a rule, and the offset from UTC at the time that rule takes effect,
 | |
| ** calculate the Epoch-relative time that rule takes effect.
 | |
| */
 | |
| static
 | |
| time_t
 | |
| transtime(
 | |
|   const time_t              janfirst,
 | |
|   const int                 year,
 | |
|   const struct rule * const rulep,
 | |
|   const LONG32                offset
 | |
|   )
 | |
| {
 | |
|   register int  leapyear;
 | |
|   register time_t value;
 | |
|   register int  i;
 | |
|   int   d, m1, yy0, yy1, yy2, dow;
 | |
| 
 | |
|   INITIALIZE(value);
 | |
|   leapyear = isleap(year);
 | |
|   switch (rulep->r_type) {
 | |
| 
 | |
|     case JULIAN_DAY:
 | |
|       /*
 | |
|     ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
 | |
|     ** years.
 | |
|     ** In non-leap years, or if the day number is 59 or less, just
 | |
|     ** add SECSPERDAY times the day number-1 to the time of
 | |
|     ** January 1, midnight, to get the day.
 | |
|     */
 | |
|       value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
 | |
|       if (leapyear && rulep->r_day >= 60)
 | |
|         value += SECSPERDAY;
 | |
|       break;
 | |
| 
 | |
|     case DAY_OF_YEAR:
 | |
|       /*
 | |
|     ** n - day of year.
 | |
|     ** Just add SECSPERDAY times the day number to the time of
 | |
|     ** January 1, midnight, to get the day.
 | |
|     */
 | |
|       value = janfirst + rulep->r_day * SECSPERDAY;
 | |
|       break;
 | |
| 
 | |
|     case MONTH_NTH_DAY_OF_WEEK:
 | |
|       /*
 | |
|     ** Mm.n.d - nth "dth day" of month m.
 | |
|     */
 | |
|       value = janfirst;
 | |
|       for (i = 0; i < rulep->r_mon - 1; ++i)
 | |
|         value += mon_lengths[leapyear][i] * SECSPERDAY;
 | |
| 
 | |
|       /*
 | |
|     ** Use Zeller's Congruence to get day-of-week of first day of
 | |
|     ** month.
 | |
|     */
 | |
|       m1 = (rulep->r_mon + 9) % 12 + 1;
 | |
|       yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
 | |
|       yy1 = yy0 / 100;
 | |
|       yy2 = yy0 % 100;
 | |
|       dow = ((26 * m1 - 2) / 10 +
 | |
|              1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
 | |
|       if (dow < 0)
 | |
|         dow += DAYSPERWEEK;
 | |
| 
 | |
|       /*
 | |
|     ** "dow" is the day-of-week of the first day of the month.  Get
 | |
|     ** the day-of-month (zero-origin) of the first "dow" day of the
 | |
|     ** month.
 | |
|     */
 | |
|       d = rulep->r_day - dow;
 | |
|       if (d < 0)
 | |
|         d += DAYSPERWEEK;
 | |
|       for (i = 1; i < rulep->r_week; ++i) {
 | |
|         if (d + DAYSPERWEEK >=
 | |
|             mon_lengths[leapyear][rulep->r_mon - 1])
 | |
|           break;
 | |
|         d += DAYSPERWEEK;
 | |
|       }
 | |
| 
 | |
|       /*
 | |
|     ** "d" is the day-of-month (zero-origin) of the day we want.
 | |
|     */
 | |
|       value += d * SECSPERDAY;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|   ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
 | |
|   ** question.  To get the Epoch-relative time of the specified local
 | |
|   ** time on that day, add the transition time and the current offset
 | |
|   ** from UTC.
 | |
|   */
 | |
|   return value + rulep->r_time + offset;
 | |
| }
 | |
| 
 | |
| /*
 | |
| ** Given a POSIX section 8-style TZ string, fill in the rule tables as
 | |
| ** appropriate.
 | |
| */
 | |
| static int
 | |
| tzparse(
 | |
|   const char    *       name,
 | |
|   struct state  * const sp,
 | |
|   const int             lastditch
 | |
|   )
 | |
| {
 | |
|   const char     *stdname;
 | |
|   const char     *dstname;
 | |
|   size_t          stdlen;
 | |
|   size_t          dstlen;
 | |
|   LONG32            stdoffset;
 | |
|   LONG32            dstoffset;
 | |
|   time_t         *atp;
 | |
|   unsigned char  *typep;
 | |
|   char           *cp;
 | |
|   int             load_result;
 | |
| 
 | |
|   dstname = NULL;
 | |
|   stdname = name;
 | |
|   if (lastditch) {
 | |
|     stdlen = strlen(name);  /* length of standard zone name */
 | |
|     name += stdlen;
 | |
|     if (stdlen >= sizeof sp->chars)
 | |
|       stdlen = (sizeof sp->chars) - 1;
 | |
|     stdoffset = 0;
 | |
|   } else {
 | |
|     name = getzname(name);
 | |
|     stdlen = name - stdname;
 | |
|     if (stdlen < 3)
 | |
|       return -1;
 | |
|     if (*name == '\0')
 | |
|       return -1;
 | |
|     name = getoffset(name, &stdoffset);
 | |
|     if (name == NULL)
 | |
|       return -1;
 | |
|   }
 | |
|   load_result = tzload(TZDEFRULES, sp);
 | |
|   if (load_result != 0)
 | |
|     sp->leapcnt = 0;    /* so, we're off a little */
 | |
|   if (*name != '\0') {
 | |
|     dstname = name;
 | |
|     name = getzname(name);
 | |
|     dstlen = name - dstname;  /* length of DST zone name */
 | |
|     if (dstlen < 3)
 | |
|       return -1;
 | |
|     if (*name != '\0' && *name != ',' && *name != ';') {
 | |
|       name = getoffset(name, &dstoffset);
 | |
|       if (name == NULL)
 | |
|         return -1;
 | |
|     } else  dstoffset = stdoffset - SECSPERHOUR;
 | |
|     if (*name == '\0' && load_result != 0)
 | |
|       name = TZDEFRULESTRING;
 | |
|     if (*name == ',' || *name == ';') {
 | |
|       struct rule start;
 | |
|       struct rule end;
 | |
|       register int  year;
 | |
|       register time_t janfirst;
 | |
|       time_t    starttime;
 | |
|       time_t    endtime;
 | |
| 
 | |
|       ++name;
 | |
|       if ((name = getrule(name, &start)) == NULL)
 | |
|         return -1;
 | |
|       if (*name++ != ',')
 | |
|         return -1;
 | |
|       if ((name = getrule(name, &end)) == NULL)
 | |
|         return -1;
 | |
|       if (*name != '\0')
 | |
|         return -1;
 | |
|       sp->typecnt = 2;  /* standard time and DST */
 | |
|       /*
 | |
|       ** Two transitions per year, from EPOCH_YEAR to 2037.
 | |
|       */
 | |
|       sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
 | |
|       if (sp->timecnt > TZ_MAX_TIMES)
 | |
|         return -1;
 | |
|       sp->ttis[0].tt_gmtoff = -dstoffset;
 | |
|       sp->ttis[0].tt_isdst = 1;
 | |
|       sp->ttis[0].tt_abbrind = (int)stdlen + 1;
 | |
|       sp->ttis[1].tt_gmtoff = -stdoffset;
 | |
|       sp->ttis[1].tt_isdst = 0;
 | |
|       sp->ttis[1].tt_abbrind = 0;
 | |
|       atp = sp->ats;
 | |
|       typep = sp->types;
 | |
|       janfirst = 0;
 | |
|       for (year = EPOCH_YEAR; year <= 2037; ++year) {
 | |
|         starttime = transtime(janfirst, year, &start,
 | |
|                               stdoffset);
 | |
|         endtime = transtime(janfirst, year, &end,
 | |
|                             dstoffset);
 | |
|         if (starttime > endtime) {
 | |
|           *atp++ = endtime;
 | |
|           *typep++ = 1; /* DST ends */
 | |
|           *atp++ = starttime;
 | |
|           *typep++ = 0; /* DST begins */
 | |
|         } else {
 | |
|           *atp++ = starttime;
 | |
|           *typep++ = 0; /* DST begins */
 | |
|           *atp++ = endtime;
 | |
|           *typep++ = 1; /* DST ends */
 | |
|         }
 | |
|         janfirst += year_lengths[isleap(year)] *
 | |
|           SECSPERDAY;
 | |
|       }
 | |
|     } else {
 | |
|       register LONG32 theirstdoffset;
 | |
|       register LONG32 theiroffset;
 | |
|       register int  i;
 | |
|       register int  j;
 | |
| 
 | |
|       if (*name != '\0')
 | |
|         return -1;
 | |
|       /*
 | |
|       ** Initial values of theirstdoffset
 | |
|       */
 | |
|       theirstdoffset = 0;
 | |
|       for (i = 0; i < sp->timecnt; ++i) {
 | |
|         j = sp->types[i];
 | |
|         if (!sp->ttis[j].tt_isdst) {
 | |
|           theirstdoffset =
 | |
|             -sp->ttis[j].tt_gmtoff;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       /*
 | |
|       ** Initially we're assumed to be in standard time.
 | |
|       */
 | |
|       theiroffset = theirstdoffset;
 | |
|       /*
 | |
|       ** Now juggle transition times and types
 | |
|       ** tracking offsets as you do.
 | |
|       */
 | |
|       for (i = 0; i < sp->timecnt; ++i) {
 | |
|         j = sp->types[i];
 | |
|         sp->types[i] = (unsigned char)sp->ttis[j].tt_isdst;
 | |
|         if (sp->ttis[j].tt_ttisgmt) {
 | |
|           /* No adjustment to transition time */
 | |
|         } else {
 | |
|           /*
 | |
|           ** If summer time is in effect, and the
 | |
|           ** transition time was not specified as
 | |
|           ** standard time, add the summer time
 | |
|           ** offset to the transition time;
 | |
|           ** otherwise, add the standard time
 | |
|           ** offset to the transition time.
 | |
|           */
 | |
|           /*
 | |
|           ** Transitions from DST to DDST
 | |
|           ** will effectively disappear since
 | |
|           ** POSIX provides for only one DST
 | |
|           ** offset.
 | |
|           */
 | |
|           sp->ats[i] += stdoffset -
 | |
|             theirstdoffset;
 | |
|         }
 | |
|         theiroffset = -sp->ttis[j].tt_gmtoff;
 | |
|         if (!sp->ttis[j].tt_isdst)
 | |
|           theirstdoffset = theiroffset;
 | |
|       }
 | |
|       /*
 | |
|       ** Finally, fill in ttis.
 | |
|       ** ttisstd and ttisgmt need not be handled.
 | |
|       */
 | |
|       sp->ttis[0].tt_gmtoff = -stdoffset;
 | |
|       sp->ttis[0].tt_isdst = FALSE;
 | |
|       sp->ttis[0].tt_abbrind = 0;
 | |
|       sp->ttis[1].tt_gmtoff = -dstoffset;
 | |
|       sp->ttis[1].tt_isdst = TRUE;
 | |
|       sp->ttis[1].tt_abbrind = (int)stdlen + 1;
 | |
|       sp->typecnt = 2;
 | |
|     }
 | |
|   } else {
 | |
|     dstlen = 0;
 | |
|     sp->typecnt = 1;    /* only standard time */
 | |
|     sp->timecnt = 0;
 | |
|     sp->ttis[0].tt_gmtoff = -stdoffset;
 | |
|     sp->ttis[0].tt_isdst = 0;
 | |
|     sp->ttis[0].tt_abbrind = 0;
 | |
|   }
 | |
|   sp->charcnt = (int)stdlen + 1;
 | |
|   if (dstlen != 0)
 | |
|     sp->charcnt += (int)dstlen + 1;
 | |
|   if ((size_t) sp->charcnt > sizeof sp->chars)
 | |
|     return -1;
 | |
|   cp = sp->chars;
 | |
|   (void) strncpy(cp, stdname, stdlen);
 | |
|   cp += stdlen;
 | |
|   *cp++ = '\0';
 | |
|   if (dstlen != 0) {
 | |
|     (void) strncpy(cp, dstname, dstlen);
 | |
|     *(cp + dstlen) = '\0';
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| gmtload(struct state * const sp)
 | |
| {
 | |
|   if (tzload(gmt, sp) != 0)
 | |
|     (void) tzparse(gmt, sp, TRUE);
 | |
| }
 | |
| 
 | |
| static void
 | |
| tzsetwall(void)
 | |
| {
 | |
|   if (lcl_is_set < 0)
 | |
|     return;
 | |
|   lcl_is_set = -1;
 | |
| 
 | |
|   if (lclptr == NULL) {
 | |
|     lclptr = (struct state *) malloc(sizeof *lclptr);
 | |
|     if (lclptr == NULL) {
 | |
|       settzname();  /* all we can do */
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
|   if (tzload((char *) NULL, lclptr) != 0)
 | |
|     gmtload(lclptr);
 | |
|   settzname();
 | |
| }
 | |
| 
 | |
| void
 | |
| tzset(void)
 | |
| {
 | |
|   register const char * name;
 | |
| 
 | |
|   name = getenv("TZ");
 | |
|   if (name == NULL) {
 | |
|     tzsetwall();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)
 | |
|     return;
 | |
|   lcl_is_set = strlen(name) < sizeof lcl_TZname;
 | |
|   if (lcl_is_set)
 | |
|     (void)strncpyX(lcl_TZname, name, sizeof(lcl_TZname));
 | |
| 
 | |
|   if (lclptr == NULL) {
 | |
|     lclptr = (struct state *) malloc(sizeof *lclptr);
 | |
|     if (lclptr == NULL) {
 | |
|       settzname();  /* all we can do */
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
|   if (*name == '\0') {
 | |
|     /*
 | |
|     ** User wants it fast rather than right.
 | |
|     */
 | |
|     lclptr->leapcnt = 0;    /* so, we're off a little */
 | |
|     lclptr->timecnt = 0;
 | |
|     lclptr->typecnt = 0;
 | |
|     lclptr->ttis[0].tt_isdst = 0;
 | |
|     lclptr->ttis[0].tt_gmtoff = 0;
 | |
|     lclptr->ttis[0].tt_abbrind = 0;
 | |
|     (void)strncpyX(lclptr->chars, gmt, sizeof(lclptr->chars));
 | |
|   } else if (tzload(name, lclptr) != 0)
 | |
|     if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
 | |
|     (void) gmtload(lclptr);
 | |
|   settzname();
 | |
| }
 |