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