xref: /freebsd/usr.bin/sort/bwstring.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
5  * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <err.h>
34 #include <langinfo.h>
35 #include <math.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <wchar.h>
39 #include <wctype.h>
40 
41 #include "bwstring.h"
42 #include "sort.h"
43 
44 bool byte_sort;
45 
46 static wchar_t **wmonths;
47 static char **cmonths;
48 
49 /* initialise months */
50 
51 void
52 initialise_months(void)
53 {
54 	const nl_item item[12] = { ABMON_1, ABMON_2, ABMON_3, ABMON_4,
55 	    ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10,
56 	    ABMON_11, ABMON_12 };
57 	char *tmp;
58 	size_t len;
59 
60 	if (mb_cur_max == 1) {
61 		if (cmonths == NULL) {
62 			char *m;
63 
64 			cmonths = sort_malloc(sizeof(char*) * 12);
65 			for (int i = 0; i < 12; i++) {
66 				cmonths[i] = NULL;
67 				tmp = nl_langinfo(item[i]);
68 				if (debug_sort)
69 					printf("month[%d]=%s\n", i, tmp);
70 				if (*tmp == '\0')
71 					continue;
72 				m = sort_strdup(tmp);
73 				len = strlen(tmp);
74 				for (unsigned int j = 0; j < len; j++)
75 					m[j] = toupper(m[j]);
76 				cmonths[i] = m;
77 			}
78 		}
79 
80 	} else {
81 		if (wmonths == NULL) {
82 			wchar_t *m;
83 
84 			wmonths = sort_malloc(sizeof(wchar_t *) * 12);
85 			for (int i = 0; i < 12; i++) {
86 				wmonths[i] = NULL;
87 				tmp = nl_langinfo(item[i]);
88 				if (debug_sort)
89 					printf("month[%d]=%s\n", i, tmp);
90 				if (*tmp == '\0')
91 					continue;
92 				len = strlen(tmp);
93 				m = sort_malloc(SIZEOF_WCHAR_STRING(len + 1));
94 				if (mbstowcs(m, tmp, len) ==
95 				    ((size_t) - 1)) {
96 					sort_free(m);
97 					continue;
98 				}
99 				m[len] = L'\0';
100 				for (unsigned int j = 0; j < len; j++)
101 					m[j] = towupper(m[j]);
102 				wmonths[i] = m;
103 			}
104 		}
105 	}
106 }
107 
108 /*
109  * Compare two wide-character strings
110  */
111 static int
112 wide_str_coll(const wchar_t *s1, const wchar_t *s2)
113 {
114 	int ret;
115 
116 	errno = 0;
117 	ret = wcscoll(s1, s2);
118 	if (errno == EILSEQ) {
119 		errno = 0;
120 		ret = wcscmp(s1, s2);
121 		if (errno != 0) {
122 			for (size_t i = 0; ; ++i) {
123 				wchar_t c1 = s1[i];
124 				wchar_t c2 = s2[i];
125 				if (c1 == L'\0')
126 					return ((c2 == L'\0') ? 0 : -1);
127 				if (c2 == L'\0')
128 					return (+1);
129 				if (c1 == c2)
130 					continue;
131 				return ((int)(c1 - c2));
132 			}
133 		}
134 	}
135 	return (ret);
136 }
137 
138 /* counterparts of wcs functions */
139 
140 void
141 bwsprintf(FILE *f, struct bwstring *bws, const char *prefix, const char *suffix)
142 {
143 
144 	if (mb_cur_max == 1)
145 		fprintf(f, "%s%s%s", prefix, bws->cdata.str, suffix);
146 	else
147 		fprintf(f, "%s%S%s", prefix, bws->wdata.str, suffix);
148 }
149 
150 const void* bwsrawdata(const struct bwstring *bws)
151 {
152 
153 	return (bws->wdata.str);
154 }
155 
156 size_t bwsrawlen(const struct bwstring *bws)
157 {
158 
159 	return ((mb_cur_max == 1) ? bws->cdata.len :
160 	    SIZEOF_WCHAR_STRING(bws->wdata.len));
161 }
162 
163 size_t
164 bws_memsize(const struct bwstring *bws)
165 {
166 
167 	return ((mb_cur_max == 1) ?
168 	    (bws->cdata.len + 2 + sizeof(struct bwstring)) :
169 	    (SIZEOF_WCHAR_STRING(bws->wdata.len + 1) + sizeof(struct bwstring)));
170 }
171 
172 void
173 bws_setlen(struct bwstring *bws, size_t newlen)
174 {
175 
176 	if (mb_cur_max == 1 && bws && newlen != bws->cdata.len &&
177 	    newlen <= bws->cdata.len) {
178 		bws->cdata.len = newlen;
179 		bws->cdata.str[newlen] = '\0';
180 	} else if (bws && newlen != bws->wdata.len && newlen <= bws->wdata.len) {
181 		bws->wdata.len = newlen;
182 		bws->wdata.str[newlen] = L'\0';
183 	}
184 }
185 
186 /*
187  * Allocate a new binary string of specified size
188  */
189 struct bwstring *
190 bwsalloc(size_t sz)
191 {
192 	struct bwstring *ret;
193 
194 	if (mb_cur_max == 1) {
195 		ret = sort_malloc(sizeof(struct bwstring) + 1 + sz);
196 		ret->cdata.len = sz;
197 		ret->cdata.str[sz] = '\0';
198 	} else {
199 		ret = sort_malloc(
200 		    sizeof(struct bwstring) + SIZEOF_WCHAR_STRING(sz + 1));
201 		ret->wdata.len = sz;
202 		ret->wdata.str[sz] = L'\0';
203 	}
204 
205 	return (ret);
206 }
207 
208 /*
209  * Create a copy of binary string.
210  * New string size equals the length of the old string.
211  */
212 struct bwstring *
213 bwsdup(const struct bwstring *s)
214 {
215 
216 	if (s == NULL)
217 		return (NULL);
218 	else {
219 		struct bwstring *ret = bwsalloc(BWSLEN(s));
220 
221 		if (mb_cur_max == 1)
222 			memcpy(ret->cdata.str, s->cdata.str, (s->cdata.len));
223 		else
224 			memcpy(ret->wdata.str, s->wdata.str,
225 			    SIZEOF_WCHAR_STRING(s->wdata.len));
226 
227 		return (ret);
228 	}
229 }
230 
231 /*
232  * Create a new binary string from a wide character buffer.
233  */
234 struct bwstring *
235 bwssbdup(const wchar_t *str, size_t len)
236 {
237 
238 	if (str == NULL)
239 		return ((len == 0) ? bwsalloc(0) : NULL);
240 	else {
241 		struct bwstring *ret;
242 
243 		ret = bwsalloc(len);
244 
245 		if (mb_cur_max == 1)
246 			for (size_t i = 0; i < len; ++i)
247 				ret->cdata.str[i] = (char)str[i];
248 		else
249 			memcpy(ret->wdata.str, str, SIZEOF_WCHAR_STRING(len));
250 
251 		return (ret);
252 	}
253 }
254 
255 /*
256  * Create a new binary string from a raw binary buffer.
257  */
258 struct bwstring *
259 bwscsbdup(const unsigned char *str, size_t len)
260 {
261 	struct bwstring *ret;
262 
263 	ret = bwsalloc(len);
264 
265 	if (str) {
266 		if (mb_cur_max == 1)
267 			memcpy(ret->cdata.str, str, len);
268 		else {
269 			mbstate_t mbs;
270 			const char *s;
271 			size_t charlen, chars, cptr;
272 
273 			chars = 0;
274 			cptr = 0;
275 			s = (const char *) str;
276 
277 			memset(&mbs, 0, sizeof(mbs));
278 
279 			while (cptr < len) {
280 				size_t n = mb_cur_max;
281 
282 				if (n > len - cptr)
283 					n = len - cptr;
284 				charlen = mbrlen(s + cptr, n, &mbs);
285 				switch (charlen) {
286 				case 0:
287 					/* FALLTHROUGH */
288 				case (size_t) -1:
289 					/* FALLTHROUGH */
290 				case (size_t) -2:
291 					ret->wdata.str[chars++] =
292 					    (unsigned char) s[cptr];
293 					++cptr;
294 					break;
295 				default:
296 					n = mbrtowc(ret->wdata.str + (chars++),
297 					    s + cptr, charlen, &mbs);
298 					if ((n == (size_t)-1) || (n == (size_t)-2))
299 						/* NOTREACHED */
300 						err(2, "mbrtowc error");
301 					cptr += charlen;
302 				}
303 			}
304 
305 			ret->wdata.len = chars;
306 			ret->wdata.str[ret->wdata.len] = L'\0';
307 		}
308 	}
309 	return (ret);
310 }
311 
312 /*
313  * De-allocate object memory
314  */
315 void
316 bwsfree(const struct bwstring *s)
317 {
318 
319 	if (s)
320 		sort_free(s);
321 }
322 
323 /*
324  * Copy content of src binary string to dst,
325  * with specified number of symbols to be copied.
326  * An offset value can be specified, from the start of src string.
327  * If the capacity of the dst string is not sufficient,
328  * then the data is truncated.
329  */
330 struct bwstring *
331 bwsnocpy(struct bwstring *dst, const struct bwstring *src, size_t offset,
332     size_t size)
333 {
334 
335 	if (offset >= BWSLEN(src)) {
336 		bws_setlen(dst, 0);
337 	} else {
338 		size_t nums = BWSLEN(src) - offset;
339 
340 		if (nums > BWSLEN(dst))
341 			nums = BWSLEN(dst);
342 		if (nums > size)
343 			nums = size;
344 		if (mb_cur_max == 1) {
345 			memcpy(dst->cdata.str, src->cdata.str + offset, nums);
346 			dst->cdata.len = nums;
347 			dst->cdata.str[nums] = '\0';
348 		} else {
349 			memcpy(dst->wdata.str, src->wdata.str + offset,
350 			    SIZEOF_WCHAR_STRING(nums));
351 			dst->wdata.len = nums;
352 			dst->wdata.str[nums] = L'\0';
353 		}
354 	}
355 	return (dst);
356 }
357 
358 /*
359  * Write binary string to the file.
360  * The output is ended either with '\n' (nl == true)
361  * or '\0' (nl == false).
362  */
363 size_t
364 bwsfwrite(struct bwstring *bws, FILE *f, bool zero_ended)
365 {
366 
367 	if (mb_cur_max == 1) {
368 		size_t len = bws->cdata.len;
369 
370 		if (!zero_ended) {
371 			bws->cdata.str[len] = '\n';
372 
373 			if (fwrite(bws->cdata.str, len + 1, 1, f) < 1)
374 				err(2, NULL);
375 
376 			bws->cdata.str[len] = '\0';
377 		} else if (fwrite(bws->cdata.str, len + 1, 1, f) < 1)
378 			err(2, NULL);
379 
380 		return (len + 1);
381 
382 	} else {
383 		wchar_t eols;
384 		size_t printed = 0;
385 
386 		eols = zero_ended ? btowc('\0') : btowc('\n');
387 
388 		while (printed < BWSLEN(bws)) {
389 			const wchar_t *s = bws->wdata.str + printed;
390 
391 			if (*s == L'\0') {
392 				int nums;
393 
394 				nums = fwprintf(f, L"%lc", *s);
395 
396 				if (nums != 1)
397 					err(2, NULL);
398 				++printed;
399 			} else {
400 				int nums;
401 
402 				nums = fwprintf(f, L"%ls", s);
403 
404 				if (nums < 1)
405 					err(2, NULL);
406 				printed += nums;
407 			}
408 		}
409 		fwprintf(f, L"%lc", eols);
410 		return (printed + 1);
411 	}
412 }
413 
414 int
415 bwsncmp(const struct bwstring *bws1, const struct bwstring *bws2,
416     size_t offset, size_t len)
417 {
418 	size_t cmp_len, len1, len2;
419 	int res;
420 
421 	len1 = BWSLEN(bws1);
422 	len2 = BWSLEN(bws2);
423 
424 	if (len1 <= offset) {
425 		return ((len2 <= offset) ? 0 : -1);
426 	} else {
427 		if (len2 <= offset)
428 			return (+1);
429 		else {
430 			len1 -= offset;
431 			len2 -= offset;
432 
433 			cmp_len = len1;
434 
435 			if (len2 < cmp_len)
436 				cmp_len = len2;
437 
438 			if (len < cmp_len)
439 				cmp_len = len;
440 
441 			if (mb_cur_max == 1) {
442 				const char *s1, *s2;
443 
444 				s1 = bws1->cdata.str + offset;
445 				s2 = bws2->cdata.str + offset;
446 
447 				res = memcmp(s1, s2, cmp_len);
448 
449 			} else {
450 				const wchar_t *s1, *s2;
451 
452 				s1 = bws1->wdata.str + offset;
453 				s2 = bws2->wdata.str + offset;
454 
455 				res = memcmp(s1, s2, SIZEOF_WCHAR_STRING(cmp_len));
456 			}
457 		}
458 	}
459 
460 	if (res == 0) {
461 		if (len1 < cmp_len && len1 < len2)
462 			res = -1;
463 		else if (len2 < cmp_len && len2 < len1)
464 			res = +1;
465 	}
466 
467 	return (res);
468 }
469 
470 int
471 bwscmp(const struct bwstring *bws1, const struct bwstring *bws2, size_t offset)
472 {
473 	size_t len1, len2, cmp_len;
474 	int res;
475 
476 	len1 = BWSLEN(bws1);
477 	len2 = BWSLEN(bws2);
478 
479 	len1 -= offset;
480 	len2 -= offset;
481 
482 	cmp_len = len1;
483 
484 	if (len2 < cmp_len)
485 		cmp_len = len2;
486 
487 	res = bwsncmp(bws1, bws2, offset, cmp_len);
488 
489 	if (res == 0) {
490 		if( len1 < len2)
491 			res = -1;
492 		else if (len2 < len1)
493 			res = +1;
494 	}
495 
496 	return (res);
497 }
498 
499 int
500 bws_iterator_cmp(bwstring_iterator iter1, bwstring_iterator iter2, size_t len)
501 {
502 	wchar_t c1, c2;
503 	size_t i;
504 
505 	for (i = 0; i < len; ++i) {
506 		c1 = bws_get_iter_value(iter1);
507 		c2 = bws_get_iter_value(iter2);
508 		if (c1 != c2)
509 			return (c1 - c2);
510 		iter1 = bws_iterator_inc(iter1, 1);
511 		iter2 = bws_iterator_inc(iter2, 1);
512 	}
513 
514 	return (0);
515 }
516 
517 int
518 bwscoll(const struct bwstring *bws1, const struct bwstring *bws2, size_t offset)
519 {
520 	size_t len1, len2;
521 
522 	len1 = BWSLEN(bws1);
523 	len2 = BWSLEN(bws2);
524 
525 	if (len1 <= offset)
526 		return ((len2 <= offset) ? 0 : -1);
527 	else {
528 		if (len2 <= offset)
529 			return (+1);
530 		else {
531 			len1 -= offset;
532 			len2 -= offset;
533 
534 			if (mb_cur_max == 1) {
535 				const char *s1, *s2;
536 
537 				s1 = bws1->cdata.str + offset;
538 				s2 = bws2->cdata.str + offset;
539 
540 				if (byte_sort) {
541 					int res;
542 
543 					if (len1 > len2) {
544 						res = memcmp(s1, s2, len2);
545 						if (!res)
546 							res = +1;
547 					} else if (len1 < len2) {
548 						res = memcmp(s1, s2, len1);
549 						if (!res)
550 							res = -1;
551 					} else
552 						res = memcmp(s1, s2, len1);
553 
554 					return (res);
555 
556 				} else {
557 					int res;
558 					size_t i, maxlen;
559 
560 					i = 0;
561 					maxlen = len1;
562 
563 					if (maxlen > len2)
564 						maxlen = len2;
565 
566 					while (i < maxlen) {
567 						/* goto next non-zero part: */
568 						while ((i < maxlen) &&
569 						    !s1[i] && !s2[i])
570 							++i;
571 
572 						if (i >= maxlen)
573 							break;
574 
575 						if (s1[i] == 0) {
576 							if (s2[i] == 0)
577 								/* NOTREACHED */
578 								err(2, "bwscoll error 01");
579 							else
580 								return (-1);
581 						} else if (s2[i] == 0)
582 							return (+1);
583 
584 						res = strcoll((const char*)(s1 + i), (const char*)(s2 + i));
585 						if (res)
586 							return (res);
587 
588 						while ((i < maxlen) &&
589 						    s1[i] && s2[i])
590 							++i;
591 
592 						if (i >= maxlen)
593 							break;
594 
595 						if (s1[i] == 0) {
596 							if (s2[i] == 0) {
597 								++i;
598 								continue;
599 							} else
600 								return (-1);
601 						} else if (s2[i] == 0)
602 							return (+1);
603 						else
604 							/* NOTREACHED */
605 							err(2, "bwscoll error 02");
606 					}
607 
608 					if (len1 < len2)
609 						return (-1);
610 					else if (len1 > len2)
611 						return (+1);
612 
613 					return (0);
614 				}
615 			} else {
616 				const wchar_t *s1, *s2;
617 				size_t i, maxlen;
618 				int res;
619 
620 				s1 = bws1->wdata.str + offset;
621 				s2 = bws2->wdata.str + offset;
622 
623 				i = 0;
624 				maxlen = len1;
625 
626 				if (maxlen > len2)
627 					maxlen = len2;
628 
629 				while (i < maxlen) {
630 
631 					/* goto next non-zero part: */
632 					while ((i < maxlen) &&
633 					    !s1[i] && !s2[i])
634 						++i;
635 
636 					if (i >= maxlen)
637 						break;
638 
639 					if (s1[i] == 0) {
640 						if (s2[i] == 0)
641 							/* NOTREACHED */
642 							err(2, "bwscoll error 1");
643 						else
644 							return (-1);
645 					} else if (s2[i] == 0)
646 						return (+1);
647 
648 					res = wide_str_coll(s1 + i, s2 + i);
649 					if (res)
650 						return (res);
651 
652 					while ((i < maxlen) && s1[i] && s2[i])
653 						++i;
654 
655 					if (i >= maxlen)
656 						break;
657 
658 					if (s1[i] == 0) {
659 						if (s2[i] == 0) {
660 							++i;
661 							continue;
662 						} else
663 							return (-1);
664 					} else if (s2[i] == 0)
665 						return (+1);
666 					else
667 						/* NOTREACHED */
668 						err(2, "bwscoll error 2");
669 				}
670 
671 				if (len1 < len2)
672 					return (-1);
673 				else if (len1 > len2)
674 					return (+1);
675 
676 				return (0);
677 			}
678 		}
679 	}
680 }
681 
682 /*
683  * Correction of the system API
684  */
685 double
686 bwstod(struct bwstring *s0, bool *empty)
687 {
688 	double ret;
689 
690 	if (mb_cur_max == 1) {
691 		char *end, *s;
692 		char *ep;
693 
694 		s = s0->cdata.str;
695 		end = s + s0->cdata.len;
696 		ep = NULL;
697 
698 		while (isblank(*s) && s < end)
699 			++s;
700 
701 		if (!isprint(*s)) {
702 			*empty = true;
703 			return (0);
704 		}
705 
706 		ret = strtod((char*)s, &ep);
707 		if (ep == s) {
708 			*empty = true;
709 			return (0);
710 		}
711 	} else {
712 		wchar_t *end, *ep, *s;
713 
714 		s = s0->wdata.str;
715 		end = s + s0->wdata.len;
716 		ep = NULL;
717 
718 		while (iswblank(*s) && s < end)
719 			++s;
720 
721 		if (!iswprint(*s)) {
722 			*empty = true;
723 			return (0);
724 		}
725 
726 		ret = wcstod(s, &ep);
727 		if (ep == s) {
728 			*empty = true;
729 			return (0);
730 		}
731 	}
732 
733 	*empty = false;
734 	return (ret);
735 }
736 
737 /*
738  * A helper function for monthcoll.  If a line matches
739  * a month name, it returns (number of the month - 1),
740  * while if there is no match, it just return -1.
741  */
742 
743 int
744 bws_month_score(const struct bwstring *s0)
745 {
746 
747 	if (mb_cur_max == 1) {
748 		const char *end, *s;
749 
750 		s = s0->cdata.str;
751 		end = s + s0->cdata.len;
752 
753 		while (isblank(*s) && s < end)
754 			++s;
755 
756 		for (int i = 11; i >= 0; --i) {
757 			if (cmonths[i] &&
758 			    (s == strstr(s, cmonths[i])))
759 				return (i);
760 		}
761 
762 	} else {
763 		const wchar_t *end, *s;
764 
765 		s = s0->wdata.str;
766 		end = s + s0->wdata.len;
767 
768 		while (iswblank(*s) && s < end)
769 			++s;
770 
771 		for (int i = 11; i >= 0; --i) {
772 			if (wmonths[i] && (s == wcsstr(s, wmonths[i])))
773 				return (i);
774 		}
775 	}
776 
777 	return (-1);
778 }
779 
780 /*
781  * Rips out leading blanks (-b).
782  */
783 struct bwstring *
784 ignore_leading_blanks(struct bwstring *str)
785 {
786 
787 	if (mb_cur_max == 1) {
788 		char *dst, *end, *src;
789 
790 		src = str->cdata.str;
791 		dst = src;
792 		end = src + str->cdata.len;
793 
794 		while (src < end && isblank(*src))
795 			++src;
796 
797 		if (src != dst) {
798 			size_t newlen;
799 
800 			newlen = BWSLEN(str) - (src - dst);
801 
802 			while (src < end) {
803 				*dst = *src;
804 				++dst;
805 				++src;
806 			}
807 			bws_setlen(str, newlen);
808 		}
809 	} else {
810 		wchar_t *dst, *end, *src;
811 
812 		src = str->wdata.str;
813 		dst = src;
814 		end = src + str->wdata.len;
815 
816 		while (src < end && iswblank(*src))
817 			++src;
818 
819 		if (src != dst) {
820 
821 			size_t newlen = BWSLEN(str) - (src - dst);
822 
823 			while (src < end) {
824 				*dst = *src;
825 				++dst;
826 				++src;
827 			}
828 			bws_setlen(str, newlen);
829 
830 		}
831 	}
832 	return (str);
833 }
834 
835 /*
836  * Rips out nonprinting characters (-i).
837  */
838 struct bwstring *
839 ignore_nonprinting(struct bwstring *str)
840 {
841 	size_t newlen = BWSLEN(str);
842 
843 	if (mb_cur_max == 1) {
844 		char *dst, *end, *src;
845 		char c;
846 
847 		src = str->cdata.str;
848 		dst = src;
849 		end = src + str->cdata.len;
850 
851 		while (src < end) {
852 			c = *src;
853 			if (isprint(c)) {
854 				*dst = c;
855 				++dst;
856 				++src;
857 			} else {
858 				++src;
859 				--newlen;
860 			}
861 		}
862 	} else {
863 		wchar_t *dst, *end, *src;
864 		wchar_t c;
865 
866 		src = str->wdata.str;
867 		dst = src;
868 		end = src + str->wdata.len;
869 
870 		while (src < end) {
871 			c = *src;
872 			if (iswprint(c)) {
873 				*dst = c;
874 				++dst;
875 				++src;
876 			} else {
877 				++src;
878 				--newlen;
879 			}
880 		}
881 	}
882 	bws_setlen(str, newlen);
883 
884 	return (str);
885 }
886 
887 /*
888  * Rips out any characters that are not alphanumeric characters
889  * nor blanks (-d).
890  */
891 struct bwstring *
892 dictionary_order(struct bwstring *str)
893 {
894 	size_t newlen = BWSLEN(str);
895 
896 	if (mb_cur_max == 1) {
897 		char *dst, *end, *src;
898 		char c;
899 
900 		src = str->cdata.str;
901 		dst = src;
902 		end = src + str->cdata.len;
903 
904 		while (src < end) {
905 			c = *src;
906 			if (isalnum(c) || isblank(c)) {
907 				*dst = c;
908 				++dst;
909 				++src;
910 			} else {
911 				++src;
912 				--newlen;
913 			}
914 		}
915 	} else {
916 		wchar_t *dst, *end, *src;
917 		wchar_t c;
918 
919 		src = str->wdata.str;
920 		dst = src;
921 		end = src + str->wdata.len;
922 
923 		while (src < end) {
924 			c = *src;
925 			if (iswalnum(c) || iswblank(c)) {
926 				*dst = c;
927 				++dst;
928 				++src;
929 			} else {
930 				++src;
931 				--newlen;
932 			}
933 		}
934 	}
935 	bws_setlen(str, newlen);
936 
937 	return (str);
938 }
939 
940 /*
941  * Converts string to lower case(-f).
942  */
943 struct bwstring *
944 ignore_case(struct bwstring *str)
945 {
946 
947 	if (mb_cur_max == 1) {
948 		char *end, *s;
949 
950 		s = str->cdata.str;
951 		end = s + str->cdata.len;
952 
953 		while (s < end) {
954 			*s = toupper(*s);
955 			++s;
956 		}
957 	} else {
958 		wchar_t *end, *s;
959 
960 		s = str->wdata.str;
961 		end = s + str->wdata.len;
962 
963 		while (s < end) {
964 			*s = towupper(*s);
965 			++s;
966 		}
967 	}
968 	return (str);
969 }
970 
971 void
972 bws_disorder_warnx(struct bwstring *s, const char *fn, size_t pos)
973 {
974 
975 	if (mb_cur_max == 1)
976 		warnx("%s:%zu: disorder: %s", fn, pos + 1, s->cdata.str);
977 	else
978 		warnx("%s:%zu: disorder: %ls", fn, pos + 1, s->wdata.str);
979 }
980