sort: reformat entire file wrt style.
[oweals/busybox.git] / coreutils / sort.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * SuS3 compliant sort implementation for busybox
4  *
5  * Copyright (C) 2004 by Rob Landley <rob@landley.net>
6  *
7  * MAINTAINER: Rob Landley <rob@landley.net>
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10  *
11  * See SuS3 sort standard at:
12  * http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
13  */
14
15 #include "busybox.h"
16
17 static int global_flags;
18
19 /*
20         sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...]
21         sort -c [-bdfinru][-t char][-k keydef][file]
22 */
23
24 /* These are sort types */
25 #define FLAG_n                  1               /* Numeric sort */
26 #define FLAG_g                  2               /* Sort using strtod() */
27 #define FLAG_M                  4               /* Sort date */
28 /* ucsz apply to root level only, not keys.  b at root level implies bb */
29 #define FLAG_u                  8               /* Unique */
30 #define FLAG_c                  16              /* Check: no output, exit(!ordered) */
31 #define FLAG_s                  32              /* Stable sort, no ascii fallback at end */
32 #define FLAG_z                  64              /* Input is null terminated, not \n */
33 /* These can be applied to search keys, the previous four can't */
34 #define FLAG_b                  128             /* Ignore leading blanks */
35 #define FLAG_r                  256             /* Reverse */
36 #define FLAG_d                  512             /* Ignore !(isalnum()|isspace()) */
37 #define FLAG_f                  1024    /* Force uppercase */
38 #define FLAG_i                  2048    /* Ignore !isprint() */
39 #define FLAG_bb                 32768   /* Ignore trailing blanks  */
40
41
42 #if ENABLE_FEATURE_SORT_BIG
43 static char key_separator;
44
45 static struct sort_key {
46         struct sort_key *next_key;      /* linked list */
47         unsigned short range[4];        /* start word, start char, end word, end char */
48         int flags;
49 } *key_list;
50
51 static char *get_key(char *str, struct sort_key *key, int flags)
52 {
53         int start = 0, end = 0, len, i, j;
54
55         /* Special case whole string, so we don't have to make a copy */
56         if (key->range[0] == 1 && !key->range[1] && !key->range[2] && !key->range[3]
57          && !(flags & (FLAG_b | FLAG_d | FLAG_f | FLAG_i | FLAG_bb))
58         ) {
59                 return str;
60         }
61
62         /* Find start of key on first pass, end on second pass*/
63         len = strlen(str);
64         for (j = 0; j < 2; j++) {
65                 if (!key->range[2*j])
66                         end = len;
67                 /* Loop through fields */
68                 else {
69                         end = 0;
70                         for (i = 1; i < key->range[2*j] + j; i++) {
71                                 /* Skip leading blanks or first separator */
72                                 if (str[end]) {
73                                         if (!key_separator && isspace(str[end]))
74 /* TODO: remove "&& isspace(str[end])" */
75                                                 while (isspace(str[end])) end++;
76                                 }
77                                 /* Skip body of key */
78                                 for (; str[end]; end++) {
79                                         if (key_separator) {
80                                                 if (str[end] == key_separator)
81                                                         break;
82                                         } else {
83                                                 if (isspace(str[end]))
84                                                         break;
85                                         }
86                                 }
87                         }
88                 }
89                 if (!j) start = end;
90         }
91         /* Key with explicit separator starts after separator */
92         if (key_separator && str[start] == key_separator)
93                 start++;
94         /* Strip leading whitespace if necessary */
95 //XXX: skip_whitespace()
96         if (flags & FLAG_b)
97                 while (isspace(str[start])) start++;
98         /* Strip trailing whitespace if necessary */
99         if (flags & FLAG_bb)
100                 while (end > start && isspace(str[end-1])) end--;
101         /* Handle offsets on start and end */
102         if (key->range[3]) {
103                 end += key->range[3] - 1;
104                 if (end > len) end = len;
105         }
106         if (key->range[1]) {
107                 start += key->range[1] - 1;
108                 if (start > len) start = len;
109         }
110         /* Make the copy */
111         if (end < start) end = start;
112         str = xstrndup(str+start, end-start);
113         /* Handle -d */
114         if (flags & FLAG_d) {
115                 for (start = end = 0; str[end]; end++)
116                         if (isspace(str[end]) || isalnum(str[end]))
117                                 str[start++] = str[end];
118                 str[start] = '\0';
119         }
120         /* Handle -i */
121         if (flags & FLAG_i) {
122                 for (start = end = 0; str[end]; end++)
123                         if (isprint(str[end]))
124                                 str[start++] = str[end];
125                 str[start] = '\0';
126         }
127         /* Handle -f */
128         if (flags & FLAG_f)
129                 for (i = 0; str[i]; i++)
130                         str[i] = toupper(str[i]);
131
132         return str;
133 }
134
135 static struct sort_key *add_key(void)
136 {
137         struct sort_key **pkey = &key_list;
138         while (*pkey)
139                 pkey = &((*pkey)->next_key);
140         return *pkey = xzalloc(sizeof(struct sort_key));
141 }
142
143 #define GET_LINE(fp) \
144         ((global_flags & FLAG_z) \
145         ? bb_get_chunk_from_file(fp, NULL) \
146         : xmalloc_getline(fp))
147 #else
148 #define GET_LINE(fp) xmalloc_getline(fp)
149 #endif
150
151 /* Iterate through keys list and perform comparisons */
152 static int compare_keys(const void *xarg, const void *yarg)
153 {
154         int flags = global_flags, retval = 0;
155         char *x, *y;
156
157 #if ENABLE_FEATURE_SORT_BIG
158         struct sort_key *key;
159
160         for (key = key_list; !retval && key; key = key->next_key) {
161                 flags = (key->flags) ? key->flags : global_flags;
162                 /* Chop out and modify key chunks, handling -dfib */
163                 x = get_key(*(char **)xarg, key, flags);
164                 y = get_key(*(char **)yarg, key, flags);
165 #else
166         /* This curly bracket serves no purpose but to match the nesting
167            level of the for() loop we're not using */
168         {
169                 x = *(char **)xarg;
170                 y = *(char **)yarg;
171 #endif
172                 /* Perform actual comparison */
173                 switch (flags & 7) {
174                 default:
175                         bb_error_msg_and_die("unknown sort type");
176                         break;
177                 /* Ascii sort */
178                 case 0:
179                         retval = strcmp(x, y);
180                         break;
181 #if ENABLE_FEATURE_SORT_BIG
182                 case FLAG_g: {
183                         char *xx, *yy;
184                         double dx = strtod(x, &xx), dy = strtod(y, &yy);
185                         /* not numbers < NaN < -infinity < numbers < +infinity) */
186                         if (x == xx)
187                                 retval = (y == yy ? 0 : -1);
188                         else if (y == yy)
189                                 retval = 1;
190                         /* Check for isnan */
191                         else if (dx != dx)
192                                 retval = (dy != dy) ? 0 : -1;
193                         else if (dy != dy)
194                                 retval = 1;
195                         /* Check for infinity.  Could underflow, but it avoids libm. */
196                         else if (1.0 / dx == 0.0) {
197                                 if (dx < 0)
198                                         retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1;
199                                 else
200                                         retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1;
201                         } else if (1.0 / dy == 0.0)
202                                 retval = (dy < 0) ? 1 : -1;
203                         else
204                                 retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0);
205                         break;
206                 }
207                 case FLAG_M: {
208                         struct tm thyme;
209                         int dx;
210                         char *xx, *yy;
211
212                         xx = strptime(x, "%b", &thyme);
213                         dx = thyme.tm_mon;
214                         yy = strptime(y, "%b", &thyme);
215                         if (!xx)
216                                 retval = (!yy) ? 0 : -1;
217                         else if (!yy)
218                                 retval = 1;
219                         else
220                                 retval = (dx==thyme.tm_mon) ? 0 : dx-thyme.tm_mon;
221                         break;
222                 }
223                 /* Full floating point version of -n */
224                 case FLAG_n: {
225                         double dx = atof(x), dy  =atof(y);
226                         retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0);
227                         break;
228                 }
229                 }
230                 /* Free key copies. */
231                 if (x != *(char **)xarg) free(x);
232                 if (y != *(char **)yarg) free(y);
233                 if (retval) break;
234 #else
235                 /* Integer version of -n for tiny systems */
236                 case FLAG_n:
237                         retval = atoi(x) - atoi(y);
238                         break;
239                 }
240 #endif
241         }
242         /* Perform fallback sort if necessary */
243         if (!retval && !(global_flags & FLAG_s))
244                 retval = strcmp(*(char **)xarg, *(char **)yarg);
245
246         if (flags & FLAG_r) return -retval;
247         return retval;
248 }
249
250 int sort_main(int argc, char **argv)
251 {
252         FILE *fp, *outfile = NULL;
253         int linecount = 0, i, flag;
254         char *line, **lines = NULL, *optlist = "ngMucszbrdfimS:T:o:k:t:";
255         int c;
256
257         xfunc_error_retval = 2;
258         /* Parse command line options */
259         while ((c = getopt(argc, argv, optlist)) > 0) {
260                 line = strchr(optlist, c);
261                 if (!line) bb_show_usage();
262                 switch (*line) {
263 #if ENABLE_FEATURE_SORT_BIG
264                 case 'o':
265                         if (outfile) bb_error_msg_and_die("too many -o");
266                         outfile = xfopen(optarg, "w");
267                         break;
268                 case 't':
269                         if (key_separator || optarg[1])
270                                 bb_error_msg_and_die("too many -t");
271                         key_separator = *optarg;
272                         break;
273                 /* parse sort key */
274                 case 'k': {
275                         struct sort_key *key = add_key();
276                         char *temp, *temp2;
277
278                         temp = optarg;
279                         for (i = 0; *temp;) {
280                                 /* Start of range */
281                                 key->range[2*i] = (unsigned short)strtol(temp, &temp, 10);
282                                 if (*temp == '.')
283                                         key->range[(2*i)+1] = (unsigned short)strtol(temp+1, &temp, 10);
284                                 for (; *temp; temp++) {
285                                         if (*temp == ',' && !i++) {
286                                                 temp++;
287                                                 break;
288                                         } /* no else needed: fall through to syntax error
289                                                  because comma isn't in optlist */
290                                         temp2 = strchr(optlist, *temp);
291                                         flag = (1 << (temp2 - optlist));
292                                         if (!temp2 || (flag > FLAG_M && flag < FLAG_b))
293                                                 bb_error_msg_and_die("unknown key option");
294                                         /* b after ',' means strip _trailing_ space */
295                                         if (i && flag == FLAG_b) flag = FLAG_bb;
296                                         key->flags |= flag;
297                                 }
298                         }
299                         break;
300                 }
301 #endif
302                 default:
303                         global_flags |= (1 << (line - optlist));
304                         /* global b strips leading and trailing spaces */
305                         if (global_flags & FLAG_b) global_flags |= FLAG_bb;
306                         break;
307                 }
308         }
309         /* Open input files and read data */
310         for (i = argv[optind] ? optind : optind-1; argv[i]; i++) {
311                 fp = stdin;
312                 if (i >= optind && (argv[i][0] != '-' || argv[i][1]))
313                         fp = xfopen(argv[i], "r");
314                 for (;;) {
315                         line = GET_LINE(fp);
316                         if (!line) break;
317                         if (!(linecount & 63))
318                                 lines = xrealloc(lines, sizeof(char *) * (linecount + 64));
319                         lines[linecount++] = line;
320                 }
321                 fclose(fp);
322         }
323 #if ENABLE_FEATURE_SORT_BIG
324         /* if no key, perform alphabetic sort */
325         if (!key_list)
326                 add_key()->range[0] = 1;
327         /* handle -c */
328         if (global_flags & FLAG_c) {
329                 int j = (global_flags & FLAG_u) ? -1 : 0;
330                 for (i = 1; i < linecount; i++)
331                         if (compare_keys(&lines[i-1], &lines[i]) > j) {
332                                 fprintf(stderr, "Check line %d\n", i);
333                                 return 1;
334                         }
335                 return 0;
336         }
337 #endif
338         /* Perform the actual sort */
339         qsort(lines, linecount, sizeof(char *), compare_keys);
340         /* handle -u */
341         if (global_flags & FLAG_u) {
342                 for (flag = 0, i = 1; i < linecount; i++) {
343                         if (!compare_keys(&lines[flag], &lines[i]))
344                                 free(lines[i]);
345                         else
346                                 lines[++flag] = lines[i];
347                 }
348                 if (linecount) linecount = flag+1;
349         }
350         /* Print it */
351         if (!outfile) outfile = stdout;
352         for (i = 0; i < linecount; i++)
353                 fprintf(outfile, "%s\n", lines[i]);
354
355         fflush_stdout_and_exit(EXIT_SUCCESS);
356 }