Both of these have been a nuisance for years. Do something about it.
The command
git grep -En '\<(is|to)[a-z]+\(|\<(str|mem)[a-z]*cmp\(' -- '*.[ch]'
finds code which it might be good to change.
The hairy Emacs command
(query-replace-regexp
"\\<\\(\\(str\\|mem\\)\\sw*cmp\\)(\\([^,]*\\), \\([^)]*\\)) \\(==\\|!=\\|<\\|<=\\|>\\|>=\\) 0"
'(replace-eval-replacement
. (concat (replace-quote (upcase (match-string 1)))
"(\\3, \\5, \\4)"))
nil
(if (and transient-mark-mode mark-active) (region-beginning))
(if (and transient-mark-mode mark-active) (region-end))
nil)
will convert easy cases of `foocmp' calls, but hard ones have to be done
by hand.
#include "alloc.h"
#include "codec.h"
#include "dstr.h"
+#include "macros.h"
#include "sub.h"
#include "base64.h"
#define PUTWRAP(x) WRAP({ \
char ch = encodemap[x]; \
- if (f & CDCF_LOWERC) ch = tolower((unsigned char)ch); \
+ if (f & CDCF_LOWERC) ch = TOLOWER(ch); \
DPUTC(d, ch); \
})
case 0: \
break; \
case CDCF_LOWERC: \
- if (isupper(ch)) goto badch; \
+ if (ISUPPER(ch)) goto badch; \
default: \
- ch = toupper(ch); \
+ ch = TOUPPER(ch); \
} \
x = decodemap[ch]; \
switch (x) { \
#include "codec.h"
#include "dstr.h"
+#include "macros.h"
#include "mdwopt.h"
#include "quis.h"
#include "report.h"
n = strcspn(p, ",");
for (i = 0; flagtab[i].name; i++) {
if (strlen(flagtab[i].name) == n &&
- strncmp(flagtab[i].name, p, n) == 0)
+ STRNCMP(flagtab[i].name, ==, p, n))
goto found;
}
die(EXIT_FAILURE, "unknown flag `%.*s'", (int)n, p);
for (cc = cctab;; cc++) {
if (!*cc) die(EXIT_FAILURE, "unknown codec `%s'", *argv);
- else if (strcmp(*argv, (*cc)->name) == 0) break;
+ else if (STRCMP(*argv, ==, (*cc)->name)) break;
}
argv++; argc--;
if (!argc)
code(c, "<stdin>", stdin, ofp);
else for (i = 0; i < argc; i++) {
- if (strcmp(argv[i], "-") == 0)
+ if (STRCMP(argv[i], ==, "-"))
code(c, "<stdin>", stdin, ofp);
else if ((ifp = fopen(argv[i], imode)) == 0) {
die(EXIT_FAILURE, "couldn't open `%s' for reading: %s",
#include <string.h>
#include "dstr.h"
+#include "macros.h"
#include "url.h"
/*----- Main code ---------------------------------------------------------*/
case ' ': DPUTC(d, '+');
break;
default:
- if (isspace((unsigned char)*p)) goto unsafe;
- else if (isalnum((unsigned char)*p)) goto safe;
+ if (ISSPACE(*p)) goto unsafe;
+ else if (ISALNUM(*p)) goto safe;
else if (ctx->f&URLF_LAX) goto safe;
else goto unsafe;
case '/': case '~':
#include <stdlib.h>
#include <string.h>
+#include "macros.h"
#include "mdwopt.h"
#include "quis.h"
#include "report.h"
die(EXIT_FAILURE, "not enough memory");
guard = p;
for (q = file; *q; p++, q++) {
- if (isalnum((unsigned char)*q))
- *p = toupper((unsigned char)*q);
+ if (ISALNUM(*q))
+ *p = TOUPPER(*q);
else
*p = '_';
}
die(EXIT_FAILURE, "not enough memory");
guard = p;
for (q = file; *q; p++, q++) {
- if (isalnum((unsigned char)*q))
- *p = toupper((unsigned char)*q);
+ if (ISALNUM(*q))
+ *p = TOUPPER(*q);
else
*p = '_';
}
if (a->cname) n[j++] = a->cname;
else { n[j++] = *nv; nv++; nn--; }
for (i = 0; i < nn && j < N(n) - 1; i++)
- if (strcmp(n[0], nv[i]) != 0) n[j++] = nv[i];
+ if (STRCMP(n[0], !=, nv[i])) n[j++] = nv[i];
n[j++] = 0;
for (i = j = 0; i < an && j < N(aa) - 1; i++) {
if (av[i].addr.sa.sa_family == AF_INET)
#include "dstr.h"
#include "exc.h"
#include "ident.h"
+#include "macros.h"
#include "selbuf.h"
/*----- Main code ---------------------------------------------------------*/
/* --- Skip past any leading whitespace --- */
- while (isspace((unsigned char)*p))
+ while (ISSPACE(*p))
p++;
/* --- Now start work on the string itself --- */
for (;;) {
- if (*p == 0 || *p == ':' || *p == ',' || isspace((unsigned char)*p))
+ if (*p == 0 || *p == ':' || *p == ',' || ISSPACE(*p))
break;
else if (*p == '\\') {
p++;
/* --- Tidy up afterwards --- */
- while (isspace((unsigned char)*p))
+ while (ISSPACE(*p))
p++;
if (*p == 0)
*pp = 0;
/* --- Find out what sort of a reply this is --- */
q = next(&p);
- if (strcmp(q, "USERID") == 0) {
+ if (STRCMP(q, ==, "USERID")) {
i->type = IDENT_USERID;
i->u.userid.os = next(&p);
i->u.userid.user = next(&p);
- } else if (strcmp(q, "ERROR") == 0) {
+ } else if (STRCMP(q, ==, "ERROR")) {
i->type = IDENT_ERROR;
i->u.error = next(&p);
} else
#include "darray.h"
#include "dstr.h"
+#include "macros.h"
/*----- Tunable constants -------------------------------------------------*/
done_flags:
i = 0;
- while (isdigit((unsigned char)*p)) i = 10*i + *p++ - '0';
+ while (ISDIGIT(*p)) i = 10*i + *p++ - '0';
/* --- Snag: this might have been an argument position indicator --- */
fs->wd = i;
} else if (*p == '*') {
p++;
- if (!isdigit((unsigned char)*p))
+ if (!ISDIGIT(*p))
i = anext++;
else {
i = *p++ - '0';
- while (isdigit((unsigned char)*p)) i = 10*i + *p++ - '0';
+ while (ISDIGIT(*p)) i = 10*i + *p++ - '0';
assert(*p == '$'); p++;
assert(i > 0); i--;
}
if (*p == '.') {
p++;
f |= f_prec;
- if (isdigit((unsigned char)*p)) {
+ if (ISDIGIT(*p)) {
i = *p++ - '0';
- while (isdigit((unsigned char)*p)) i = 10*i + *p++ - '0';
+ while (ISDIGIT(*p)) i = 10*i + *p++ - '0';
fs->prec = i;
} else if (*p != '*')
fs->prec = 0;
else {
p++;
- if (!isdigit((unsigned char)*p))
+ if (!ISDIGIT(*p))
i = anext++;
else {
i = *p++ - '0';
- while (isdigit((unsigned char)*p)) i = 10*i + *p++ - '0';
+ while (ISDIGIT(*p)) i = 10*i + *p++ - '0';
assert(*p == '$'); p++;
assert(i > 0); i--;
}
#include "bits.h"
#include "exc.h"
#include "hash.h"
+#include "macros.h"
#include "sub.h"
#include "sym.h"
#include "unihash.h"
for (p = bin; *p; p = &(*p)->next) {
q = (sym_base *)*p;
- if (hash == q->b.hash && len == q->len && !memcmp(n, SYM_NAME(q), len)) {
+ if (hash == q->b.hash && len == q->len &&
+ MEMCMP(n, ==, SYM_NAME(q), len)) {
/* --- Found a match --- *
*
#include "assoc.h"
#include "atom.h"
+#include "macros.h"
typedef struct word {
assoc_base _b;
buf[strlen(buf) - 1] = 0;
p = strtok(buf, " ");
- if (strcmp(p, "set") == 0) {
+ if (STRCMP(p, ==, "set")) {
char *k = strtok(0, " ");
int i = atoi(strtok(0, " "));
unsigned f;
w->i = i;
if (!f)
n++;
- } else if (strcmp(p, "get") == 0) {
+ } else if (STRCMP(p, ==, "get")) {
char *k = strtok(0, " ");
word *w = assoc_find(&t, atom_intern(&at, k), 0, 0);
if (w)
printf("%i\n", w->i);
else
puts("*MISSING*");
- } else if (strcmp(p, "del") == 0) {
+ } else if (STRCMP(p, ==, "del")) {
char *k = strtok(0, " ");
word *w = assoc_find(&t, atom_intern(&at, k), 0, 0);
if (w) {
n--;
} else
puts("*MISSING*");
- } else if (strcmp(p, "count") == 0) {
+ } else if (STRCMP(p, ==, "count")) {
printf("%lu\n", (unsigned long)n);
- } else if (strcmp(p, "show") == 0) {
+ } else if (STRCMP(p, ==, "show")) {
assoc_iter i;
word *w;
word **v, **vv;
#include "darray.h"
#include "exc.h"
+#include "macros.h"
DA_DECL(vec, int);
p = strtok(buf, " ");
TRY {
- if (strcmp(p, "push") == 0) {
+ if (STRCMP(p, ==, "push")) {
int n = atoi(strtok(0, " "));
DA_PUSH(&v, n);
- } else if (strcmp(p, "unshift") == 0) {
+ } else if (STRCMP(p, ==, "unshift")) {
int n = atoi(strtok(0, " "));
DA_UNSHIFT(&v, n);
- } else if (strcmp(p, "pop") == 0) {
+ } else if (STRCMP(p, ==, "pop")) {
printf("%i\n", DA_POP(&v));
- } else if (strcmp(p, "shift") == 0) {
+ } else if (STRCMP(p, ==, "shift")) {
printf("%i\n", DA_SHIFT(&v));
- } else if (strcmp(p, "insert") == 0 ||
- strcmp(p, "append") == 0) {
+ } else if (STRCMP(p, ==, "insert") ||
+ STRCMP(p, ==, "append")) {
vec vv;
char *q = p;
DA_CREATE(&vv);
int n = atoi(p);
DA_PUSH(&vv, n);
}
- if (strcmp(q, "insert") == 0) {
+ if (STRCMP(q, ==, "insert")) {
DA_SHUNT(&v, DA_LEN(&vv));
DA_SLIDE(&v, DA_LEN(&vv));
memcpy(DA(&v), DA(&vv), DA_LEN(&vv) * sizeof(int));
memcpy(DA(&v) + l, DA(&vv), DA_LEN(&vv) * sizeof(int));
}
DA_DESTROY(&vv);
- } else if (strcmp(p, "delete") == 0) {
+ } else if (STRCMP(p, ==, "delete")) {
int n = atoi(strtok(0, " "));
DA_UNSLIDE(&v, n);
- } else if (strcmp(p, "reduce") == 0) {
+ } else if (STRCMP(p, ==, "reduce")) {
int n = atoi(strtok(0, " "));
DA_SHRINK(&v, n);
- } else if (strcmp(p, "set") == 0) {
+ } else if (STRCMP(p, ==, "set")) {
size_t i = atoi(strtok(0, " "));
int n = atoi(strtok(0, " "));
size_t l = DA_LEN(&v);
DA(&v)[j] = -1;
}
DA(&v)[i] = n;
- } else if (strcmp(p, "get") == 0) {
+ } else if (STRCMP(p, ==, "get")) {
size_t i = atoi(strtok(0, " "));
if (i >= DA_LEN(&v))
puts("*RANGE*");
else
printf("%i\n", DA(&v)[i]);
- } else if (strcmp(p, "first") == 0) {
+ } else if (STRCMP(p, ==, "first")) {
if (DA_LEN(&v))
printf("%i\n", DA_FIRST(&v));
else
puts("*RANGE*");
- } else if (strcmp(p, "last") == 0) {
+ } else if (STRCMP(p, ==, "last")) {
if (DA_LEN(&v))
printf("%i\n", DA_LAST(&v));
else
puts("*RANGE*");
- } else if (strcmp(p, "show") == 0) {
+ } else if (STRCMP(p, ==, "show")) {
if (DA_LEN(&v) == 0)
puts("*EMPTY*");
else {
#include <string.h>
#include "dstr.h"
+#include "macros.h"
static int win = 0, lose = 0;
static dstr d = DSTR_INIT;
static void check(const char *what, const char *want)
{
- if (strcmp(want, d.buf) == 0)
+ if (STRCMP(want, ==, d.buf))
win++;
else {
lose++;
#include <stdlib.h>
#include <string.h>
+#include "macros.h"
#include "sym.h"
typedef struct word {
buf[strlen(buf) - 1] = 0;
p = strtok(buf, " ");
- if (strcmp(p, "set") == 0) {
+ if (STRCMP(p, ==, "set")) {
char *k = strtok(0, " ");
int i = atoi(strtok(0, " "));
unsigned f;
w->i = i;
if (!f)
n++;
- } else if (strcmp(p, "get") == 0) {
+ } else if (STRCMP(p, ==, "get")) {
char *k = strtok(0, " ");
word *w = sym_find(&t, k, -1, 0, 0);
if (w)
printf("%i\n", w->i);
else
puts("*MISSING*");
- } else if (strcmp(p, "del") == 0) {
+ } else if (STRCMP(p, ==, "del")) {
char *k = strtok(0, " ");
word *w = sym_find(&t, k, -1, 0, 0);
if (w) {
n--;
} else
puts("*MISSING*");
- } else if (strcmp(p, "count") == 0) {
+ } else if (STRCMP(p, ==, "count")) {
printf("%lu\n", (unsigned long)n);
- } else if (strcmp(p, "show") == 0) {
+ } else if (STRCMP(p, ==, "show")) {
sym_iter i;
word *w;
word **v, **vv;
ego(argv[0]);
- if (argc == 3 && strcmp(argv[1], "server") == 0) {
+ if (argc == 3 && STRCMP(argv[1], ==, "server")) {
set_unix_addr(&sun, argv[2]);
lsk = socket(PF_UNIX, SOCK_STREAM, 0);
if (lsk < 0) die(2, "socket: %s", strerror(errno));
if (n < 0) die(2, "fdrecv: %s", strerror(errno));
close(sk);
if (fd == -1) die(2, "no fd found");
- if (n != sizeof(sockmsg) || strcmp(buf, sockmsg) != 0)
+ if (n != sizeof(sockmsg) || STRCMP(buf, !=, sockmsg))
die(2, "socket message mismatch (found `%.*s')", (int)n, buf);
n = read(fd, buf, sizeof(buf));
if (n < 0) die(2, "read: %s", strerror(errno));
- if (n != sizeof(pipemsg) || strcmp(buf, pipemsg) != 0)
+ if (n != sizeof(pipemsg) || STRCMP(buf, !=, pipemsg))
die(2, "pipe message mismatch (found `%.*s')", (int)n, buf);
close(fd);
- } else if (argc == 3 && strcmp(argv[1], "client") == 0) {
+ } else if (argc == 3 && STRCMP(argv[1], ==, "client")) {
set_unix_addr(&sun, argv[2]);
if (pipe(pfd)) die(2, "pipe: %s", strerror(errno));
sk = socket(PF_UNIX, SOCK_STREAM, 0);
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include "macros.h"
#include "mdup.h"
#define MAXFD 256
int verbose = 0;
for (i = 1, j = 0; i < argc; i++) {
- if (strcmp(argv[i], "-v") == 0) { verbose++; continue; }
+ if (STRCMP(argv[i], ==, "-v")) { verbose++; continue; }
if (j >= MAXFD) { fprintf(stderr, "too many\n"); exit(1); }
if (sscanf(argv[i], "%d:%d", &fds[j].cur, &fds[j].want) < 2 ||
fds[j].cur >= MAXFD)
#include <string.h>
#include "dstr.h"
+#include "macros.h"
#include "report.h"
#include "quis.h"
#include "testrig.h"
/* --- Skip leading whitespace --- */
- while (isspace((unsigned char)ch))
+ while (ISSPACE(ch))
ch = getc(fp);
/* --- Trap some special characters --- */
case '`':
goto done;
default:
- if (isspace((unsigned char)ch))
+ if (ISSPACE(ch))
goto done;
}
if (ch == '\\') {
goto done;
}
- if (strcmp(tok.buf, "SUITE") == 0) {
+ if (STRCMP(tok.buf, ==, "SUITE")) {
t = gettok(fp);
if (t != TOK_WORD) {
moan("expected <word>; found `%s'", decode(t));
chunks = 0;
break;
}
- if (strcmp(tok.buf, ss->name) == 0) {
+ if (STRCMP(tok.buf, ==, ss->name)) {
chunks = ss->chunks;
break;
}
for (cch = chunks; ; cch++) {
if (!cch->name)
goto skip_chunk;
- if (strcmp(tok.buf, cch->name) == 0)
+ if (STRCMP(tok.buf, ==, cch->name))
break;
}
if (i >= argc - 1)
break;
p = argv[++i];
- if (strcmp(p, "--") == 0) {
+ if (STRCMP(p, ==, "--")) {
i++;
break;
}
/* --- Local headers --- */
#include "dstr.h"
+#include "macros.h"
#include "quis.h"
#include "trace.h"
dstr_puts(&d, ": ");
for (i = 0; i < 8; i++) {
if (i < sz)
- dstr_putc(&d, isprint(p[i]) ? p[i] : '.');
+ dstr_putc(&d, ISPRINT(p[i]) ? p[i] : '.');
else
dstr_putc(&d, '*');
}
#include <string.h>
#include <stdlib.h>
+#include "macros.h"
#include "report.h"
#include "trace.h"
/* --- Dump out help text --- */
- if (!p || !strcmp(p, "?")) {
+ if (!p || STRCMP(p, ==, "?")) {
const trace_opt *tt;
puts("Trace options:");
for (tt = t; tt->ch; tt++) {
.\" @STR
.\" @GLUE
.\" @STATIC_ASSERT
+.\" @ISALNUM
+.\" @ISALPHA
+.\" @ISASCII
+.\" @ISBLANK
+.\" @ISCNTRL
+.\" @ISDIGIT
+.\" @ISGRAPH
+.\" @ISLOWER
+.\" @ISPRINT
+.\" @ISPUNCT
+.\" @ISSPACE
+.\" @ISUPPER
+.\" @ISXDIGIT
+.\" @TOASCII
+.\" @TOLOWER
+.\" @TOUPPER
+.\" @MEMCMP
+.\" @STRCMP
+.\" @STRNCMP
.\" @DISCARD
.\" @IGNORE
.\" @DEPRECATED
.BI "GLUE(" tokens\fR... ", " tokens\fR... ")"
.BI "STATIC_ASSERT(" cond ", " msg ");"
+.BI "ISALNUM(int " ch ");"
+.BI "ISALPHA(int " ch ");"
+.BI "ISASCII(int " ch ");"
+.BI "ISBLANK(int " ch ");"
+.BI "ISCNTRL(int " ch ");"
+.BI "ISDIGIT(int " ch ");"
+.BI "ISGRAPH(int " ch ");"
+.BI "ISLOWER(int " ch ");"
+.BI "ISPRINT(int " ch ");"
+.BI "ISPUNCT(int " ch ");"
+.BI "ISSPACE(int " ch ");"
+.BI "ISUPPER(int " ch ");"
+.BI "ISXDIGIT(int " ch ");"
+.BI "TOASCII(int " ch ");"
+.BI "TOLOWER(int " ch ");"
+.BI "TOUPPER(int " ch ");"
+
+.BI "MEMCMP(const void *" x ", " op ", const void *" y ", size_t " n ");"
+.BI "STRCMP(const char *" x ", " op ", const char *" y ");"
+.BI "STRNCMP(const char *" x ", " op ", const char *" y ", size_t " n ");"
+
.BI "void DISCARD(" scalar ");"
.BI "void IGNORE(" variable ");"
.IR msg .
.PP
The
+.BR IS ...\&
+and
+.BR TO ...\&
+macros are wrappers around the corresponding standard
+.B <ctype.h>
+macros with the corresponding lowercase names. They take care of
+forcing the character argument
+.I ch
+to
+.BR "unsigned char" :
+this conversion is necessary on platforms with signed
+.B char
+to avoid passing negative values into the standard macros.
+.PP
+The
+.BR MEMCMP ,
+.BR STRCMP ,
+and
+.B STRNCMP
+macros are wrappers around the standard
+.B <string.h>
+functions with the corresponding lowercase names. They take an
+additional argument
+.I op
+which is a equality or ordering operator (e.g.,
+.B ==
+or
+.BR > )
+inserted between the two operands. The standard functions return a
+false value if and only if the operands are equal, which is
+counterintuitive and leads to mistakes; requiring an explicit relational
+operator should reduce the number of such mistakes.
+.PP
+The
.B DISCARD
macro discards its argument, which must be of some scalar type. This
can be useful in muffling warnings about ignoring return codes in cases
IGNORABLE extern char static_assert_failed[2*!!(cond) - 1]
#endif
+/*----- String and character hacks ----------------------------------------*/
+
+#define CTYPE_HACK(func, ch) (func((unsigned char)(ch)))
+
+#define ISALNUM(ch) CTYPE_HACK(isalnum, ch)
+#define ISALPHA(ch) CTYPE_HACK(isalpha, ch)
+#define ISASCII(ch) CTYPE_HACK(isascii, ch)
+#define ISBLANK(ch) CTYPE_HACK(isblank, ch)
+#define ISCNTRL(ch) CTYPE_HACK(iscntrl, ch)
+#define ISDIGIT(ch) CTYPE_HACK(isdigit, ch)
+#define ISGRAPH(ch) CTYPE_HACK(isgraph, ch)
+#define ISLOWER(ch) CTYPE_HACK(islower, ch)
+#define ISPRINT(ch) CTYPE_HACK(isprint, ch)
+#define ISPUNCT(ch) CTYPE_HACK(ispunct, ch)
+#define ISSPACE(ch) CTYPE_HACK(isspace, ch)
+#define ISUPPER(ch) CTYPE_HACK(isupper, ch)
+#define ISXDIGIT(ch) CTYPE_HACK(isxdigit, ch)
+
+#define TOASCII(ch) CTYPE_HACK(toascii, ch)
+#define TOLOWER(ch) CTYPE_HACK(tolower, ch)
+#define TOUPPER(ch) CTYPE_HACK(toupper, ch)
+
+#define MEMCMP(x, op, y, n) (memcmp((x), (y), (n)) op 0)
+#define STRCMP(x, op, y) (strcmp((x), (y)) op 0)
+#define STRNCMP(x, op, y, n) (strncmp((x), (y), (n)) op 0)
+
/*----- Compiler diagnostics ----------------------------------------------*/
/* --- Compiler-specific definitions --- */
#include <stdlib.h>
#include <string.h>
+#include "macros.h"
#include "str.h"
/*----- Main code ---------------------------------------------------------*/
if (!p)
return (0);
- while (isspace((unsigned char)*p))
+ while (ISSPACE(*p))
p++;
if (!*p) {
*pp = 0;
*qq++ = *q;
break;
default:
- if (isspace((unsigned char)*q)) {
- do q++; while (*q && isspace((unsigned char)*q));
+ if (ISSPACE(*q)) {
+ do q++; while (*q && ISSPACE(*q));
goto done;
} else if (!(f & STRF_QUOTE))
goto stdchar;
sz--;
while (*p && sz) {
int ch = *p++;
- if (!isgraph((unsigned char)ch))
+ if (!ISGRAPH(ch))
ch = '_';
*d++ = ch;
sz--;
#include <ctype.h>
#include <string.h>
+#include "macros.h"
#include "versioncmp.h"
/*----- Main code ---------------------------------------------------------*/
while (v < vl) {
ch = *v;
- if (!isdigit((unsigned char)ch))
+ if (!ISDIGIT(ch))
break;
v++;
n = n * 10 + (ch - '0');
while (v < vl) {
ch = *v;
- if (isdigit((unsigned char)ch))
+ if (ISDIGIT(ch))
break;
v++;
}
pa = vchr(&va, val); pb = vchr(&vb, vbl);
for (;;) {
if (pa == va) ia = 1;
- else if (isalpha((unsigned char)*pa)) ia = 2;
+ else if (ISALPHA(*pa)) ia = 2;
else if (*pa == '~') ia = 0;
else ia = 3;
if (pb == vb) ib = 1;
- else if (isalpha((unsigned char)*pb)) ib = 2;
+ else if (ISALPHA(*pb)) ib = 2;
else if (*pb == '~') ib = 0;
else ib = 3;