[tz] [PROPOSED PATCH 5/5] zic now uses chdir to avoid long file names
Paul Eggert
eggert at cs.ucla.edu
Tue Sep 6 04:40:37 UTC 2016
The main reason for this is to simplify debugging.
It also improves performance slightly, I suppose.
* NEWS: Document this.
* zic.c (close_file): New arg DIR. All callers changed.
(change_directory): New function.
(relname): Remove.
(dolink, writezone): Simplify by assuming chdir.
(itsdir) [!S_ISDIR]: Append "." by hand.
(mkdirs): First arg is now a const pointer, since the storage is
not modified. New arg ANCESTORS. Return void, not bool. All
callers changed.
---
NEWS | 5 +-
zic.c | 167 +++++++++++++++++++++++++++++++++---------------------------------
2 files changed, 87 insertions(+), 85 deletions(-)
diff --git a/NEWS b/NEWS
index 7f92e17..d7ba940 100644
--- a/NEWS
+++ b/NEWS
@@ -60,8 +60,9 @@ Unreleased, experimental changes
compatibility with platforms like OpenSUSE where other programs
configure these files as symlinks.
- zic now avoids hard linking to symbolic links, and avoids some
- unnecessary mkdir and stat system calls.
+ zic now avoids hard linking to symbolic links, avoids some
+ unnecessary mkdir and stat system calls, and uses shorter file
+ names internally.
zdump has a new -i option to generate transitions in a
more-compact but still human-readable format. This option is
diff --git a/zic.c b/zic.c
index 011b3cc..2505c11 100644
--- a/zic.c
+++ b/zic.c
@@ -130,7 +130,7 @@ static bool inzsub(char **, int, bool);
static int itsdir(const char * name);
static bool is_alpha(char a);
static char lowerit(char);
-static bool mkdirs(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);
@@ -498,15 +498,15 @@ warning(const char *const string, ...)
}
static void
-close_file(FILE *stream, char const *name)
+close_file(FILE *stream, char const *dir, char const *name)
{
char const *e = (ferror(stream) ? _("I/O error")
: fclose(stream) != 0 ? strerror(errno) : NULL);
if (e) {
- fprintf(stderr, "%s: ", progname);
- if (name)
- fprintf(stderr, "%s: ", name);
- fprintf(stderr, "%s\n", e);
+ fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
+ dir ? dir : "", dir ? "/" : "",
+ name ? name : "", name ? ": " : "",
+ e);
exit(EXIT_FAILURE);
}
}
@@ -521,10 +521,30 @@ usage(FILE *stream, int status)
"Report bugs to %s.\n"),
progname, progname, REPORT_BUGS_TO);
if (status == EXIT_SUCCESS)
- close_file(stream, NULL);
+ close_file(stream, NULL, NULL);
exit(status);
}
+/* Change the working directory to DIR, possibly creating DIR and its
+ ancestors. After this is done, all files are accessed with names
+ relative to DIR. */
+static void
+change_directory (char const *dir)
+{
+ if (chdir(dir) != 0) {
+ int chdir_errno = errno;
+ if (chdir_errno == ENOENT) {
+ mkdirs(dir, false);
+ chdir_errno = chdir(dir) == 0 ? 0 : errno;
+ }
+ if (chdir_errno != 0) {
+ fprintf(stderr, _("%s: Can't chdir to %s: %s\n"),
+ progname, dir, strerror(chdir_errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
static const char * psxrules;
static const char * lcltime;
static const char * directory;
@@ -557,7 +577,7 @@ main(int argc, char **argv)
for (i = 1; i < argc; ++i)
if (strcmp(argv[i], "--version") == 0) {
printf("zic %s%s\n", PKGVERSION, TZVERSION);
- close_file(stdout, NULL);
+ close_file(stdout, NULL, NULL);
return EXIT_SUCCESS;
} else if (strcmp(argv[i], "--help") == 0) {
usage(stdout, EXIT_SUCCESS);
@@ -640,6 +660,7 @@ _("%s: More than one -L option specified\n"),
if (errors)
return EXIT_FAILURE;
associate();
+ change_directory(directory);
for (i = 0; i < nzones; i = j) {
/*
** Find the next non-continuation zone entry.
@@ -743,58 +764,40 @@ namecheck(const char *name)
return componentcheck(name, component, cp);
}
-static char *
-relname(char const *dir, char const *base)
-{
- if (*base == '/')
- return ecpyalloc(base);
- else {
- size_t dir_len = strlen(dir);
- bool needs_slash = dir_len && dir[dir_len - 1] != '/';
- char *result = emalloc(dir_len + needs_slash + strlen(base) + 1);
- result[dir_len] = '/';
- strcpy(result + dir_len + needs_slash, base);
- return memcpy(result, dir, dir_len);
- }
-}
-
static void
dolink(char const *fromfield, char const *tofield, bool staysymlink)
{
- register char * fromname;
- register char * toname;
register int fromisdir;
- int todirs_made = -1;
+ bool todirs_made = false;
int link_errno;
- fromname = relname(directory, fromfield);
- toname = relname(directory, tofield);
/*
** We get to be careful here since
** there's a fair chance of root running us.
*/
- fromisdir = itsdir(fromname);
+ fromisdir = itsdir(fromfield);
if (fromisdir) {
char const *e = strerror(fromisdir < 0 ? errno : EPERM);
- fprintf(stderr, _("%s: link from %s failed: %s\n"),
- progname, fromname, e);
+ fprintf(stderr, _("%s: link from %s/%s failed: %s\n"),
+ progname, directory, fromfield, e);
exit(EXIT_FAILURE);
}
if (staysymlink)
- staysymlink = itsdir(toname) == 2;
- if (remove(toname) == 0)
- todirs_made = 0;
+ staysymlink = itsdir(tofield) == 2;
+ if (remove(tofield) == 0)
+ todirs_made = true;
else if (errno != ENOENT) {
char const *e = strerror(errno);
- fprintf(stderr, _("%s: Can't remove %s: %s\n"), progname, toname, e);
+ fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
+ progname, directory, tofield, e);
exit(EXIT_FAILURE);
}
link_errno = (staysymlink ? ENOTSUP
- : link(fromname, toname) == 0 ? 0 : errno);
- if (link_errno == ENOENT && todirs_made < 0) {
- todirs_made = mkdirs(toname);
- if (todirs_made)
- link_errno = link(fromname, toname) == 0 ? 0 : errno;
+ : link(fromfield, tofield) == 0 ? 0 : errno);
+ if (link_errno == ENOENT && !todirs_made) {
+ mkdirs(tofield, true);
+ todirs_made = true;
+ link_errno = link(fromfield, tofield) == 0 ? 0 : errno;
}
if (link_errno != 0) {
const char *s = fromfield;
@@ -815,9 +818,11 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink)
for (p = symlinkcontents; dotdots-- != 0; p += 3)
memcpy(p, "../", 3);
strcpy(p, t);
- symlink_errno = symlink(symlinkcontents, toname) == 0 ? 0 : errno;
- if (symlink_errno == ENOENT && todirs_made < 0 && mkdirs(toname))
- symlink_errno = symlink(symlinkcontents, toname) == 0 ? 0 : errno;
+ symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
+ if (symlink_errno == ENOENT && !todirs_made) {
+ mkdirs(tofield, true);
+ symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
+ }
free(symlinkcontents);
if (symlink_errno == 0) {
if (link_errno != ENOTSUP)
@@ -826,24 +831,24 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink)
} else {
FILE *fp, *tp;
int c;
- fp = fopen(fromname, "rb");
+ fp = fopen(fromfield, "rb");
if (!fp) {
char const *e = strerror(errno);
- fprintf(stderr, _("%s: Can't read %s: %s\n"),
- progname, fromname, e);
+ fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
+ progname, directory, fromfield, e);
exit(EXIT_FAILURE);
}
- tp = fopen(toname, "wb");
+ tp = fopen(tofield, "wb");
if (!tp) {
char const *e = strerror(errno);
- fprintf(stderr, _("%s: Can't create %s: %s\n"),
- progname, toname, e);
+ fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
+ progname, directory, tofield, e);
exit(EXIT_FAILURE);
}
while ((c = getc(fp)) != EOF)
putc(c, tp);
- close_file(fp, fromname);
- close_file(tp, toname);
+ close_file(fp, directory, fromfield);
+ close_file(tp, directory, tofield);
if (link_errno != ENOTSUP)
warning(_("copy used because hard link failed: %s"),
strerror(link_errno));
@@ -852,8 +857,6 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink)
strerror(symlink_errno));
}
}
- free(fromname);
- free(toname);
}
#define TIME_T_BITS_IN_FILE 64
@@ -910,8 +913,12 @@ itsdir(char const *name)
#ifdef S_ISDIR
return S_ISDIR(st.st_mode) ? 1 : S_ISLNK(st.st_mode) ? 2 : 0;
#else
- char *nameslashdot = relname(name, ".");
- bool dir = lstat(nameslashdot, &st) == 0;
+ 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;
free(nameslashdot);
return dir;
#endif
@@ -1088,7 +1095,7 @@ _("%s: panic: Invalid l_value %d\n"),
}
free(fields);
}
- close_file(fp, filename);
+ close_file(fp, NULL, filename);
if (wantcont)
error(_("expected continuation line not found"));
}
@@ -1638,7 +1645,6 @@ writezone(const char *const name, const char *const string, char version)
register int leapcnt32, leapi32;
register int timecnt32, timei32;
register int pass;
- char * fullname;
static const struct tzhead tzh0;
static struct tzhead tzh;
bool dir_checked = false;
@@ -1741,30 +1747,29 @@ writezone(const char *const name, const char *const string, char version)
--leapcnt32;
++leapi32;
}
- fullname = relname(directory, name);
/*
** Remove old file, if any, to snap links.
*/
- if (remove(fullname) == 0)
+ if (remove(name) == 0)
dir_checked = true;
else if (errno != ENOENT) {
const char *e = strerror(errno);
- fprintf(stderr, _("%s: Can't remove %s: %s\n"),
- progname, fullname, e);
+ fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
+ progname, directory, name, e);
exit(EXIT_FAILURE);
}
- fp = fopen(fullname, "wb");
+ fp = fopen(name, "wb");
if (!fp) {
int fopen_errno = errno;
- if (fopen_errno == ENOENT && !dir_checked && mkdirs(fullname)) {
- fp = fopen(fullname, "wb");
+ if (fopen_errno == ENOENT && !dir_checked) {
+ mkdirs(name, true);
+ fp = fopen(name, "wb");
fopen_errno = errno;
}
if (!fp) {
- char const *e = strerror(fopen_errno);
- fprintf(stderr, _("%s: Can't create %s: %s\n"),
- progname, fullname, e);
+ fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
+ progname, directory, name, strerror(fopen_errno));
exit(EXIT_FAILURE);
}
}
@@ -1959,9 +1964,8 @@ writezone(const char *const name, const char *const string, char version)
putc(ttisgmts[i], fp);
}
fprintf(fp, "\n%s\n", string);
- close_file(fp, fullname);
+ close_file(fp, directory, name);
free(ats);
- free(fullname);
}
static char const *
@@ -3032,16 +3036,14 @@ mp = _("time zone abbreviation differs from POSIX standard");
charcnt += i;
}
-/* Ensure that the parent directories of ARGNAME exist, by making any
- missing ones. Return true if some directories are made (perhaps by
- some other process), false if the directories already exist, and
- exit with failure if there is trouble. */
-static bool
-mkdirs(char *argname)
+/* 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. */
+static void
+mkdirs(char const *argname, bool ancestors)
{
register char * name;
register char * cp;
- bool dirs_made = false;
cp = name = ecpyalloc(argname);
@@ -3053,8 +3055,9 @@ mkdirs(char *argname)
while (*cp == '/')
cp++;
- for (; (cp = strchr(cp, '/')) != 0; cp++) {
- *cp = '\0';
+ while (cp && ((cp = strchr(cp, '/')) || !ancestors)) {
+ if (cp)
+ *cp = '\0';
/*
** Try to create it. It's OK if creation fails because
** the directory already exists, perhaps because some
@@ -3065,15 +3068,13 @@ mkdirs(char *argname)
if (mkdir(name, MKDIR_UMASK) != 0) {
int err = errno;
if (err != EEXIST && itsdir(name) < 0) {
- char const *e = strerror(err);
error(_("%s: Can't create directory %s: %s"),
- progname, name, e);
+ progname, name, strerror(err));
exit(EXIT_FAILURE);
}
}
- *cp = '/';
- dirs_made = true;
+ if (cp)
+ *cp++ = '/';
}
free(name);
- return dirs_made;
}
--
2.7.4
More information about the tz
mailing list