]> granicus.if.org Git - postgresql/commitdiff
Sync our copy of the timezone library with IANA tzcode master.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Nov 2016 02:24:34 +0000 (22:24 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 4 Nov 2016 02:24:34 +0000 (22:24 -0400)
This patch absorbs some unreleased fixes for symlink manipulation bugs
introduced in tzcode 2016g.  Ordinarily I'd wait around for a released
version, but in this case it seems like we could do with extra testing,
in particular checking whether it works in EDB's VMware build environment.
This corresponds to commit aec59156abbf8472ba201b6c7ca2592f9c10e077 in
https://github.com/eggert/tz.

Per a report from Sandeep Thakkar, building in an environment where hard
links are not supported in the timezone data installation directory failed,
because upstream code refactoring had broken the case of symlinking from an
existing symlink.  Further experimentation also showed that the symlinks
were sometimes made incorrectly, with too many or too few "../"'s in the
symlink contents.

This should get back-patched, but first let's see what the buildfarm
makes of it.  I'm not too sure about the new dependency on linkat(2).

Report: <CANFyU94_p6mqRQc2i26PFp5QAOQGB++AjGX=FO8LDpXw0GSTjw@mail.gmail.com>
Discussion: http://mm.icann.org/pipermail/tz/2016-November/024431.html

src/timezone/zic.c

index 64adb6bd465c9d834d9c4417b773009615483c18..7da95aad53cf503768ca0f6f025b5be6cb592b3e 100644 (file)
@@ -8,9 +8,11 @@
 
 #include "postgres_fe.h"
 
+#include <fcntl.h>
 #include <locale.h>
 #include <sys/stat.h>
 #include <time.h>
+#include <unistd.h>
 
 #include "pg_getopt.h"
 
@@ -35,14 +37,24 @@ typedef int64 zic_t;
 #define MKDIR_UMASK 0755
 #endif
 #endif
-#ifndef S_ISLNK
-#define S_ISLNK(m) 0
+#ifndef AT_SYMLINK_FOLLOW
+#define linkat(fromdir, from, todir, to, flag) \
+       (itssymlink(from) ? (errno = ENOTSUP, -1) : link(from, to))
 #endif
 
+/* The maximum ptrdiff_t value, for pre-C99 platforms.  */
+#ifndef PTRDIFF_MAX
+static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
+#endif
+
+/* The type and printf format for line numbers.  */
+typedef int lineno;
+#define PRIdLINENO "d"
+
 struct rule
 {
        const char *r_filename;
-       int                     r_linenum;
+       lineno          r_linenum;
        const char *r_name;
 
        zic_t           r_loyear;               /* for example, 1986 */
@@ -64,7 +76,7 @@ struct rule
        zic_t           r_stdoff;               /* offset from standard time */
        const char *r_abbrvar;          /* variable part of abbreviation */
 
-       int                     r_todo;                 /* a rule to do (used in outzone) */
+       bool            r_todo;                 /* a rule to do (used in outzone) */
        zic_t           r_temp;                 /* used in outzone */
 };
 
@@ -79,7 +91,7 @@ struct rule
 struct zone
 {
        const char *z_filename;
-       int                     z_linenum;
+       lineno          z_linenum;
 
        const char *z_name;
        zic_t           z_gmtoff;
@@ -90,7 +102,7 @@ struct zone
        zic_t           z_stdoff;
 
        struct rule *z_rules;
-       int                     z_nrules;
+       ptrdiff_t       z_nrules;
 
        struct rule z_untilrule;
        zic_t           z_untiltime;
@@ -119,20 +131,21 @@ static void inrule(char **fields, int nfields);
 static bool inzcont(char **fields, int nfields);
 static bool inzone(char **fields, int nfields);
 static bool inzsub(char **, int, bool);
-static int     itsdir(const char *name);
+static bool itsdir(char const *);
+static bool itssymlink(char const *);
 static bool is_alpha(char a);
 static char lowerit(char);
 static void mkdirs(char const *, bool);
 static void newabbr(const char *abbr);
 static zic_t oadd(zic_t t1, zic_t t2);
-static void outzone(const struct zone * zp, int ntzones);
+static void outzone(const struct zone * zp, ptrdiff_t ntzones);
 static zic_t rpytime(const struct rule * rp, zic_t wantedy);
 static void rulesub(struct rule * rp,
                const char *loyearp, const char *hiyearp,
                const char *typep, const char *monthp,
                const char *dayp, const char *timep);
 static zic_t tadd(zic_t t1, zic_t t2);
-static bool yearistype(int year, const char *type);
+static bool yearistype(zic_t year, const char *type);
 
 /* Bound on length of what %z can expand to.  */
 enum
@@ -156,7 +169,7 @@ static int  leapcnt;
 static bool leapseen;
 static zic_t leapminyear;
 static zic_t leapmaxyear;
-static int     linenum;
+static lineno linenum;
 static int     max_abbrvar_len = PERCENT_Z_LEN_BOUND;
 static int     max_format_len;
 static zic_t max_year;
@@ -165,10 +178,10 @@ static bool noise;
 static bool print_abbrevs;
 static zic_t print_cutoff;
 static const char *rfilename;
-static int     rlinenum;
+static lineno rlinenum;
 static const char *progname;
-static int     timecnt;
-static int     timecnt_alloc;
+static ptrdiff_t timecnt;
+static ptrdiff_t timecnt_alloc;
 static int     typecnt;
 
 /*
@@ -253,24 +266,24 @@ static int        typecnt;
 #define YR_ONLY                2
 
 static struct rule *rules;
-static int     nrules;                         /* number of rules */
-static int     nrules_alloc;
+static ptrdiff_t nrules;               /* number of rules */
+static ptrdiff_t nrules_alloc;
 
 static struct zone *zones;
-static int     nzones;                         /* number of zones */
-static int     nzones_alloc;
+static ptrdiff_t nzones;               /* number of zones */
+static ptrdiff_t nzones_alloc;
 
 struct link
 {
        const char *l_filename;
-       int                     l_linenum;
+       lineno          l_linenum;
        const char *l_from;
        const char *l_to;
 };
 
 static struct link *links;
-static int     nlinks;
-static int     nlinks_alloc;
+static ptrdiff_t nlinks;
+static ptrdiff_t nlinks_alloc;
 
 struct lookup
 {
@@ -417,17 +430,18 @@ ecpyalloc(char const * str)
 }
 
 static void *
-growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
+growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t * nitems_alloc)
 {
        if (nitems < *nitems_alloc)
                return ptr;
        else
        {
-               int                     nitems_max = INT_MAX - WORK_AROUND_QTBUG_53071;
+               ptrdiff_t       nitems_max = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
+               ptrdiff_t       amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX;
 
-               if ((nitems_max - 1) / 3 * 2 < *nitems_alloc)
-                       memory_exhausted(_("int overflow"));
-               *nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1;
+               if ((amax - 1) / 3 * 2 < *nitems_alloc)
+                       memory_exhausted(_("integer overflow"));
+               *nitems_alloc += (*nitems_alloc >> 1) + 1;
                return erealloc(ptr, size_product(*nitems_alloc, itemsize));
        }
 }
@@ -437,7 +451,7 @@ growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
  */
 
 static void
-eats(const char *name, int num, const char *rname, int rnum)
+eats(char const * name, lineno num, char const * rname, lineno rnum)
 {
        filename = name;
        linenum = num;
@@ -446,7 +460,7 @@ eats(const char *name, int num, const char *rname, int rnum)
 }
 
 static void
-eat(const char *name, int num)
+eat(char const * name, lineno num)
 {
        eats(name, num, NULL, -1);
 }
@@ -459,10 +473,10 @@ verror(const char *string, va_list args)
         * "*" -v on BSD systems.
         */
        if (filename)
-               fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
+               fprintf(stderr, _("\"%s\", line %" PRIdLINENO ": "), filename, linenum);
        vfprintf(stderr, string, args);
        if (rfilename != NULL)
-               fprintf(stderr, _(" (rule from \"%s\", line %d)"),
+               fprintf(stderr, _(" (rule from \"%s\", line %" PRIdLINENO ")"),
                                rfilename, rlinenum);
        fprintf(stderr, "\n");
 }
@@ -553,9 +567,10 @@ static const char *yitcommand;
 int
 main(int argc, char *argv[])
 {
-       int                     i;
-       int                     j;
-       int                     c;
+       int                     c,
+                               k;
+       ptrdiff_t       i,
+                               j;
 
 #ifndef WIN32
        umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
@@ -567,14 +582,14 @@ main(int argc, char *argv[])
                                _("wild compilation-time specification of zic_t"));
                return EXIT_FAILURE;
        }
