xref: /freebsd/usr.bin/sort/coll.c (revision ed7aec1e4566c0084f2c8009e91336994530113a)
1c66bbc91SGabor Kovesdan /*-
2c66bbc91SGabor Kovesdan  * Copyright (C) 2009 Gabor Kovesdan <gabor@FreeBSD.org>
3c859c6ddSGabor Kovesdan  * Copyright (C) 2012 Oleg Moskalenko <mom040267@gmail.com>
4c66bbc91SGabor Kovesdan  * All rights reserved.
5c66bbc91SGabor Kovesdan  *
6c66bbc91SGabor Kovesdan  * Redistribution and use in source and binary forms, with or without
7c66bbc91SGabor Kovesdan  * modification, are permitted provided that the following conditions
8c66bbc91SGabor Kovesdan  * are met:
9c66bbc91SGabor Kovesdan  * 1. Redistributions of source code must retain the above copyright
10c66bbc91SGabor Kovesdan  *    notice, this list of conditions and the following disclaimer.
11c66bbc91SGabor Kovesdan  * 2. Redistributions in binary form must reproduce the above copyright
12c66bbc91SGabor Kovesdan  *    notice, this list of conditions and the following disclaimer in the
13c66bbc91SGabor Kovesdan  *    documentation and/or other materials provided with the distribution.
14c66bbc91SGabor Kovesdan  *
15c66bbc91SGabor Kovesdan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16c66bbc91SGabor Kovesdan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17c66bbc91SGabor Kovesdan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18c66bbc91SGabor Kovesdan  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19c66bbc91SGabor Kovesdan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20c66bbc91SGabor Kovesdan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21c66bbc91SGabor Kovesdan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22c66bbc91SGabor Kovesdan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23c66bbc91SGabor Kovesdan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24c66bbc91SGabor Kovesdan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25c66bbc91SGabor Kovesdan  * SUCH DAMAGE.
26c66bbc91SGabor Kovesdan  */
27c66bbc91SGabor Kovesdan 
28c66bbc91SGabor Kovesdan #include <sys/cdefs.h>
29c66bbc91SGabor Kovesdan __FBSDID("$FreeBSD$");
30c66bbc91SGabor Kovesdan 
31c66bbc91SGabor Kovesdan #include <sys/types.h>
32c66bbc91SGabor Kovesdan 
33c66bbc91SGabor Kovesdan #include <errno.h>
34c66bbc91SGabor Kovesdan #include <err.h>
35c66bbc91SGabor Kovesdan #include <langinfo.h>
36c66bbc91SGabor Kovesdan #include <limits.h>
37c66bbc91SGabor Kovesdan #include <math.h>
38c66bbc91SGabor Kovesdan #include <md5.h>
39c66bbc91SGabor Kovesdan #include <stdlib.h>
40c66bbc91SGabor Kovesdan #include <string.h>
41c66bbc91SGabor Kovesdan #include <wchar.h>
42c66bbc91SGabor Kovesdan #include <wctype.h>
43c66bbc91SGabor Kovesdan 
44c66bbc91SGabor Kovesdan #include "coll.h"
45c66bbc91SGabor Kovesdan #include "vsort.h"
46c66bbc91SGabor Kovesdan 
47ce1e997fSGabor Kovesdan struct key_specs *keys;
48c66bbc91SGabor Kovesdan size_t keys_num = 0;
49c66bbc91SGabor Kovesdan 
50e8da8c74SGabor Kovesdan wint_t symbol_decimal_point = L'.';
51c66bbc91SGabor Kovesdan /* there is no default thousands separator in collate rules: */
52e8da8c74SGabor Kovesdan wint_t symbol_thousands_sep = 0;
53e8da8c74SGabor Kovesdan wint_t symbol_negative_sign = L'-';
54e8da8c74SGabor Kovesdan wint_t symbol_positive_sign = L'+';
55c66bbc91SGabor Kovesdan 
56c66bbc91SGabor Kovesdan static int wstrcoll(struct key_value *kv1, struct key_value *kv2, size_t offset);
57c66bbc91SGabor Kovesdan static int gnumcoll(struct key_value*, struct key_value *, size_t offset);
58c66bbc91SGabor Kovesdan static int monthcoll(struct key_value*, struct key_value *, size_t offset);
59c66bbc91SGabor Kovesdan static int numcoll(struct key_value*, struct key_value *, size_t offset);
60c66bbc91SGabor Kovesdan static int hnumcoll(struct key_value*, struct key_value *, size_t offset);
61c66bbc91SGabor Kovesdan static int randomcoll(struct key_value*, struct key_value *, size_t offset);
62c66bbc91SGabor Kovesdan static int versioncoll(struct key_value*, struct key_value *, size_t offset);
63c66bbc91SGabor Kovesdan 
64c66bbc91SGabor Kovesdan /*
65c66bbc91SGabor Kovesdan  * Allocate keys array
66c66bbc91SGabor Kovesdan  */
67c66bbc91SGabor Kovesdan struct keys_array *
68c66bbc91SGabor Kovesdan keys_array_alloc(void)
69c66bbc91SGabor Kovesdan {
70c66bbc91SGabor Kovesdan 	struct keys_array *ka;
71c66bbc91SGabor Kovesdan 	size_t sz;
72c66bbc91SGabor Kovesdan 
73c66bbc91SGabor Kovesdan 	sz = keys_array_size();
74c66bbc91SGabor Kovesdan 	ka = sort_malloc(sz);
75c66bbc91SGabor Kovesdan 	memset(ka, 0, sz);
76c66bbc91SGabor Kovesdan 
77c66bbc91SGabor Kovesdan 	return (ka);
78c66bbc91SGabor Kovesdan }
79c66bbc91SGabor Kovesdan 
80c66bbc91SGabor Kovesdan /*
81c66bbc91SGabor Kovesdan  * Calculate whether we need key hint space
82c66bbc91SGabor Kovesdan  */
83c66bbc91SGabor Kovesdan static size_t
84c66bbc91SGabor Kovesdan key_hint_size(void)
85c66bbc91SGabor Kovesdan {
86c66bbc91SGabor Kovesdan 
87c66bbc91SGabor Kovesdan 	return (need_hint ? sizeof(struct key_hint) : 0);
88c66bbc91SGabor Kovesdan }
89c66bbc91SGabor Kovesdan 
90c66bbc91SGabor Kovesdan /*
91c66bbc91SGabor Kovesdan  * Calculate keys array size
92c66bbc91SGabor Kovesdan  */
93c66bbc91SGabor Kovesdan size_t
94c66bbc91SGabor Kovesdan keys_array_size(void)
95c66bbc91SGabor Kovesdan {
96e5f71a07SPedro F. Giffuni 
97c66bbc91SGabor Kovesdan 	return (keys_num * (sizeof(struct key_value) + key_hint_size()));
98c66bbc91SGabor Kovesdan }
99c66bbc91SGabor Kovesdan 
100c66bbc91SGabor Kovesdan /*
101c66bbc91SGabor Kovesdan  * Clean data of keys array
102c66bbc91SGabor Kovesdan  */
103c66bbc91SGabor Kovesdan void
104c66bbc91SGabor Kovesdan clean_keys_array(const struct bwstring *s, struct keys_array *ka)
105c66bbc91SGabor Kovesdan {
106e5f71a07SPedro F. Giffuni 
107c66bbc91SGabor Kovesdan 	if (ka) {
108*ed7aec1eSMarius Strobl 		for (size_t i = 0; i < keys_num; ++i) {
109*ed7aec1eSMarius Strobl 			const struct key_value *kv;
110*ed7aec1eSMarius Strobl 
111*ed7aec1eSMarius Strobl 			kv = get_key_from_keys_array(ka, i);
112*ed7aec1eSMarius Strobl 			if (kv->k && kv->k != s)
113*ed7aec1eSMarius Strobl 				bwsfree(kv->k);
114*ed7aec1eSMarius Strobl 		}
115c66bbc91SGabor Kovesdan 		memset(ka, 0, keys_array_size());
116c66bbc91SGabor Kovesdan 	}
117c66bbc91SGabor Kovesdan }
118c66bbc91SGabor Kovesdan 
119c66bbc91SGabor Kovesdan /*
120*ed7aec1eSMarius Strobl  * Get pointer to a key value in the keys set
121*ed7aec1eSMarius Strobl  */
122*ed7aec1eSMarius Strobl struct key_value *
123*ed7aec1eSMarius Strobl get_key_from_keys_array(struct keys_array *ka, size_t ind)
124*ed7aec1eSMarius Strobl {
125*ed7aec1eSMarius Strobl 
126*ed7aec1eSMarius Strobl 	return ((struct key_value *)((caddr_t)ka->key +
127*ed7aec1eSMarius Strobl 	    ind * (sizeof(struct key_value) + key_hint_size())));
128*ed7aec1eSMarius Strobl }
129*ed7aec1eSMarius Strobl 
130*ed7aec1eSMarius Strobl /*
131c66bbc91SGabor Kovesdan  * Set value of a key in the keys set
132c66bbc91SGabor Kovesdan  */
133c66bbc91SGabor Kovesdan void
134c66bbc91SGabor Kovesdan set_key_on_keys_array(struct keys_array *ka, struct bwstring *s, size_t ind)
135c66bbc91SGabor Kovesdan {
136e5f71a07SPedro F. Giffuni 
137c66bbc91SGabor Kovesdan 	if (ka && keys_num > ind) {
138c66bbc91SGabor Kovesdan 		struct key_value *kv;
139c66bbc91SGabor Kovesdan 
140*ed7aec1eSMarius Strobl 		kv = get_key_from_keys_array(ka, ind);
141c66bbc91SGabor Kovesdan 
142c66bbc91SGabor Kovesdan 		if (kv->k && kv->k != s)
143c66bbc91SGabor Kovesdan 			bwsfree(kv->k);
144c66bbc91SGabor Kovesdan 		kv->k = s;
145c66bbc91SGabor Kovesdan 	}
146c66bbc91SGabor Kovesdan }
147c66bbc91SGabor Kovesdan 
148c66bbc91SGabor Kovesdan /*
149c66bbc91SGabor Kovesdan  * Initialize a sort list item
150c66bbc91SGabor Kovesdan  */
151c66bbc91SGabor Kovesdan struct sort_list_item *
152c66bbc91SGabor Kovesdan sort_list_item_alloc(void)
153c66bbc91SGabor Kovesdan {
154c66bbc91SGabor Kovesdan 	struct sort_list_item *si;
155c66bbc91SGabor Kovesdan 	size_t sz;
156c66bbc91SGabor Kovesdan 
157c66bbc91SGabor Kovesdan 	sz = sizeof(struct sort_list_item) + keys_array_size();
158c66bbc91SGabor Kovesdan 	si = sort_malloc(sz);
159c66bbc91SGabor Kovesdan 	memset(si, 0, sz);
160c66bbc91SGabor Kovesdan 
161c66bbc91SGabor Kovesdan 	return (si);
162c66bbc91SGabor Kovesdan }
163c66bbc91SGabor Kovesdan 
164c66bbc91SGabor Kovesdan size_t
165c66bbc91SGabor Kovesdan sort_list_item_size(struct sort_list_item *si)
166c66bbc91SGabor Kovesdan {
167c66bbc91SGabor Kovesdan 	size_t ret = 0;
168c66bbc91SGabor Kovesdan 
169c66bbc91SGabor Kovesdan 	if (si) {
170c66bbc91SGabor Kovesdan 		ret = sizeof(struct sort_list_item) + keys_array_size();
171c66bbc91SGabor Kovesdan 		if (si->str)
172c66bbc91SGabor Kovesdan 			ret += bws_memsize(si->str);
173c66bbc91SGabor Kovesdan 		for (size_t i = 0; i < keys_num; ++i) {
174*ed7aec1eSMarius Strobl 			const struct key_value *kv;
175c66bbc91SGabor Kovesdan 
176*ed7aec1eSMarius Strobl 			kv = get_key_from_keys_array(&si->ka, i);
177c66bbc91SGabor Kovesdan 
178c66bbc91SGabor Kovesdan 			if (kv->k != si->str)
179c66bbc91SGabor Kovesdan 				ret += bws_memsize(kv->k);
180c66bbc91SGabor Kovesdan 		}
181c66bbc91SGabor Kovesdan 	}
182c66bbc91SGabor Kovesdan 	return (ret);
183c66bbc91SGabor Kovesdan }
184c66bbc91SGabor Kovesdan 
185c66bbc91SGabor Kovesdan /*
186c66bbc91SGabor Kovesdan  * Calculate key for a sort list item
187c66bbc91SGabor Kovesdan  */
188c66bbc91SGabor Kovesdan static void
189c66bbc91SGabor Kovesdan sort_list_item_make_key(struct sort_list_item *si)
190c66bbc91SGabor Kovesdan {
191e5f71a07SPedro F. Giffuni 
192c66bbc91SGabor Kovesdan 	preproc(si->str, &(si->ka));
193c66bbc91SGabor Kovesdan }
194c66bbc91SGabor Kovesdan 
195c66bbc91SGabor Kovesdan /*
196c66bbc91SGabor Kovesdan  * Set value of a sort list item.
197c66bbc91SGabor Kovesdan  * Return combined string and keys memory size.
198c66bbc91SGabor Kovesdan  */
199c66bbc91SGabor Kovesdan void
200c66bbc91SGabor Kovesdan sort_list_item_set(struct sort_list_item *si, struct bwstring *str)
201c66bbc91SGabor Kovesdan {
202e5f71a07SPedro F. Giffuni 
203c66bbc91SGabor Kovesdan 	if (si) {
204c66bbc91SGabor Kovesdan 		clean_keys_array(si->str, &(si->ka));
205c66bbc91SGabor Kovesdan 		if (si->str) {
206c66bbc91SGabor Kovesdan 			if (si->str == str) {
207c66bbc91SGabor Kovesdan 				/* we are trying to reset the same string */
208c66bbc91SGabor Kovesdan 				return;
209c66bbc91SGabor Kovesdan 			} else {
210c66bbc91SGabor Kovesdan 				bwsfree(si->str);
211c66bbc91SGabor Kovesdan 				si->str = NULL;
212c66bbc91SGabor Kovesdan 			}
213c66bbc91SGabor Kovesdan 		}
214c66bbc91SGabor Kovesdan 		si->str = str;
215c66bbc91SGabor Kovesdan 		sort_list_item_make_key(si);
216c66bbc91SGabor Kovesdan 	}
217c66bbc91SGabor Kovesdan }
218c66bbc91SGabor Kovesdan 
219c66bbc91SGabor Kovesdan /*
220c66bbc91SGabor Kovesdan  * De-allocate a sort list item object memory
221c66bbc91SGabor Kovesdan  */
222c66bbc91SGabor Kovesdan void
223c66bbc91SGabor Kovesdan sort_list_item_clean(struct sort_list_item *si)
224c66bbc91SGabor Kovesdan {
225e5f71a07SPedro F. Giffuni 
226c66bbc91SGabor Kovesdan 	if (si) {
227c66bbc91SGabor Kovesdan 		clean_keys_array(si->str, &(si->ka));
228c66bbc91SGabor Kovesdan 		if (si->str) {
229c66bbc91SGabor Kovesdan 			bwsfree(si->str);
230c66bbc91SGabor Kovesdan 			si->str = NULL;
231c66bbc91SGabor Kovesdan 		}
232c66bbc91SGabor Kovesdan 	}
233c66bbc91SGabor Kovesdan }
234c66bbc91SGabor Kovesdan 
235c66bbc91SGabor Kovesdan /*
236c66bbc91SGabor Kovesdan  * Skip columns according to specs
237c66bbc91SGabor Kovesdan  */
238c66bbc91SGabor Kovesdan static size_t
239c66bbc91SGabor Kovesdan skip_cols_to_start(const struct bwstring *s, size_t cols, size_t start,
240c66bbc91SGabor Kovesdan     bool skip_blanks, bool *empty_key)
241c66bbc91SGabor Kovesdan {
242c66bbc91SGabor Kovesdan 	if (cols < 1)
243c66bbc91SGabor Kovesdan 		return (BWSLEN(s) + 1);
244c66bbc91SGabor Kovesdan 
245c66bbc91SGabor Kovesdan 	if (skip_blanks)
246c66bbc91SGabor Kovesdan 		while (start < BWSLEN(s) && iswblank(BWS_GET(s,start)))
247c66bbc91SGabor Kovesdan 			++start;
248c66bbc91SGabor Kovesdan 
249c66bbc91SGabor Kovesdan 	while (start < BWSLEN(s) && cols > 1) {
250c66bbc91SGabor Kovesdan 		--cols;
251c66bbc91SGabor Kovesdan 		++start;
252c66bbc91SGabor Kovesdan 	}
253c66bbc91SGabor Kovesdan 
254c66bbc91SGabor Kovesdan 	if (start >= BWSLEN(s))
255c66bbc91SGabor Kovesdan 		*empty_key = true;
256c66bbc91SGabor Kovesdan 
257c66bbc91SGabor Kovesdan 	return (start);
258c66bbc91SGabor Kovesdan }
259c66bbc91SGabor Kovesdan 
260c66bbc91SGabor Kovesdan /*
261c66bbc91SGabor Kovesdan  * Skip fields according to specs
262c66bbc91SGabor Kovesdan  */
263c66bbc91SGabor Kovesdan static size_t
264c66bbc91SGabor Kovesdan skip_fields_to_start(const struct bwstring *s, size_t fields, bool *empty_field)
265c66bbc91SGabor Kovesdan {
266e5f71a07SPedro F. Giffuni 
267c66bbc91SGabor Kovesdan 	if (fields < 2) {
268c66bbc91SGabor Kovesdan 		if (BWSLEN(s) == 0)
269c66bbc91SGabor Kovesdan 			*empty_field = true;
270c66bbc91SGabor Kovesdan 		return (0);
271c66bbc91SGabor Kovesdan 	} else if (!(sort_opts_vals.tflag)) {
272c66bbc91SGabor Kovesdan 		size_t cpos = 0;
273c66bbc91SGabor Kovesdan 		bool pb = true;
274c66bbc91SGabor Kovesdan 
275c66bbc91SGabor Kovesdan 		while (cpos < BWSLEN(s)) {
276c66bbc91SGabor Kovesdan 			bool isblank;
277c66bbc91SGabor Kovesdan 
278c66bbc91SGabor Kovesdan 			isblank = iswblank(BWS_GET(s, cpos));
279c66bbc91SGabor Kovesdan 
280c66bbc91SGabor Kovesdan 			if (isblank && !pb) {
281c66bbc91SGabor Kovesdan 				--fields;
282c66bbc91SGabor Kovesdan 				if (fields <= 1)
283c66bbc91SGabor Kovesdan 					return (cpos);
284c66bbc91SGabor Kovesdan 			}
285c66bbc91SGabor Kovesdan 			pb = isblank;
286c66bbc91SGabor Kovesdan 			++cpos;
287c66bbc91SGabor Kovesdan 		}
288c66bbc91SGabor Kovesdan 		if (fields > 1)
289c66bbc91SGabor Kovesdan 			*empty_field = true;
290c66bbc91SGabor Kovesdan 		return (cpos);
291c66bbc91SGabor Kovesdan 	} else {
292c66bbc91SGabor Kovesdan 		size_t cpos = 0;
293c66bbc91SGabor Kovesdan 
294c66bbc91SGabor Kovesdan 		while (cpos < BWSLEN(s)) {
295e8da8c74SGabor Kovesdan 			if (BWS_GET(s,cpos) == (wchar_t)sort_opts_vals.field_sep) {
296c66bbc91SGabor Kovesdan 				--fields;
297c66bbc91SGabor Kovesdan 				if (fields <= 1)
298c66bbc91SGabor Kovesdan 					return (cpos + 1);
299c66bbc91SGabor Kovesdan 			}
300c66bbc91SGabor Kovesdan 			++cpos;
301c66bbc91SGabor Kovesdan 		}
302c66bbc91SGabor Kovesdan 		if (fields > 1)
303c66bbc91SGabor Kovesdan 			*empty_field = true;
304c66bbc91SGabor Kovesdan 		return (cpos);
305c66bbc91SGabor Kovesdan 	}
306c66bbc91SGabor Kovesdan }
307c66bbc91SGabor Kovesdan 
308c66bbc91SGabor Kovesdan /*
309c66bbc91SGabor Kovesdan  * Find fields start
310c66bbc91SGabor Kovesdan  */
311c66bbc91SGabor Kovesdan static void
312c66bbc91SGabor Kovesdan find_field_start(const struct bwstring *s, struct key_specs *ks,
313c66bbc91SGabor Kovesdan     size_t *field_start, size_t *key_start, bool *empty_field, bool *empty_key)
314c66bbc91SGabor Kovesdan {
315e5f71a07SPedro F. Giffuni 
316c66bbc91SGabor Kovesdan 	*field_start = skip_fields_to_start(s, ks->f1, empty_field);
317c66bbc91SGabor Kovesdan 	if (!*empty_field)
318c66bbc91SGabor Kovesdan 		*key_start = skip_cols_to_start(s, ks->c1, *field_start,
319c66bbc91SGabor Kovesdan 		    ks->pos1b, empty_key);
320c66bbc91SGabor Kovesdan 	else
321c66bbc91SGabor Kovesdan 		*empty_key = true;
322c66bbc91SGabor Kovesdan }
323c66bbc91SGabor Kovesdan 
324c66bbc91SGabor Kovesdan /*
325c66bbc91SGabor Kovesdan  * Find end key position
326c66bbc91SGabor Kovesdan  */
327c66bbc91SGabor Kovesdan static size_t
328c66bbc91SGabor Kovesdan find_field_end(const struct bwstring *s, struct key_specs *ks)
329c66bbc91SGabor Kovesdan {
330c66bbc91SGabor Kovesdan 	size_t f2, next_field_start, pos_end;
331c66bbc91SGabor Kovesdan 	bool empty_field, empty_key;
332c66bbc91SGabor Kovesdan 
333c66bbc91SGabor Kovesdan 	pos_end = 0;
334c66bbc91SGabor Kovesdan 	next_field_start = 0;
335c66bbc91SGabor Kovesdan 	empty_field = false;
336c66bbc91SGabor Kovesdan 	empty_key = false;
337c66bbc91SGabor Kovesdan 	f2 = ks->f2;
338c66bbc91SGabor Kovesdan 
339c66bbc91SGabor Kovesdan 	if (f2 == 0)
340c66bbc91SGabor Kovesdan 		return (BWSLEN(s) + 1);
341c66bbc91SGabor Kovesdan 	else {
342c66bbc91SGabor Kovesdan 		if (ks->c2 == 0) {
343c66bbc91SGabor Kovesdan 			next_field_start = skip_fields_to_start(s, f2 + 1,
344c66bbc91SGabor Kovesdan 			    &empty_field);
345c66bbc91SGabor Kovesdan 			if ((next_field_start > 0) && sort_opts_vals.tflag &&
346e8da8c74SGabor Kovesdan 			    ((wchar_t)sort_opts_vals.field_sep == BWS_GET(s,
347c66bbc91SGabor Kovesdan 			    next_field_start - 1)))
348c66bbc91SGabor Kovesdan 				--next_field_start;
349c66bbc91SGabor Kovesdan 		} else
350c66bbc91SGabor Kovesdan 			next_field_start = skip_fields_to_start(s, f2,
351c66bbc91SGabor Kovesdan 			    &empty_field);
352c66bbc91SGabor Kovesdan 	}
353c66bbc91SGabor Kovesdan 
354c66bbc91SGabor Kovesdan 	if (empty_field || (next_field_start >= BWSLEN(s)))
355c66bbc91SGabor Kovesdan 		return (BWSLEN(s) + 1);
356c66bbc91SGabor Kovesdan 
357c66bbc91SGabor Kovesdan 	if (ks->c2) {
358c66bbc91SGabor Kovesdan 		pos_end = skip_cols_to_start(s, ks->c2, next_field_start,
359c66bbc91SGabor Kovesdan 		    ks->pos2b, &empty_key);
360c66bbc91SGabor Kovesdan 		if (pos_end < BWSLEN(s))
361c66bbc91SGabor Kovesdan 			++pos_end;
362c66bbc91SGabor Kovesdan 	} else
363c66bbc91SGabor Kovesdan 		pos_end = next_field_start;
364c66bbc91SGabor Kovesdan 
365c66bbc91SGabor Kovesdan 	return (pos_end);
366c66bbc91SGabor Kovesdan }
367c66bbc91SGabor Kovesdan 
368c66bbc91SGabor Kovesdan /*
369c66bbc91SGabor Kovesdan  * Cut a field according to the key specs
370c66bbc91SGabor Kovesdan  */
371c66bbc91SGabor Kovesdan static struct bwstring *
372c66bbc91SGabor Kovesdan cut_field(const struct bwstring *s, struct key_specs *ks)
373c66bbc91SGabor Kovesdan {
374c66bbc91SGabor Kovesdan 	struct bwstring *ret = NULL;
375c66bbc91SGabor Kovesdan 
376c66bbc91SGabor Kovesdan 	if (s && ks) {
377c66bbc91SGabor Kovesdan 		size_t field_start, key_end, key_start, sz;
378c66bbc91SGabor Kovesdan 		bool empty_field, empty_key;
379c66bbc91SGabor Kovesdan 
380c66bbc91SGabor Kovesdan 		field_start = 0;
381c66bbc91SGabor Kovesdan 		key_start = 0;
382c66bbc91SGabor Kovesdan 		empty_field = false;
383c66bbc91SGabor Kovesdan 		empty_key = false;
384c66bbc91SGabor Kovesdan 
385c66bbc91SGabor Kovesdan 		find_field_start(s, ks, &field_start, &key_start,
386c66bbc91SGabor Kovesdan 		    &empty_field, &empty_key);
387c66bbc91SGabor Kovesdan 
388c66bbc91SGabor Kovesdan 		if (empty_key)
389c66bbc91SGabor Kovesdan 			sz = 0;
390c66bbc91SGabor Kovesdan 		else {
391c66bbc91SGabor Kovesdan 			key_end = find_field_end(s, ks);
392c66bbc91SGabor Kovesdan 			sz = (key_end < key_start) ? 0 : (key_end - key_start);
393c66bbc91SGabor Kovesdan 		}
394c66bbc91SGabor Kovesdan 
395c66bbc91SGabor Kovesdan 		ret = bwsalloc(sz);
396c66bbc91SGabor Kovesdan 		if (sz)
397c66bbc91SGabor Kovesdan 			bwsnocpy(ret, s, key_start, sz);
398c66bbc91SGabor Kovesdan 	} else
399c66bbc91SGabor Kovesdan 		ret = bwsalloc(0);
400c66bbc91SGabor Kovesdan 
401c66bbc91SGabor Kovesdan 	return (ret);
402c66bbc91SGabor Kovesdan }
403c66bbc91SGabor Kovesdan 
404c66bbc91SGabor Kovesdan /*
405c66bbc91SGabor Kovesdan  * Preprocesses a line applying the necessary transformations
406c66bbc91SGabor Kovesdan  * specified by command line options and returns the preprocessed
407c66bbc91SGabor Kovesdan  * string, which can be used to compare.
408c66bbc91SGabor Kovesdan  */
409c66bbc91SGabor Kovesdan int
410c66bbc91SGabor Kovesdan preproc(struct bwstring *s, struct keys_array *ka)
411c66bbc91SGabor Kovesdan {
412e5f71a07SPedro F. Giffuni 
413c66bbc91SGabor Kovesdan 	if (sort_opts_vals.kflag)
414c66bbc91SGabor Kovesdan 		for (size_t i = 0; i < keys_num; i++) {
415c66bbc91SGabor Kovesdan 			struct bwstring *key;
416c66bbc91SGabor Kovesdan 			struct key_specs *kspecs;
417c66bbc91SGabor Kovesdan 			struct sort_mods *sm;
418c66bbc91SGabor Kovesdan 
419c66bbc91SGabor Kovesdan 			kspecs = &(keys[i]);
420c66bbc91SGabor Kovesdan 			key = cut_field(s, kspecs);
421c66bbc91SGabor Kovesdan 
422c66bbc91SGabor Kovesdan 			sm = &(kspecs->sm);
423c66bbc91SGabor Kovesdan 			if (sm->dflag)
424c66bbc91SGabor Kovesdan 				key = dictionary_order(key);
425c66bbc91SGabor Kovesdan 			else if (sm->iflag)
426c66bbc91SGabor Kovesdan 				key = ignore_nonprinting(key);
427c66bbc91SGabor Kovesdan 			if (sm->fflag || sm->Mflag)
428c66bbc91SGabor Kovesdan 				key = ignore_case(key);
429c66bbc91SGabor Kovesdan 
430c66bbc91SGabor Kovesdan 			set_key_on_keys_array(ka, key, i);
431c66bbc91SGabor Kovesdan 		}
432c66bbc91SGabor Kovesdan 	else {
433c66bbc91SGabor Kovesdan 		struct bwstring *ret = NULL;
434c66bbc91SGabor Kovesdan 		struct sort_mods *sm = default_sort_mods;
435c66bbc91SGabor Kovesdan 
436c66bbc91SGabor Kovesdan 		if (sm->bflag) {
437c66bbc91SGabor Kovesdan 			if (ret == NULL)
438c66bbc91SGabor Kovesdan 				ret = bwsdup(s);
439c66bbc91SGabor Kovesdan 			ret = ignore_leading_blanks(ret);
440c66bbc91SGabor Kovesdan 		}
441c66bbc91SGabor Kovesdan 		if (sm->dflag) {
442c66bbc91SGabor Kovesdan 			if (ret == NULL)
443c66bbc91SGabor Kovesdan 				ret = bwsdup(s);
444c66bbc91SGabor Kovesdan 			ret = dictionary_order(ret);
445c66bbc91SGabor Kovesdan 		} else if (sm->iflag) {
446c66bbc91SGabor Kovesdan 			if (ret == NULL)
447c66bbc91SGabor Kovesdan 				ret = bwsdup(s);
448c66bbc91SGabor Kovesdan 			ret = ignore_nonprinting(ret);
449c66bbc91SGabor Kovesdan 		}
450c66bbc91SGabor Kovesdan 		if (sm->fflag || sm->Mflag) {
451c66bbc91SGabor Kovesdan 			if (ret == NULL)
452c66bbc91SGabor Kovesdan 				ret = bwsdup(s);
453c66bbc91SGabor Kovesdan 			ret = ignore_case(ret);
454c66bbc91SGabor Kovesdan 		}
455c66bbc91SGabor Kovesdan 		if (ret == NULL)
456c66bbc91SGabor Kovesdan 			set_key_on_keys_array(ka, s, 0);
457c66bbc91SGabor Kovesdan 		else
458c66bbc91SGabor Kovesdan 			set_key_on_keys_array(ka, ret, 0);
459c66bbc91SGabor Kovesdan 	}
460c66bbc91SGabor Kovesdan 
461c66bbc91SGabor Kovesdan 	return 0;
462c66bbc91SGabor Kovesdan }
463c66bbc91SGabor Kovesdan 
464c66bbc91SGabor Kovesdan cmpcoll_t
465c66bbc91SGabor Kovesdan get_sort_func(struct sort_mods *sm)
466c66bbc91SGabor Kovesdan {
467e5f71a07SPedro F. Giffuni 
468c66bbc91SGabor Kovesdan 	if (sm->nflag)
469c66bbc91SGabor Kovesdan 		return (numcoll);
470c66bbc91SGabor Kovesdan 	else if (sm->hflag)
471c66bbc91SGabor Kovesdan 		return (hnumcoll);
472c66bbc91SGabor Kovesdan 	else if (sm->gflag)
473c66bbc91SGabor Kovesdan 		return (gnumcoll);
474c66bbc91SGabor Kovesdan 	else if (sm->Mflag)
475c66bbc91SGabor Kovesdan 		return (monthcoll);
476c66bbc91SGabor Kovesdan 	else if (sm->Rflag)
477c66bbc91SGabor Kovesdan 		return (randomcoll);
478c66bbc91SGabor Kovesdan 	else if (sm->Vflag)
479c66bbc91SGabor Kovesdan 		return (versioncoll);
480c66bbc91SGabor Kovesdan 	else
481c66bbc91SGabor Kovesdan 		return (wstrcoll);
482c66bbc91SGabor Kovesdan }
483c66bbc91SGabor Kovesdan 
484c66bbc91SGabor Kovesdan /*
485c66bbc91SGabor Kovesdan  * Compares the given strings.  Returns a positive number if
486c66bbc91SGabor Kovesdan  * the first precedes the second, a negative number if the second is
487c66bbc91SGabor Kovesdan  * the preceding one, and zero if they are equal.  This function calls
488c66bbc91SGabor Kovesdan  * the underlying collate functions, which done the actual comparison.
489c66bbc91SGabor Kovesdan  */
490c66bbc91SGabor Kovesdan int
491c66bbc91SGabor Kovesdan key_coll(struct keys_array *ps1, struct keys_array *ps2, size_t offset)
492c66bbc91SGabor Kovesdan {
493*ed7aec1eSMarius Strobl 	struct key_value *kv1, *kv2;
494c66bbc91SGabor Kovesdan 	struct sort_mods *sm;
495c66bbc91SGabor Kovesdan 	int res = 0;
496c66bbc91SGabor Kovesdan 
497c66bbc91SGabor Kovesdan 	for (size_t i = 0; i < keys_num; ++i) {
498*ed7aec1eSMarius Strobl 		kv1 = get_key_from_keys_array(ps1, i);
499*ed7aec1eSMarius Strobl 		kv2 = get_key_from_keys_array(ps2, i);
500c66bbc91SGabor Kovesdan 		sm = &(keys[i].sm);
501c66bbc91SGabor Kovesdan 
502c66bbc91SGabor Kovesdan 		if (sm->rflag)
503*ed7aec1eSMarius Strobl 			res = sm->func(kv2, kv1, offset);
504c66bbc91SGabor Kovesdan 		else
505*ed7aec1eSMarius Strobl 			res = sm->func(kv1, kv2, offset);
506c66bbc91SGabor Kovesdan 
507c66bbc91SGabor Kovesdan 		if (res)
508c66bbc91SGabor Kovesdan 			break;
509c66bbc91SGabor Kovesdan 
510c66bbc91SGabor Kovesdan 		/* offset applies to only the first key */
511c66bbc91SGabor Kovesdan 		offset = 0;
512c66bbc91SGabor Kovesdan 	}
513c66bbc91SGabor Kovesdan 	return (res);
514c66bbc91SGabor Kovesdan }
515c66bbc91SGabor Kovesdan 
516c66bbc91SGabor Kovesdan /*
517c66bbc91SGabor Kovesdan  * Compare two strings.
518c66bbc91SGabor Kovesdan  * Plain symbol-by-symbol comparison.
519c66bbc91SGabor Kovesdan  */
520c66bbc91SGabor Kovesdan int
521c66bbc91SGabor Kovesdan top_level_str_coll(const struct bwstring *s1, const struct bwstring *s2)
522c66bbc91SGabor Kovesdan {
523e5f71a07SPedro F. Giffuni 
524c66bbc91SGabor Kovesdan 	if (default_sort_mods->rflag) {
525c66bbc91SGabor Kovesdan 		const struct bwstring *tmp;
526c66bbc91SGabor Kovesdan 
527c66bbc91SGabor Kovesdan 		tmp = s1;
528c66bbc91SGabor Kovesdan 		s1 = s2;
529c66bbc91SGabor Kovesdan 		s2 = tmp;
530c66bbc91SGabor Kovesdan 	}
531c66bbc91SGabor Kovesdan 
532c66bbc91SGabor Kovesdan 	return (bwscoll(s1, s2, 0));
533c66bbc91SGabor Kovesdan }
534c66bbc91SGabor Kovesdan 
535c66bbc91SGabor Kovesdan /*
536c66bbc91SGabor Kovesdan  * Compare a string and a sort list item, according to the sort specs.
537c66bbc91SGabor Kovesdan  */
538c66bbc91SGabor Kovesdan int
539c66bbc91SGabor Kovesdan str_list_coll(struct bwstring *str1, struct sort_list_item **ss2)
540c66bbc91SGabor Kovesdan {
541c66bbc91SGabor Kovesdan 	struct keys_array *ka1;
542c66bbc91SGabor Kovesdan 	int ret = 0;
543c66bbc91SGabor Kovesdan 
544c66bbc91SGabor Kovesdan 	ka1 = keys_array_alloc();
545c66bbc91SGabor Kovesdan 
546c66bbc91SGabor Kovesdan 	preproc(str1, ka1);
547c66bbc91SGabor Kovesdan 
548c66bbc91SGabor Kovesdan 	sort_list_item_make_key(*ss2);
549c66bbc91SGabor Kovesdan 
550c66bbc91SGabor Kovesdan 	if (debug_sort) {
551c66bbc91SGabor Kovesdan 		bwsprintf(stdout, str1, "; s1=<", ">");
552c66bbc91SGabor Kovesdan 		bwsprintf(stdout, (*ss2)->str, ", s2=<", ">");
553c66bbc91SGabor Kovesdan 	}
554c66bbc91SGabor Kovesdan 
555c66bbc91SGabor Kovesdan 	ret = key_coll(ka1, &((*ss2)->ka), 0);
556c66bbc91SGabor Kovesdan 
557c66bbc91SGabor Kovesdan 	if (debug_sort)
558c66bbc91SGabor Kovesdan 		printf("; cmp1=%d", ret);
559c66bbc91SGabor Kovesdan 
560c66bbc91SGabor Kovesdan 	clean_keys_array(str1, ka1);
561c66bbc91SGabor Kovesdan 	sort_free(ka1);
562c66bbc91SGabor Kovesdan 
563c66bbc91SGabor Kovesdan 	if ((ret == 0) && !(sort_opts_vals.sflag) && sort_opts_vals.complex_sort) {
564c66bbc91SGabor Kovesdan 		ret = top_level_str_coll(str1, ((*ss2)->str));
565c66bbc91SGabor Kovesdan 		if (debug_sort)
566c66bbc91SGabor Kovesdan 			printf("; cmp2=%d", ret);
567c66bbc91SGabor Kovesdan 	}
568c66bbc91SGabor Kovesdan 
569c66bbc91SGabor Kovesdan 	if (debug_sort)
570c66bbc91SGabor Kovesdan 		printf("\n");
571c66bbc91SGabor Kovesdan 
572c66bbc91SGabor Kovesdan 	return (ret);
573c66bbc91SGabor Kovesdan }
574c66bbc91SGabor Kovesdan 
575c66bbc91SGabor Kovesdan /*
576c66bbc91SGabor Kovesdan  * Compare two sort list items, according to the sort specs.
577c66bbc91SGabor Kovesdan  */
578c66bbc91SGabor Kovesdan int
579c66bbc91SGabor Kovesdan list_coll_offset(struct sort_list_item **ss1, struct sort_list_item **ss2,
580c66bbc91SGabor Kovesdan     size_t offset)
581c66bbc91SGabor Kovesdan {
582c66bbc91SGabor Kovesdan 	int ret;
583c66bbc91SGabor Kovesdan 
584c66bbc91SGabor Kovesdan 	ret = key_coll(&((*ss1)->ka), &((*ss2)->ka), offset);
585c66bbc91SGabor Kovesdan 
586c66bbc91SGabor Kovesdan 	if (debug_sort) {
587c66bbc91SGabor Kovesdan 		if (offset)
588c66bbc91SGabor Kovesdan 			printf("; offset=%d", (int) offset);
589c66bbc91SGabor Kovesdan 		bwsprintf(stdout, ((*ss1)->str), "; s1=<", ">");
590c66bbc91SGabor Kovesdan 		bwsprintf(stdout, ((*ss2)->str), ", s2=<", ">");
591c66bbc91SGabor Kovesdan 		printf("; cmp1=%d\n", ret);
592c66bbc91SGabor Kovesdan 	}
593c66bbc91SGabor Kovesdan 
594c66bbc91SGabor Kovesdan 	if (ret)
595c66bbc91SGabor Kovesdan 		return (ret);
596c66bbc91SGabor Kovesdan 
597c66bbc91SGabor Kovesdan 	if (!(sort_opts_vals.sflag) && sort_opts_vals.complex_sort) {
598c66bbc91SGabor Kovesdan 		ret = top_level_str_coll(((*ss1)->str), ((*ss2)->str));
599c66bbc91SGabor Kovesdan 		if (debug_sort)
600c66bbc91SGabor Kovesdan 			printf("; cmp2=%d\n", ret);
601c66bbc91SGabor Kovesdan 	}
602c66bbc91SGabor Kovesdan 
603c66bbc91SGabor Kovesdan 	return (ret);
604c66bbc91SGabor Kovesdan }
605c66bbc91SGabor Kovesdan 
606c66bbc91SGabor Kovesdan /*
607c66bbc91SGabor Kovesdan  * Compare two sort list items, according to the sort specs.
608c66bbc91SGabor Kovesdan  */
609c66bbc91SGabor Kovesdan int
610c66bbc91SGabor Kovesdan list_coll(struct sort_list_item **ss1, struct sort_list_item **ss2)
611c66bbc91SGabor Kovesdan {
612e5f71a07SPedro F. Giffuni 
613c66bbc91SGabor Kovesdan 	return (list_coll_offset(ss1, ss2, 0));
614c66bbc91SGabor Kovesdan }
615c66bbc91SGabor Kovesdan 
616c66bbc91SGabor Kovesdan #define	LSCDEF(N)							\
617c66bbc91SGabor Kovesdan static int 								\
618c66bbc91SGabor Kovesdan list_coll_##N(struct sort_list_item **ss1, struct sort_list_item **ss2)	\
619c66bbc91SGabor Kovesdan {									\
620c66bbc91SGabor Kovesdan 									\
621c66bbc91SGabor Kovesdan 	return (list_coll_offset(ss1, ss2, N));				\
622c66bbc91SGabor Kovesdan }
623c66bbc91SGabor Kovesdan 
624c66bbc91SGabor Kovesdan LSCDEF(1)
625c66bbc91SGabor Kovesdan LSCDEF(2)
626c66bbc91SGabor Kovesdan LSCDEF(3)
627c66bbc91SGabor Kovesdan LSCDEF(4)
628c66bbc91SGabor Kovesdan LSCDEF(5)
629c66bbc91SGabor Kovesdan LSCDEF(6)
630c66bbc91SGabor Kovesdan LSCDEF(7)
631c66bbc91SGabor Kovesdan LSCDEF(8)
632c66bbc91SGabor Kovesdan LSCDEF(9)
633c66bbc91SGabor Kovesdan LSCDEF(10)
634c66bbc91SGabor Kovesdan LSCDEF(11)
635c66bbc91SGabor Kovesdan LSCDEF(12)
636c66bbc91SGabor Kovesdan LSCDEF(13)
637c66bbc91SGabor Kovesdan LSCDEF(14)
638c66bbc91SGabor Kovesdan LSCDEF(15)
639c66bbc91SGabor Kovesdan LSCDEF(16)
640c66bbc91SGabor Kovesdan LSCDEF(17)
641c66bbc91SGabor Kovesdan LSCDEF(18)
642c66bbc91SGabor Kovesdan LSCDEF(19)
643c66bbc91SGabor Kovesdan LSCDEF(20)
644c66bbc91SGabor Kovesdan 
645c66bbc91SGabor Kovesdan listcoll_t
646c66bbc91SGabor Kovesdan get_list_call_func(size_t offset)
647c66bbc91SGabor Kovesdan {
648c66bbc91SGabor Kovesdan 	static const listcoll_t lsarray[] = { list_coll, list_coll_1,
649c66bbc91SGabor Kovesdan 	    list_coll_2, list_coll_3, list_coll_4, list_coll_5,
650c66bbc91SGabor Kovesdan 	    list_coll_6, list_coll_7, list_coll_8, list_coll_9,
651c66bbc91SGabor Kovesdan 	    list_coll_10, list_coll_11, list_coll_12, list_coll_13,
652c66bbc91SGabor Kovesdan 	    list_coll_14, list_coll_15, list_coll_16, list_coll_17,
653c66bbc91SGabor Kovesdan 	    list_coll_18, list_coll_19, list_coll_20 };
654c66bbc91SGabor Kovesdan 
655c66bbc91SGabor Kovesdan 	if (offset <= 20)
656c66bbc91SGabor Kovesdan 		return (lsarray[offset]);
657c66bbc91SGabor Kovesdan 
658c66bbc91SGabor Kovesdan 	return (list_coll);
659c66bbc91SGabor Kovesdan }
660c66bbc91SGabor Kovesdan 
661c66bbc91SGabor Kovesdan /*
662c66bbc91SGabor Kovesdan  * Compare two sort list items, only by their original string.
663c66bbc91SGabor Kovesdan  */
664c66bbc91SGabor Kovesdan int
665c66bbc91SGabor Kovesdan list_coll_by_str_only(struct sort_list_item **ss1, struct sort_list_item **ss2)
666c66bbc91SGabor Kovesdan {
667e5f71a07SPedro F. Giffuni 
668c66bbc91SGabor Kovesdan 	return (top_level_str_coll(((*ss1)->str), ((*ss2)->str)));
669c66bbc91SGabor Kovesdan }
670c66bbc91SGabor Kovesdan 
671c66bbc91SGabor Kovesdan /*
672c66bbc91SGabor Kovesdan  * Maximum size of a number in the string (before or after decimal point)
673c66bbc91SGabor Kovesdan  */
674c66bbc91SGabor Kovesdan #define MAX_NUM_SIZE (128)
675c66bbc91SGabor Kovesdan 
676c66bbc91SGabor Kovesdan /*
677c66bbc91SGabor Kovesdan  * Set suffix value
678c66bbc91SGabor Kovesdan  */
679c66bbc91SGabor Kovesdan static void setsuffix(wchar_t c, unsigned char *si)
680c66bbc91SGabor Kovesdan {
681c66bbc91SGabor Kovesdan 	switch (c){
682c66bbc91SGabor Kovesdan 	case L'k':
683c66bbc91SGabor Kovesdan 	case L'K':
684c66bbc91SGabor Kovesdan 		*si = 1;
685c66bbc91SGabor Kovesdan 		break;
686c66bbc91SGabor Kovesdan 	case L'M':
687c66bbc91SGabor Kovesdan 		*si = 2;
688c66bbc91SGabor Kovesdan 		break;
689c66bbc91SGabor Kovesdan 	case L'G':
690c66bbc91SGabor Kovesdan 		*si = 3;
691c66bbc91SGabor Kovesdan 		break;
692c66bbc91SGabor Kovesdan 	case L'T':
693c66bbc91SGabor Kovesdan 		*si = 4;
694c66bbc91SGabor Kovesdan 		break;
695c66bbc91SGabor Kovesdan 	case L'P':
696c66bbc91SGabor Kovesdan 		*si = 5;
697c66bbc91SGabor Kovesdan 		break;
698c66bbc91SGabor Kovesdan 	case L'E':
699c66bbc91SGabor Kovesdan 		*si = 6;
700c66bbc91SGabor Kovesdan 		break;
701c66bbc91SGabor Kovesdan 	case L'Z':
702c66bbc91SGabor Kovesdan 		*si = 7;
703c66bbc91SGabor Kovesdan 		break;
704c66bbc91SGabor Kovesdan 	case L'Y':
705c66bbc91SGabor Kovesdan 		*si = 8;
706c66bbc91SGabor Kovesdan 		break;
707c66bbc91SGabor Kovesdan 	default:
708c66bbc91SGabor Kovesdan 		*si = 0;
70980c7cc1cSPedro F. Giffuni 	}
710c66bbc91SGabor Kovesdan }
711c66bbc91SGabor Kovesdan 
712c66bbc91SGabor Kovesdan /*
713c66bbc91SGabor Kovesdan  * Read string s and parse the string into a fixed-decimal-point number.
714c66bbc91SGabor Kovesdan  * sign equals -1 if the number is negative (explicit plus is not allowed,
715c66bbc91SGabor Kovesdan  * according to GNU sort's "info sort".
716c66bbc91SGabor Kovesdan  * The number part before decimal point is in the smain, after the decimal
717c66bbc91SGabor Kovesdan  * point is in sfrac, tail is the pointer to the remainder of the string.
718c66bbc91SGabor Kovesdan  */
719c66bbc91SGabor Kovesdan static int
720e8da8c74SGabor Kovesdan read_number(struct bwstring *s0, int *sign, wchar_t *smain, size_t *main_len, wchar_t *sfrac, size_t *frac_len, unsigned char *si)
721c66bbc91SGabor Kovesdan {
722c66bbc91SGabor Kovesdan 	bwstring_iterator s;
723c66bbc91SGabor Kovesdan 
724c66bbc91SGabor Kovesdan 	s = bws_begin(s0);
725c66bbc91SGabor Kovesdan 
726c66bbc91SGabor Kovesdan 	/* always end the fraction with zero, even if we have no fraction */
727c66bbc91SGabor Kovesdan 	sfrac[0] = 0;
728c66bbc91SGabor Kovesdan 
729c66bbc91SGabor Kovesdan 	while (iswblank(bws_get_iter_value(s)))
730c66bbc91SGabor Kovesdan 		s = bws_iterator_inc(s, 1);
731c66bbc91SGabor Kovesdan 
732e8da8c74SGabor Kovesdan 	if (bws_get_iter_value(s) == (wchar_t)symbol_negative_sign) {
733c66bbc91SGabor Kovesdan 		*sign = -1;
734c66bbc91SGabor Kovesdan 		s = bws_iterator_inc(s, 1);
735c66bbc91SGabor Kovesdan 	}
736c66bbc91SGabor Kovesdan 
737c66bbc91SGabor Kovesdan 	// This is '0', not '\0', do not change this
738c66bbc91SGabor Kovesdan 	while (iswdigit(bws_get_iter_value(s)) &&
739c66bbc91SGabor Kovesdan 	    (bws_get_iter_value(s) == L'0'))
740c66bbc91SGabor Kovesdan 		s = bws_iterator_inc(s, 1);
741c66bbc91SGabor Kovesdan 
742c66bbc91SGabor Kovesdan 	while (bws_get_iter_value(s) && *main_len < MAX_NUM_SIZE) {
743c66bbc91SGabor Kovesdan 		if (iswdigit(bws_get_iter_value(s))) {
744c66bbc91SGabor Kovesdan 			smain[*main_len] = bws_get_iter_value(s);
745c66bbc91SGabor Kovesdan 			s = bws_iterator_inc(s, 1);
746c66bbc91SGabor Kovesdan 			*main_len += 1;
747c66bbc91SGabor Kovesdan 		} else if (symbol_thousands_sep &&
748e8da8c74SGabor Kovesdan 		    (bws_get_iter_value(s) == (wchar_t)symbol_thousands_sep))
749c66bbc91SGabor Kovesdan 			s = bws_iterator_inc(s, 1);
750c66bbc91SGabor Kovesdan 		else
751c66bbc91SGabor Kovesdan 			break;
752c66bbc91SGabor Kovesdan 	}
753c66bbc91SGabor Kovesdan 
754c66bbc91SGabor Kovesdan 	smain[*main_len] = 0;
755c66bbc91SGabor Kovesdan 
756e8da8c74SGabor Kovesdan 	if (bws_get_iter_value(s) == (wchar_t)symbol_decimal_point) {
757c66bbc91SGabor Kovesdan 		s = bws_iterator_inc(s, 1);
758c66bbc91SGabor Kovesdan 		while (iswdigit(bws_get_iter_value(s)) &&
759c66bbc91SGabor Kovesdan 		    *frac_len < MAX_NUM_SIZE) {
760c66bbc91SGabor Kovesdan 			sfrac[*frac_len] = bws_get_iter_value(s);
761c66bbc91SGabor Kovesdan 			s = bws_iterator_inc(s, 1);
762c66bbc91SGabor Kovesdan 			*frac_len += 1;
763c66bbc91SGabor Kovesdan 		}
764c66bbc91SGabor Kovesdan 		sfrac[*frac_len] = 0;
765c66bbc91SGabor Kovesdan 
766c66bbc91SGabor Kovesdan 		while (*frac_len > 0 && sfrac[*frac_len - 1] == L'0') {
767c66bbc91SGabor Kovesdan 			--(*frac_len);
768c66bbc91SGabor Kovesdan 			sfrac[*frac_len] = L'\0';
769c66bbc91SGabor Kovesdan 		}
770c66bbc91SGabor Kovesdan 	}
771c66bbc91SGabor Kovesdan 
772c66bbc91SGabor Kovesdan 	setsuffix(bws_get_iter_value(s),si);
773c66bbc91SGabor Kovesdan 
774c66bbc91SGabor Kovesdan 	if ((*main_len + *frac_len) == 0)
775c66bbc91SGabor Kovesdan 		*sign = 0;
776c66bbc91SGabor Kovesdan 
777c66bbc91SGabor Kovesdan 	return (0);
778c66bbc91SGabor Kovesdan }
779c66bbc91SGabor Kovesdan 
780c66bbc91SGabor Kovesdan /*
781c66bbc91SGabor Kovesdan  * Implements string sort.
782c66bbc91SGabor Kovesdan  */
783c66bbc91SGabor Kovesdan static int
784c66bbc91SGabor Kovesdan wstrcoll(struct key_value *kv1, struct key_value *kv2, size_t offset)
785c66bbc91SGabor Kovesdan {
786c66bbc91SGabor Kovesdan 
787c66bbc91SGabor Kovesdan 	if (debug_sort) {
788c66bbc91SGabor Kovesdan 		if (offset)
789c66bbc91SGabor Kovesdan 			printf("; offset=%d\n", (int) offset);
790c66bbc91SGabor Kovesdan 		bwsprintf(stdout, kv1->k, "; k1=<", ">");
791c66bbc91SGabor Kovesdan 		printf("(%zu)", BWSLEN(kv1->k));
792c66bbc91SGabor Kovesdan 		bwsprintf(stdout, kv2->k, ", k2=<", ">");
793c66bbc91SGabor Kovesdan 		printf("(%zu)", BWSLEN(kv2->k));
794c66bbc91SGabor Kovesdan 	}
795c66bbc91SGabor Kovesdan 
796c66bbc91SGabor Kovesdan 	return (bwscoll(kv1->k, kv2->k, offset));
797c66bbc91SGabor Kovesdan }
798c66bbc91SGabor Kovesdan 
799c66bbc91SGabor Kovesdan /*
800c66bbc91SGabor Kovesdan  * Compare two suffixes
801c66bbc91SGabor Kovesdan  */
802c66bbc91SGabor Kovesdan static inline int
803c66bbc91SGabor Kovesdan cmpsuffix(unsigned char si1, unsigned char si2)
804c66bbc91SGabor Kovesdan {
805e5f71a07SPedro F. Giffuni 
806c66bbc91SGabor Kovesdan 	return ((char)si1 - (char)si2);
807c66bbc91SGabor Kovesdan }
808c66bbc91SGabor Kovesdan 
809c66bbc91SGabor Kovesdan /*
810c66bbc91SGabor Kovesdan  * Implements numeric sort for -n and -h.
811c66bbc91SGabor Kovesdan  */
812c66bbc91SGabor Kovesdan static int
81389b7a587SGabor Kovesdan numcoll_impl(struct key_value *kv1, struct key_value *kv2,
81489b7a587SGabor Kovesdan     size_t offset __unused, bool use_suffix)
815c66bbc91SGabor Kovesdan {
816c66bbc91SGabor Kovesdan 	struct bwstring *s1, *s2;
817c66bbc91SGabor Kovesdan 	wchar_t sfrac1[MAX_NUM_SIZE + 1], sfrac2[MAX_NUM_SIZE + 1];
818c66bbc91SGabor Kovesdan 	wchar_t smain1[MAX_NUM_SIZE + 1], smain2[MAX_NUM_SIZE + 1];
819e8da8c74SGabor Kovesdan 	int cmp_res, sign1, sign2;
820e8da8c74SGabor Kovesdan 	size_t frac1, frac2, main1, main2;
821c66bbc91SGabor Kovesdan 	unsigned char SI1, SI2;
822c66bbc91SGabor Kovesdan 	bool e1, e2, key1_read, key2_read;
823c66bbc91SGabor Kovesdan 
824c66bbc91SGabor Kovesdan 	s1 = kv1->k;
825c66bbc91SGabor Kovesdan 	s2 = kv2->k;
826c66bbc91SGabor Kovesdan 	sign1 = sign2 = 0;
827c66bbc91SGabor Kovesdan 	main1 = main2 = 0;
828c66bbc91SGabor Kovesdan 	frac1 = frac2 = 0;
829c66bbc91SGabor Kovesdan 
830c66bbc91SGabor Kovesdan 	cmp_res = 0;
831c66bbc91SGabor Kovesdan 	key1_read = key2_read = false;
832c66bbc91SGabor Kovesdan 
833c66bbc91SGabor Kovesdan 	if (debug_sort) {
834c66bbc91SGabor Kovesdan 		bwsprintf(stdout, s1, "; k1=<", ">");
835c66bbc91SGabor Kovesdan 		bwsprintf(stdout, s2, ", k2=<", ">");
836c66bbc91SGabor Kovesdan 	}
837c66bbc91SGabor Kovesdan 
838c66bbc91SGabor Kovesdan 	if (s1 == s2)
839c66bbc91SGabor Kovesdan 		return (0);
840c66bbc91SGabor Kovesdan 
841c66bbc91SGabor Kovesdan 	if (kv1->hint->status == HS_UNINITIALIZED) {
842c66bbc91SGabor Kovesdan 		/* read the number from the string */
843c66bbc91SGabor Kovesdan 		read_number(s1, &sign1, smain1, &main1, sfrac1, &frac1, &SI1);
844c66bbc91SGabor Kovesdan 		key1_read = true;
845c66bbc91SGabor Kovesdan 		kv1->hint->v.nh.n1 = wcstoull(smain1, NULL, 10);
846c66bbc91SGabor Kovesdan 		if(main1 < 1 && frac1 < 1)
847c66bbc91SGabor Kovesdan 			kv1->hint->v.nh.empty=true;
848c66bbc91SGabor Kovesdan 		kv1->hint->v.nh.si = SI1;
849c66bbc91SGabor Kovesdan 		kv1->hint->status = (kv1->hint->v.nh.n1 != ULLONG_MAX) ?
850c66bbc91SGabor Kovesdan 		    HS_INITIALIZED : HS_ERROR;
851c66bbc91SGabor Kovesdan 		kv1->hint->v.nh.neg = (sign1 < 0) ? true : false;
852c66bbc91SGabor Kovesdan 	}
853c66bbc91SGabor Kovesdan 
854c66bbc91SGabor Kovesdan 	if (kv2->hint->status == HS_UNINITIALIZED) {
855c66bbc91SGabor Kovesdan 		/* read the number from the string */
856c66bbc91SGabor Kovesdan 		read_number(s2, &sign2, smain2, &main2, sfrac2, &frac2,&SI2);
857c66bbc91SGabor Kovesdan 		key2_read = true;
858c66bbc91SGabor Kovesdan 		kv2->hint->v.nh.n1 = wcstoull(smain2, NULL, 10);
859c66bbc91SGabor Kovesdan 		if(main2 < 1 && frac2 < 1)
860c66bbc91SGabor Kovesdan 			kv2->hint->v.nh.empty=true;
861c66bbc91SGabor Kovesdan 		kv2->hint->v.nh.si = SI2;
862c66bbc91SGabor Kovesdan 		kv2->hint->status = (kv2->hint->v.nh.n1 != ULLONG_MAX) ?
863c66bbc91SGabor Kovesdan 		    HS_INITIALIZED : HS_ERROR;
864c66bbc91SGabor Kovesdan 		kv2->hint->v.nh.neg = (sign2 < 0) ? true : false;
865c66bbc91SGabor Kovesdan 	}
866c66bbc91SGabor Kovesdan 
867c66bbc91SGabor Kovesdan 	if (kv1->hint->status == HS_INITIALIZED && kv2->hint->status ==
868c66bbc91SGabor Kovesdan 	    HS_INITIALIZED) {
869c66bbc91SGabor Kovesdan 		unsigned long long n1, n2;
870c66bbc91SGabor Kovesdan 		bool neg1, neg2;
871c66bbc91SGabor Kovesdan 
872c66bbc91SGabor Kovesdan 		e1 = kv1->hint->v.nh.empty;
873c66bbc91SGabor Kovesdan 		e2 = kv2->hint->v.nh.empty;
874c66bbc91SGabor Kovesdan 
875c66bbc91SGabor Kovesdan 		if (e1 && e2)
876c66bbc91SGabor Kovesdan 			return (0);
877c66bbc91SGabor Kovesdan 
878c66bbc91SGabor Kovesdan 		neg1 = kv1->hint->v.nh.neg;
879c66bbc91SGabor Kovesdan 		neg2 = kv2->hint->v.nh.neg;
880c66bbc91SGabor Kovesdan 
881c66bbc91SGabor Kovesdan 		if (neg1 && !neg2)
882c66bbc91SGabor Kovesdan 			return (-1);
883c66bbc91SGabor Kovesdan 		if (neg2 && !neg1)
884c66bbc91SGabor Kovesdan 			return (+1);
885c66bbc91SGabor Kovesdan 
886c66bbc91SGabor Kovesdan 		if (e1)
887c66bbc91SGabor Kovesdan 			return (neg2 ? +1 : -1);
888c66bbc91SGabor Kovesdan 		else if (e2)
889c66bbc91SGabor Kovesdan 			return (neg1 ? -1 : +1);
890c66bbc91SGabor Kovesdan 
891c66bbc91SGabor Kovesdan 
892c66bbc91SGabor Kovesdan 		if (use_suffix) {
893c66bbc91SGabor Kovesdan 			cmp_res = cmpsuffix(kv1->hint->v.nh.si, kv2->hint->v.nh.si);
894c66bbc91SGabor Kovesdan 			if (cmp_res)
895c66bbc91SGabor Kovesdan 				return (neg1 ? -cmp_res : cmp_res);
896c66bbc91SGabor Kovesdan 		}
897c66bbc91SGabor Kovesdan 
898c66bbc91SGabor Kovesdan 		n1 = kv1->hint->v.nh.n1;
899c66bbc91SGabor Kovesdan 		n2 = kv2->hint->v.nh.n1;
900c66bbc91SGabor Kovesdan 		if (n1 < n2)
901c66bbc91SGabor Kovesdan 			return (neg1 ? +1 : -1);
902c66bbc91SGabor Kovesdan 		else if (n1 > n2)
903c66bbc91SGabor Kovesdan 			return (neg1 ? -1 : +1);
904c66bbc91SGabor Kovesdan 	}
905c66bbc91SGabor Kovesdan 
906c66bbc91SGabor Kovesdan 	/* read the numbers from the strings */
907c66bbc91SGabor Kovesdan 	if (!key1_read)
908c66bbc91SGabor Kovesdan 		read_number(s1, &sign1, smain1, &main1, sfrac1, &frac1, &SI1);
909c66bbc91SGabor Kovesdan 	if (!key2_read)
910c66bbc91SGabor Kovesdan 		read_number(s2, &sign2, smain2, &main2, sfrac2, &frac2, &SI2);
911c66bbc91SGabor Kovesdan 
912c66bbc91SGabor Kovesdan 	e1 = ((main1 + frac1) == 0);
913c66bbc91SGabor Kovesdan 	e2 = ((main2 + frac2) == 0);
914c66bbc91SGabor Kovesdan 
915c66bbc91SGabor Kovesdan 	if (e1 && e2)
916c66bbc91SGabor Kovesdan 		return (0);
917c66bbc91SGabor Kovesdan 
918c66bbc91SGabor Kovesdan 	/* we know the result if the signs are different */
919c66bbc91SGabor Kovesdan 	if (sign1 < 0 && sign2 >= 0)
920c66bbc91SGabor Kovesdan 		return (-1);
921c66bbc91SGabor Kovesdan 	if (sign1 >= 0 && sign2 < 0)
922c66bbc91SGabor Kovesdan 		return (+1);
923c66bbc91SGabor Kovesdan 
924c66bbc91SGabor Kovesdan 	if (e1)
925c66bbc91SGabor Kovesdan 		return ((sign2 < 0) ? +1 : -1);
926c66bbc91SGabor Kovesdan 	else if (e2)
927c66bbc91SGabor Kovesdan 		return ((sign1 < 0) ? -1 : +1);
928c66bbc91SGabor Kovesdan 
929c66bbc91SGabor Kovesdan 	if (use_suffix) {
930c66bbc91SGabor Kovesdan 		cmp_res = cmpsuffix(SI1, SI2);
931c66bbc91SGabor Kovesdan 		if (cmp_res)
932c66bbc91SGabor Kovesdan 			return ((sign1 < 0) ? -cmp_res : cmp_res);
933c66bbc91SGabor Kovesdan 	}
934c66bbc91SGabor Kovesdan 
935c66bbc91SGabor Kovesdan 	/* if both numbers are empty assume that the strings are equal */
936c66bbc91SGabor Kovesdan 	if (main1 < 1 && main2 < 1 && frac1 < 1 && frac2 < 1)
937c66bbc91SGabor Kovesdan 		return (0);
938c66bbc91SGabor Kovesdan 
939c66bbc91SGabor Kovesdan 	/*
940c66bbc91SGabor Kovesdan 	 * if the main part is of different size, we know the result
941c66bbc91SGabor Kovesdan 	 * (because the leading zeros are removed)
942c66bbc91SGabor Kovesdan 	 */
943c66bbc91SGabor Kovesdan 	if (main1 < main2)
944c66bbc91SGabor Kovesdan 		cmp_res = -1;
945c66bbc91SGabor Kovesdan 	else if (main1 > main2)
946c66bbc91SGabor Kovesdan 		cmp_res = +1;
947c66bbc91SGabor Kovesdan 	/* if the sizes are equal then simple non-collate string compare gives the correct result */
948c66bbc91SGabor Kovesdan 	else
949c66bbc91SGabor Kovesdan 		cmp_res = wcscmp(smain1, smain2);
950c66bbc91SGabor Kovesdan 
951c66bbc91SGabor Kovesdan 	/* check fraction */
952c66bbc91SGabor Kovesdan 	if (!cmp_res)
953c66bbc91SGabor Kovesdan 		cmp_res = wcscmp(sfrac1, sfrac2);
954c66bbc91SGabor Kovesdan 
955c66bbc91SGabor Kovesdan 	if (!cmp_res)
956c66bbc91SGabor Kovesdan 		return (0);
957c66bbc91SGabor Kovesdan 
958c66bbc91SGabor Kovesdan 	/* reverse result if the signs are negative */
959c66bbc91SGabor Kovesdan 	if (sign1 < 0 && sign2 < 0)
960c66bbc91SGabor Kovesdan 		cmp_res = -cmp_res;
961c66bbc91SGabor Kovesdan 
962c66bbc91SGabor Kovesdan 	return (cmp_res);
963c66bbc91SGabor Kovesdan }
964c66bbc91SGabor Kovesdan 
965c66bbc91SGabor Kovesdan /*
966c66bbc91SGabor Kovesdan  * Implements numeric sort (-n).
967c66bbc91SGabor Kovesdan  */
968c66bbc91SGabor Kovesdan static int
969c66bbc91SGabor Kovesdan numcoll(struct key_value *kv1, struct key_value *kv2, size_t offset)
970c66bbc91SGabor Kovesdan {
971e5f71a07SPedro F. Giffuni 
972c66bbc91SGabor Kovesdan 	return (numcoll_impl(kv1, kv2, offset, false));
973c66bbc91SGabor Kovesdan }
974c66bbc91SGabor Kovesdan 
975c66bbc91SGabor Kovesdan /*
976c66bbc91SGabor Kovesdan  * Implements 'human' numeric sort (-h).
977c66bbc91SGabor Kovesdan  */
978c66bbc91SGabor Kovesdan static int
979c66bbc91SGabor Kovesdan hnumcoll(struct key_value *kv1, struct key_value *kv2, size_t offset)
980c66bbc91SGabor Kovesdan {
981e5f71a07SPedro F. Giffuni 
982c66bbc91SGabor Kovesdan 	return (numcoll_impl(kv1, kv2, offset, true));
983c66bbc91SGabor Kovesdan }
984c66bbc91SGabor Kovesdan 
985c66bbc91SGabor Kovesdan /*
986c66bbc91SGabor Kovesdan  * Implements random sort (-R).
987c66bbc91SGabor Kovesdan  */
988c66bbc91SGabor Kovesdan static int
98989b7a587SGabor Kovesdan randomcoll(struct key_value *kv1, struct key_value *kv2,
99089b7a587SGabor Kovesdan     size_t offset __unused)
991c66bbc91SGabor Kovesdan {
992c66bbc91SGabor Kovesdan 	struct bwstring *s1, *s2;
993c66bbc91SGabor Kovesdan 	MD5_CTX ctx1, ctx2;
994c66bbc91SGabor Kovesdan 	char *b1, *b2;
995c66bbc91SGabor Kovesdan 
996c66bbc91SGabor Kovesdan 	s1 = kv1->k;
997c66bbc91SGabor Kovesdan 	s2 = kv2->k;
998c66bbc91SGabor Kovesdan 
999c66bbc91SGabor Kovesdan 	if (debug_sort) {
1000c66bbc91SGabor Kovesdan 		bwsprintf(stdout, s1, "; k1=<", ">");
1001c66bbc91SGabor Kovesdan 		bwsprintf(stdout, s2, ", k2=<", ">");
1002c66bbc91SGabor Kovesdan 	}
1003c66bbc91SGabor Kovesdan 
1004c66bbc91SGabor Kovesdan 	if (s1 == s2)
1005c66bbc91SGabor Kovesdan 		return (0);
1006c66bbc91SGabor Kovesdan 
1007c66bbc91SGabor Kovesdan 	memcpy(&ctx1,&md5_ctx,sizeof(MD5_CTX));
1008c66bbc91SGabor Kovesdan 	memcpy(&ctx2,&md5_ctx,sizeof(MD5_CTX));
1009c66bbc91SGabor Kovesdan 
1010c66bbc91SGabor Kovesdan 	MD5Update(&ctx1, bwsrawdata(s1), bwsrawlen(s1));
1011c66bbc91SGabor Kovesdan 	MD5Update(&ctx2, bwsrawdata(s2), bwsrawlen(s2));
1012c66bbc91SGabor Kovesdan 	b1 = MD5End(&ctx1, NULL);
1013c66bbc91SGabor Kovesdan 	b2 = MD5End(&ctx2, NULL);
1014c66bbc91SGabor Kovesdan 	if (b1 == NULL) {
1015c66bbc91SGabor Kovesdan 		if (b2 == NULL)
1016c66bbc91SGabor Kovesdan 			return (0);
1017c66bbc91SGabor Kovesdan 		else {
1018c66bbc91SGabor Kovesdan 			sort_free(b2);
1019c66bbc91SGabor Kovesdan 			return (-1);
1020c66bbc91SGabor Kovesdan 		}
1021c66bbc91SGabor Kovesdan 	} else if (b2 == NULL) {
1022c66bbc91SGabor Kovesdan 		sort_free(b1);
1023c66bbc91SGabor Kovesdan 		return (+1);
1024c66bbc91SGabor Kovesdan 	} else {
1025c66bbc91SGabor Kovesdan 		int cmp_res;
1026c66bbc91SGabor Kovesdan 
1027c66bbc91SGabor Kovesdan 		cmp_res = strcmp(b1,b2);
1028c66bbc91SGabor Kovesdan 		sort_free(b1);
1029c66bbc91SGabor Kovesdan 		sort_free(b2);
1030c66bbc91SGabor Kovesdan 
1031c66bbc91SGabor Kovesdan 		if (!cmp_res)
1032c66bbc91SGabor Kovesdan 			cmp_res = bwscoll(s1, s2, 0);
1033c66bbc91SGabor Kovesdan 
1034c66bbc91SGabor Kovesdan 		return (cmp_res);
1035c66bbc91SGabor Kovesdan 	}
1036c66bbc91SGabor Kovesdan }
1037c66bbc91SGabor Kovesdan 
1038c66bbc91SGabor Kovesdan /*
1039c66bbc91SGabor Kovesdan  * Implements version sort (-V).
1040c66bbc91SGabor Kovesdan  */
1041c66bbc91SGabor Kovesdan static int
104289b7a587SGabor Kovesdan versioncoll(struct key_value *kv1, struct key_value *kv2,
104389b7a587SGabor Kovesdan     size_t offset __unused)
1044c66bbc91SGabor Kovesdan {
1045c66bbc91SGabor Kovesdan 	struct bwstring *s1, *s2;
1046c66bbc91SGabor Kovesdan 
1047c66bbc91SGabor Kovesdan 	s1 = kv1->k;
1048c66bbc91SGabor Kovesdan 	s2 = kv2->k;
1049c66bbc91SGabor Kovesdan 
1050c66bbc91SGabor Kovesdan 	if (debug_sort) {
1051c66bbc91SGabor Kovesdan 		bwsprintf(stdout, s1, "; k1=<", ">");
1052c66bbc91SGabor Kovesdan 		bwsprintf(stdout, s2, ", k2=<", ">");
1053c66bbc91SGabor Kovesdan 	}
1054c66bbc91SGabor Kovesdan 
1055c66bbc91SGabor Kovesdan 	if (s1 == s2)
1056c66bbc91SGabor Kovesdan 		return (0);
1057c66bbc91SGabor Kovesdan 
1058c66bbc91SGabor Kovesdan 	return (vcmp(s1, s2));
1059c66bbc91SGabor Kovesdan }
1060c66bbc91SGabor Kovesdan 
1061c66bbc91SGabor Kovesdan /*
1062c66bbc91SGabor Kovesdan  * Check for minus infinity
1063c66bbc91SGabor Kovesdan  */
1064c66bbc91SGabor Kovesdan static inline bool
1065c66bbc91SGabor Kovesdan huge_minus(double d, int err1)
1066c66bbc91SGabor Kovesdan {
1067e5f71a07SPedro F. Giffuni 
1068c66bbc91SGabor Kovesdan 	if (err1 == ERANGE)
1069c66bbc91SGabor Kovesdan 		if (d == -HUGE_VAL || d == -HUGE_VALF || d == -HUGE_VALL)
1070c66bbc91SGabor Kovesdan 			return (+1);
1071c66bbc91SGabor Kovesdan 
1072c66bbc91SGabor Kovesdan 	return (0);
1073c66bbc91SGabor Kovesdan }
1074c66bbc91SGabor Kovesdan 
1075c66bbc91SGabor Kovesdan /*
1076c66bbc91SGabor Kovesdan  * Check for plus infinity
1077c66bbc91SGabor Kovesdan  */
1078c66bbc91SGabor Kovesdan static inline bool
1079c66bbc91SGabor Kovesdan huge_plus(double d, int err1)
1080c66bbc91SGabor Kovesdan {
1081e5f71a07SPedro F. Giffuni 
1082c66bbc91SGabor Kovesdan 	if (err1 == ERANGE)
1083c66bbc91SGabor Kovesdan 		if (d == HUGE_VAL || d == HUGE_VALF || d == HUGE_VALL)
1084c66bbc91SGabor Kovesdan 			return (+1);
1085c66bbc91SGabor Kovesdan 
1086c66bbc91SGabor Kovesdan 	return (0);
1087c66bbc91SGabor Kovesdan }
1088c66bbc91SGabor Kovesdan 
1089c66bbc91SGabor Kovesdan /*
1090c66bbc91SGabor Kovesdan  * Check whether a function is a NAN
1091c66bbc91SGabor Kovesdan  */
1092c66bbc91SGabor Kovesdan static bool
1093c66bbc91SGabor Kovesdan is_nan(double d)
1094c66bbc91SGabor Kovesdan {
1095e5f71a07SPedro F. Giffuni 
1096c66bbc91SGabor Kovesdan 	return ((d == NAN) || (isnan(d)));
1097c66bbc91SGabor Kovesdan }
1098c66bbc91SGabor Kovesdan 
1099c66bbc91SGabor Kovesdan /*
1100c66bbc91SGabor Kovesdan  * Compare two NANs
1101c66bbc91SGabor Kovesdan  */
1102c66bbc91SGabor Kovesdan static int
1103c66bbc91SGabor Kovesdan cmp_nans(double d1, double d2)
1104c66bbc91SGabor Kovesdan {
1105e5f71a07SPedro F. Giffuni 
1106c66bbc91SGabor Kovesdan 	if (d1 < d2)
1107c66bbc91SGabor Kovesdan 		return (-1);
1108*ed7aec1eSMarius Strobl 	if (d1 > d2)
1109c66bbc91SGabor Kovesdan 		return (+1);
1110c66bbc91SGabor Kovesdan 	return (0);
1111c66bbc91SGabor Kovesdan }
1112c66bbc91SGabor Kovesdan 
1113c66bbc91SGabor Kovesdan /*
1114c66bbc91SGabor Kovesdan  * Implements general numeric sort (-g).
1115c66bbc91SGabor Kovesdan  */
1116c66bbc91SGabor Kovesdan static int
111789b7a587SGabor Kovesdan gnumcoll(struct key_value *kv1, struct key_value *kv2,
111889b7a587SGabor Kovesdan     size_t offset __unused)
1119c66bbc91SGabor Kovesdan {
1120c66bbc91SGabor Kovesdan 	double d1, d2;
1121c66bbc91SGabor Kovesdan 	int err1, err2;
1122c66bbc91SGabor Kovesdan 	bool empty1, empty2, key1_read, key2_read;
1123c66bbc91SGabor Kovesdan 
1124c66bbc91SGabor Kovesdan 	d1 = d2 = 0;
1125c66bbc91SGabor Kovesdan 	err1 = err2 = 0;
1126c66bbc91SGabor Kovesdan 	key1_read = key2_read = false;
1127c66bbc91SGabor Kovesdan 
1128c66bbc91SGabor Kovesdan 	if (debug_sort) {
1129c66bbc91SGabor Kovesdan 		bwsprintf(stdout, kv1->k, "; k1=<", ">");
1130c66bbc91SGabor Kovesdan 		bwsprintf(stdout, kv2->k, "; k2=<", ">");
1131c66bbc91SGabor Kovesdan 	}
1132c66bbc91SGabor Kovesdan 
1133c66bbc91SGabor Kovesdan 	if (kv1->hint->status == HS_UNINITIALIZED) {
1134c66bbc91SGabor Kovesdan 		errno = 0;
1135c66bbc91SGabor Kovesdan 		d1 = bwstod(kv1->k, &empty1);
1136c66bbc91SGabor Kovesdan 		err1 = errno;
1137c66bbc91SGabor Kovesdan 
1138c66bbc91SGabor Kovesdan 		if (empty1)
1139c66bbc91SGabor Kovesdan 			kv1->hint->v.gh.notnum = true;
1140c66bbc91SGabor Kovesdan 		else if (err1 == 0) {
1141c66bbc91SGabor Kovesdan 			kv1->hint->v.gh.d = d1;
1142c66bbc91SGabor Kovesdan 			kv1->hint->v.gh.nan = is_nan(d1);
1143c66bbc91SGabor Kovesdan 			kv1->hint->status = HS_INITIALIZED;
1144c66bbc91SGabor Kovesdan 		} else
1145c66bbc91SGabor Kovesdan 			kv1->hint->status = HS_ERROR;
1146c66bbc91SGabor Kovesdan 
1147c66bbc91SGabor Kovesdan 		key1_read = true;
1148c66bbc91SGabor Kovesdan 	}
1149c66bbc91SGabor Kovesdan 
1150c66bbc91SGabor Kovesdan 	if (kv2->hint->status == HS_UNINITIALIZED) {
1151c66bbc91SGabor Kovesdan 		errno = 0;
1152c66bbc91SGabor Kovesdan 		d2 = bwstod(kv2->k, &empty2);
1153c66bbc91SGabor Kovesdan 		err2 = errno;
1154c66bbc91SGabor Kovesdan 
1155c66bbc91SGabor Kovesdan 		if (empty2)
1156c66bbc91SGabor Kovesdan 			kv2->hint->v.gh.notnum = true;
1157c66bbc91SGabor Kovesdan 		else if (err2 == 0) {
1158c66bbc91SGabor Kovesdan 			kv2->hint->v.gh.d = d2;
1159c66bbc91SGabor Kovesdan 			kv2->hint->v.gh.nan = is_nan(d2);
1160c66bbc91SGabor Kovesdan 			kv2->hint->status = HS_INITIALIZED;
1161c66bbc91SGabor Kovesdan 		} else
1162c66bbc91SGabor Kovesdan 			kv2->hint->status = HS_ERROR;
1163c66bbc91SGabor Kovesdan 
1164c66bbc91SGabor Kovesdan 		key2_read = true;
1165c66bbc91SGabor Kovesdan 	}
1166c66bbc91SGabor Kovesdan 
1167c66bbc91SGabor Kovesdan 	if (kv1->hint->status == HS_INITIALIZED &&
1168c66bbc91SGabor Kovesdan 	    kv2->hint->status == HS_INITIALIZED) {
1169c66bbc91SGabor Kovesdan 		if (kv1->hint->v.gh.notnum)
1170c66bbc91SGabor Kovesdan 			return ((kv2->hint->v.gh.notnum) ? 0 : -1);
1171c66bbc91SGabor Kovesdan 		else if (kv2->hint->v.gh.notnum)
1172c66bbc91SGabor Kovesdan 			return (+1);
1173c66bbc91SGabor Kovesdan 
1174c66bbc91SGabor Kovesdan 		if (kv1->hint->v.gh.nan)
1175c66bbc91SGabor Kovesdan 			return ((kv2->hint->v.gh.nan) ?
1176c66bbc91SGabor Kovesdan 			    cmp_nans(kv1->hint->v.gh.d, kv2->hint->v.gh.d) :
1177c66bbc91SGabor Kovesdan 			    -1);
1178c66bbc91SGabor Kovesdan 		else if (kv2->hint->v.gh.nan)
1179c66bbc91SGabor Kovesdan 			return (+1);
1180c66bbc91SGabor Kovesdan 
1181c66bbc91SGabor Kovesdan 		d1 = kv1->hint->v.gh.d;
1182c66bbc91SGabor Kovesdan 		d2 = kv2->hint->v.gh.d;
1183c66bbc91SGabor Kovesdan 
1184c66bbc91SGabor Kovesdan 		if (d1 < d2)
1185c66bbc91SGabor Kovesdan 			return (-1);
1186c66bbc91SGabor Kovesdan 		else if (d1 > d2)
1187c66bbc91SGabor Kovesdan 			return (+1);
1188c66bbc91SGabor Kovesdan 		else
1189c66bbc91SGabor Kovesdan 			return (0);
1190c66bbc91SGabor Kovesdan 	}
1191c66bbc91SGabor Kovesdan 
1192c66bbc91SGabor Kovesdan 	if (!key1_read) {
1193c66bbc91SGabor Kovesdan 		errno = 0;
1194c66bbc91SGabor Kovesdan 		d1 = bwstod(kv1->k, &empty1);
1195c66bbc91SGabor Kovesdan 		err1 = errno;
1196c66bbc91SGabor Kovesdan 	}
1197c66bbc91SGabor Kovesdan 
1198c66bbc91SGabor Kovesdan 	if (!key2_read) {
1199c66bbc91SGabor Kovesdan 		errno = 0;
1200c66bbc91SGabor Kovesdan 		d2 = bwstod(kv2->k, &empty2);
1201c66bbc91SGabor Kovesdan 		err2 = errno;
1202c66bbc91SGabor Kovesdan 	}
1203c66bbc91SGabor Kovesdan 
1204c66bbc91SGabor Kovesdan 	/* Non-value case: */
1205c66bbc91SGabor Kovesdan 	if (empty1)
1206c66bbc91SGabor Kovesdan 		return (empty2 ? 0 : -1);
1207c66bbc91SGabor Kovesdan 	else if (empty2)
1208c66bbc91SGabor Kovesdan 		return (+1);
1209c66bbc91SGabor Kovesdan 
1210c66bbc91SGabor Kovesdan 	/* NAN case */
1211c66bbc91SGabor Kovesdan 	if (is_nan(d1))
1212c66bbc91SGabor Kovesdan 		return (is_nan(d2) ? cmp_nans(d1, d2) : -1);
1213c66bbc91SGabor Kovesdan 	else if (is_nan(d2))
1214c66bbc91SGabor Kovesdan 		return (+1);
1215c66bbc91SGabor Kovesdan 
1216c66bbc91SGabor Kovesdan 	/* Infinities */
1217c66bbc91SGabor Kovesdan 	if (err1 == ERANGE || err2 == ERANGE) {
1218c66bbc91SGabor Kovesdan 		/* Minus infinity case */
1219c66bbc91SGabor Kovesdan 		if (huge_minus(d1, err1)) {
1220c66bbc91SGabor Kovesdan 			if (huge_minus(d2, err2)) {
1221c66bbc91SGabor Kovesdan 				if (d1 < d2)
1222c66bbc91SGabor Kovesdan 					return (-1);
1223c66bbc91SGabor Kovesdan 				if (d1 > d2)
1224c66bbc91SGabor Kovesdan 					return (+1);
1225c66bbc91SGabor Kovesdan 				return (0);
1226c66bbc91SGabor Kovesdan 			} else
1227c66bbc91SGabor Kovesdan 				return (-1);
1228c66bbc91SGabor Kovesdan 
1229c66bbc91SGabor Kovesdan 		} else if (huge_minus(d2, err2)) {
1230c66bbc91SGabor Kovesdan 			if (huge_minus(d1, err1)) {
1231c66bbc91SGabor Kovesdan 				if (d1 < d2)
1232c66bbc91SGabor Kovesdan 					return (-1);
1233c66bbc91SGabor Kovesdan 				if (d1 > d2)
1234c66bbc91SGabor Kovesdan 					return (+1);
1235c66bbc91SGabor Kovesdan 				return (0);
1236c66bbc91SGabor Kovesdan 			} else
1237c66bbc91SGabor Kovesdan 				return (+1);
1238c66bbc91SGabor Kovesdan 		}
1239c66bbc91SGabor Kovesdan 
1240c66bbc91SGabor Kovesdan 		/* Plus infinity case */
1241c66bbc91SGabor Kovesdan 		if (huge_plus(d1, err1)) {
1242c66bbc91SGabor Kovesdan 			if (huge_plus(d2, err2)) {
1243c66bbc91SGabor Kovesdan 				if (d1 < d2)
1244c66bbc91SGabor Kovesdan 					return (-1);
1245c66bbc91SGabor Kovesdan 				if (d1 > d2)
1246c66bbc91SGabor Kovesdan 					return (+1);
1247c66bbc91SGabor Kovesdan 				return (0);
1248c66bbc91SGabor Kovesdan 			} else
1249c66bbc91SGabor Kovesdan 				return (+1);
1250c66bbc91SGabor Kovesdan 		} else if (huge_plus(d2, err2)) {
1251c66bbc91SGabor Kovesdan 			if (huge_plus(d1, err1)) {
1252c66bbc91SGabor Kovesdan 				if (d1 < d2)
1253c66bbc91SGabor Kovesdan 					return (-1);
1254c66bbc91SGabor Kovesdan 				if (d1 > d2)
1255c66bbc91SGabor Kovesdan 					return (+1);
1256c66bbc91SGabor Kovesdan 				return (0);
1257c66bbc91SGabor Kovesdan 			} else
1258c66bbc91SGabor Kovesdan 				return (-1);
1259c66bbc91SGabor Kovesdan 		}
1260c66bbc91SGabor Kovesdan 	}
1261c66bbc91SGabor Kovesdan 
1262c66bbc91SGabor Kovesdan 	if (d1 < d2)
1263c66bbc91SGabor Kovesdan 		return (-1);
1264c66bbc91SGabor Kovesdan 	if (d1 > d2)
1265c66bbc91SGabor Kovesdan 		return (+1);
1266c66bbc91SGabor Kovesdan 
1267c66bbc91SGabor Kovesdan 	return (0);
1268c66bbc91SGabor Kovesdan }
1269c66bbc91SGabor Kovesdan 
1270c66bbc91SGabor Kovesdan /*
1271c66bbc91SGabor Kovesdan  * Implements month sort (-M).
1272c66bbc91SGabor Kovesdan  */
1273c66bbc91SGabor Kovesdan static int
127489b7a587SGabor Kovesdan monthcoll(struct key_value *kv1, struct key_value *kv2, size_t offset __unused)
1275c66bbc91SGabor Kovesdan {
1276c66bbc91SGabor Kovesdan 	int val1, val2;
1277c66bbc91SGabor Kovesdan 	bool key1_read, key2_read;
1278c66bbc91SGabor Kovesdan 
1279c66bbc91SGabor Kovesdan 	val1 = val2 = 0;
1280c66bbc91SGabor Kovesdan 	key1_read = key2_read = false;
1281c66bbc91SGabor Kovesdan 
1282c66bbc91SGabor Kovesdan 	if (debug_sort) {
1283c66bbc91SGabor Kovesdan 		bwsprintf(stdout, kv1->k, "; k1=<", ">");
1284c66bbc91SGabor Kovesdan 		bwsprintf(stdout, kv2->k, "; k2=<", ">");
1285c66bbc91SGabor Kovesdan 	}
1286c66bbc91SGabor Kovesdan 
1287c66bbc91SGabor Kovesdan 	if (kv1->hint->status == HS_UNINITIALIZED) {
1288c66bbc91SGabor Kovesdan 		kv1->hint->v.Mh.m = bws_month_score(kv1->k);
1289c66bbc91SGabor Kovesdan 		key1_read = true;
1290c66bbc91SGabor Kovesdan 		kv1->hint->status = HS_INITIALIZED;
1291c66bbc91SGabor Kovesdan 	}
1292c66bbc91SGabor Kovesdan 
1293c66bbc91SGabor Kovesdan 	if (kv2->hint->status == HS_UNINITIALIZED) {
1294c66bbc91SGabor Kovesdan 		kv2->hint->v.Mh.m = bws_month_score(kv2->k);
1295c66bbc91SGabor Kovesdan 		key2_read = true;
1296c66bbc91SGabor Kovesdan 		kv2->hint->status = HS_INITIALIZED;
1297c66bbc91SGabor Kovesdan 	}
1298c66bbc91SGabor Kovesdan 
1299c66bbc91SGabor Kovesdan 	if (kv1->hint->status == HS_INITIALIZED) {
1300c66bbc91SGabor Kovesdan 		val1 = kv1->hint->v.Mh.m;
1301c66bbc91SGabor Kovesdan 		key1_read = true;
1302c66bbc91SGabor Kovesdan 	}
1303c66bbc91SGabor Kovesdan 
1304c66bbc91SGabor Kovesdan 	if (kv2->hint->status == HS_INITIALIZED) {
1305c66bbc91SGabor Kovesdan 		val2 = kv2->hint->v.Mh.m;
1306c66bbc91SGabor Kovesdan 		key2_read = true;
1307c66bbc91SGabor Kovesdan 	}
1308c66bbc91SGabor Kovesdan 
1309c66bbc91SGabor Kovesdan 	if (!key1_read)
1310c66bbc91SGabor Kovesdan 		val1 = bws_month_score(kv1->k);
1311c66bbc91SGabor Kovesdan 	if (!key2_read)
1312c66bbc91SGabor Kovesdan 		val2 = bws_month_score(kv2->k);
1313c66bbc91SGabor Kovesdan 
1314c66bbc91SGabor Kovesdan 	if (val1 == val2) {
1315c66bbc91SGabor Kovesdan 		return (0);
1316c66bbc91SGabor Kovesdan 	}
1317c66bbc91SGabor Kovesdan 	if (val1 < val2)
1318c66bbc91SGabor Kovesdan 		return (-1);
1319c66bbc91SGabor Kovesdan 	return (+1);
1320c66bbc91SGabor Kovesdan }
1321