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