-       for (i = 1; i < argc; ++i)
-               if (strcmp(argv[i], "--version") == 0)
+       for (k = 1; k < argc; k++)
+               if (strcmp(argv[k], "--version") == 0)
                {
                        printf("zic %s\n", PG_VERSION);
                        close_file(stdout, NULL, NULL);
                        return EXIT_SUCCESS;
                }
-               else if (strcmp(argv[i], "--help") == 0)
+               else if (strcmp(argv[k], "--help") == 0)
                {
                        usage(stdout, EXIT_SUCCESS);
                }
@@ -662,8 +677,8 @@ main(int argc, char *argv[])
                adjleap();
        }
 
-       for (i = optind; i < argc; ++i)
-               infile(argv[i]);
+       for (k = optind; k < argc; k++)
+               infile(argv[k]);
        if (errors)
                return EXIT_FAILURE;
        associate();
@@ -713,7 +728,7 @@ componentcheck(char const * name, char const * component,
        enum
        {
        component_len_max = 14};
-       size_t          component_len = component_end - component;
+       ptrdiff_t       component_len = component_end - component;
 
        if (component_len == 0)
        {
@@ -731,8 +746,10 @@ componentcheck(char const * name, char const * component,
        if (0 < component_len && component_len <= 2
                && component[0] == '.' && component_end[-1] == '.')
        {
+               int                     len = component_len;
+
                error(_("file name '%s' contains '%.*s' component"),
-                         name, (int) component_len, component);
+                         name, len, component);
                return false;
        }
        if (noise)
@@ -823,9 +840,9 @@ relname(char const * from, char const * to)
        for (i = 0; f[i] && f[i] == to[i]; i++)
                if (f[i] == '/')
                        dir_len = i + 1;
-       for (; f[i]; i++)
-               dotdots += f[i] == '/' && f[i - 1] != '/';
-       taillen = i - dir_len;
+       for (; to[i]; i++)
+               dotdots += to[i] == '/' && to[i - 1] != '/';
+       taillen = strlen(f + dir_len);
        dotdotetcsize = 3 * dotdots + taillen + 1;
        if (dotdotetcsize <= linksize)
        {
@@ -839,10 +856,19 @@ relname(char const * from, char const * to)
 }
 #endif   /* HAVE_SYMLINK */
 
+/* Hard link FROM to TO, following any symbolic links.
+   Return 0 if successful, an error number otherwise.  */
+static int
+hardlinkerr(char const * from, char const * to)
+{
+       int                     r = linkat(AT_FDCWD, from, AT_FDCWD, to, AT_SYMLINK_FOLLOW);
+
+       return r == 0 ? 0 : errno;
+}
+
 static void
 dolink(char const * fromfield, char const * tofield, bool staysymlink)
 {
-       int                     fromisdir;
        bool            todirs_made = false;
        int                     link_errno;
 
@@ -850,17 +876,14 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink)
         * We get to be careful here since there's a fair chance of root running
         * us.
         */
-       fromisdir = itsdir(fromfield);
-       if (fromisdir)
+       if (itsdir(fromfield))
        {
-               char const *e = strerror(fromisdir < 0 ? errno : EPERM);
-
                fprintf(stderr, _("%s: link from %s/%s failed: %s\n"),
-                               progname, directory, fromfield, e);
+                               progname, directory, fromfield, strerror(EPERM));
                exit(EXIT_FAILURE);
        }
        if (staysymlink)
-               staysymlink = itsdir(tofield) == 2;
+               staysymlink = itssymlink(tofield);
        if (remove(tofield) == 0)
                todirs_made = true;
        else if (errno != ENOENT)
@@ -871,13 +894,12 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink)
                                progname, directory, tofield, e);
                exit(EXIT_FAILURE);
        }
