1
0
mirror of https://frontier.innolan.net/rainlance/amiga-tz.git synced 2026-05-06 12:14:26 +00:00

Avoid <ctype.h> locale problems.

* zdump.c, zic.c: Don't include <ctype.h>, because the behavior if
its macros is locale-dependent if HAVE_GETTEXT, and we want the C
locale's semantics.  Instead, use portable replacements for
ctype.h operations, with the desired semantics.  All uses of
isalpha etc. replaced by calls to new functions is_alpha etc.  or
by inline code.
(isascii): Remove.
(is_alpha): New function.
* zic.c (doabbr): Simplify by using is_alpha.
(is_space): New function.
* NEWS: Document the above.
This commit is contained in:
Paul Eggert
2014-06-26 14:41:07 -07:00
parent 100a770913
commit 52518704ee
3 changed files with 96 additions and 45 deletions

4
NEWS
View File

@@ -12,6 +12,10 @@ Unreleased, experimental changes
'localtime', 'mktime', etc. now use much less stack space if ALL_STATE
is defined. (Thanks to Elliott Hughes for reporting the problem.)
'zic' no longer mishandles input when ignoring case in locales that
are not compatible with English, e.g., unibyte Turkish locales when
compiled with HAVE_GETTEXT.
Error diagnostics of 'zic' and 'yearistype' have been reworded so that
they no longer use ASCII '-' as if it were a dash.

32
zdump.c
View File

@@ -24,10 +24,6 @@
#include "time.h" /* for struct tm */
#include "stdlib.h" /* for exit, malloc, atoi */
#include "limits.h" /* for CHAR_BIT, LLONG_MAX */
#include "ctype.h" /* for isalpha et al. */
#ifndef isascii
#define isascii(x) 1
#endif /* !defined isascii */
/*
** Substitutes for pre-C99 compilers.
@@ -220,6 +216,25 @@ static void show(char * zone, time_t t, int v);
static const char * tformat(void);
static time_t yeartot(intmax_t y) ATTRIBUTE_PURE;
/* Is A an alphabetic character in the C locale? */
static int
is_alpha(char a)
{
switch (a) {
default:
return 0;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
case 'V': case 'W': case 'X': case 'Y': case 'Z':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
case 'v': case 'w': case 'x': case 'y': case 'z':
return 1;
}
}
#ifndef TYPECHECK
#define my_localtime localtime
#else /* !defined TYPECHECK */
@@ -266,7 +281,7 @@ abbrok(const char *const abbrp, const char *const zone)
return;
cp = abbrp;
wp = NULL;
while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
while (is_alpha(*cp))
++cp;
if (cp - abbrp == 0)
wp = _("lacks alphabetic at start");
@@ -276,10 +291,9 @@ abbrok(const char *const abbrp, const char *const zone)
wp = _("has more than 6 alphabetics");
if (wp == NULL && (*cp == '+' || *cp == '-')) {
++cp;
if (isascii((unsigned char) *cp) &&
isdigit((unsigned char) *cp))
if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
++cp;
if ('0' <= *cp && *cp <= '9')
if (*cp++ == '1' && '0' <= *cp && *cp <= '4')
cp++;
if (*cp != '\0')
wp = _("differs from POSIX standard");
}

105
zic.c
View File

