xref: /dflybsd-src/usr.bin/sort/coll.c (revision a98f7024a73d3aaccc56d8f7c0082a60a274a0a5)
150fc853eSJohn Marino /*-
250fc853eSJohn Marino  * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
350fc853eSJohn Marino  * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
450fc853eSJohn Marino  * All rights reserved.
550fc853eSJohn Marino  *
650fc853eSJohn Marino  * Redistribution and use in source and binary forms, with or without
750fc853eSJohn Marino  * modification, are permitted provided that the following conditions
850fc853eSJohn Marino  * are met:
950fc853eSJohn Marino  * 1. Redistributions of source code must retain the above copyright
1050fc853eSJohn Marino  *    notice, this list of conditions and the following disclaimer.
1150fc853eSJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
1250fc853eSJohn Marino  *    notice, this list of conditions and the following disclaimer in the
1350fc853eSJohn Marino  *    documentation and/or other materials provided with the distribution.
1450fc853eSJohn Marino  *
1550fc853eSJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1650fc853eSJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1750fc853eSJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1850fc853eSJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1950fc853eSJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2050fc853eSJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2150fc853eSJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2250fc853eSJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2350fc853eSJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2450fc853eSJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2550fc853eSJohn Marino  * SUCH DAMAGE.
2650fc853eSJohn Marino  *
2750fc853eSJohn Marino  * $FreeBSD: head/usr.bin/sort/coll.c 281132 2015-04-06 02:35:55Z pfg $
2850fc853eSJohn Marino  */
2950fc853eSJohn Marino 
3050fc853eSJohn Marino 
3150fc853eSJohn Marino #include <sys/types.h>
3250fc853eSJohn Marino 
3350fc853eSJohn Marino #include <errno.h>
3450fc853eSJohn Marino #include <err.h>
3550fc853eSJohn Marino #include <langinfo.h>
3650fc853eSJohn Marino #include <limits.h>
3750fc853eSJohn Marino #include <math.h>
3850fc853eSJohn Marino #include <stdlib.h>
3950fc853eSJohn Marino #include <string.h>
4050fc853eSJohn Marino #include <wchar.h>
4150fc853eSJohn Marino #include <wctype.h>
42*a98f7024Szrj #if defined(SORT_RANDOM)
43*a98f7024Szrj #include <openssl/md5.h>
44*a98f7024Szrj #endif
4550fc853eSJohn Marino 
4650fc853eSJohn Marino #include "coll.h"
4750fc853eSJohn Marino #include "vsort.h"
4850fc853eSJohn Marino 
4950fc853eSJohn Marino struct key_specs *keys;
5050fc853eSJohn Marino size_t keys_num = 0;
5150fc853eSJohn Marino 
5250fc853eSJohn Marino wint_t symbol_decimal_point = L'.';
5350fc853eSJohn Marino /* there is no default thousands separator in collate rules: */
5450fc853eSJohn Marino wint_t symbol_thousands_sep = 0;
5550fc853eSJohn Marino wint_t symbol_negative_sign = L'-';
5650fc853eSJohn Marino wint_t symbol_positive_sign = L'+';
5750fc853eSJohn Marino 
5850fc853eSJohn Marino static int wstrcoll(struct key_value *kv1, struct key_value *kv2, size_t offset);
5950fc853eSJohn Marino static int gnumcoll(struct key_value*, struct key_value *, size_t offset);
6050fc853eSJohn Marino static int monthcoll(struct key_value*, struct key_value *, size_t offset);
6150fc853eSJohn Marino static int numcoll(struct key_value*, struct key_value *, size_t offset);
6250fc853eSJohn Marino static int hnumcoll(struct key_value*, struct key_value *, size_t offset);
636d7e22ecSzrj #if defined(SORT_RANDOM)
6450fc853eSJohn Marino static int randomcoll(struct key_value*, struct key_value *, size_t offset);
656d7e22ecSzrj #endif
6650fc853eSJohn Marino static int versioncoll(struct key_value*, struct key_value *, size_t offset);
6750fc853eSJohn Marino 
6850fc853eSJohn Marino /*
6950fc853eSJohn Marino  * Allocate keys array
7050fc853eSJohn Marino  */
7150fc853eSJohn Marino struct keys_array *
keys_array_alloc(void)7250fc853eSJohn Marino keys_array_alloc(void)
7350fc853eSJohn Marino {
7450fc853eSJohn Marino 	struct keys_array *ka;
7550fc853eSJohn Marino 	size_t sz;
7650fc853eSJohn Marino 
7750fc853eSJohn Marino 	sz = keys_array_size();
7850fc853eSJohn Marino 	ka = sort_malloc(sz);
7950fc853eSJohn Marino 	memset(ka, 0, sz);
8050fc853eSJohn Marino 
8150fc853eSJohn Marino 	return (ka);
8250fc853eSJohn Marino }
8350fc853eSJohn Marino 
8450fc853eSJohn Marino /*
8550fc853eSJohn Marino  * Calculate whether we need key hint space
8650fc853eSJohn Marino  */
8750fc853eSJohn Marino static size_t
key_hint_size(void)8850fc853eSJohn Marino key_hint_size(void)
8950fc853eSJohn Marino {
9050fc853eSJohn Marino 
9150fc853eSJohn Marino 	return (need_hint ? sizeof(struct key_hint) : 0);
9250fc853eSJohn Marino }
9350fc853eSJohn Marino 
9450fc853eSJohn Marino /*
9550fc853eSJohn Marino  * Calculate keys array size
9650fc853eSJohn Marino  */
9750fc853eSJohn Marino size_t
keys_array_size(void)9850fc853eSJohn Marino keys_array_size(void)
9950fc853eSJohn Marino {
10050fc853eSJohn Marino 
10150fc853eSJohn Marino 	return (keys_num * (sizeof(struct key_value) + key_hint_size()));
10250fc853eSJohn Marino }
10350fc853eSJohn Marino 
10450fc853eSJohn Marino /*
10550fc853eSJohn Marino  * Clean data of keys array
10650fc853eSJohn Marino  */
10750fc853eSJohn Marino void
clean_keys_array(const struct bwstring * s,struct keys_array * ka)10850fc853eSJohn Marino clean_keys_array(const struct bwstring *s, struct keys_array *ka)
10950fc853eSJohn Marino {
11050fc853eSJohn Marino 
11150fc853eSJohn Marino 	if (ka) {
11250fc853eSJohn Marino 		for (size_t i = 0; i < keys_num; ++i)
11350fc853eSJohn Marino 			if (ka->key[i].k && ka->key[i].k != s)
11450fc853eSJohn Marino 				bwsfree(ka->key[i].k);
11550fc853eSJohn Marino 		memset(ka, 0, keys_array_size());
11650fc853eSJohn Marino 	}
11750fc853eSJohn Marino }
11850fc853eSJohn Marino 
11950fc853eSJohn Marino /*
12050fc853eSJohn Marino  * Set value of a key in the keys set
12150fc853eSJohn Marino  */
12250fc853eSJohn Marino void
set_key_on_keys_array(struct keys_array * ka,struct bwstring * s,size_t ind)12350fc853eSJohn Marino set_key_on_keys_array(struct keys_array *ka, struct bwstring *s, size_t ind)
12450fc853eSJohn Marino {
12550fc853eSJohn Marino 
12650fc853eSJohn Marino 	if (ka && keys_num > ind) {
12750fc853eSJohn Marino 		struct key_value *kv;
12850fc853eSJohn Marino 
12950fc853eSJohn Marino 		kv = &(ka->key[ind]);
13050fc853eSJohn Marino 
13150fc853eSJohn Marino 		if (kv->k && kv->k != s)
13250fc853eSJohn Marino 			bwsfree(kv->k);
13350fc853eSJohn Marino 		kv->k = s;
13450fc853eSJohn Marino 	}
13550fc853eSJohn Marino }
13650fc853eSJohn Marino 
13750fc853eSJohn Marino /*
13850fc853eSJohn Marino  * Initialize a sort list item
13950fc853eSJohn Marino  */
14050fc853eSJohn Marino struct sort_list_item *
sort_list_item_alloc(void)14150fc853eSJohn Marino sort_list_item_alloc(void)
14250fc853eSJohn Marino {
14350fc853eSJohn Marino 	struct sort_list_item *si;
14450fc853eSJohn Marino 	size_t sz;
14550fc853eSJohn Marino 
14650fc853eSJohn Marino 	sz = sizeof(struct sort_list_item) + keys_array_size();
14750fc853eSJohn Marino 	si = sort_malloc(sz);
14850fc853eSJohn Marino 	memset(si, 0, sz);
14950fc853eSJohn Marino 
15050fc853eSJohn Marino 	return (si);
15150fc853eSJohn Marino }
15250fc853eSJohn Marino 
15350fc853eSJohn Marino size_t
sort_list_item_size(struct sort_list_item * si)15450fc853eSJohn Marino sort_list_item_size(struct sort_list_item *si)
15550fc853eSJohn Marino {
15650fc853eSJohn Marino 	size_t ret = 0;
15750fc853eSJohn Marino 
15850fc853eSJohn Marino 	if (si) {
15950fc853eSJohn Marino 		ret = sizeof(struct sort_list_item) + keys_array_size();
16050fc853eSJohn Marino 		if (si->str)
16150fc853eSJohn Marino 			ret += bws_memsize(si->str);
16250fc853eSJohn Marino 		for (size_t i = 0; i < keys_num; ++i) {
16350fc853eSJohn Marino 			struct key_value *kv;
16450fc853eSJohn Marino 
16550fc853eSJohn Marino 			kv = &(si->ka.key[i]);
16650fc853eSJohn Marino 
16750fc853eSJohn Marino 			if (kv->k != si->str)
16850fc853eSJohn Marino 				ret += bws_memsize(kv->k);
16950fc853eSJohn Marino 		}
17050fc853eSJohn Marino 	}
17150fc853eSJohn Marino 	return (ret);
17250fc853eSJohn Marino }
17350fc853eSJohn Marino 
17450fc853eSJohn Marino /*
17550fc853eSJohn Marino  * Calculate key for a sort list item
17650fc853eSJohn Marino  */
17750fc853eSJohn Marino static void
sort_list_item_make_key(struct sort_list_item * si)17850fc853eSJohn Marino sort_list_item_make_key(struct sort_list_item *si)
17950fc853eSJohn Marino {
18050fc853eSJohn Marino 
18150fc853eSJohn Marino 	preproc(si->str, &(si->ka));
18250fc853eSJohn Marino }
18350fc853eSJohn Marino 
18450fc853eSJohn Marino /*
18550fc853eSJohn Marino  * Set value of a sort list item.
18650fc853eSJohn Marino  * Return combined string and keys memory size.
18750fc853eSJohn Marino  */
18850fc853eSJohn Marino void
sort_list_item_set(struct sort_list_item * si,struct bwstring * str)18950fc853eSJohn Marino sort_list_item_set(struct sort_list_item *si, struct bwstring *str)
19050fc853eSJohn Marino {
19150fc853eSJohn Marino 
19250fc853eSJohn Marino 	if (si) {
19350fc853eSJohn Marino 		clean_keys_array(si->str, &(si->ka));
19450fc853eSJohn Marino 		if (si->str) {
19550fc853eSJohn Marino 			if (si->str == str) {
19650fc853eSJohn Marino 				/* we are trying to reset the same string */
19750fc853eSJohn Marino 				return;
19850fc853eSJohn Marino 			} else {
19950fc853eSJohn Marino 				bwsfree(si->str);
20050fc853eSJohn Marino 				si->str = NULL;
20150fc853eSJohn Marino 			}
20250fc853eSJohn Marino 		}
20350fc853eSJohn Marino 		si->str = str;
20450fc853eSJohn Marino 		sort_list_item_make_key(si);
20550fc853eSJohn Marino 	}
20650fc853eSJohn Marino }
20750fc853eSJohn Marino 
20850fc853eSJohn Marino /*
20950fc853eSJohn Marino  * De-allocate a sort list item object memory
21050fc853eSJohn Marino  */
21150fc853eSJohn Marino void
sort_list_item_clean(struct sort_list_item * si)21250fc853eSJohn Marino sort_list_item_clean(struct sort_list_item *si)
21350fc853eSJohn Marino {
21450fc853eSJohn Marino 
21550fc853eSJohn Marino 	if (si) {
21650fc853eSJohn Marino 		clean_keys_array(si->str, &(si->ka));
21750fc853eSJohn Marino 		if (si->str) {
21850fc853eSJohn Marino 			bwsfree(si->str);
21950fc853eSJohn Marino 			si->str = NULL;
22050fc853eSJohn Marino 		}
22150fc853eSJohn Marino 	}
22250fc853eSJohn Marino }
22350fc853eSJohn Marino 
22450fc853eSJohn Marino /*
22550fc853eSJohn Marino  * Skip columns according to specs
22650fc853eSJohn Marino  */
22750fc853eSJohn Marino static size_t
skip_cols_to_start(const struct bwstring * s,size_t cols,size_t start,bool skip_blanks,bool * empty_key)22850fc853eSJohn Marino skip_cols_to_start(const struct bwstring *s, size_t cols, size_t start,
22950fc853eSJohn Marino     bool skip_blanks, bool *empty_key)
23050fc853eSJohn Marino {
23150fc853eSJohn Marino 	if (cols < 1)
23250fc853eSJohn Marino 		return (BWSLEN(s) + 1);
23350fc853eSJohn Marino 
23450fc853eSJohn Marino 	if (skip_blanks)
23550fc853eSJohn Marino 		while (start < BWSLEN(s) && iswblank(BWS_GET(s,start)))
23650fc853eSJohn Marino 			++start;
23750fc853eSJohn Marino 
23850fc853eSJohn Marino 	while (start < BWSLEN(s) && cols > 1) {
23950fc853eSJohn Marino 		--cols;
24050fc853eSJohn Marino 		++start;
24150fc853eSJohn Marino 	}
24250fc853eSJohn Marino 
24350fc853eSJohn Marino 	if (start >= BWSLEN(s))
24450fc853eSJohn Marino 		*empty_key = true;
24550fc853eSJohn Marino 
24650fc853eSJohn Marino 	return (start);
24750fc853eSJohn Marino }
24850fc853eSJohn Marino 
24950fc853eSJohn Marino /*
25050fc853eSJohn Marino  * Skip fields according to specs
25150fc853eSJohn Marino  */
25250fc853eSJohn Marino static size_t
skip_fields_to_start(const struct bwstring * s,size_t fields,bool * empty_field)25350fc853eSJohn Marino skip_fields_to_start(const struct bwstring *s, size_t fields, bool *empty_field)
25450fc853eSJohn Marino {
25550fc853eSJohn Marino 
25650fc853eSJohn Marino 	if (fields < 2) {
25750fc853eSJohn Marino 		if (BWSLEN(s) == 0)
25850fc853eSJohn Marino 			*empty_field = true;
25950fc853eSJohn Marino 		return (0);
26050fc853eSJohn Marino 	} else if (!(sort_opts_vals.tflag)) {
26150fc853eSJohn Marino 		size_t cpos = 0;
26250fc853eSJohn Marino 		bool pb = true;
26350fc853eSJohn Marino 
26450fc853eSJohn Marino 		while (cpos < BWSLEN(s)) {
26550fc853eSJohn Marino 			bool isblank;
26650fc853eSJohn Marino 
26750fc853eSJohn Marino 			isblank = iswblank(BWS_GET(s, cpos));
26850fc853eSJohn Marino 
26950fc853eSJohn Marino 			if (isblank && !pb) {
27050fc853eSJohn Marino 				--fields;
27150fc853eSJohn Marino 				if (fields <= 1)
27250fc853eSJohn Marino 					return (cpos);
27350fc853eSJohn Marino 			}
27450fc853eSJohn Marino 			pb = isblank;
27550fc853eSJohn Marino 			++cpos;
27650fc853eSJohn Marino 		}
27750fc853eSJohn Marino 		if (fields > 1)
27850fc853eSJohn Marino 			*empty_field = true;
27950fc853eSJohn Marino 		return (cpos);
28050fc853eSJohn Marino 	} else {
28150fc853eSJohn Marino 		size_t cpos = 0;
28250fc853eSJohn Marino 
28350fc853eSJohn Marino 		while (cpos < BWSLEN(s)) {
28450fc853eSJohn Marino 			if (BWS_GET(s,cpos) == (wchar_t)sort_opts_vals.field_sep) {
28550fc853eSJohn Marino 				--fields;
28650fc853eSJohn Marino 				if (fields <= 1)
28750fc853eSJohn Marino 					return (cpos + 1);
28850fc853eSJohn Marino 			}
28950fc853eSJohn Marino 			++cpos;
29050fc853eSJohn Marino 		}
29150fc853eSJohn Marino 		if (fields > 1)
29250fc853eSJohn Marino 			*empty_field = true;
29350fc853eSJohn Marino 		return (cpos);
29450fc853eSJohn Marino 	}
29550fc853eSJohn Marino }
29650fc853eSJohn Marino 
29750fc853eSJohn Marino /*
29850fc853eSJohn Marino  * Find fields start
29950fc853eSJohn Marino  */
30050fc853eSJohn Marino static void
find_field_start(const struct bwstring * s,struct key_specs * ks,size_t * field_start,size_t * key_start,bool * empty_field,bool * empty_key)30150fc853eSJohn Marino find_field_start(const struct bwstring *s, struct key_specs *ks,
30250fc853eSJohn Marino     size_t *field_start, size_t *key_start, bool *empty_field, bool *empty_key)
30350fc853eSJohn Marino {
30450fc853eSJohn Marino 
30550fc853eSJohn Marino 	*field_start = skip_fields_to_start(s, ks->f1, empty_field);
30650fc853eSJohn Marino 	if (!*empty_field)
30750fc853eSJohn Marino 		*key_start = skip_cols_to_start(s, ks->c1, *field_start,
30850fc853eSJohn Marino 		    ks->pos1b, empty_key);
30950fc853eSJohn Marino 	else
31050fc853eSJohn Marino 		*empty_key = true;
31150fc853eSJohn Marino }
31250fc853eSJohn Marino 
31350fc853eSJohn Marino /*
31450fc853eSJohn Marino  * Find end key position
31550fc853eSJohn Marino  */
31650fc853eSJohn Marino static size_t
find_field_end(const struct bwstring * s,struct key_specs * ks)31750fc853eSJohn Marino find_field_end(const struct bwstring *s, struct key_specs *ks)
31850fc853eSJohn Marino {
31950fc853eSJohn Marino 	size_t f2, next_field_start, pos_end;
32050fc853eSJohn Marino 	bool empty_field, empty_key;
32150fc853eSJohn Marino 
32250fc853eSJohn Marino 	pos_end = 0;
32350fc853eSJohn Marino 	next_field_start = 0;
32450fc853eSJohn Marino 	empty_field = false;
32550fc853eSJohn Marino 	empty_key = false;
32650fc853eSJohn Marino 	f2 = ks->f2;
32750fc853eSJohn Marino 
32850fc853eSJohn Marino 	if (f2 == 0)
32950fc853eSJohn Marino 		return (BWSLEN(s) + 1);
33050fc853eSJohn Marino 	else {
33150fc853eSJohn Marino 		if (ks->c2 == 0) {
33250fc853eSJohn Marino 			next_field_start = skip_fields_to_start(s, f2 + 1,
33350fc853eSJohn Marino 			    &empty_field);
33450fc853eSJohn Marino 			if ((next_field_start > 0) && sort_opts_vals.tflag &&
33550fc853eSJohn Marino 			    ((wchar_t)sort_opts_vals.field_sep == BWS_GET(s,
33650fc853eSJohn Marino 			    next_field_start - 1)))
33750fc853eSJohn Marino 				--next_field_start;
33850fc853eSJohn Marino 		} else
33950fc853eSJohn Marino 			next_field_start = skip_fields_to_start(s, f2,
34050fc853eSJohn Marino 			    &empty_field);
34150fc853eSJohn Marino 	}
34250fc853eSJohn Marino 
34350fc853eSJohn Marino 	if (empty_field || (next_field_start >= BWSLEN(s)))
34450fc853eSJohn Marino 		return (BWSLEN(s) + 1);
34550fc853eSJohn Marino 
34650fc853eSJohn Marino 	if (ks->c2) {
34750fc853eSJohn Marino 		pos_end = skip_cols_to_start(s, ks->c2, next_field_start,
34850fc853eSJohn Marino 		    ks->pos2b, &empty_key);
34950fc853eSJohn Marino 		if (pos_end < BWSLEN(s))
35050fc853eSJohn Marino 			++pos_end;
35150fc853eSJohn Marino 	} else
35250fc853eSJohn Marino 		pos_end = next_field_start;
35350fc853eSJohn Marino 
35450fc853eSJohn Marino 	return (pos_end);
35550fc853eSJohn Marino }
35650fc853eSJohn Marino 
35750fc853eSJohn Marino /*
35850fc853eSJohn Marino  * Cut a field according to the key specs
35950fc853eSJohn Marino  */
36050fc853eSJohn Marino static struct bwstring *
cut_field(const struct bwstring * s,struct key_specs * ks)36150fc853eSJohn Marino cut_field(const struct bwstring *s, struct key_specs *ks)
36250fc853eSJohn Marino {
36350fc853eSJohn Marino 	struct bwstring *ret = NULL;
36450fc853eSJohn Marino 
36550fc853eSJohn Marino 	if (s && ks) {
36650fc853eSJohn Marino 		size_t field_start, key_end, key_start, sz;
36750fc853eSJohn Marino 		bool empty_field, empty_key;
36850fc853eSJohn Marino 
36950fc853eSJohn Marino 		field_start = 0;
37050fc853eSJohn Marino 		key_start = 0;
37150fc853eSJohn Marino 		empty_field = false;
37250fc853eSJohn Marino 		empty_key = false;
37350fc853eSJohn Marino 
37450fc853eSJohn Marino 		find_field_start(s, ks, &field_start, &key_start,
37550fc853eSJohn Marino 		    &empty_field, &empty_key);
37650fc853eSJohn Marino 
37750fc853eSJohn Marino 		if (empty_key)
37850fc853eSJohn Marino 			sz = 0;
37950fc853eSJohn Marino 		else {
38050fc853eSJohn Marino 			key_end = find_field_end(s, ks);
38150fc853eSJohn Marino 			sz = (key_end < key_start) ? 0 : (key_end - key_start);
38250fc853eSJohn Marino 		}
38350fc853eSJohn Marino 
38450fc853eSJohn Marino 		ret = bwsalloc(sz);
38550fc853eSJohn Marino 		if (sz)
38650fc853eSJohn Marino 			bwsnocpy(ret, s, key_start, sz);
38750fc853eSJohn Marino 	} else
38850fc853eSJohn Marino 		ret = bwsalloc(0);
38950fc853eSJohn Marino 
39050fc853eSJohn Marino 	return (ret);
39150fc853eSJohn Marino }
39250fc853eSJohn Marino 
39350fc853eSJohn Marino /*
39450fc853eSJohn Marino  * Preprocesses a line applying the necessary transformations
39550fc853eSJohn Marino  * specified by command line options and returns the preprocessed
39650fc853eSJohn Marino  * string, which can be used to compare.
39750fc853eSJohn Marino  */
39850fc853eSJohn Marino int
preproc(struct bwstring * s,struct keys_array * ka)39950fc853eSJohn Marino preproc(struct bwstring *s, struct keys_array *ka)
40050fc853eSJohn Marino {
40150fc853eSJohn Marino 
40250fc853eSJohn Marino 	if (sort_opts_vals.kflag)
40350fc853eSJohn Marino 		for (size_t i = 0; i < keys_num; i++) {
40450fc853eSJohn Marino 			struct bwstring *key;
40550fc853eSJohn Marino 			struct key_specs *kspecs;
40650fc853eSJohn Marino 			struct sort_mods *sm;
40750fc853eSJohn Marino 
40850fc853eSJohn Marino 			kspecs = &(keys[i]);
40950fc853eSJohn Marino 			key = cut_field(s, kspecs);
41050fc853eSJohn Marino 
41150fc853eSJohn Marino 			sm = &(kspecs->sm);
41250fc853eSJohn Marino 			if (sm->dflag)
41350fc853eSJohn Marino 				key = dictionary_order(key);
41450fc853eSJohn Marino 			else if (sm->iflag)
41550fc853eSJohn Marino 				key = ignore_nonprinting(key);
41650fc853eSJohn Marino 			if (sm->fflag || sm->Mflag)
41750fc853eSJohn Marino 				key = ignore_case(key);
41850fc853eSJohn Marino 
41950fc853eSJohn Marino 			set_key_on_keys_array(ka, key, i);
42050fc853eSJohn Marino 		}
42150fc853eSJohn Marino 	else {
42250fc853eSJohn Marino 		struct bwstring *ret = NULL;
42350fc853eSJohn Marino 		struct sort_mods *sm = default_sort_mods;
42450fc853eSJohn Marino 
42550fc853eSJohn Marino 		if (sm->bflag) {
42650fc853eSJohn Marino 			if (ret == NULL)
42750fc853eSJohn Marino 				ret = bwsdup(s);
42850fc853eSJohn Marino 			ret = ignore_leading_blanks(ret);
42950fc853eSJohn Marino 		}
43050fc853eSJohn Marino 		if (sm->dflag) {
43150fc853eSJohn Marino 			if (ret == NULL)
43250fc853eSJohn Marino 				ret = bwsdup(s);
43350fc853eSJohn Marino 			ret = dictionary_order(ret);
43450fc853eSJohn Marino 		} else if (sm->iflag) {
43550fc853eSJohn Marino 			if (ret == NULL)
43650fc853eSJohn Marino 				ret = bwsdup(s);
43750fc853eSJohn Marino 			ret = ignore_nonprinting(ret);
43850fc853eSJohn Marino 		}
43950fc853eSJohn Marino 		if (sm->fflag || sm->Mflag) {
44050fc853eSJohn Marino 			if (ret == NULL)
44150fc853eSJohn Marino 				ret = bwsdup(s);
44250fc853eSJohn Marino 			ret = ignore_case(ret);
44350fc853eSJohn Marino 		}
44450fc853eSJohn Marino 		if (ret == NULL)
44550fc853eSJohn Marino 			set_key_on_keys_array(ka, s, 0);
44650fc853eSJohn Marino 		else
44750fc853eSJohn Marino 			set_key_on_keys_array(ka, ret, 0);
44850fc853eSJohn Marino 	}
44950fc853eSJohn Marino 
45050fc853eSJohn Marino 	return 0;
45150fc853eSJohn Marino }
45250fc853eSJohn Marino 
45350fc853eSJohn Marino cmpcoll_t
get_sort_func(struct sort_mods * sm)45450fc853eSJohn Marino get_sort_func(struct sort_mods *sm)
45550fc853eSJohn Marino {
45650fc853eSJohn Marino 
45750fc853eSJohn Marino 	if (sm->nflag)
45850fc853eSJohn Marino 		return (numcoll);
45950fc853eSJohn Marino 	else if (sm->hflag)
46050fc853eSJohn Marino 		return (hnumcoll);
46150fc853eSJohn Marino 	else if (sm->gflag)
46250fc853eSJohn Marino 		return (gnumcoll);
46350fc853eSJohn Marino 	else if (sm->Mflag)
46450fc853eSJohn Marino 		return (monthcoll);
4656d7e22ecSzrj #if defined(SORT_RANDOM)
46650fc853eSJohn Marino 	else if (sm->Rflag)
46750fc853eSJohn Marino 		return (randomcoll);
4686d7e22ecSzrj #endif
46950fc853eSJohn Marino 	else if (sm->Vflag)
47050fc853eSJohn Marino 		return (versioncoll);
47150fc853eSJohn Marino 	else
47250fc853eSJohn Marino 		return (wstrcoll);
47350fc853eSJohn Marino }
47450fc853eSJohn Marino 
47550fc853eSJohn Marino /*
47650fc853eSJohn Marino  * Compares the given strings.  Returns a positive number if
47750fc853eSJohn Marino  * the first precedes the second, a negative number if the second is
47850fc853eSJohn Marino  * the preceding one, and zero if they are equal.  This function calls
47950fc853eSJohn Marino  * the underlying collate functions, which done the actual comparison.
48050fc853eSJohn Marino  */
48150fc853eSJohn Marino int
key_coll(struct keys_array * ps1,struct keys_array * ps2,size_t offset)48250fc853eSJohn Marino key_coll(struct keys_array *ps1, struct keys_array *ps2, size_t offset)
48350fc853eSJohn Marino {
48450fc853eSJohn Marino 	struct sort_mods *sm;
48550fc853eSJohn Marino 	int res = 0;
48650fc853eSJohn Marino 
48750fc853eSJohn Marino 	for (size_t i = 0; i < keys_num; ++i) {
48850fc853eSJohn Marino 		sm = &(keys[i].sm);
48950fc853eSJohn Marino 
49050fc853eSJohn Marino 		if (sm->rflag)
49150fc853eSJohn Marino 			res = sm->func(&(ps2->key[i]), &(ps1->key[i]), offset);
49250fc853eSJohn Marino 		else
49350fc853eSJohn Marino 			res = sm->func(&(ps1->key[i]), &(ps2->key[i]), offset);
49450fc853eSJohn Marino 
49550fc853eSJohn Marino 		if (res)
49650fc853eSJohn Marino 			break;
49750fc853eSJohn Marino 
49850fc853eSJohn Marino 		/* offset applies to only the first key */
49950fc853eSJohn Marino 		offset = 0;
50050fc853eSJohn Marino 	}
50150fc853eSJohn Marino 	return (res);
50250fc853eSJohn Marino }
50350fc853eSJohn Marino 
50450fc853eSJohn Marino /*
50550fc853eSJohn Marino  * Compare two strings.
50650fc853eSJohn Marino  * Plain symbol-by-symbol comparison.
50750fc853eSJohn Marino  */
50850fc853eSJohn Marino int
top_level_str_coll(const struct bwstring * s1,const struct bwstring * s2)50950fc853eSJohn Marino top_level_str_coll(const struct bwstring *s1, const struct bwstring *s2)
51050fc853eSJohn Marino {
51150fc853eSJohn Marino 
51250fc853eSJohn Marino 	if (default_sort_mods->rflag) {
51350fc853eSJohn Marino 		const struct bwstring *tmp;
51450fc853eSJohn Marino 
51550fc853eSJohn Marino 		tmp = s1;
51650fc853eSJohn Marino 		s1 = s2;
51750fc853eSJohn Marino 		s2 = tmp;
51850fc853eSJohn Marino 	}
51950fc853eSJohn Marino 
52050fc853eSJohn Marino 	return (bwscoll(s1, s2, 0));
52150fc853eSJohn Marino }
52250fc853eSJohn Marino 
52350fc853eSJohn Marino /*
52450fc853eSJohn Marino  * Compare a string and a sort list item, according to the sort specs.
52550fc853eSJohn Marino  */
52650fc853eSJohn Marino int
str_list_coll(struct bwstring * str1,struct sort_list_item ** ss2)52750fc853eSJohn Marino str_list_coll(struct bwstring *str1, struct sort_list_item **ss2)
52850fc853eSJohn Marino {
52950fc853eSJohn Marino 	struct keys_array *ka1;
53050fc853eSJohn Marino 	int ret = 0;
53150fc853eSJohn Marino 
53250fc853eSJohn Marino 	ka1 = keys_array_alloc();
53350fc853eSJohn Marino 
53450fc853eSJohn Marino 	preproc(str1, ka1);
53550fc853eSJohn Marino 
53650fc853eSJohn Marino 	sort_list_item_make_key(*ss2);
53750fc853eSJohn Marino 
53850fc853eSJohn Marino 	if (debug_sort) {
53950fc853eSJohn Marino 		bwsprintf(stdout, str1, "; s1=<", ">");
54050fc853eSJohn Marino 		bwsprintf(stdout, (*ss2)->str, ", s2=<", ">");
54150fc853eSJohn Marino 	}
54250fc853eSJohn Marino 
54350fc853eSJohn Marino 	ret = key_coll(ka1, &((*ss2)->ka), 0);
54450fc853eSJohn Marino 
54550fc853eSJohn Marino 	if (debug_sort)
54650fc853eSJohn Marino 		printf("; cmp1=%d", ret);
54750fc853eSJohn Marino 
54850fc853eSJohn Marino 	clean_keys_array(str1, ka1);
54950fc853eSJohn Marino 	sort_free(ka1);
55050fc853eSJohn Marino 
55150fc853eSJohn Marino 	if ((ret == 0) && !(sort_opts_vals.sflag) && sort_opts_vals.complex_sort) {
55250fc853eSJohn Marino 		ret = top_level_str_coll(str1, ((*ss2)->str));
55350fc853eSJohn Marino 		if (debug_sort)
55450fc853eSJohn Marino 			printf("; cmp2=%d", ret);
55550fc853eSJohn Marino 	}
55650fc853eSJohn Marino 
55750fc853eSJohn Marino 	if (debug_sort)
55850fc853eSJohn Marino 		printf("\n");
55950fc853eSJohn Marino 
56050fc853eSJohn Marino 	return (ret);
56150fc853eSJohn Marino }
56250fc853eSJohn Marino 
56350fc853eSJohn Marino /*
56450fc853eSJohn Marino  * Compare two sort list items, according to the sort specs.
56550fc853eSJohn Marino  */
56650fc853eSJohn Marino int
list_coll_offset(struct sort_list_item ** ss1,struct sort_list_item ** ss2,size_t offset)56750fc853eSJohn Marino list_coll_offset(struct sort_list_item **ss1, struct sort_list_item **ss2,
56850fc853eSJohn Marino     size_t offset)
56950fc853eSJohn Marino {
57050fc853eSJohn Marino 	int ret;
57150fc853eSJohn Marino 
57250fc853eSJohn Marino 	ret = key_coll(&((*ss1)->ka), &((*ss2)->ka), offset);
57350fc853eSJohn Marino 
57450fc853eSJohn Marino 	if (debug_sort) {
57550fc853eSJohn Marino 		if (offset)
57650fc853eSJohn Marino 			printf("; offset=%d", (int) offset);
57750fc853eSJohn Marino 		bwsprintf(stdout, ((*ss1)->str), "; s1=<", ">");
57850fc853eSJohn Marino 		bwsprintf(stdout, ((*ss2)->str), ", s2=<", ">");
57950fc853eSJohn Marino 		printf("; cmp1=%d\n", ret);
58050fc853eSJohn Marino 	}
58150fc853eSJohn Marino 
58250fc853eSJohn Marino 	if (ret)
58350fc853eSJohn Marino 		return (ret);
58450fc853eSJohn Marino 
58550fc853eSJohn Marino 	if (!(sort_opts_vals.sflag) && sort_opts_vals.complex_sort) {
58650fc853eSJohn Marino 		ret = top_level_str_coll(((*ss1)->str), ((*ss2)->str));
58750fc853eSJohn Marino 		if (debug_sort)
58850fc853eSJohn Marino 			printf("; cmp2=%d\n", ret);
58950fc853eSJohn Marino 	}
59050fc853eSJohn Marino 
59150fc853eSJohn Marino 	return (ret);
59250fc853eSJohn Marino }
59350fc853eSJohn Marino 
59450fc853eSJohn Marino /*
59550fc853eSJohn Marino  * Compare two sort list items, according to the sort specs.
59650fc853eSJohn Marino  */
59750fc853eSJohn Marino int
list_coll(struct sort_list_item ** ss1,struct sort_list_item ** ss2)59850fc853eSJohn Marino list_coll(struct sort_list_item **ss1, struct sort_list_item **ss2)
59950fc853eSJohn Marino {
60050fc853eSJohn Marino 
60150fc853eSJohn Marino 	return (list_coll_offset(ss1, ss2, 0));
60250fc853eSJohn Marino }
60350fc853eSJohn Marino 
60450fc853eSJohn Marino #define	LSCDEF(N)							\
60550fc853eSJohn Marino static int 								\
60650fc853eSJohn Marino list_coll_##N(struct sort_list_item **ss1, struct sort_list_item **ss2)	\
60750fc853eSJohn Marino {									\
60850fc853eSJohn Marino 									\
60950fc853eSJohn Marino 	return (list_coll_offset(ss1, ss2, N));				\
61050fc853eSJohn Marino }
61150fc853eSJohn Marino 
61250fc853eSJohn Marino LSCDEF(1)
61350fc853eSJohn Marino LSCDEF(2)
61450fc853eSJohn Marino LSCDEF(3)
61550fc853eSJohn Marino LSCDEF(4)
61650fc853eSJohn Marino LSCDEF(5)
61750fc853eSJohn Marino LSCDEF(6)
61850fc853eSJohn Marino LSCDEF(7)
61950fc853eSJohn Marino LSCDEF(8)
62050fc853eSJohn Marino LSCDEF(9)
62150fc853eSJohn Marino LSCDEF(10)
62250fc853eSJohn Marino LSCDEF(11)
62350fc853eSJohn Marino LSCDEF(12)
62450fc853eSJohn Marino LSCDEF(13)
62550fc853eSJohn Marino LSCDEF(14)
62650fc853eSJohn Marino LSCDEF(15)
62750fc853eSJohn Marino LSCDEF(16)
62850fc853eSJohn Marino LSCDEF(17)
62950fc853eSJohn Marino LSCDEF(18)
63050fc853eSJohn Marino LSCDEF(19)
63150fc853eSJohn Marino LSCDEF(20)
63250fc853eSJohn Marino 
63350fc853eSJohn Marino listcoll_t
get_list_call_func(size_t offset)63450fc853eSJohn Marino get_list_call_func(size_t offset)
63550fc853eSJohn Marino {
63650fc853eSJohn Marino 	static const listcoll_t lsarray[] = { list_coll, list_coll_1,
63750fc853eSJohn Marino 	    list_coll_2, list_coll_3, list_coll_4, list_coll_5,
63850fc853eSJohn Marino 	    list_coll_6, list_coll_7, list_coll_8, list_coll_9,
63950fc853eSJohn Marino 	    list_coll_10, list_coll_11, list_coll_12, list_coll_13,
64050fc853eSJohn Marino 	    list_coll_14, list_coll_15, list_coll_16, list_coll_17,
64150fc853eSJohn Marino 	    list_coll_18, list_coll_19, list_coll_20 };
64250fc853eSJohn Marino 
64350fc853eSJohn Marino 	if (offset <= 20)
64450fc853eSJohn Marino 		return (lsarray[offset]);
64550fc853eSJohn Marino 
64650fc853eSJohn Marino 	return (list_coll);
64750fc853eSJohn Marino }
64850fc853eSJohn Marino 
64950fc853eSJohn Marino /*
65050fc853eSJohn Marino  * Compare two sort list items, only by their original string.
65150fc853eSJohn Marino  */
65250fc853eSJohn Marino int
list_coll_by_str_only(struct sort_list_item ** ss1,struct sort_list_item ** ss2)65350fc853eSJohn Marino list_coll_by_str_only(struct sort_list_item **ss1, struct sort_list_item **ss2)
65450fc853eSJohn Marino {
65550fc853eSJohn Marino 
65650fc853eSJohn Marino 	return (top_level_str_coll(((*ss1)->str), ((*ss2)->str)));
65750fc853eSJohn Marino }
65850fc853eSJohn Marino 
65950fc853eSJohn Marino /*
66050fc853eSJohn Marino  * Maximum size of a number in the string (before or after decimal point)
66150fc853eSJohn Marino  */
66250fc853eSJohn Marino #define MAX_NUM_SIZE (128)
66350fc853eSJohn Marino 
66450fc853eSJohn Marino /*
66550fc853eSJohn Marino  * Set suffix value
66650fc853eSJohn Marino  */
setsuffix(wchar_t c,unsigned char * si)66750fc853eSJohn Marino static void setsuffix(wchar_t c, unsigned char *si)
66850fc853eSJohn Marino {
66950fc853eSJohn Marino 	switch (c){
67050fc853eSJohn Marino 	case L'k':
67150fc853eSJohn Marino 	case L'K':
67250fc853eSJohn Marino 		*si = 1;
67350fc853eSJohn Marino 		break;
67450fc853eSJohn Marino 	case L'M':
67550fc853eSJohn Marino 		*si = 2;
67650fc853eSJohn Marino 		break;
67750fc853eSJohn Marino 	case L'G':
67850fc853eSJohn Marino 		*si = 3;
67950fc853eSJohn Marino 		break;
68050fc853eSJohn Marino 	case L'T':
68150fc853eSJohn Marino 		*si = 4;
68250fc853eSJohn Marino 		break;
68350fc853eSJohn Marino 	case L'P':
68450fc853eSJohn Marino 		*si = 5;
68550fc853eSJohn Marino 		break;
68650fc853eSJohn Marino 	case L'E':
68750fc853eSJohn Marino 		*si = 6;
68850fc853eSJohn Marino 		break;
68950fc853eSJohn Marino 	case L'Z':
69050fc853eSJohn Marino 		*si = 7;
69150fc853eSJohn Marino 		break;
69250fc853eSJohn Marino 	case L'Y':
69350fc853eSJohn Marino 		*si = 8;
69450fc853eSJohn Marino 		break;
69550fc853eSJohn Marino 	default:
69650fc853eSJohn Marino 		*si = 0;
69750fc853eSJohn Marino 	};
69850fc853eSJohn Marino }
69950fc853eSJohn Marino 
70050fc853eSJohn Marino /*
70150fc853eSJohn Marino  * Read string s and parse the string into a fixed-decimal-point number.
70250fc853eSJohn Marino  * sign equals -1 if the number is negative (explicit plus is not allowed,
70350fc853eSJohn Marino  * according to GNU sort's "info sort".
70450fc853eSJohn Marino  * The number part before decimal point is in the smain, after the decimal
70550fc853eSJohn Marino  * point is in sfrac, tail is the pointer to the remainder of the string.
70650fc853eSJohn Marino  */
70750fc853eSJohn Marino static int
read_number(struct bwstring * s0,int * sign,wchar_t * smain,size_t * main_len,wchar_t * sfrac,size_t * frac_len,unsigned char * si)70850fc853eSJohn Marino read_number(struct bwstring *s0, int *sign, wchar_t *smain, size_t *main_len, wchar_t *sfrac, size_t *frac_len, unsigned char *si)
70950fc853eSJohn Marino {
71050fc853eSJohn Marino 	bwstring_iterator s;
71150fc853eSJohn Marino 
71250fc853eSJohn Marino 	s = bws_begin(s0);
71350fc853eSJohn Marino 
71450fc853eSJohn Marino 	/* always end the fraction with zero, even if we have no fraction */
71550fc853eSJohn Marino 	sfrac[0] = 0;
71650fc853eSJohn Marino 
71750fc853eSJohn Marino 	while (iswblank(bws_get_iter_value(s)))
71850fc853eSJohn Marino 		s = bws_iterator_inc(s, 1);
71950fc853eSJohn Marino 
72050fc853eSJohn Marino 	if (bws_get_iter_value(s) == (wchar_t)symbol_negative_sign) {
72150fc853eSJohn Marino 		*sign = -1;
72250fc853eSJohn Marino 		s = bws_iterator_inc(s, 1);
72350fc853eSJohn Marino 	}
72450fc853eSJohn Marino 
72550fc853eSJohn Marino 	// This is '0', not '\0', do not change this
72650fc853eSJohn Marino 	while (iswdigit(bws_get_iter_value(s)) &&
72750fc853eSJohn Marino 	    (bws_get_iter_value(s) == L'0'))
72850fc853eSJohn Marino 		s = bws_iterator_inc(s, 1);
72950fc853eSJohn Marino 
73050fc853eSJohn Marino 	while (bws_get_iter_value(s) && *main_len < MAX_NUM_SIZE) {
73150fc853eSJohn Marino 		if (iswdigit(bws_get_iter_value(s))) {
73250fc853eSJohn Marino 			smain[*main_len] = bws_get_iter_value(s);
73350fc853eSJohn Marino 			s = bws_iterator_inc(s, 1);
73450fc853eSJohn Marino 			*main_len += 1;
73550fc853eSJohn Marino 		} else if (symbol_thousands_sep &&
73650fc853eSJohn Marino 		    (bws_get_iter_value(s) == (wchar_t)symbol_thousands_sep))
73750fc853eSJohn Marino 			s = bws_iterator_inc(s, 1);
73850fc853eSJohn Marino 		else
73950fc853eSJohn Marino 			break;
74050fc853eSJohn Marino 	}
74150fc853eSJohn Marino 
74250fc853eSJohn Marino 	smain[*main_len] = 0;
74350fc853eSJohn Marino 
74450fc853eSJohn Marino 	if (bws_get_iter_value(s) == (wchar_t)symbol_decimal_point) {
74550fc853eSJohn Marino 		s = bws_iterator_inc(s, 1);
74650fc853eSJohn Marino 		while (iswdigit(bws_get_iter_value(s)) &&
74750fc853eSJohn Marino 		    *frac_len < MAX_NUM_SIZE) {
74850fc853eSJohn Marino 			sfrac[*frac_len] = bws_get_iter_value(s);
74950fc853eSJohn Marino 			s = bws_iterator_inc(s, 1);
75050fc853eSJohn Marino 			*frac_len += 1;
75150fc853eSJohn Marino 		}
75250fc853eSJohn Marino 		sfrac[*frac_len] = 0;
75350fc853eSJohn Marino 
75450fc853eSJohn Marino 		while (*frac_len > 0 && sfrac[*frac_len - 1] == L'0') {
75550fc853eSJohn Marino 			--(*frac_len);
75650fc853eSJohn Marino 			sfrac[*frac_len] = L'\0';
75750fc853eSJohn Marino 		}
75850fc853eSJohn Marino 	}
75950fc853eSJohn Marino 
76050fc853eSJohn Marino 	setsuffix(bws_get_iter_value(s),si);
76150fc853eSJohn Marino 
76250fc853eSJohn Marino 	if ((*main_len + *frac_len) == 0)
76350fc853eSJohn Marino 		*sign = 0;
76450fc853eSJohn Marino 
76550fc853eSJohn Marino 	return (0);
76650fc853eSJohn Marino }
76750fc853eSJohn Marino 
76850fc853eSJohn Marino /*
76950fc853eSJohn Marino  * Implements string sort.
77050fc853eSJohn Marino  */
77150fc853eSJohn Marino static int
wstrcoll(struct key_value * kv1,struct key_value * kv2,size_t offset)77250fc853eSJohn Marino wstrcoll(struct key_value *kv1, struct key_value *kv2, size_t offset)
77350fc853eSJohn Marino {
77450fc853eSJohn Marino 
77550fc853eSJohn Marino 	if (debug_sort) {
77650fc853eSJohn Marino 		if (offset)
77750fc853eSJohn Marino 			printf("; offset=%d\n", (int) offset);
77850fc853eSJohn Marino 		bwsprintf(stdout, kv1->k, "; k1=<", ">");
77950fc853eSJohn Marino 		printf("(%zu)", BWSLEN(kv1->k));
78050fc853eSJohn Marino 		bwsprintf(stdout, kv2->k, ", k2=<", ">");
78150fc853eSJohn Marino 		printf("(%zu)", BWSLEN(kv2->k));
78250fc853eSJohn Marino 	}
78350fc853eSJohn Marino 
78450fc853eSJohn Marino 	return (bwscoll(kv1->k, kv2->k, offset));
78550fc853eSJohn Marino }
78650fc853eSJohn Marino 
78750fc853eSJohn Marino /*
78850fc853eSJohn Marino  * Compare two suffixes
78950fc853eSJohn Marino  */
79050fc853eSJohn Marino static inline int
cmpsuffix(unsigned char si1,unsigned char si2)79150fc853eSJohn Marino cmpsuffix(unsigned char si1, unsigned char si2)
79250fc853eSJohn Marino {
79350fc853eSJohn Marino 
79450fc853eSJohn Marino 	return ((char)si1 - (char)si2);
79550fc853eSJohn Marino }
79650fc853eSJohn Marino 
79750fc853eSJohn Marino /*
79850fc853eSJohn Marino  * Implements numeric sort for -n and -h.
79950fc853eSJohn Marino  */
80050fc853eSJohn Marino static int
numcoll_impl(struct key_value * kv1,struct key_value * kv2,size_t offset __unused,bool use_suffix)80150fc853eSJohn Marino numcoll_impl(struct key_value *kv1, struct key_value *kv2,
80250fc853eSJohn Marino     size_t offset __unused, bool use_suffix)
80350fc853eSJohn Marino {
80450fc853eSJohn Marino 	struct bwstring *s1, *s2;
80550fc853eSJohn Marino 	wchar_t sfrac1[MAX_NUM_SIZE + 1], sfrac2[MAX_NUM_SIZE + 1];
80650fc853eSJohn Marino 	wchar_t smain1[MAX_NUM_SIZE + 1], smain2[MAX_NUM_SIZE + 1];
80750fc853eSJohn Marino 	int cmp_res, sign1, sign2;
80850fc853eSJohn Marino 	size_t frac1, frac2, main1, main2;
80950fc853eSJohn Marino 	unsigned char SI1, SI2;
81050fc853eSJohn Marino 	bool e1, e2, key1_read, key2_read;
81150fc853eSJohn Marino 
81250fc853eSJohn Marino 	s1 = kv1->k;
81350fc853eSJohn Marino 	s2 = kv2->k;
81450fc853eSJohn Marino 	sign1 = sign2 = 0;
81550fc853eSJohn Marino 	main1 = main2 = 0;
81650fc853eSJohn Marino 	frac1 = frac2 = 0;
81750fc853eSJohn Marino 
81850fc853eSJohn Marino 	cmp_res = 0;
81950fc853eSJohn Marino 	key1_read = key2_read = false;
82050fc853eSJohn Marino 
82150fc853eSJohn Marino 	if (debug_sort) {
82250fc853eSJohn Marino 		bwsprintf(stdout, s1, "; k1=<", ">");
82350fc853eSJohn Marino 		bwsprintf(stdout, s2, ", k2=<", ">");
82450fc853eSJohn Marino 	}
82550fc853eSJohn Marino 
82650fc853eSJohn Marino 	if (s1 == s2)
82750fc853eSJohn Marino 		return (0);
82850fc853eSJohn Marino 
82950fc853eSJohn Marino 	if (kv1->hint->status == HS_UNINITIALIZED) {
83050fc853eSJohn Marino 		/* read the number from the string */
83150fc853eSJohn Marino 		read_number(s1, &sign1, smain1, &main1, sfrac1, &frac1, &SI1);
83250fc853eSJohn Marino 		key1_read = true;
83350fc853eSJohn Marino 		kv1->hint->v.nh.n1 = wcstoull(smain1, NULL, 10);
83450fc853eSJohn Marino 		if(main1 < 1 && frac1 < 1)
83550fc853eSJohn Marino 			kv1->hint->v.nh.empty=true;
83650fc853eSJohn Marino 		kv1->hint->v.nh.si = SI1;
83750fc853eSJohn Marino 		kv1->hint->status = (kv1->hint->v.nh.n1 != ULLONG_MAX) ?
83850fc853eSJohn Marino 		    HS_INITIALIZED : HS_ERROR;
83950fc853eSJohn Marino 		kv1->hint->v.nh.neg = (sign1 < 0) ? true : false;
84050fc853eSJohn Marino 	}
84150fc853eSJohn Marino 
84250fc853eSJohn Marino 	if (kv2->hint->status == HS_UNINITIALIZED) {
84350fc853eSJohn Marino 		/* read the number from the string */
84450fc853eSJohn Marino 		read_number(s2, &sign2, smain2, &main2, sfrac2, &frac2,&SI2);
84550fc853eSJohn Marino 		key2_read = true;
84650fc853eSJohn Marino 		kv2->hint->v.nh.n1 = wcstoull(smain2, NULL, 10);
84750fc853eSJohn Marino 		if(main2 < 1 && frac2 < 1)
84850fc853eSJohn Marino 			kv2->hint->v.nh.empty=true;
84950fc853eSJohn Marino 		kv2->hint->v.nh.si = SI2;
85050fc853eSJohn Marino 		kv2->hint->status = (kv2->hint->v.nh.n1 != ULLONG_MAX) ?
85150fc853eSJohn Marino 		    HS_INITIALIZED : HS_ERROR;
85250fc853eSJohn Marino 		kv2->hint->v.nh.neg = (sign2 < 0) ? true : false;
85350fc853eSJohn Marino 	}
85450fc853eSJohn Marino 
85550fc853eSJohn Marino 	if (kv1->hint->status == HS_INITIALIZED && kv2->hint->status ==
85650fc853eSJohn Marino 	    HS_INITIALIZED) {
85750fc853eSJohn Marino 		unsigned long long n1, n2;
85850fc853eSJohn Marino 		bool neg1, neg2;
85950fc853eSJohn Marino 
86050fc853eSJohn Marino 		e1 = kv1->hint->v.nh.empty;
86150fc853eSJohn Marino 		e2 = kv2->hint->v.nh.empty;
86250fc853eSJohn Marino 
86350fc853eSJohn Marino 		if (e1 && e2)
86450fc853eSJohn Marino 			return (0);
86550fc853eSJohn Marino 
86650fc853eSJohn Marino 		neg1 = kv1->hint->v.nh.neg;
86750fc853eSJohn Marino 		neg2 = kv2->hint->v.nh.neg;
86850fc853eSJohn Marino 
86950fc853eSJohn Marino 		if (neg1 && !neg2)
87050fc853eSJohn Marino 			return (-1);
87150fc853eSJohn Marino 		if (neg2 && !neg1)
87250fc853eSJohn Marino 			return (+1);
87350fc853eSJohn Marino 
87450fc853eSJohn Marino 		if (e1)
87550fc853eSJohn Marino 			return (neg2 ? +1 : -1);
87650fc853eSJohn Marino 		else if (e2)
87750fc853eSJohn Marino 			return (neg1 ? -1 : +1);
87850fc853eSJohn Marino 
87950fc853eSJohn Marino 
88050fc853eSJohn Marino 		if (use_suffix) {
88150fc853eSJohn Marino 			cmp_res = cmpsuffix(kv1->hint->v.nh.si, kv2->hint->v.nh.si);
88250fc853eSJohn Marino 			if (cmp_res)
88350fc853eSJohn Marino 				return (neg1 ? -cmp_res : cmp_res);
88450fc853eSJohn Marino 		}
88550fc853eSJohn Marino 
88650fc853eSJohn Marino 		n1 = kv1->hint->v.nh.n1;
88750fc853eSJohn Marino 		n2 = kv2->hint->v.nh.n1;
88850fc853eSJohn Marino 		if (n1 < n2)
88950fc853eSJohn Marino 			return (neg1 ? +1 : -1);
89050fc853eSJohn Marino 		else if (n1 > n2)
89150fc853eSJohn Marino 			return (neg1 ? -1 : +1);
89250fc853eSJohn Marino 	}
89350fc853eSJohn Marino 
89450fc853eSJohn Marino 	/* read the numbers from the strings */
89550fc853eSJohn Marino 	if (!key1_read)
89650fc853eSJohn Marino 		read_number(s1, &sign1, smain1, &main1, sfrac1, &frac1, &SI1);
89750fc853eSJohn Marino 	if (!key2_read)
89850fc853eSJohn Marino 		read_number(s2, &sign2, smain2, &main2, sfrac2, &frac2, &SI2);
89950fc853eSJohn Marino 
90050fc853eSJohn Marino 	e1 = ((main1 + frac1) == 0);
90150fc853eSJohn Marino 	e2 = ((main2 + frac2) == 0);
90250fc853eSJohn Marino 
90350fc853eSJohn Marino 	if (e1 && e2)
90450fc853eSJohn Marino 		return (0);
90550fc853eSJohn Marino 
90650fc853eSJohn Marino 	/* we know the result if the signs are different */
90750fc853eSJohn Marino 	if (sign1 < 0 && sign2 >= 0)
90850fc853eSJohn Marino 		return (-1);
90950fc853eSJohn Marino 	if (sign1 >= 0 && sign2 < 0)
91050fc853eSJohn Marino 		return (+1);
91150fc853eSJohn Marino 
91250fc853eSJohn Marino 	if (e1)
91350fc853eSJohn Marino 		return ((sign2 < 0) ? +1 : -1);
91450fc853eSJohn Marino 	else if (e2)
91550fc853eSJohn Marino 		return ((sign1 < 0) ? -1 : +1);
91650fc853eSJohn Marino 
91750fc853eSJohn Marino 	if (use_suffix) {
91850fc853eSJohn Marino 		cmp_res = cmpsuffix(SI1, SI2);
91950fc853eSJohn Marino 		if (cmp_res)
92050fc853eSJohn Marino 			return ((sign1 < 0) ? -cmp_res : cmp_res);
92150fc853eSJohn Marino 	}
92250fc853eSJohn Marino 
92350fc853eSJohn Marino 	/* if both numbers are empty assume that the strings are equal */
92450fc853eSJohn Marino 	if (main1 < 1 && main2 < 1 && frac1 < 1 && frac2 < 1)
92550fc853eSJohn Marino 		return (0);
92650fc853eSJohn Marino 
92750fc853eSJohn Marino 	/*
92850fc853eSJohn Marino 	 * if the main part is of different size, we know the result
92950fc853eSJohn Marino 	 * (because the leading zeros are removed)
93050fc853eSJohn Marino 	 */
93150fc853eSJohn Marino 	if (main1 < main2)
93250fc853eSJohn Marino 		cmp_res = -1;
93350fc853eSJohn Marino 	else if (main1 > main2)
93450fc853eSJohn Marino 		cmp_res = +1;
93550fc853eSJohn Marino 	/* if the sizes are equal then simple non-collate string compare gives the correct result */
93650fc853eSJohn Marino 	else
93750fc853eSJohn Marino 		cmp_res = wcscmp(smain1, smain2);
93850fc853eSJohn Marino 
93950fc853eSJohn Marino 	/* check fraction */
94050fc853eSJohn Marino 	if (!cmp_res)
94150fc853eSJohn Marino 		cmp_res = wcscmp(sfrac1, sfrac2);
94250fc853eSJohn Marino 
94350fc853eSJohn Marino 	if (!cmp_res)
94450fc853eSJohn Marino 		return (0);
94550fc853eSJohn Marino 
94650fc853eSJohn Marino 	/* reverse result if the signs are negative */
94750fc853eSJohn Marino 	if (sign1 < 0 && sign2 < 0)
94850fc853eSJohn Marino 		cmp_res = -cmp_res;
94950fc853eSJohn Marino 
95050fc853eSJohn Marino 	return (cmp_res);
95150fc853eSJohn Marino }
95250fc853eSJohn Marino 
95350fc853eSJohn Marino /*
95450fc853eSJohn Marino  * Implements numeric sort (-n).
95550fc853eSJohn Marino  */
95650fc853eSJohn Marino static int
numcoll(struct key_value * kv1,struct key_value * kv2,size_t offset)95750fc853eSJohn Marino numcoll(struct key_value *kv1, struct key_value *kv2, size_t offset)
95850fc853eSJohn Marino {
95950fc853eSJohn Marino 
96050fc853eSJohn Marino 	return (numcoll_impl(kv1, kv2, offset, false));
96150fc853eSJohn Marino }
96250fc853eSJohn Marino 
96350fc853eSJohn Marino /*
96450fc853eSJohn Marino  * Implements 'human' numeric sort (-h).
96550fc853eSJohn Marino  */
96650fc853eSJohn Marino static int
hnumcoll(struct key_value * kv1,struct key_value * kv2,size_t offset)96750fc853eSJohn Marino hnumcoll(struct key_value *kv1, struct key_value *kv2, size_t offset)
96850fc853eSJohn Marino {
96950fc853eSJohn Marino 
97050fc853eSJohn Marino 	return (numcoll_impl(kv1, kv2, offset, true));
97150fc853eSJohn Marino }
97250fc853eSJohn Marino 
97350fc853eSJohn Marino /*
97450fc853eSJohn Marino  * Implements random sort (-R).
97550fc853eSJohn Marino  */
9766d7e22ecSzrj #if defined(SORT_RANDOM)
977*a98f7024Szrj static char *
randomcollend(MD5_CTX * ctx)978*a98f7024Szrj randomcollend(MD5_CTX *ctx)
979*a98f7024Szrj {
980*a98f7024Szrj 	unsigned char digest[MD5_DIGEST_LENGTH];
981*a98f7024Szrj 	static const char hex[]="0123456789abcdef";
982*a98f7024Szrj 	char *buf;
983*a98f7024Szrj 	int i;
984*a98f7024Szrj 
985*a98f7024Szrj 	buf = malloc(MD5_DIGEST_LENGTH * 2 + 1);
986*a98f7024Szrj 	if (!buf)
987*a98f7024Szrj 		return NULL;
988*a98f7024Szrj 	MD5_Final(digest, ctx);
989*a98f7024Szrj 	for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
990*a98f7024Szrj 		buf[2*i] = hex[digest[i] >> 4];
991*a98f7024Szrj 		buf[2*i+1] = hex[digest[i] & 0x0f];
992*a98f7024Szrj 	}
993*a98f7024Szrj 	buf[MD5_DIGEST_LENGTH * 2] = '\0';
994*a98f7024Szrj 	return buf;
995*a98f7024Szrj }
996*a98f7024Szrj 
99750fc853eSJohn Marino static int
randomcoll(struct key_value * kv1,struct key_value * kv2,size_t offset __unused)99850fc853eSJohn Marino randomcoll(struct key_value *kv1, struct key_value *kv2,
99950fc853eSJohn Marino     size_t offset __unused)
100050fc853eSJohn Marino {
100150fc853eSJohn Marino 	struct bwstring *s1, *s2;
100250fc853eSJohn Marino 	MD5_CTX ctx1, ctx2;
100350fc853eSJohn Marino 	char *b1, *b2;
100450fc853eSJohn Marino 
100550fc853eSJohn Marino 	s1 = kv1->k;
100650fc853eSJohn Marino 	s2 = kv2->k;
100750fc853eSJohn Marino 
100850fc853eSJohn Marino 	if (debug_sort) {
100950fc853eSJohn Marino 		bwsprintf(stdout, s1, "; k1=<", ">");
101050fc853eSJohn Marino 		bwsprintf(stdout, s2, ", k2=<", ">");
101150fc853eSJohn Marino 	}
101250fc853eSJohn Marino 
101350fc853eSJohn Marino 	if (s1 == s2)
101450fc853eSJohn Marino 		return (0);
101550fc853eSJohn Marino 
101650fc853eSJohn Marino 	memcpy(&ctx1,&md5_ctx,sizeof(MD5_CTX));
101750fc853eSJohn Marino 	memcpy(&ctx2,&md5_ctx,sizeof(MD5_CTX));
101850fc853eSJohn Marino 
1019*a98f7024Szrj 	MD5_Update(&ctx1, bwsrawdata(s1), bwsrawlen(s1));
1020*a98f7024Szrj 	MD5_Update(&ctx2, bwsrawdata(s2), bwsrawlen(s2));
1021*a98f7024Szrj 	b1 = randomcollend(&ctx1);
1022*a98f7024Szrj 	b2 = randomcollend(&ctx2);
102350fc853eSJohn Marino 	if (b1 == NULL) {
102450fc853eSJohn Marino 		if (b2 == NULL)
102550fc853eSJohn Marino 			return (0);
102650fc853eSJohn Marino 		else {
102750fc853eSJohn Marino 			sort_free(b2);
102850fc853eSJohn Marino 			return (-1);
102950fc853eSJohn Marino 		}
103050fc853eSJohn Marino 	} else if (b2 == NULL) {
103150fc853eSJohn Marino 		sort_free(b1);
103250fc853eSJohn Marino 		return (+1);
103350fc853eSJohn Marino 	} else {
103450fc853eSJohn Marino 		int cmp_res;
103550fc853eSJohn Marino 
103650fc853eSJohn Marino 		cmp_res = strcmp(b1,b2);
103750fc853eSJohn Marino 		sort_free(b1);
103850fc853eSJohn Marino 		sort_free(b2);
103950fc853eSJohn Marino 
104050fc853eSJohn Marino 		if (!cmp_res)
104150fc853eSJohn Marino 			cmp_res = bwscoll(s1, s2, 0);
104250fc853eSJohn Marino 
104350fc853eSJohn Marino 		return (cmp_res);
104450fc853eSJohn Marino 	}
104550fc853eSJohn Marino }
10466d7e22ecSzrj #endif
104750fc853eSJohn Marino 
104850fc853eSJohn Marino /*
104950fc853eSJohn Marino  * Implements version sort (-V).
105050fc853eSJohn Marino  */
105150fc853eSJohn Marino static int
versioncoll(struct key_value * kv1,struct key_value * kv2,size_t offset __unused)105250fc853eSJohn Marino versioncoll(struct key_value *kv1, struct key_value *kv2,
105350fc853eSJohn Marino     size_t offset __unused)
105450fc853eSJohn Marino {
105550fc853eSJohn Marino 	struct bwstring *s1, *s2;
105650fc853eSJohn Marino 
105750fc853eSJohn Marino 	s1 = kv1->k;
105850fc853eSJohn Marino 	s2 = kv2->k;
105950fc853eSJohn Marino 
106050fc853eSJohn Marino 	if (debug_sort) {
106150fc853eSJohn Marino 		bwsprintf(stdout, s1, "; k1=<", ">");
106250fc853eSJohn Marino 		bwsprintf(stdout, s2, ", k2=<", ">");
106350fc853eSJohn Marino 	}
106450fc853eSJohn Marino 
106550fc853eSJohn Marino 	if (s1 == s2)
106650fc853eSJohn Marino 		return (0);
106750fc853eSJohn Marino 
106850fc853eSJohn Marino 	return (vcmp(s1, s2));
106950fc853eSJohn Marino }
107050fc853eSJohn Marino 
107150fc853eSJohn Marino /*
107250fc853eSJohn Marino  * Check for minus infinity
107350fc853eSJohn Marino  */
107450fc853eSJohn Marino static inline bool
huge_minus(double d,int err1)107550fc853eSJohn Marino huge_minus(double d, int err1)
107650fc853eSJohn Marino {
107750fc853eSJohn Marino 
107850fc853eSJohn Marino 	if (err1 == ERANGE)
107950fc853eSJohn Marino 		if (d == -HUGE_VAL || d == -HUGE_VALF || d == -HUGE_VALL)
108050fc853eSJohn Marino 			return (+1);
108150fc853eSJohn Marino 
108250fc853eSJohn Marino 	return (0);
108350fc853eSJohn Marino }
108450fc853eSJohn Marino 
108550fc853eSJohn Marino /*
108650fc853eSJohn Marino  * Check for plus infinity
108750fc853eSJohn Marino  */
108850fc853eSJohn Marino static inline bool
huge_plus(double d,int err1)108950fc853eSJohn Marino huge_plus(double d, int err1)
109050fc853eSJohn Marino {
109150fc853eSJohn Marino 
109250fc853eSJohn Marino 	if (err1 == ERANGE)
109350fc853eSJohn Marino 		if (d == HUGE_VAL || d == HUGE_VALF || d == HUGE_VALL)
109450fc853eSJohn Marino 			return (+1);
109550fc853eSJohn Marino 
109650fc853eSJohn Marino 	return (0);
109750fc853eSJohn Marino }
109850fc853eSJohn Marino 
109950fc853eSJohn Marino /*
110050fc853eSJohn Marino  * Check whether a function is a NAN
110150fc853eSJohn Marino  */
110250fc853eSJohn Marino static bool
is_nan(double d)110350fc853eSJohn Marino is_nan(double d)
110450fc853eSJohn Marino {
110550fc853eSJohn Marino 	return ((d == NAN) || (isnan(d)));
110650fc853eSJohn Marino }
110750fc853eSJohn Marino 
110850fc853eSJohn Marino /*
110950fc853eSJohn Marino  * Compare two NANs
111050fc853eSJohn Marino  */
111150fc853eSJohn Marino static int
cmp_nans(double d1,double d2)111250fc853eSJohn Marino cmp_nans(double d1, double d2)
111350fc853eSJohn Marino {
111450fc853eSJohn Marino 
111550fc853eSJohn Marino 	if (d1 < d2)
111650fc853eSJohn Marino 		return (-1);
111794400e62SJohn Marino 	if (d1 > d2)
111850fc853eSJohn Marino 		return (+1);
111950fc853eSJohn Marino 	return (0);
112050fc853eSJohn Marino }
112150fc853eSJohn Marino 
112250fc853eSJohn Marino /*
112350fc853eSJohn Marino  * Implements general numeric sort (-g).
112450fc853eSJohn Marino  */
112550fc853eSJohn Marino static int
gnumcoll(struct key_value * kv1,struct key_value * kv2,size_t offset __unused)112650fc853eSJohn Marino gnumcoll(struct key_value *kv1, struct key_value *kv2,
112750fc853eSJohn Marino     size_t offset __unused)
112850fc853eSJohn Marino {
112950fc853eSJohn Marino 	double d1, d2;
113050fc853eSJohn Marino 	int err1, err2;
113150fc853eSJohn Marino 	bool empty1, empty2, key1_read, key2_read;
113250fc853eSJohn Marino 
113350fc853eSJohn Marino 	d1 = d2 = 0;
113450fc853eSJohn Marino 	err1 = err2 = 0;
113550fc853eSJohn Marino 	key1_read = key2_read = false;
113650fc853eSJohn Marino 
113750fc853eSJohn Marino 	if (debug_sort) {
113850fc853eSJohn Marino 		bwsprintf(stdout, kv1->k, "; k1=<", ">");
113950fc853eSJohn Marino 		bwsprintf(stdout, kv2->k, "; k2=<", ">");
114050fc853eSJohn Marino 	}
114150fc853eSJohn Marino 
114250fc853eSJohn Marino 	if (kv1->hint->status == HS_UNINITIALIZED) {
114350fc853eSJohn Marino 		errno = 0;
114450fc853eSJohn Marino 		d1 = bwstod(kv1->k, &empty1);
114550fc853eSJohn Marino 		err1 = errno;
114650fc853eSJohn Marino 
114750fc853eSJohn Marino 		if (empty1)
114850fc853eSJohn Marino 			kv1->hint->v.gh.notnum = true;
114950fc853eSJohn Marino 		else if (err1 == 0) {
115050fc853eSJohn Marino 			kv1->hint->v.gh.d = d1;
115150fc853eSJohn Marino 			kv1->hint->v.gh.nan = is_nan(d1);
115250fc853eSJohn Marino 			kv1->hint->status = HS_INITIALIZED;
115350fc853eSJohn Marino 		} else
115450fc853eSJohn Marino 			kv1->hint->status = HS_ERROR;
115550fc853eSJohn Marino 
115650fc853eSJohn Marino 		key1_read = true;
115750fc853eSJohn Marino 	}
115850fc853eSJohn Marino 
115950fc853eSJohn Marino 	if (kv2->hint->status == HS_UNINITIALIZED) {
116050fc853eSJohn Marino 		errno = 0;
116150fc853eSJohn Marino 		d2 = bwstod(kv2->k, &empty2);
116250fc853eSJohn Marino 		err2 = errno;
116350fc853eSJohn Marino 
116450fc853eSJohn Marino 		if (empty2)
116550fc853eSJohn Marino 			kv2->hint->v.gh.notnum = true;
116650fc853eSJohn Marino 		else if (err2 == 0) {
116750fc853eSJohn Marino 			kv2->hint->v.gh.d = d2;
116850fc853eSJohn Marino 			kv2->hint->v.gh.nan = is_nan(d2);
116950fc853eSJohn Marino 			kv2->hint->status = HS_INITIALIZED;
117050fc853eSJohn Marino 		} else
117150fc853eSJohn Marino 			kv2->hint->status = HS_ERROR;
117250fc853eSJohn Marino 
117350fc853eSJohn Marino 		key2_read = true;
117450fc853eSJohn Marino 	}
117550fc853eSJohn Marino 
117650fc853eSJohn Marino 	if (kv1->hint->status == HS_INITIALIZED &&
117750fc853eSJohn Marino 	    kv2->hint->status == HS_INITIALIZED) {
117850fc853eSJohn Marino 		if (kv1->hint->v.gh.notnum)
117950fc853eSJohn Marino 			return ((kv2->hint->v.gh.notnum) ? 0 : -1);
118050fc853eSJohn Marino 		else if (kv2->hint->v.gh.notnum)
118150fc853eSJohn Marino 			return (+1);
118250fc853eSJohn Marino 
118350fc853eSJohn Marino 		if (kv1->hint->v.gh.nan)
118450fc853eSJohn Marino 			return ((kv2->hint->v.gh.nan) ?
118550fc853eSJohn Marino 			    cmp_nans(kv1->hint->v.gh.d, kv2->hint->v.gh.d) :
118650fc853eSJohn Marino 			    -1);
118750fc853eSJohn Marino 		else if (kv2->hint->v.gh.nan)
118850fc853eSJohn Marino 			return (+1);
118950fc853eSJohn Marino 
119050fc853eSJohn Marino 		d1 = kv1->hint->v.gh.d;
119150fc853eSJohn Marino 		d2 = kv2->hint->v.gh.d;
119250fc853eSJohn Marino 
119350fc853eSJohn Marino 		if (d1 < d2)
119450fc853eSJohn Marino 			return (-1);
119550fc853eSJohn Marino 		else if (d1 > d2)
119650fc853eSJohn Marino 			return (+1);
119750fc853eSJohn Marino 		else
119850fc853eSJohn Marino 			return (0);
119950fc853eSJohn Marino 	}
120050fc853eSJohn Marino 
120150fc853eSJohn Marino 	if (!key1_read) {
120250fc853eSJohn Marino 		errno = 0;
120350fc853eSJohn Marino 		d1 = bwstod(kv1->k, &empty1);
120450fc853eSJohn Marino 		err1 = errno;
120550fc853eSJohn Marino 	}
120650fc853eSJohn Marino 
120750fc853eSJohn Marino 	if (!key2_read) {
120850fc853eSJohn Marino 		errno = 0;
120950fc853eSJohn Marino 		d2 = bwstod(kv2->k, &empty2);
121050fc853eSJohn Marino 		err2 = errno;
121150fc853eSJohn Marino 	}
121250fc853eSJohn Marino 
121350fc853eSJohn Marino 	/* Non-value case: */
121450fc853eSJohn Marino 	if (empty1)
121550fc853eSJohn Marino 		return (empty2 ? 0 : -1);
121650fc853eSJohn Marino 	else if (empty2)
121750fc853eSJohn Marino 		return (+1);
121850fc853eSJohn Marino 
121950fc853eSJohn Marino 	/* NAN case */
122050fc853eSJohn Marino 	if (is_nan(d1))
122150fc853eSJohn Marino 		return (is_nan(d2) ? cmp_nans(d1, d2) : -1);
122250fc853eSJohn Marino 	else if (is_nan(d2))
122350fc853eSJohn Marino 		return (+1);
122450fc853eSJohn Marino 
122550fc853eSJohn Marino 	/* Infinities */
122650fc853eSJohn Marino 	if (err1 == ERANGE || err2 == ERANGE) {
122750fc853eSJohn Marino 		/* Minus infinity case */
122850fc853eSJohn Marino 		if (huge_minus(d1, err1)) {
122950fc853eSJohn Marino 			if (huge_minus(d2, err2)) {
123050fc853eSJohn Marino 				if (d1 < d2)
123150fc853eSJohn Marino 					return (-1);
123250fc853eSJohn Marino 				if (d1 > d2)
123350fc853eSJohn Marino 					return (+1);
123450fc853eSJohn Marino 				return (0);
123550fc853eSJohn Marino 			} else
123650fc853eSJohn Marino 				return (-1);
123750fc853eSJohn Marino 
123850fc853eSJohn Marino 		} else if (huge_minus(d2, err2)) {
123950fc853eSJohn Marino 			if (huge_minus(d1, err1)) {
124050fc853eSJohn Marino 				if (d1 < d2)
124150fc853eSJohn Marino 					return (-1);
124250fc853eSJohn Marino 				if (d1 > d2)
124350fc853eSJohn Marino 					return (+1);
124450fc853eSJohn Marino 				return (0);
124550fc853eSJohn Marino 			} else
124650fc853eSJohn Marino 				return (+1);
124750fc853eSJohn Marino 		}
124850fc853eSJohn Marino 
124950fc853eSJohn Marino 		/* Plus infinity case */
125050fc853eSJohn Marino 		if (huge_plus(d1, err1)) {
125150fc853eSJohn Marino 			if (huge_plus(d2, err2)) {
125250fc853eSJohn Marino 				if (d1 < d2)
125350fc853eSJohn Marino 					return (-1);
125450fc853eSJohn Marino 				if (d1 > d2)
125550fc853eSJohn Marino 					return (+1);
125650fc853eSJohn Marino 				return (0);
125750fc853eSJohn Marino 			} else
125850fc853eSJohn Marino 				return (+1);
125950fc853eSJohn Marino 		} else if (huge_plus(d2, err2)) {
126050fc853eSJohn Marino 			if (huge_plus(d1, err1)) {
126150fc853eSJohn Marino 				if (d1 < d2)
126250fc853eSJohn Marino 					return (-1);
126350fc853eSJohn Marino 				if (d1 > d2)
126450fc853eSJohn Marino 					return (+1);
126550fc853eSJohn Marino 				return (0);
126650fc853eSJohn Marino 			} else
126750fc853eSJohn Marino 				return (-1);
126850fc853eSJohn Marino 		}
126950fc853eSJohn Marino 	}
127050fc853eSJohn Marino 
127150fc853eSJohn Marino 	if (d1 < d2)
127250fc853eSJohn Marino 		return (-1);
127350fc853eSJohn Marino 	if (d1 > d2)
127450fc853eSJohn Marino 		return (+1);
127550fc853eSJohn Marino 
127650fc853eSJohn Marino 	return (0);
127750fc853eSJohn Marino }
127850fc853eSJohn Marino 
127950fc853eSJohn Marino /*
128050fc853eSJohn Marino  * Implements month sort (-M).
128150fc853eSJohn Marino  */
128250fc853eSJohn Marino static int
monthcoll(struct key_value * kv1,struct key_value * kv2,size_t offset __unused)128350fc853eSJohn Marino monthcoll(struct key_value *kv1, struct key_value *kv2, size_t offset __unused)
128450fc853eSJohn Marino {
128550fc853eSJohn Marino 	int val1, val2;
128650fc853eSJohn Marino 	bool key1_read, key2_read;
128750fc853eSJohn Marino 
128850fc853eSJohn Marino 	val1 = val2 = 0;
128950fc853eSJohn Marino 	key1_read = key2_read = false;
129050fc853eSJohn Marino 
129150fc853eSJohn Marino 	if (debug_sort) {
129250fc853eSJohn Marino 		bwsprintf(stdout, kv1->k, "; k1=<", ">");
129350fc853eSJohn Marino 		bwsprintf(stdout, kv2->k, "; k2=<", ">");
129450fc853eSJohn Marino 	}
129550fc853eSJohn Marino 
129650fc853eSJohn Marino 	if (kv1->hint->status == HS_UNINITIALIZED) {
129750fc853eSJohn Marino 		kv1->hint->v.Mh.m = bws_month_score(kv1->k);
129850fc853eSJohn Marino 		key1_read = true;
129950fc853eSJohn Marino 		kv1->hint->status = HS_INITIALIZED;
130050fc853eSJohn Marino 	}
130150fc853eSJohn Marino 
130250fc853eSJohn Marino 	if (kv2->hint->status == HS_UNINITIALIZED) {
130350fc853eSJohn Marino 		kv2->hint->v.Mh.m = bws_month_score(kv2->k);
130450fc853eSJohn Marino 		key2_read = true;
130550fc853eSJohn Marino 		kv2->hint->status = HS_INITIALIZED;
130650fc853eSJohn Marino 	}
130750fc853eSJohn Marino 
130850fc853eSJohn Marino 	if (kv1->hint->status == HS_INITIALIZED) {
130950fc853eSJohn Marino 		val1 = kv1->hint->v.Mh.m;
131050fc853eSJohn Marino 		key1_read = true;
131150fc853eSJohn Marino 	}
131250fc853eSJohn Marino 
131350fc853eSJohn Marino 	if (kv2->hint->status == HS_INITIALIZED) {
131450fc853eSJohn Marino 		val2 = kv2->hint->v.Mh.m;
131550fc853eSJohn Marino 		key2_read = true;
131650fc853eSJohn Marino 	}
131750fc853eSJohn Marino 
131850fc853eSJohn Marino 	if (!key1_read)
131950fc853eSJohn Marino 		val1 = bws_month_score(kv1->k);
132050fc853eSJohn Marino 	if (!key2_read)
132150fc853eSJohn Marino 		val2 = bws_month_score(kv2->k);
132250fc853eSJohn Marino 
132350fc853eSJohn Marino 	if (val1 == val2) {
132450fc853eSJohn Marino 		return (0);
132550fc853eSJohn Marino 	}
132650fc853eSJohn Marino 	if (val1 < val2)
132750fc853eSJohn Marino 		return (-1);
132850fc853eSJohn Marino 	return (+1);
132950fc853eSJohn Marino }
1330