diff -Naur 2017b/asctime.c new/asctime.c --- 2017b/asctime.c 2017-05-09 15:48:58.381956368 +0200 +++ new/asctime.c 2017-05-09 15:57:23.445916219 +0200 @@ -70,10 +70,10 @@ char * asctime_r(register const struct tm *timeptr, char *buf) { - static const char wday_name[][3] = { + static const char wday_name[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - static const char mon_name[][3] = { + static const char mon_name[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; diff -Naur 2017b/localtime.c new/localtime.c --- 2017b/localtime.c 2017-05-09 15:48:58.381956368 +0200 +++ new/localtime.c 2017-05-09 16:05:15.600818385 +0200 @@ -11,10 +11,19 @@ /*LINTLIBRARY*/ #define LOCALTIME_IMPLEMENTATION +#define USE_TIME_SUCCESSIVE + #include "private.h" #include "tzfile.h" + #include +#include + +#ifdef _WIN32 +/* Do not handle warning as error, otherwise the code won't build */ +#pragma warning(default:4267) +#endif #if defined THREAD_SAFE && THREAD_SAFE # include @@ -159,6 +168,24 @@ static bool typesequiv(struct state const *, int, int); static bool tzparse(char const *, struct state *, bool); +#ifdef USE_TIME_SUCCESSIVE + +static int time_successive( + struct tm * (* const funcp)( + struct state const *, + time_t const *, + int_fast32_t, + struct tm * + ), + struct state const * sp, + time_t * tp, + int_fast32_t offset, + struct tm * tmp, + int * compensate +); + +#endif // USE_TIME_SUCCESSIVE + #ifdef ALL_STATE static struct state * lclptr; static struct state * gmtptr; @@ -188,12 +215,18 @@ static struct tm tm; +/* Visual Studio is its own counterparts with slightly different definitions in time.h, + but as of the Windows 10 SDK, these definitions have gone (replaced by underscore prefixed ones). + There is unfortunaly no macro that tells the SDK version. The used SDK version can differ, even + if the Visual Studio (compiler) version is the same. Depending on the Visual Studio version being + used, you may or may not need next declarations. + */ #if !HAVE_POSIX_DECLS char * tzname[2] = { (char *) wildabbr, (char *) wildabbr }; -# ifdef USG_COMPAT +# if defined(USG_COMPAT) long timezone; int daylight; # endif @@ -203,6 +236,15 @@ long altzone; #endif /* defined ALTZONE */ +#ifdef _MSC_VER +# define isSlash(c) ( ((c) == '/') || ((c) == '\\') ) +/* for Windows, also check on :\ */ +# define pathIsAbsolute(path) (isSlash(path[0]) || (isalpha(path[0]) && path[1] == ':' && isSlash(path[2]))) +#else +# define isSlash(c) ( (c) == '/' ) +# define pathIsAbsolute(path) (isSlash(path[0])) +#endif + /* Initialize *S to a value based on GMTOFF, ISDST, and ABBRIND. */ static void init_ttinfo(struct ttinfo *s, int_fast32_t gmtoff, bool isdst, int abbrind) @@ -367,6 +409,46 @@ } u; }; +#ifndef DO_NOT_USE_TZ_LIB_EXTENSIONS +static const char *tzdir; + +void set_tz_dir(const char *dir) +{ + /* override zonerules directory at runtime */ + tzdir = dir; +} + +const char *get_tz_dir(void) +{ + /* set if set_tz_dir() was called */ + if (tzdir == NULL) { + /* built-in compile-time default */ + set_tz_dir(TZDIR); + } + + return tzdir; +} + +static const char *tzvalue; + +void set_TZ_value(const char *timezone) +{ + /* allow to set timezone without using the environment, thus prevening memory leakage + by using putenv/setenv() in case this code is called more than once. + */ + tzvalue = timezone; +} + +const char *get_TZ_value(void) +{ + if (tzvalue == NULL) { + return getenv("TZ"); + } else { + return tzvalue; + } +} +#endif /* DO_NOT_USE_TZ_LIB_EXTENSIONS */ + /* Load tz data from the file named NAME into *SP. Read extended format if DOEXTEND. Use *LSP for temporary storage. Return 0 on success, an errno value on failure. */ @@ -393,9 +475,13 @@ if (name[0] == ':') ++name; - doaccess = name[0] == '/'; + doaccess = pathIsAbsolute(name); if (!doaccess) { +#ifndef DO_NOT_USE_TZ_LIB_EXTENSIONS + char const *p = get_tz_dir(); +#else char const *p = TZDIR; +#endif if (! p) return EINVAL; if (sizeof lsp->fullname - 1 <= strlen(p) + strlen(name)) @@ -1018,6 +1104,7 @@ register char * cp; register bool load_ok; + INITIALIZE(dstname); stdname = name; if (lastditch) { stdlen = sizeof gmt - 1; @@ -1332,7 +1419,11 @@ static void tzset_unlocked(void) { - tzsetlcl(getenv("TZ")); +#ifndef DO_NOT_USE_TZ_LIB_EXTENSIONS + tzsetlcl(get_TZ_value()); +#else + tzsetlcl(getenv("TZ")); +#endif } void @@ -1845,17 +1936,30 @@ bool *okayp, bool do_norm_secs) { +#ifndef USE_TIME_SUCCESSIVE register int dir; +#endif // USE_TIME_SUCCESSIVE + register int i, j; register int saved_seconds; register int_fast32_t li; + +#ifndef USE_TIME_SUCCESSIVE register time_t lo; register time_t hi; +#endif // USE_TIME_SUCCESSIVE + int_fast32_t y; time_t newt; time_t t; struct tm yourtm, mytm; +#ifdef USE_TIME_SUCCESSIVE + int compensate; + int ignore_isdst = 0; + int different; +#endif // USE_TIME_SUCCESSIVE + *okayp = false; yourtm = *tmp; if (do_norm_secs) { @@ -1923,6 +2027,38 @@ saved_seconds = yourtm.tm_sec; yourtm.tm_sec = 0; } + +#ifdef USE_TIME_SUCCESSIVE + + if (time_successive(funcp, sp, &t, offset, &yourtm, &compensate)) + return WRONG; + funcp(sp, &t, offset, &mytm); + + /* verification, should never fail (if compensate: this is done below) */ + different = tmcomp(&mytm, &yourtm); + if (yourtm.tm_isdst == -1 && different) + { + yourtm.tm_isdst = 1 - mytm.tm_isdst; + compensate = 1; + ignore_isdst = 1; + } + if (!compensate && different) + { + /* + * it is allowed to the user to override DST with tm_isdst flag + * lateron the correct structure will be returned in time1() + * no logmessage over here + log_mesg(__SRC__, __LINE__, "utc", TO_SET, + "successive algorithm failed, offset=%d, t=%d, yourtm=%s", + offset, t, asctime(&yourtm)); + */ + return WRONG; + } + if (yourtm.tm_isdst >= 0 && compensate) + { + +#else // USE_TIME_SUCCESSIVE + /* ** Do a binary search (this works whatever time_t's type is). */ @@ -1992,6 +2128,9 @@ #endif if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) break; + +#endif // USE_TIME_SUCCESSIVE + /* ** Right time, wrong type. ** Hunt for right time, right type. @@ -2001,10 +2140,18 @@ if (sp == NULL) return WRONG; for (i = sp->typecnt - 1; i >= 0; --i) { + +#ifndef USE_TIME_SUCCESSIVE if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) continue; +#endif // USE_TIME_SUCCESSIVE + for (j = sp->typecnt - 1; j >= 0; --j) { +#ifdef USE_TIME_SUCCESSIVE + if (sp->ttis[j].tt_isdst == sp->ttis[i].tt_isdst) +#else // USE_TIME_SUCCESSIVE if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) +#endif // USE_TIME_SUCCESSIVE continue; newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; @@ -2013,6 +2160,16 @@ if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) + +#ifdef USE_TIME_SUCCESSIVE + /* if we have done a compensation for a + * tm_isdst = -1, we can ignore a different + * yourtm.is_dst, since that was a wild + * guess above (where the ignore flag was set). + */ + if (!ignore_isdst) +#endif // USE_TIME_SUCCESSIVE + continue; /* ** We have a match. @@ -2324,3 +2481,148 @@ } #endif + +#ifdef USE_TIME_SUCCESSIVE + +/* +* This is another implementation of the "successive approximation" +* algorithm that can be found in Perl's timelocal.pl (see www.perl.org). +* +* A binary search algorithm takes 32 calls to gmtime, which is rather +* expensive. In contrast this algoritm takes just only one such call. +* +* However, the implementation of time2() makes 2 additional calls to +* the <>, giving a call rate of 3. +* +* The gmtime calls mentioned above are in fact calls of the funcp argument, +* which points to localsub or gmtsub. +* +* January 2011 +* Jan Koen Annot +*/ + +static const int sCumulativeMonthLength[] = { + 0, + 31, + 59, + 90, + 120, + 151, + 181, + 212, + 243, + 273, + 304, + 334 +}; + +static int time_successive( + struct tm * (* const funcp)( + struct state const *, + time_t const *, + int_fast32_t, + struct tm * + ), + struct state const * sp, + time_t * tp, + int_fast32_t offset, + struct tm * tmp, + int * compensate +) +{ + time_t guess; + time_t guess_offset; + struct tm g; + + /* As an initial guess for the correct time_t value, + take the utc value of 00:00:00 at the 3rd day of the specified month and year. + When calculating that value back to a struct tm, + at least the year and the month will be correct. + */ + time_t lYear; + time_t lMonth; + time_t lDays; + + lYear = tmp->tm_year; + lYear += TM_YEAR_BASE; /* change years from 1900 to years from 0 */ + + lMonth = tmp->tm_mon; + + lDays = lYear * DAYSPERNYEAR; + + if (0 < lYear) { + /* adapt for leap years until the previous year */ + time_t lPreviousYear = lYear - 1; + + lDays += 1; /* year 0 is a leap year */ + lDays += lPreviousYear / 4; /* every 4 year add a leap year */ + lDays -= lPreviousYear / 100; /* every 100 year subtract a leap year */ + lDays += lPreviousYear / 400; /* every 400 year add a leap year */ + } + + lDays += sCumulativeMonthLength[lMonth]; + + if ( 2 <= lMonth + && isleap(lYear) + ) { + /* in leap years, add a leap day from March onwards */ + lDays += 1; + } + + /* change days from January 1, 0 to days from January 1, 1970 */ + lDays -= EPOCH_YEAR * DAYSPERNYEAR + + 1 + + (EPOCH_YEAR - 1) / 4 + - (EPOCH_YEAR - 1) / 100 + + (EPOCH_YEAR - 1) / 400 + ; + + lDays += 3; /* take the 3rd day of the month */ + + guess = lDays; + + if (guess <= time_t_max / SECSPERDAY) { + guess *= SECSPERDAY; + } else { + *tp = -1; + return WRONG; + } + + funcp(sp, &guess, offset, &g); + + if ( g.tm_year != tmp->tm_year + || g.tm_mon != tmp->tm_mon + ) { + *tp = -1; + return WRONG; + } + + guess_offset = ((time_t)tmp->tm_sec - (time_t)g.tm_sec) + + ((time_t)tmp->tm_min - (time_t)g.tm_min) * SECSPERMIN + + ((time_t)tmp->tm_hour - (time_t)g.tm_hour) * SECSPERHOUR + + ((time_t)tmp->tm_mday - (time_t)g.tm_mday) * SECSPERDAY; + + if ( guess_offset < 0 + ? time_t_min - guess_offset <= guess /* avoid time_t underflow */ + : guess <= time_t_max - guess_offset /* avoid time_t overflow */ + ) { + guess += guess_offset; + } else { + *tp = -1; + return WRONG; + } + + /* it could happen to be wrong because dailight savings state changed + * in between the fixed calculation above. This is compensated after + * returning from this func. + */ + *compensate = (tmp->tm_isdst != g.tm_isdst); + if (tmp->tm_isdst < 0) { + *compensate = 0; + } + + *tp = guess; + return 0; +} + +#endif // USE_TIME_SUCCESSIVE diff -Naur 2017b/private.h new/private.h --- 2017b/private.h 2017-05-09 15:49:01.086961508 +0200 +++ new/private.h 2017-05-09 15:57:49.240965242 +0200 @@ -35,11 +35,15 @@ #define HAVE_INCOMPATIBLE_CTIME_R 0 #endif /* !defined INCOMPATIBLE_CTIME_R */ +/* a lot of stuff does not exist on Windows/Visual Studio */ +#define DEFAULT_FOR_UNIX (!_MSC_VER) + #ifndef HAVE_LINK -#define HAVE_LINK 1 +#define HAVE_LINK DEFAULT_FOR_UNIX #endif /* !defined HAVE_LINK */ -#ifndef HAVE_POSIX_DECLS +/* Visual Studio is not (fully) POSIX compliant */ +#if !defined(HAVE_POSIX_DECLS) && !defined(_MSC_VER) #define HAVE_POSIX_DECLS 1 #endif @@ -48,27 +52,34 @@ #endif #ifndef HAVE_SYMLINK -#define HAVE_SYMLINK 1 +#define HAVE_SYMLINK DEFAULT_FOR_UNIX #endif /* !defined HAVE_SYMLINK */ #ifndef HAVE_SYS_STAT_H #define HAVE_SYS_STAT_H 1 #endif /* !defined HAVE_SYS_STAT_H */ -#ifndef HAVE_SYS_WAIT_H -#define HAVE_SYS_WAIT_H 1 +#if !defined(HAVE_SYS_WAIT_H) +#define HAVE_SYS_WAIT_H DEFAULT_FOR_UNIX #endif /* !defined HAVE_SYS_WAIT_H */ -#ifndef HAVE_UNISTD_H +#if !defined(HAVE_UNISTD_H) +#ifdef _MSC_VER +/* Visual Studio has other header files that (partly) define counterparts for functions that are declared in unistd.h */ +#define HAVE_IO_H 1 +#define HAVE_DIRECT_H 1 +#else +/* Visual Stdio does not have unistd.h */ #define HAVE_UNISTD_H 1 +#endif #endif /* !defined HAVE_UNISTD_H */ #ifndef HAVE_UTMPX_H -#define HAVE_UTMPX_H 1 +#define HAVE_UTMPX_H DEFAULT_FOR_UNIX #endif /* !defined HAVE_UTMPX_H */ #ifndef NETBSD_INSPIRED -# define NETBSD_INSPIRED 1 +# define NETBSD_INSPIRED DEFAULT_FOR_UNIX #endif #if HAVE_INCOMPATIBLE_CTIME_R @@ -141,14 +152,24 @@ #include /* for F_OK, R_OK, and other POSIX goodness */ #endif /* HAVE_UNISTD_H */ +#if HAVE_IO_H +#include /* for access() etc */ +#endif + #ifndef HAVE_STRFTIME_L -# if _POSIX_VERSION < 200809 +/* Visual Studio has strftime_l() */ +# if _POSIX_VERSION < 200809 && !defined(_MSC_VER) # define HAVE_STRFTIME_L 0 # else # define HAVE_STRFTIME_L 1 # endif #endif +#if defined(_MSC_VER) +/* see also MSDN on strftime(). Microsoft has renamed locale_t to _localte_t */ +#define locale_t _locale_t +#endif + #ifndef F_OK #define F_OK 0 #endif /* !defined F_OK */ @@ -163,13 +184,13 @@ ** Define HAVE_STDINT_H's default value here, rather than at the ** start, since __GLIBC__ and INTMAX_MAX's values depend on ** previously-included files. glibc 2.1 and Solaris 10 and later have -** stdint.h, even with pre-C99 compilers. +** stdint.h, even with pre-C99 compilers. The same applies to Visual Studio. */ #ifndef HAVE_STDINT_H #define HAVE_STDINT_H \ (199901 <= __STDC_VERSION__ \ || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \ - || __CYGWIN__ || INTMAX_MAX) + || __CYGWIN__ || INTMAX_MAX || _MSC_VER) #endif /* !defined HAVE_STDINT_H */ #if HAVE_STDINT_H @@ -316,6 +337,12 @@ # define restrict /* empty */ #endif +#if defined(_MSC_VER) +#include +/* Visual Studio does not define ssize_t at default */ +typedef SSIZE_T ssize_t; +#endif + /* ** Workarounds for compilers/systems. */ @@ -402,20 +429,36 @@ void tzset(void); #endif +void set_tz_dir(const char *dir); +const char *get_tz_dir(void); +void set_TZ_value(const char *timezone); +const char *get_TZ_value(void); + #if !HAVE_DECL_ASCTIME_R && !defined asctime_r extern char *asctime_r(struct tm const *restrict, char *restrict); #endif -#if !HAVE_POSIX_DECLS +/* As of the compiler shipped with Visual Studio 2015 (=MSC_VER >= 1900), + the definitions for timezone and daylight differ (prefixed with underscore), so we can use next ones. + */ +#if !HAVE_POSIX_DECLS || _MSC_VER >= 1900 # ifdef USG_COMPAT + # ifndef timezone extern long timezone; # endif + # ifndef daylight extern int daylight; # endif -# endif -#endif + +# endif /* USG_COMPAT */ + +# ifndef tzname +extern char *tzname[]; +# endif + +#endif /* HAVE_POSIX_DECLS */ #if defined ALTZONE && !defined altzone extern long altzone; @@ -492,7 +535,8 @@ ** Finally, some convenience items. */ -#if __STDC_VERSION__ < 199901 +/* Visual Studio follows here STDC, but does not set this define */ +#if !defined(_MSC_VER) && __STDC_VERSION__ < 199901 # define true 1 # define false 0 # define bool int @@ -500,8 +544,14 @@ # include #endif +#ifndef TYPE_BIT #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) +#endif /* !defined TYPE_BIT */ + +#ifndef TYPE_SIGNED #define TYPE_SIGNED(type) (((type) -1) < 0) +#endif /* !defined TYPE_SIGNED */ + #define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0) /* Max and min values of the integer type T, of which only the bottom @@ -536,6 +586,7 @@ static time_t const time_t_max = MAXVAL(time_t, TYPE_BIT(time_t)); #endif +#ifndef INT_STRLEN_MAXIMUM /* ** 302 / 1000 is log10(2.0) rounded up. ** Subtract one for the sign bit if the type is signed; @@ -545,12 +596,13 @@ #define INT_STRLEN_MAXIMUM(type) \ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \ 1 + TYPE_SIGNED(type)) +#endif /* !defined INT_STRLEN_MAXIMUM */ /* ** INITIALIZE(x) */ -#ifdef lint +#if defined(lint) || defined(_MSC_VER) || defined(__hpux) # define INITIALIZE(x) ((x) = 0) #else # define INITIALIZE(x) diff -Naur 2017b/zdump.c new/zdump.c --- 2017b/zdump.c 2017-05-09 15:48:58.381956368 +0200 +++ new/zdump.c 2017-05-09 16:05:12.704812812 +0200 @@ -72,7 +72,10 @@ # define timezone_t char ** #endif +#ifndef _MSC_VER +/* Visual Studio has its own, slightly different declaration */ extern char ** environ; +#endif #if !HAVE_POSIX_DECLS extern int getopt(int argc, char * const argv[], @@ -233,33 +236,53 @@ static timezone_t tzalloc(char const *val) { - static char **fakeenv; - char **env = fakeenv; - char *env0; - if (! env) { - char **e = environ; - int to; - - while (*e++) - continue; - env = xmalloc(sumsize(sizeof *environ, - (e - environ) * sizeof *environ)); - to = 1; - for (e = environ; (env[to] = *e); e++) - to += strncmp(*e, "TZ=", 3) != 0; - } - env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val))); - env[0] = strcat(strcpy(env0, "TZ="), val); - environ = fakeenv = env; - tzset(); - return env; +#ifndef DO_NOT_USE_TZ_LIB_EXTENSIONS + set_TZ_value(val); +#elif HAVE_SETENV + setenv("TZ", val, 1); +#elif _MSC_VER + /* reassigning global environment variable causes heap corruption, which is reported when debugging from within visual studio */ + _putenv_s("TZ", val); +#else + static char **fakeenv; + char **env = fakeenv; + char *env0; + if (! env) { + char **e = environ; + int to; + + while (*e++) + continue; + env = xmalloc(sumsize(sizeof *environ, + (e - environ) * sizeof *environ)); + to = 1; + for (e = environ; (env[to] = *e); e++) + to += strncmp(*e, "TZ=", 3) != 0; + } + env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val))); + env[0] = strcat(strcpy(env0, "TZ="), val); + environ = fakeenv = env; +#endif + tzset(); + return environ; } static void tzfree(timezone_t env) { - environ = env + 1; - free(env[0]); + /* Actually, there is - except the last case - to unset the TZ value, as tzalloc() is + called each time a new timezone value is processed. It is just here for symmetry. */ +#ifndef DO_NOT_USE_TZ_LIB_EXTENSIONS + set_TZ_value(""); +#elif HAVE_SETENV + unsetenv("TZ"); +#elif _MSC_VER + /* according to https://msdn.microsoft.com/en-us/library/83zh4e6k.aspx, providing an empty value will result in removal of the variable */ + _putenv_s("TZ", ""); +#else + environ = env + 1; + free(env[0]); +#endif } #endif /* ! USE_LOCALTIME_RZ */ @@ -421,7 +444,7 @@ register char * cuttimes; register time_t cutlotime; register time_t cuthitime; - time_t now; + time_t now = 0; bool iflag = false; cutlotime = absolute_min_time; @@ -523,12 +546,12 @@ for (i = optind; i < argc; i++) { size_t arglen = strlen(argv[i]); if (longest < arglen) - longest = arglen < INT_MAX ? arglen : INT_MAX; + longest = arglen < INT_MAX ? (int)arglen : INT_MAX; } for (i = optind; i < argc; ++i) { timezone_t tz = tzalloc(argv[i]); - char const *ab; + char const *ab = NULL; time_t t; struct tm tm, newtm; bool tm_ok; @@ -651,7 +674,7 @@ { static char * loab; static size_t loabsize; - char const * ab; + char const * ab = NULL; time_t t; struct tm lotm; struct tm tm; @@ -756,7 +779,7 @@ show(timezone_t tz, char *zone, time_t t, bool v) { register struct tm * tmp; - register struct tm * gmtmp; + register struct tm * gmtmp = NULL; struct tm tm, gmtm; printf("%-*s ", longest, zone); @@ -1030,10 +1053,10 @@ static void dumptime(register const struct tm *timeptr) { - static const char wday_name[][3] = { + static const char wday_name[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - static const char mon_name[][3] = { + static const char mon_name[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; diff -Naur 2017b/zic.c new/zic.c --- 2017b/zic.c 2017-05-09 15:48:58.382956370 +0200 +++ new/zic.c 2017-05-09 16:20:40.800590435 +0200 @@ -11,6 +11,16 @@ #include #include #include +#include +#include + +#ifdef _MSC_VER +/* do not handle warnings as errors. Revert to default behavior +warning C4101: unreferenced local variable +warning C4267: '=' : conversion from 'size_t' to 'int', possible loss of data +*/ +#pragma warning(default: 4101 4267) +#endif #define ZIC_VERSION_PRE_2013 '2' #define ZIC_VERSION '3' @@ -118,7 +128,8 @@ # define link(from, to) (errno = ENOTSUP, -1) #endif #if ! HAVE_SYMLINK -# define readlink(file, buf, size) (errno = ENOTSUP, -1) +/* prevent warning C4101: 'c' : unreferenced local variable with Visual Studio, use provided buffer */ +# define readlink(file, buf, size) (*buf = 0, errno = ENOTSUP, -1) # define symlink(from, to) (errno = ENOTSUP, -1) # define S_ISLNK(m) 0 #endif @@ -127,6 +138,15 @@ (itssymlink(from) ? (errno = ENOTSUP, -1) : link(from, to)) #endif +#ifdef _MSC_VER +# define isSlash(c) ( ((c) == '/') || ((c) == '\\') ) +/* for Windows, also check on :\ */ +# define pathIsAbsolute(path) (isSlash(path[0]) || (isalpha(path[0]) && path[1] == ':' && isSlash(path[2]))) +#else +# define isSlash(c) ( (c) == '/' ) +# define pathIsAbsolute(path) (isSlash(path[0])) +#endif + static void addtt(zic_t starttime, int type); static int addtype(zic_t, char const *, bool, bool, bool); static void leapadd(zic_t, bool, int, int); @@ -589,7 +609,7 @@ _("wild compilation-time specification of zic_t")); return EXIT_FAILURE; } - for (k = 1; k < argc; k++) + for (k = 1; k < argc; k++) { if (strcmp(argv[k], "--version") == 0) { printf("zic %s%s\n", PKGVERSION, TZVERSION); close_file(stdout, NULL, NULL); @@ -597,7 +617,15 @@ } else if (strcmp(argv[k], "--help") == 0) { usage(stdout, EXIT_SUCCESS); } - while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1) + } + +#ifdef _MSC_VER + /* Windows does not have shell scripts */ + const char *getoptopt = "d:l:p:L:vs"; +#else + const char *getoptopt = "d:l:p:L:vsy:"; +#endif + while ((c = getopt(argc, argv, getoptopt)) != EOF && c != -1) switch (c) { default: usage(stderr, EXIT_FAILURE); @@ -631,6 +659,7 @@ return EXIT_FAILURE; } break; +#ifndef _MSC_VER case 'y': if (yitcommand == NULL) yitcommand = optarg; @@ -641,6 +670,7 @@ return EXIT_FAILURE; } break; +#endif /* _MSC_VER */ case 'L': if (leapsec == NULL) leapsec = optarg; @@ -791,21 +821,25 @@ size_t dir_len = 0, dotdots = 0, linksize = SIZE_MAX; char const *f = from; char *result = NULL; - if (*to == '/') { - /* Make F absolute too. */ - size_t len = strlen(directory); - bool needslash = len && directory[len - 1] != '/'; - linksize = len + needslash + strlen(from) + 1; - f = result = emalloc(linksize); - strcpy(result, directory); - result[len] = '/'; - strcpy(result + len + needslash, from); + if (pathIsAbsolute(to)) { + /* Make F absolute too. */ + size_t len = strlen(directory); + bool needslash = len && !isSlash(directory[len - 1]); + size_t offset = needslash ? 1 : 0; + linksize = len + offset + strlen(from) + 1; + f = result = emalloc(linksize); + strcpy(result, directory); + result[len] = '/'; + strcpy(result + len + offset, from); + } + for (i = 0; f[i] && f[i] == to[i]; i++) { + if (isSlash(f[i])) { + dir_len = i + 1; + } + } + for (; to[i]; i++) { + dotdots += isSlash(to[i]) && !isSlash(to[i - 1]); } - for (i = 0; f[i] && f[i] == to[i]; i++) - if (f[i] == '/') - dir_len = i + 1; - for (; to[i]; i++) - dotdots += to[i] == '/' && to[i - 1] != '/'; taillen = strlen(f + dir_len); dotdotetcsize = 3 * dotdots + taillen + 1; if (dotdotetcsize <= linksize) { @@ -859,15 +893,23 @@ link_errno = hardlinkerr(fromfield, tofield); } if (link_errno != 0) { - bool absolute = *fromfield == '/'; - char *linkalloc = absolute ? NULL : relname(fromfield, tofield); - char const *contents = absolute ? fromfield : linkalloc; - int symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno; - if (symlink_errno == ENOENT && !todirs_made) { - mkdirs(tofield, true); - symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno; - } - free(linkalloc); + int symlink_errno; +#if HAVE_SYMLINK + bool absolute = pathIsAbsolute(fromfield); + char *linkalloc = absolute ? NULL : relname(fromfield, tofield); + char const *contents = absolute ? fromfield : linkalloc; + symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno; + if (symlink_errno == ENOENT && !todirs_made) { + mkdirs(tofield, true); + symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno; + } + free(linkalloc); +#else + /* for systems that do not support symlinks, there is no need to call relname() (=alloc) + free, + but the to directory need to be created */ + symlink_errno = ENOTSUP; + mkdirs(tofield, true); +#endif if (symlink_errno == 0) { if (link_errno != ENOTSUP) warning(_("symbolic link used because hard link failed: %s"), @@ -946,6 +988,19 @@ ? BIG_BANG : MINVAL(zic_t, TIME_T_BITS_IN_FILE)); +#ifdef _MSC_VER +#define HAVE_DOS_FILE_NAMES 1 + +/* following define does not exist on Win32 platforms (and maybe also not on others) */ +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & _S_IFMT)==_S_IFDIR) +#endif +#endif /* _MSC_VER */ + /* Return true if NAME is a directory. */ static bool itsdir(char const *name) @@ -1695,7 +1750,7 @@ register int leapcnt32, leapi32; register ptrdiff_t timecnt32, timei32; register int pass; - static const struct tzhead tzh0; + static const struct tzhead tzh0 = { 0 }; // HP aCC requires initializer here static struct tzhead tzh; bool dir_checked = false; zic_t one = 1; @@ -2350,7 +2405,7 @@ register struct rule * rp; register ptrdiff_t i, j; register bool usestart, useuntil; - register zic_t starttime, untiltime; + register zic_t starttime = 0, untiltime = 0; // initialization required by e.g. Visual Studio, HP aCC register zic_t gmtoff; register zic_t stdoff; register zic_t year; @@ -2804,32 +2859,68 @@ static bool yearistype(zic_t year, const char *type) { - char *buf; - char *b; - int result; - - if (type == NULL || *type == '\0') + if (type == NULL || *type == '\0') { return true; - buf = emalloc(1 + 4 * strlen(yitcommand) + 2 - + INT_STRLEN_MAXIMUM(zic_t) + 2 + 4 * strlen(type) + 2); - b = shellquote(buf, yitcommand); - *b++ = ' '; - b += sprintf(b, "%"PRIdZIC, year); - *b++ = ' '; - b = shellquote(b, type); - *b = '\0'; - result = system(buf); - if (WIFEXITED(result)) { - int status = WEXITSTATUS(result); - if (status <= 1) { - free(buf); - return status == 0; - } } - error(_("Wild result from command execution")); - fprintf(stderr, _("%s: command was '%s', result was %d\n"), - progname, buf, result); - exit(EXIT_FAILURE); + + if (strcmp(yitcommand, "yearistype") == 0) { + /* Windows does not have shell script. Use C-replacement. */ + bool ret; + if (strcmp(type, "even") == 0) + { + ret = ((year & 1) == 0); + } + else if (strcmp(type, "odd") == 0) + { + ret = ((year & 1) != 0); + } + else if (strcmp(type, "uspres") == 0) + { + ret = ((year % 4) == 0); + } + else if (strcmp(type, "nonpres") == 0) + { + ret = ((year % 4) != 0); + } + else + { + char buf[256]; + sprintf(buf, "yearistype: unsupported type '%s'", type); + error(buf); + ret = false; + } + return ret; + + } else { +#ifdef _MSC_VER + /* dead code, see getopt() call, but just to be sure */ + fprintf(stderr, "Shell scripts are not supported on Windows\n"); +#else + char *buf; + char *b; + + buf = emalloc(1 + 4 * strlen(yitcommand) + 2 + + INT_STRLEN_MAXIMUM(zic_t) + 2 + 4 * strlen(type) + 2); + b = shellquote(buf, yitcommand); + *b++ = ' '; + b += sprintf(b, "%"PRIdZIC, year); + *b++ = ' '; + b = shellquote(b, type); + *b = '\0'; + int result = system(buf); + if (WIFEXITED(result)) { + int status = WEXITSTATUS(result); + if (status <= 1) { + free(buf); + return status == 0; + } + } + error(_("Wild result from command execution")); + fprintf(stderr, _("%s: command was '%s', result was %d\n"), + progname, buf, result); +#endif + exit(EXIT_FAILURE); + } } /* Is A a space character in the C locale? */ @@ -3121,6 +3212,18 @@ charcnt += i; } +static char *findFirstSlash(char *path) +{ + for (; *path != '\0'; path++) + { + if (isSlash(*path)) + { + return path; + } + } + return NULL; +} + /* Ensure that the directories of ARGNAME exist, by making any missing ones. If ANCESTORS, do this only for ARGNAME's ancestors; otherwise, do it for ARGNAME too. Exit with failure if there is trouble. @@ -3130,20 +3233,25 @@ { register char * name; register char * cp; + char savcp; + + INITIALIZE(savcp); cp = name = ecpyalloc(argname); /* Do not mkdir a root directory, as it must exist. */ #ifdef HAVE_DOS_FILE_NAMES if (is_alpha(name[0]) && name[1] == ':') - cp += 2; + cp += 2; #endif - while (*cp == '/') - cp++; + while (isSlash(*cp)) + cp++; - while (cp && ((cp = strchr(cp, '/')) || !ancestors)) { - if (cp) - *cp = '\0'; + while (cp && ((cp = findFirstSlash(cp)) || !ancestors)) { + if (cp) { + savcp = *cp; + *cp = '\0'; + } /* ** Try to create it. It's OK if creation fails because ** the directory already exists, perhaps because some @@ -3153,17 +3261,18 @@ */ if (mkdir(name, MKDIR_UMASK) != 0) { /* For speed, skip itsdir if errno == EEXIST. Since - mkdirs is called only after open fails with ENOENT - on a subfile, EEXIST implies itsdir here. */ + mkdirs is called only after open fails with ENOENT + on a subfile, EEXIST implies itsdir here. */ int err = errno; if (err != EEXIST && !itsdir(name)) { error(_("%s: Can't create directory %s: %s"), - progname, name, strerror(err)); + progname, name, strerror(err)); exit(EXIT_FAILURE); } } - if (cp) - *cp++ = '/'; + if (cp) { + *cp++ = savcp; + } } free(name); }