@@ -31,19 +31,6 @@ typedef int_fast64_t zic_t;
#define MKDIR_UMASK 0755
#endif
/*
** On some ancient hosts, predicates like 'isspace(C)' are defined
** only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
** which says they are defined only if C == ((unsigned char) C) || C == EOF.
** Neither the C Standard nor Posix require that 'isascii' exist.
** For portability, we check both ancient and modern requirements.
** If isascii is not defined, the isascii check succeeds trivially.
*/
#include "ctype.h"
#ifndef isascii
#define isascii(x) 1
#endif
#define end(cp) (strchr((cp), '\0'))
struct rule {
@@ -132,7 +119,8 @@ static int inzcont(char ** fields, int nfields);
static int inzone(char ** fields, int nfields);
static int inzsub(char ** fields, int nfields, int iscont);
static int itsdir(const char * name);
static int lowerit(int c);
static int is_alpha(char a);
static char lowerit(char);
static int mkdirs(char * filename);
static void newabbr(const char * abbr);
static zic_t oadd(zic_t t1, zic_t t2);
@@ -640,16 +628,25 @@ static void
namecheck(const char *name)
{
register char const *cp;
static char const benign[] = ("-/_"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
/* Benign characters in a portable file name. */
static char const benign[] =
"-/_"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/* Non-control chars in the POSIX portable character set,
excluding the benign characters. */
static char const printable_and_not_benign[] =
" !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~";
register char const *component = name;
if (!noise)
return;
for (cp = name; *cp; cp++) {
unsigned char c = *cp;
if (!strchr(benign, c)) {
warning((isascii(c) && isprint(c)
warning((strchr(printable_and_not_benign, c)
? _("file name '%s' contains byte '%c'")
: _("file name '%s' contains byte '\\%o'")),
name, c);
@@ -1862,10 +1859,8 @@ doabbr(char *const abbr, const char *const format, const char *const letters,
}
if (!doquotes)
return;
for (cp = abbr; *cp != '\0'; ++cp)
if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
break;
for (cp = abbr; is_alpha(*cp); cp++)
continue;
len = strlen(abbr);
if (len > 0 && *cp == '\0')
return;
@@ -2582,11 +2577,54 @@ yearistype(const int year, const char *const type)
exit(EXIT_FAILURE);
}
/* Is A a space character in the C locale? */
static int
lowerit(int a)
is_space(char a)
{
a = (unsigned char) a;
return (isascii(a) && isupper(a)) ? tolower(a) : a;
switch (a) {
default:
return 0;
case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
return 1;
}
}
/* Is A an alphabetic character in the C locale? */
static int
is_alpha(char a)
{
switch (a) {
default:
return 0;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
case 'V': case 'W': case 'X': case 'Y': case 'Z':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
case 'v': case 'w': case 'x': case 'y': case 'z':
return 1;
}
}
/* If A is an uppercase character in the C locale, return its lowercase
counterpart. Otherwise, return A. */
static char
lowerit(char a)
{
switch (a) {
default: return a;
case 'A': return 'a'; case 'B': return 'b'; case 'C': return 'c';
case 'D': return 'd'; case 'E': return 'e'; case 'F': return 'f';
case 'G': return 'g'; case 'H': return 'h'; case 'I': return 'i';
case 'J': return 'j'; case 'K': return 'k'; case 'L': return 'l';
case 'M': return 'm'; case 'N': return 'n'; case 'O': return 'o';
case 'P': return 'p'; case 'Q': return 'q'; case 'R': return 'r';
case 'S': return 's'; case 'T': return 't'; case 'U': return 'u';
case 'V': return 'v'; case 'W': return 'w'; case 'X': return 'x';
case 'Y': return 'y'; case 'Z': return 'z';
}
}
/* case-insensitive equality */
@@ -2653,8 +2691,7 @@ getfields(register char *cp)
array = emalloc(size_product(strlen(cp) + 1, sizeof *array));
nsubs = 0;
for ( ; ; ) {
while (isascii((unsigned char) *cp) &&
isspace((unsigned char) *cp))
while (is_space(*cp))
++cp;
if (*cp == '\0' || *cp == '#')
break;
@@ -2671,9 +2708,8 @@ getfields(register char *cp)
));
exit(1);
}
} while (*cp != '\0' && *cp != '#' &&
(!isascii(*cp) || !isspace((unsigned char) *cp)));
if (isascii(*cp) && isspace((unsigned char) *cp))
} while (*cp && *cp != '#' && !is_space(*cp));
if (is_space(*cp))
++cp;
*dp = '\0';
}
@@ -2806,8 +2842,7 @@ newabbr(const char *const string)
*/
cp = string;
mp = NULL;
while (isascii((unsigned char) *cp) &&
isalpha((unsigned char) *cp))
while (is_alpha(*cp))
++cp;
if (cp - string == 0)
mp = _("time zone abbreviation lacks alphabetic at start");
@@ -2817,8 +2852,7 @@ mp = _("time zone abbreviation has fewer than 3 alphabetics");
mp = _("time zone abbreviation has too many alphabetics");
if (mp == NULL && (*cp == '+' || *cp == '-')) {
++cp;
if (isascii((unsigned char) *cp) &&
isdigit((unsigned char) *cp))
if (is_digit(*cp))
if (*cp++ == '1' &&
*cp >= '0' && *cp <= '4')
++cp;
@@ -2852,8 +2886,7 @@ mkdirs(char *argname)
/*
** DOS drive specifier?
*/
if (isalpha((unsigned char) name[0]) &&
name[1] == ':' && name[2] == '\0') {
if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0') {
*cp = '/';
continue;
}