1*e89934bbSchristos /* $NetBSD: strcasecmp_utf8.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */
2e262b48eSchristos
3e262b48eSchristos /*++
4e262b48eSchristos /* NAME
5e262b48eSchristos /* strcasecmp_utf8 3
6e262b48eSchristos /* SUMMARY
7e262b48eSchristos /* caseless string comparison
8e262b48eSchristos /* SYNOPSIS
9e262b48eSchristos /* #include <stringops.h>
10e262b48eSchristos /*
11e262b48eSchristos /* int strcasecmp_utf8(
12e262b48eSchristos /* const char *s1,
13e262b48eSchristos /* const char *s2)
14e262b48eSchristos /*
15e262b48eSchristos /* int strncasecmp_utf8(
16e262b48eSchristos /* const char *s1,
17e262b48eSchristos /* const char *s2,
18e262b48eSchristos /* ssize_t len)
19e262b48eSchristos /* AUXILIARY FUNCTIONS
20e262b48eSchristos /* int strcasecmp_utf8x(
21e262b48eSchristos /* int flags,
22e262b48eSchristos /* const char *s1,
23e262b48eSchristos /* const char *s2)
24e262b48eSchristos /*
25e262b48eSchristos /* int strncasecmp_utf8x(
26e262b48eSchristos /* int flags,
27e262b48eSchristos /* const char *s1,
28e262b48eSchristos /* const char *s2,
29e262b48eSchristos /* ssize_t len)
30e262b48eSchristos /* DESCRIPTION
31e262b48eSchristos /* strcasecmp_utf8() implements caseless string comparison for
32e262b48eSchristos /* UTF-8 text, with an API similar to strcasecmp(). Only ASCII
33e262b48eSchristos /* characters are casefolded when the code is compiled without
34e262b48eSchristos /* EAI support or when util_utf8_enable is zero.
35e262b48eSchristos /*
36e262b48eSchristos /* strncasecmp_utf8() implements caseless string comparison
37e262b48eSchristos /* for UTF-8 text, with an API similar to strncasecmp(). Only
38e262b48eSchristos /* ASCII characters are casefolded when the code is compiled
39e262b48eSchristos /* without EAI support or when util_utf8_enable is zero.
40e262b48eSchristos /*
41e262b48eSchristos /* strcasecmp_utf8x() and strncasecmp_utf8x() implement a more
42e262b48eSchristos /* complex API that provides the above functionality and more.
43e262b48eSchristos /*
44e262b48eSchristos /* Arguments:
45e262b48eSchristos /* .IP "s1, s2"
46e262b48eSchristos /* Null-terminated strings to be compared.
47e262b48eSchristos /* .IP len
48e262b48eSchristos /* String length before casefolding.
49e262b48eSchristos /* .IP flags
50e262b48eSchristos /* Zero or CASEF_FLAG_UTF8. The latter flag enables UTF-8 case
51e262b48eSchristos /* folding instead of folding only ASCII characters. This flag
52e262b48eSchristos /* is ignored when compiled without EAI support.
53e262b48eSchristos /* SEE ALSO
54e262b48eSchristos /* casefold(), casefold text for caseless comparison.
55e262b48eSchristos /* LICENSE
56e262b48eSchristos /* .ad
57e262b48eSchristos /* .fi
58e262b48eSchristos /* The Secure Mailer license must be distributed with this software.
59e262b48eSchristos /* AUTHOR(S)
60e262b48eSchristos /* Wietse Venema
61e262b48eSchristos /* IBM T.J. Watson Research
62e262b48eSchristos /* P.O. Box 704
63e262b48eSchristos /* Yorktown Heights, NY 10598, USA
64e262b48eSchristos /*
65e262b48eSchristos /* Wietse Venema
66e262b48eSchristos /* Google, Inc.
67e262b48eSchristos /* 111 8th Avenue
68e262b48eSchristos /* New York, NY 10011, USA
69e262b48eSchristos /*--*/
70e262b48eSchristos
71e262b48eSchristos /*
72e262b48eSchristos * System library.
73e262b48eSchristos */
74e262b48eSchristos #include <sys_defs.h>
75e262b48eSchristos #include <string.h>
76e262b48eSchristos
77e262b48eSchristos #ifdef STRCASECMP_IN_STRINGS_H
78e262b48eSchristos #include <strings.h>
79e262b48eSchristos #endif
80e262b48eSchristos
81e262b48eSchristos /*
82e262b48eSchristos * Utility library.
83e262b48eSchristos */
84e262b48eSchristos #include <stringops.h>
85e262b48eSchristos
86e262b48eSchristos #define STR(x) vstring_str(x)
87e262b48eSchristos
88e262b48eSchristos static VSTRING *f1; /* casefold result for s1 */
89e262b48eSchristos static VSTRING *f2; /* casefold result for s2 */
90e262b48eSchristos
91e262b48eSchristos /* strcasecmp_utf8_init - initialize */
92e262b48eSchristos
strcasecmp_utf8_init(void)93e262b48eSchristos static void strcasecmp_utf8_init(void)
94e262b48eSchristos {
95e262b48eSchristos f1 = vstring_alloc(100);
96e262b48eSchristos f2 = vstring_alloc(100);
97e262b48eSchristos }
98e262b48eSchristos
99e262b48eSchristos /* strcasecmp_utf8x - caseless string comparison */
100e262b48eSchristos
strcasecmp_utf8x(int flags,const char * s1,const char * s2)101e262b48eSchristos int strcasecmp_utf8x(int flags, const char *s1, const char *s2)
102e262b48eSchristos {
103e262b48eSchristos
104e262b48eSchristos /*
105e262b48eSchristos * Short-circuit optimization for ASCII-only text. This may be slower
106e262b48eSchristos * than using a cache for all results. We must not expose strcasecmp(3)
107e262b48eSchristos * to non-ASCII text.
108e262b48eSchristos */
109e262b48eSchristos if (allascii(s1) && allascii(s2))
110e262b48eSchristos return (strcasecmp(s1, s2));
111e262b48eSchristos
112e262b48eSchristos if (f1 == 0)
113e262b48eSchristos strcasecmp_utf8_init();
114e262b48eSchristos
115e262b48eSchristos /*
116e262b48eSchristos * Cross our fingers and hope that strcmp() remains agnostic of
117e262b48eSchristos * charactersets and locales.
118e262b48eSchristos */
119e262b48eSchristos flags &= CASEF_FLAG_UTF8;
120e262b48eSchristos casefoldx(flags, f1, s1, -1);
121e262b48eSchristos casefoldx(flags, f2, s2, -1);
122e262b48eSchristos return (strcmp(STR(f1), STR(f2)));
123e262b48eSchristos }
124e262b48eSchristos
125e262b48eSchristos /* strncasecmp_utf8x - caseless string comparison */
126e262b48eSchristos
strncasecmp_utf8x(int flags,const char * s1,const char * s2,ssize_t len)127e262b48eSchristos int strncasecmp_utf8x(int flags, const char *s1, const char *s2,
128e262b48eSchristos ssize_t len)
129e262b48eSchristos {
130e262b48eSchristos
131e262b48eSchristos /*
132e262b48eSchristos * Consider using a cache for all results.
133e262b48eSchristos */
134e262b48eSchristos if (f1 == 0)
135e262b48eSchristos strcasecmp_utf8_init();
136e262b48eSchristos
137e262b48eSchristos /*
138e262b48eSchristos * Short-circuit optimization for ASCII-only text. This may be slower
139e262b48eSchristos * than using a cache for all results. See comments above for limitations
140e262b48eSchristos * of strcasecmp().
141e262b48eSchristos */
142e262b48eSchristos if (allascii_len(s1, len) && allascii_len(s2, len))
143e262b48eSchristos return (strncasecmp(s1, s2, len));
144e262b48eSchristos
145e262b48eSchristos /*
146e262b48eSchristos * Caution: casefolding may change the number of bytes. See comments
147e262b48eSchristos * above for concerns about strcmp().
148e262b48eSchristos */
149e262b48eSchristos flags &= CASEF_FLAG_UTF8;
150e262b48eSchristos casefoldx(flags, f1, s1, len);
151e262b48eSchristos casefoldx(flags, f2, s2, len);
152e262b48eSchristos return (strcmp(STR(f1), STR(f2)));
153e262b48eSchristos }
154e262b48eSchristos
155e262b48eSchristos #ifdef TEST
156e262b48eSchristos #include <stdio.h>
157e262b48eSchristos #include <stdlib.h>
158e262b48eSchristos #include <vstream.h>
159e262b48eSchristos #include <vstring_vstream.h>
160e262b48eSchristos #include <msg_vstream.h>
161e262b48eSchristos #include <argv.h>
162e262b48eSchristos
main(int argc,char ** argv)163e262b48eSchristos int main(int argc, char **argv)
164e262b48eSchristos {
165e262b48eSchristos VSTRING *buffer = vstring_alloc(1);
166e262b48eSchristos ARGV *cmd;
167e262b48eSchristos char **args;
168e262b48eSchristos int len;
169e262b48eSchristos int flags;
170e262b48eSchristos int res;
171e262b48eSchristos
172e262b48eSchristos msg_vstream_init(argv[0], VSTREAM_ERR);
173e262b48eSchristos flags = CASEF_FLAG_UTF8;
174e262b48eSchristos util_utf8_enable = 1;
175e262b48eSchristos while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
176e262b48eSchristos vstream_printf("> %s\n", STR(buffer));
177e262b48eSchristos cmd = argv_split(STR(buffer), CHARS_SPACE);
178e262b48eSchristos if (cmd->argc == 0 || cmd->argv[0][0] == '#')
179e262b48eSchristos continue;
180e262b48eSchristos args = cmd->argv;
181e262b48eSchristos
182e262b48eSchristos /*
183e262b48eSchristos * Compare two strings.
184e262b48eSchristos */
185e262b48eSchristos if (strcmp(args[0], "compare") == 0 && cmd->argc == 3) {
186e262b48eSchristos res = strcasecmp_utf8x(flags, args[1], args[2]);
187e262b48eSchristos vstream_printf("\"%s\" %s \"%s\"\n",
188e262b48eSchristos args[1],
189e262b48eSchristos res < 0 ? "<" : res == 0 ? "==" : ">",
190e262b48eSchristos args[2]);
191e262b48eSchristos }
192e262b48eSchristos
193e262b48eSchristos /*
194e262b48eSchristos * Compare two substrings.
195e262b48eSchristos */
196e262b48eSchristos else if (strcmp(args[0], "compare-len") == 0 && cmd->argc == 4
197e262b48eSchristos && sscanf(args[3], "%d", &len) == 1 && len >= 0) {
198e262b48eSchristos res = strncasecmp_utf8x(flags, args[1], args[2], len);
199e262b48eSchristos vstream_printf("\"%.*s\" %s \"%.*s\"\n",
200e262b48eSchristos len, args[1],
201e262b48eSchristos res < 0 ? "<" : res == 0 ? "==" : ">",
202e262b48eSchristos len, args[2]);
203e262b48eSchristos }
204e262b48eSchristos
205e262b48eSchristos /*
206e262b48eSchristos * Usage.
207e262b48eSchristos */
208e262b48eSchristos else {
209e262b48eSchristos vstream_printf("Usage: %s compare <s1> <s2> | compare-len <s1> <s2> <len>\n",
210e262b48eSchristos argv[0]);
211e262b48eSchristos }
212e262b48eSchristos vstream_fflush(VSTREAM_OUT);
213e262b48eSchristos argv_free(cmd);
214e262b48eSchristos }
215e262b48eSchristos exit(0);
216e262b48eSchristos }
217e262b48eSchristos
218e262b48eSchristos #endif /* TEST */
219