-       link_errno = (staysymlink ? ENOTSUP
-                                 : link(fromfield, tofield) == 0 ? 0 : errno);
+       link_errno = staysymlink ? ENOTSUP : hardlinkerr(fromfield, tofield);
        if (link_errno == ENOENT && !todirs_made)
        {
                mkdirs(tofield, true);
                todirs_made = true;
-               link_errno = link(fromfield, tofield) == 0 ? 0 : errno;
+               link_errno = hardlinkerr(fromfield, tofield);
        }
        if (link_errno != 0)
        {
@@ -987,31 +1009,42 @@ static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332
                                                                 ? BIG_BANG
                                                                 : MINVAL(zic_t, TIME_T_BITS_IN_FILE));
 
-/* Return 1 if NAME is a directory, 2 if a symbolic link, 0 if
-   something else, -1 (setting errno) if trouble.  */
-static int
+/* Return true if NAME is a directory.  */
+static bool
 itsdir(char const * name)
 {
        struct stat st;
-       int                     res = lstat(name, &st);
-
+       int                     res = stat(name, &st);
+#ifdef S_ISDIR
        if (res == 0)
+               return S_ISDIR(st.st_mode) != 0;
+#endif
+       if (res == 0 || errno == EOVERFLOW)
        {
-#ifdef S_ISDIR
-               return S_ISDIR(st.st_mode) ? 1 : S_ISLNK(st.st_mode) ? 2 : 0;
-#else
                size_t          n = strlen(name);
                char       *nameslashdot = emalloc(n + 3);
                bool            dir;
 
                memcpy(nameslashdot, name, n);
                strcpy(&nameslashdot[n], &"/."[!(n && name[n - 1] != '/')]);
-               dir = lstat(nameslashdot, &st) == 0;
+               dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW;
                free(nameslashdot);
                return dir;
-#endif
        }
-       return -1;
+       return false;
+}
+
+/* Return true if NAME is a symbolic link.  */
+static bool
+itssymlink(char const * name)
+{
+#ifdef HAVE_SYMLINK
+       char            c;
+
+       return 0 <= readlink(name, &c, 1);
+#else
+       return false;
+#endif
 }
 
 /*
@@ -1034,10 +1067,10 @@ associate(void)
 {
        struct zone *zp;
        struct rule *rp;
-       int                     base,
+       ptrdiff_t       i,
+                               j,
+                               base,
                                out;
-       int                     i,
-                               j;
 
        if (nrules != 0)
        {
@@ -1124,7 +1157,7 @@ infile(const char *name)
        const struct lookup *lp;
        int                     nfields;
        bool            wantcont;
-       int                     num;
+       lineno          num;
        char            buf[BUFSIZ];
 
        if (strcmp(name, "-") == 0)
@@ -1175,7 +1208,7 @@ infile(const char *name)
                        if (lp == NULL)
                                error(_("input line of unknown type"));
                        else
-                               switch ((int) (lp->l_value))
+                               switch (lp->l_value)
                                {
                                        case LC_RULE:
                                                inrule(fields, nfields);
@@ -1302,7 +1335,7 @@ inrule(char **fields, int nfields)
 static bool
 inzone(char **fields, int nfields)
 {
-       int                     i;
+       ptrdiff_t       i;
 
        if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS)
        {
@@ -1327,8 +1360,8 @@ inzone(char **fields, int nfields)
                if (zones[i].z_name != NULL &&
                        strcmp(zones[i].z_name, fields[ZF_NAME]) == 0)
                {
-                       error(
-                                 _("duplicate zone name %s (file \"%s\", line %d)"),
+                       error(_("duplicate zone name %s"
+                                       " (file \"%s\", line %" PRIdLINENO ")"),
                                  fields[ZF_NAME],
                                  zones[i].z_filename,
                                  zones[i].z_linenum);
@@ -1452,7 +1485,7 @@ inleap(char **fields, int nfields)
 {
        const char *cp;
        const struct lookup *lp;
-       int                     i,
+       zic_t           i,
                                j;
 
        /* PG: make year be int not zic_t to avoid sscanf portability issues */
