2 Copyright (C) 2020 Free Software Foundation, Inc.
4 Bush is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 Bush is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with Bush. If not, see <http://www.gnu.org/licenses/>.
21 #include "bushtypes.h"
26 #include "bushgetopt.h"
28 typedef struct sort_element
{
29 ARRAY_ELEMENT
*v
; // used when sorting array in-place
30 char *key
; // used when sorting assoc array
31 char *value
; // points to value of array element or assoc entry
32 double num
; // used for numeric sort
35 static int reverse_flag
;
36 static int numeric_flag
;
39 compare(const void *p1
, const void *p2
) {
40 const sort_element e1
= *(sort_element
*) p1
;
41 const sort_element e2
= *(sort_element
*) p2
;
45 return (e2
.num
> e1
.num
) ? 1 : (e2
.num
< e1
.num
) ? -1 : 0;
47 return (e1
.num
> e2
.num
) ? 1 : (e1
.num
< e2
.num
) ? -1 : 0;
51 return strcoll(e2
.value
, e1
.value
);
53 return strcoll(e1
.value
, e2
.value
);
58 sort_index(SHELL_VAR
*dest
, SHELL_VAR
*source
) {
60 BUCKET_CONTENTS
*bucket
;
62 ARRAY
*array
, *dest_array
;
65 char ibuf
[INT_STRLEN_BOUND (intmax_t) + 1]; // used by fmtulong
68 dest_array
= array_cell(dest
);
70 if (assoc_p(source
)) {
71 hash
= assoc_cell(source
);
73 sa
= xmalloc(n
* sizeof(sort_element
));
75 for ( j
= 0; j
< hash
->nbuckets
; ++j
) {
76 bucket
= hash
->bucket_array
[j
];
79 sa
[i
].key
= bucket
->key
;
81 sa
[i
].num
= strtod(bucket
->data
, NULL
);
83 sa
[i
].value
= bucket
->data
;
85 bucket
= bucket
->next
;
90 array
= array_cell(source
);
91 n
= array_num_elements(array
);
92 sa
= xmalloc(n
* sizeof(sort_element
));
95 for (ae
= element_forw(array
->head
); ae
!= array
->head
; ae
= element_forw(ae
)) {
98 sa
[i
].num
= strtod(element_value(ae
), NULL
);
100 sa
[i
].value
= element_value(ae
);
107 builtin_error("%s: corrupt array", source
->name
);
108 return EXECUTION_FAILURE
;
111 qsort(sa
, n
, sizeof(sort_element
), compare
);
113 array_flush(dest_array
);
115 for ( i
= 0; i
< n
; ++i
) {
116 if ( assoc_p(source
) )
119 key
= fmtulong((long unsigned)sa
[i
].v
->ind
, 10, ibuf
, sizeof(ibuf
), 0);
121 array_insert(dest_array
, i
, key
);
124 return EXECUTION_SUCCESS
;
128 sort_inplace(SHELL_VAR
*var
) {
132 sort_element
*sa
= 0;
135 n
= array_num_elements(a
);
138 return EXECUTION_SUCCESS
;
140 sa
= xmalloc(n
* sizeof(sort_element
));
143 for (ae
= element_forw(a
->head
); ae
!= a
->head
; ae
= element_forw(ae
)) {
146 sa
[i
].num
= strtod(element_value(ae
), NULL
);
148 sa
[i
].value
= element_value(ae
);
154 builtin_error("%s: corrupt array", var
->name
);
155 return EXECUTION_FAILURE
;
158 qsort(sa
, n
, sizeof(sort_element
), compare
);
160 // for in-place sort, simply "rewire" the array elements
161 sa
[0].v
->prev
= sa
[n
-1].v
->next
= a
->head
;
162 a
->head
->next
= sa
[0].v
;
163 a
->head
->prev
= sa
[n
-1].v
;
164 a
->max_index
= n
- 1;
165 for (i
= 0; i
< n
; i
++) {
168 sa
[i
].v
->prev
= sa
[i
-1].v
;
170 sa
[i
].v
->next
= sa
[i
+1].v
;
173 return EXECUTION_SUCCESS
;
177 asort_builtin(WORD_LIST
*list
) {
178 SHELL_VAR
*var
, *var2
;
186 reset_internal_getopt();
187 while ((opt
= internal_getopt(list
, "inr")) != -1) {
189 case 'i': index_flag
= 1; break;
190 case 'n': numeric_flag
= 1; break;
191 case 'r': reverse_flag
= 1; break;
205 if (legal_identifier (list
->word
->word
) == 0) {
206 sh_invalidid (list
->word
->word
);
207 return EXECUTION_FAILURE
;
211 if ( list
->next
== 0 || list
->next
->next
) {
215 if (legal_identifier (list
->next
->word
->word
) == 0) {
216 sh_invalidid (list
->next
->word
->word
);
217 return EXECUTION_FAILURE
;
219 var
= find_or_make_array_variable(list
->word
->word
, 1);
221 return EXECUTION_FAILURE
;
222 var2
= find_variable(list
->next
->word
->word
);
223 if ( !var2
|| ( !array_p(var2
) && !assoc_p(var2
) ) ) {
224 builtin_error("%s: Not an array", list
->next
->word
->word
);
225 return EXECUTION_FAILURE
;
227 return sort_index(var
, var2
);
231 word
= list
->word
->word
;
232 var
= find_variable(word
);
235 if (var
== 0 || array_p(var
) == 0) {
236 builtin_error("%s: Not an array", word
);
239 if (readonly_p(var
) || noassign_p(var
)) {
245 if ( (ret
= sort_inplace(var
)) != EXECUTION_SUCCESS
)
248 return EXECUTION_SUCCESS
;
252 char *asort_doc
[] = {
253 "Sort arrays in-place.",
256 " -n compare according to string numerical value",
257 " -r reverse the result of comparisons",
258 " -i sort using indices/keys",
260 "If -i is supplied, SOURCE is not sorted in-place, but the indices (or keys",
261 "if associative) of SOURCE, after sorting it by its values, are placed as",
262 "values in the indexed array DEST",
264 "Associative arrays may not be sorted in-place.",
267 "Return value is zero unless an error happened (like invalid variable name",
268 "or readonly array).",
272 struct builtin asort_struct
= {
277 "asort [-nr] array ... or asort [-nr] -i dest source",