commit 39fd078a694fa762de5ae7efceca1dbfb7be94b3 Author: Paul Eggert Date: Fri Feb 26 03:33:54 2016 -0800 Port better to MS-Windows Problems reported by Ian Abbott in: http://mm.icann.org/pipermail/tz/2016-February/023286.html http://mm.icann.org/pipermail/tz/2016-February/023287.html * Makefile (CFLAGS): Add comment about new -D options. * date.c (environ, optarg, optind, tzname): * private.h (asctime_r, timezone, daylight, altzone): * strftime.c (tzname): * zdump.c (environ, getopt, optarg, optind, tzname): * zic.c (getopt, link, optarg, optind): Do not declare if HAVE_POSIX_DECLS, to avoid collisions with system declarations, which is a problem with MS-Windows and tzname and the dllimport attribute. * date.c, zdump.c (tzname): Do not specify size, as POSIX doesn’t. * private.h (HAVE_POSIX_DECLS): Default to 1. (ENOTSUP): Default to EINVAL. * zic.c: If HAVE_DIRECT_H, include direct.h and io.h and define a mkdir macro, for MS-Windows. (link, symlink): Set errno to ENOTSUP in the substitutes. (dolink): Don’t complain merely because link and/or symlink is not supported. Be a bit more economical and robust about checking for directories and existing destinations. Report errno-related string on link failures. (itsdir): Work correctly even if a directory has a timestamp that is out of time_t range, so that stat fails with errno == EOVERFLOW. (writezone): Don’t remove files we can’t stat. diff --git a/Makefile b/Makefile index 568f7f6..20c2c98 100644 --- a/Makefile +++ b/Makefile @@ -106,6 +106,7 @@ LDLIBS= # Add the following to the end of the "CFLAGS=" line as needed. # -DBIG_BANG=-9999999LL if the Big Bang occurred at time -9999999 (see zic.c) +# -DHAVE_DIRECT_H if mkdir needs (MS-Windows) # -DHAVE_DOS_FILE_NAMES if file names have drive specifiers etc. (MS-DOS) # -DHAVE_GETTEXT=1 if 'gettext' works (GNU, Linux, Solaris); also see LDLIBS # -DHAVE_INCOMPATIBLE_CTIME_R=1 if your system's time.h declares @@ -116,6 +117,8 @@ LDLIBS= # -DHAVE_LOCALTIME_RZ=0 if you do not want zdump to use localtime_rz # This defaults to 1 if a working localtime_rz seems to be available. # localtime_rz can make zdump significantly faster, but is nonstandard. +# -DHAVE_POSIX_DECLS=0 if your system's include files do not declare +# functions like 'link' or variables like 'tzname' required by POSIX # -DHAVE_STDINT_H=1 if you have a pre-C99 compiler with "stdint.h" # -DHAVE_STRFTIME_L=1 if declares locale_t and strftime_l # This defaults to 0 if _POSIX_VERSION < 200809, 1 otherwise. diff --git a/date.c b/date.c index 824e57d..4c11f61 100644 --- a/date.c +++ b/date.c @@ -42,10 +42,12 @@ #define SECSPERMIN 60 #endif /* !defined SECSPERMIN */ +#if !HAVE_POSIX_DECLS extern char ** environ; extern char * optarg; extern int optind; -extern char * tzname[2]; +extern char * tzname[]; +#endif static int retval = EXIT_SUCCESS; diff --git a/private.h b/private.h index 1c176e6..6080e71 100644 --- a/private.h +++ b/private.h @@ -34,6 +34,10 @@ #define HAVE_LINK 1 #endif /* !defined HAVE_LINK */ +#ifndef HAVE_POSIX_DECLS +#define HAVE_POSIX_DECLS 1 +#endif + #ifndef HAVE_STRDUP #define HAVE_STRDUP 1 #endif @@ -106,6 +110,9 @@ #ifndef ENAMETOOLONG # define ENAMETOOLONG EINVAL #endif +#ifndef ENOTSUP +# define ENOTSUP EINVAL +#endif #ifndef EOVERFLOW # define EOVERFLOW EINVAL #endif @@ -379,6 +386,8 @@ time_t time(time_t *); void tzset(void); #endif +#if !HAVE_POSIX_DECLS + /* ** Some time.h implementations don't declare asctime_r. ** Others might define it as a macro. @@ -402,6 +411,8 @@ extern int daylight; extern long altzone; #endif +#endif + /* ** The STD_INSPIRED functions are similar, but most also need ** declarations if time_tz is defined. diff --git a/strftime.c b/strftime.c index 7a139bd..f75f9fd 100644 --- a/strftime.c +++ b/strftime.c @@ -106,7 +106,9 @@ static char * _fmt(const char *, const struct tm *, char *, const char *, int *); static char * _yconv(int, int, bool, bool, char *, char const *); +#if !HAVE_POSIX_DECLS extern char * tzname[]; +#endif #ifndef YEAR_2000_NAME #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS" diff --git a/zdump.c b/zdump.c index 063a263..64d90f6 100644 --- a/zdump.c +++ b/zdump.c @@ -238,12 +238,14 @@ enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; # define timezone_t char ** #endif +#if !HAVE_POSIX_DECLS extern char ** environ; extern int getopt(int argc, char * const argv[], const char * options); extern char * optarg; extern int optind; -extern char * tzname[2]; +extern char * tzname[]; +#endif /* The minimum and maximum finite time values. */ enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 }; diff --git a/zic.c b/zic.c index 78ab870..0ec3359 100644 --- a/zic.c +++ b/zic.c @@ -22,6 +22,13 @@ typedef int_fast64_t zic_t; #define ZIC_MAX_ABBR_LEN_WO_WARN 6 #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ +#ifdef HAVE_DIRECT_H +# include +# include +# undef mkdir +# define mkdir(name, mode) _mkdir(name) +#endif + #if HAVE_SYS_STAT_H #include #endif @@ -87,17 +94,19 @@ struct zone { zic_t z_untiltime; }; +#if !HAVE_POSIX_DECLS extern int getopt(int argc, char * const argv[], const char * options); extern int link(const char * fromname, const char * toname); extern char * optarg; extern int optind; +#endif #if ! HAVE_LINK -# define link(from, to) (-1) +# define link(from, to) (errno = ENOTSUP, -1) #endif #if ! HAVE_SYMLINK -# define symlink(from, to) (-1) +# define symlink(from, to) (errno = ENOTSUP, -1) #endif static void addtt(zic_t starttime, int type); @@ -758,41 +767,47 @@ dolink(char const *fromfield, char const *tofield) progname, fromname, e); exit(EXIT_FAILURE); } - if (itsdir(toname) <= 0) - remove(toname); if (link(fromname, toname) != 0) { - int result; + int link_errno = errno; + bool retry_if_link_supported = false; - if (! mkdirs(toname)) - exit(EXIT_FAILURE); - - result = link(fromname, toname); - if (result != 0) { - const char *s = fromfield; - const char *t; - char *p; - size_t dotdots = 0; - register char * symlinkcontents = NULL; - - do - t = s; - while ((s = strchr(s, '/')) - && ! strncmp (fromfield, tofield, - ++s - fromfield)); - - for (s = tofield + (t - fromfield); *s; s++) - dotdots += *s == '/'; - symlinkcontents - = emalloc(3 * dotdots + strlen(t) + 1); - for (p = symlinkcontents; dotdots-- != 0; p += 3) - memcpy(p, "../", 3); - strcpy(p, t); - result = symlink(symlinkcontents, toname); - if (result == 0) -warning(_("hard link failed, symbolic link used")); - free(symlinkcontents); - } - if (result != 0) { + if (link_errno == ENOENT || link_errno == ENOTSUP) { + if (! mkdirs(toname)) + exit(EXIT_FAILURE); + retry_if_link_supported = true; + } + if ((link_errno == EEXIST || link_errno == ENOTSUP) + && itsdir(toname) == 0 + && (remove(toname) == 0 || errno == ENOENT)) + retry_if_link_supported = true; + if (retry_if_link_supported && link_errno != ENOTSUP) + link_errno = link(fromname, toname) == 0 ? 0 : errno; + if (link_errno != 0) { + const char *s = fromfield; + const char *t; + char *p; + size_t dotdots = 0; + char *symlinkcontents; + int symlink_result; + + do + t = s; + while ((s = strchr(s, '/')) + && strncmp(fromfield, tofield, ++s - fromfield) == 0); + + for (s = tofield + (t - fromfield); *s; s++) + dotdots += *s == '/'; + symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1); + for (p = symlinkcontents; dotdots-- != 0; p += 3) + memcpy(p, "../", 3); + strcpy(p, t); + symlink_result = symlink(symlinkcontents, toname); + free(symlinkcontents); + if (symlink_result == 0) { + if (link_errno != ENOTSUP) + warning(_("symbolic link used because hard link failed: %s"), + strerror (link_errno)); + } else { FILE *fp, *tp; int c; fp = fopen(fromname, "rb"); @@ -815,8 +830,11 @@ warning(_("hard link failed, symbolic link used")); putc(c, tp); close_file(fp, fromname); close_file(tp, toname); - warning(_("link failed, copy used")); - } + if (link_errno != ENOTSUP) + warning(_("copy used because hard link failed: %s"), + strerror (link_errno)); + } + } } free(fromname); free(toname); @@ -863,18 +881,17 @@ itsdir(char const *name) { struct stat st; int res = stat(name, &st); - if (res != 0) - return res; #ifdef S_ISDIR - return S_ISDIR(st.st_mode) != 0; -#else - { + if (res == 0) + return S_ISDIR(st.st_mode) != 0; +#endif + if (res == 0 || errno == EOVERFLOW) { char *nameslashdot = relname(name, "."); - res = stat(nameslashdot, &st); + bool dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW; free(nameslashdot); - return res == 0; + return dir; } -#endif + return -1; } /* @@ -1685,7 +1702,7 @@ writezone(const char *const name, const char *const string, char version) /* ** Remove old file, if any, to snap links. */ - if (itsdir(fullname) <= 0 && remove(fullname) != 0 && errno != ENOENT) { + if (itsdir(fullname) == 0 && remove(fullname) != 0 && errno != ENOENT) { const char *e = strerror(errno); fprintf(stderr, _("%s: Can't remove %s: %s\n"),