@@ -1659,7 +1692,7 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
        lp = byword(cp, begin_years);
        rp->r_lowasnum = lp == NULL;
        if (!rp->r_lowasnum)
-               switch ((int) lp->l_value)
+               switch (lp->l_value)
                {
                        case YR_MINIMUM:
                                rp->r_loyear = ZIC_MIN;
@@ -1684,7 +1717,7 @@ rulesub(struct rule * rp, const char *loyearp, const char *hiyearp,
        lp = byword(cp, end_years);
        rp->r_hiwasnum = lp == NULL;
        if (!rp->r_hiwasnum)
-               switch ((int) lp->l_value)
+               switch (lp->l_value)
                {
                        case YR_MINIMUM:
                                rp->r_hiyear = ZIC_MIN;
@@ -1834,11 +1867,11 @@ static void
 writezone(const char *const name, const char *const string, char version)
 {
        FILE       *fp;
-       int                     i,
+       ptrdiff_t       i,
                                j;
        int                     leapcnt32,
                                leapi32;
-       int                     timecnt32,
+       ptrdiff_t       timecnt32,
                                timei32;
        int                     pass;
        static const struct tzhead tzh0;
@@ -1846,7 +1879,7 @@ writezone(const char *const name, const char *const string, char version)
        bool            dir_checked = false;
        zic_t           one = 1;
        zic_t           y2038_boundary = one << 31;
-       int                     nats = timecnt + WORK_AROUND_QTBUG_53071;
+       ptrdiff_t       nats = timecnt + WORK_AROUND_QTBUG_53071;
        zic_t      *ats = emalloc(size_product(nats, sizeof *ats + 1));
        void       *typesptr = ats + nats;
        unsigned char *types = typesptr;
@@ -1861,8 +1894,8 @@ writezone(const char *const name, const char *const string, char version)
         * Optimize.
         */
        {
-               int                     fromi;
-               int                     toi;
+               ptrdiff_t       fromi,
+                                       toi;
 
                toi = 0;
                fromi = 0;
@@ -1886,9 +1919,17 @@ writezone(const char *const name, const char *const string, char version)
                }
                timecnt = toi;
        }
+
        if (noise && timecnt > 1200)
-               warning(_("pre-2014 clients may mishandle"
-                                 " more than 1200 transition times"));
+       {
+               if (timecnt > TZ_MAX_TIMES)
+                       warning(_("reference clients mishandle"
+                                         " more than %d transition times"),
+                                       TZ_MAX_TIMES);
+               else
+                       warning(_("pre-2014 clients may mishandle"
+                                         " more than 1200 transition times"));
+       }
 
        /*
         * Transfer.
@@ -1991,23 +2032,25 @@ writezone(const char *const name, const char *const string, char version)
        }
        for (pass = 1; pass <= 2; ++pass)
        {
-               int                     thistimei,
-                                       thistimecnt;
+               ptrdiff_t       thistimei,
+                                       thistimecnt,
+                                       thistimelim;
                int                     thisleapi,
-                                       thisleapcnt;
-               int                     thistimelim,
+                                       thisleapcnt,
                                        thisleaplim;
                int                     writetype[TZ_MAX_TYPES];
                int                     typemap[TZ_MAX_TYPES];
                int                     thistypecnt;
                char            thischars[TZ_MAX_CHARS];
-               char            thischarcnt;
+               int                     thischarcnt;
+               bool            toomanytimes;
                int                     indmap[TZ_MAX_CHARS];
 
                if (pass == 1)
                {
                        thistimei = timei32;
                        thistimecnt = timecnt32;
+                       toomanytimes = thistimecnt >> 31 >> 1 != 0;
                        thisleapi = leapi32;
                        thisleapcnt = leapcnt32;
                }
@@ -2015,9 +2058,12 @@ writezone(const char *const name, const char *const string, char version)
                {
                        thistimei = 0;
                        thistimecnt = timecnt;
+                       toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
                        thisleapi = 0;
                        thisleapcnt = leapcnt;
                }
+               if (toomanytimes)
+                       error(_("too many transition times"));
                thistimelim = thistimei + thistimecnt;
                thisleaplim = thisleapi + thisleapcnt;
                for (i = 0; i < typecnt; ++i)
@@ -2118,8 +2164,7 @@ writezone(const char *const name, const char *const string, char version)
                                        break;
                        if (j == thischarcnt)
                        {
-                               strcpy(&thischars[(int) thischarcnt],
-                                          thisabbr);
+                               strcpy(&thischars[thischarcnt], thisabbr);
                                thischarcnt += strlen(thisabbr) + 1;
                        }
                        indmap[abbrinds[i]] = j;
@@ -2466,13 +2511,13 @@ enum
 YEAR_BY_YEAR_ZONE = 1};
 
 static int
-stringzone(char *result, const struct zone * const zpfirst, const int zonecount)
+stringzone(char *result, struct zone const * zpfirst, ptrdiff_t zonecount)
 {
        const struct zone *zp;
        struct rule *rp;
        struct rule *stdrp;
        struct rule *dstrp;
-       int                     i;
+       ptrdiff_t       i;
        const char *abbrvar;
        int                     compat = 0;
        int                     c;
@@ -2601,11 +2646,11 @@ stringzone(char *result, const struct zone * const zpfirst, const int zonecount)
 }
 
 static void
-outzone(const struct zone * zpfirst, int zonecount)
+outzone(const struct zone * zpfirst, ptrdiff_t zonecount)
 {
        const struct zone *zp;
        struct rule *rp;
-       int                     i,
+       ptrdiff_t       i,
                                j;
        bool            usestart,
                                useuntil;
@@ -2627,7 +2672,7 @@ outzone(const struct zone * zpfirst, int zonecount)
        int                     compat;
        bool            do_extend;
        char            version;
-       int                     lastatmax = -1;
+       ptrdiff_t       lastatmax = -1;
 
        max_abbr_len = 2 + max_format_len + max_abbrvar_len;
        max_envvar_len = 2 * max_abbr_len + 5 * 9;
@@ -2796,7 +2841,7 @@ outzone(const struct zone * zpfirst, int zonecount)
                                }
                                for (;;)
                                {
-                                       int                     k;
+                                       ptrdiff_t       k;
                                        zic_t           jtime,
                                                                ktime = 0;
                                        zic_t           offset;
@@ -3091,30 +3136,52 @@ adjleap(void)
        }
 }
 
+static char *
+shellquote(char *b, char const * s)
+{
+       *b++ = '\'';
+       while (*s)
+       {
+               if (*s == '\'')
+                       *b++ = '\'', *b++ = '\\', *b++ = '\'';
+               *b++ = *s++;
+       }
+       *b++ = '\'';
+       return b;
+}
+
 static bool
-yearistype(int year, const char *type)
+yearistype(zic_t year, const char *type)
 {
-       static char *buf;
+       char       *buf;
+       char       *b;
        int                     result;
 
        if (type == NULL || *type == '\0')
                return true;
-       buf = erealloc(buf, 132 + strlen(yitcommand) + strlen(type));
-       sprintf(buf, "%s %d %s", yitcommand, year, type);
+       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, INT64_FORMAT, year);
+       *b++ = ' ';
+       b = shellquote(b, type);
+       *b = '\0';
        result = system(buf);
        if (WIFEXITED(result))
-               switch (WEXITSTATUS(result))
+       {
+               int                     status = WEXITSTATUS(result);
+
+               if (status <= 1)
                {
-                       case 0:
-                               return true;
-                       case 1:
-                               return false;
+                       free(buf);
+                       return status == 0;
                }
+       }
        error(_("Wild result from command execution"));
        fprintf(stderr, _("%s: command was '%s', result was %d\n"),
                        progname, buf, result);
-       for (;;)
-               exit(EXIT_FAILURE);
+       exit(EXIT_FAILURE);
 }
 
 /* Is A a space character in the C locale?     */
@@ -3348,7 +3415,7 @@ getfields(char *cp)
                                        else
                                        {
                                                error(_("Odd number of quotation marks"));
-                                               exit(1);
+                                               exit(EXIT_FAILURE);
                                        }
                } while (*cp && *cp != '#' && !is_space(*cp));
                if (is_space(*cp))
@@ -3537,7 +3604,8 @@ newabbr(const char *string)
 
 /* 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.  */
+   do it for ARGNAME too.  Exit with failure if there is trouble.
+   Do not consider an existing non-directory to be trouble.  */
 static void
 mkdirs(char const * argname, bool ancestors)
 {
@@ -3569,7 +3637,7 @@ mkdirs(char const * argname, bool ancestors)
                {
                        int                     err = errno;
 
-                       if (err != EEXIST && itsdir(name) < 0)
+                       if (err != EEXIST && !itsdir(name))
                        {
                                error(_("%s: Cannot create directory %s: %s"),
                                          progname, name, strerror(err));