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