xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/strcasecmp_utf8.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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