X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=coreutils%2Fsort.c;h=fb58f62790124a596aff0ff18cfad91c2321d868;hb=e135a5d746c4f163f191e12b527636b0345e1456;hp=a28122d510d420bc8bc525dd6092f6e5873b9860;hpb=1ca41775bbdc07cf67be79aebc566754c9c02855;p=oweals%2Fbusybox.git diff --git a/coreutils/sort.c b/coreutils/sort.c index a28122d51..fb58f6279 100644 --- a/coreutils/sort.c +++ b/coreutils/sort.c @@ -1,307 +1,332 @@ /* vi: set sw=4 ts=4: */ /* - * Mini sort implementation for busybox + * SuS3 compliant sort implementation for busybox * + * Copyright (C) 2004 by Rob Landley * - * Copyright (C) 1999,2000 by Lineo, inc. - * Written by John Beppu - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * MAINTAINER: Rob Landley + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. * + * See SuS3 sort standard at: + * http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html */ -#include "internal.h" -#include -#include -#include +#include +#include #include -#include - -static const char sort_usage[] = "sort [-n]" -#ifdef BB_FEATURE_SORT_REVERSE -" [-r]" -#endif -" [FILE]...\n" -#ifndef BB_FEATURE_TRIVIAL_HELP -"\nSorts lines of text in the specified files\n" -#endif -; - -#ifdef BB_FEATURE_SORT_REVERSE -#define APPLY_REVERSE(x) (reverse ? -(x) : (x)) -static int reverse = 0; -#else -#define APPLY_REVERSE(x) (x) -#endif - -/* typedefs _______________________________________________________________ */ - -/* line node */ -typedef struct Line { - char *data; /* line data */ - struct Line *next; /* pointer to next line node */ -} Line; - -/* singly-linked list of lines */ -typedef struct { - int len; /* number of Lines */ - Line **sorted; /* array fed to qsort */ - Line *head; /* head of List */ - Line *current; /* current Line */ -} List; +#include +#include +#include +#include +#include "busybox.h" -/* comparison function */ -typedef int (Compare) (const void *, const void *); +static int global_flags; - -/* methods ________________________________________________________________ */ - -static const int max = 1024; - -/* mallocate Line */ -static Line *line_alloc() +/* + sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...] + sort -c [-bdfinru][-t char][-k keydef][file] +*/ + +/* These are sort types */ +#define FLAG_n 1 /* Numeric sort */ +#define FLAG_g 2 /* Sort using strtod() */ +#define FLAG_M 4 /* Sort date */ +/* ucsz apply to root level only, not keys. b at root level implies bb */ +#define FLAG_u 8 /* Unique */ +#define FLAG_c 16 /* Check: no output, exit(!ordered) */ +#define FLAG_s 32 /* Stable sort, no ascii fallback at end */ +#define FLAG_z 64 /* Input is null terminated, not \n */ +/* These can be applied to search keys, the previous four can't */ +#define FLAG_b 128 /* Ignore leading blanks */ +#define FLAG_r 256 /* Reverse */ +#define FLAG_d 512 /* Ignore !(isalnum()|isspace()) */ +#define FLAG_f 1024 /* Force uppercase */ +#define FLAG_i 2048 /* Ignore !isprint() */ +#define FLAG_bb 32768 /* Ignore trailing blanks */ + + +#ifdef CONFIG_FEATURE_SORT_BIG +static char key_separator; + +static struct sort_key { - Line *self; - self = malloc(1 * sizeof(Line)); - return self; -} + struct sort_key *next_key; /* linked list */ + unsigned short range[4]; /* start word, start char, end word, end char */ + int flags; +} *key_list; -/* Construct Line from FILE* */ -static Line *line_newFromFile(FILE * src) +static char *get_key(char *str, struct sort_key *key, int flags) { - Line *self; - char *cstring = NULL; - - if ((cstring = get_line_from_file(src))) { - self = line_alloc(); - if (self == NULL) { - return NULL; + int start=0,end,len,i,j; + + /* Special case whole string, so we don't have to make a copy */ + if(key->range[0]==1 && !key->range[1] && !key->range[2] && !key->range[3] + && !(flags&(FLAG_b&FLAG_d&FLAG_f&FLAG_i&FLAG_bb))) return str; + /* Find start of key on first pass, end on second pass*/ + len=strlen(str); + + for(j=0;j<2;j++) { + if(!key->range[2*j]) end=len; + /* Loop through fields */ + else { + end=0; + for(i=1;irange[2*j]+j;i++) { + /* Skip leading blanks or first separator */ + if(str[end]) { + if(key_separator) { + if(str[end]==key_separator) end++; + } else if(isspace(str[end])) + while(isspace(str[end])) end++; + } + /* Skip body of key */ + for(;str[end];end++) { + if(key_separator) { + if(str[end]==key_separator) break; + } else if(isspace(str[end])) break; + } + } } - self->data = cstring; - self->next = NULL; - return self; - } - return NULL; -} - -/* Line destructor */ -static Line *line_release(Line * self) -{ - if (self->data) { - free(self->data); - free(self); + if(!j) start=end; } - return self; -} - - -/* Comparison */ - -/* ascii order */ -static int compare_ascii(const void *a, const void *b) -{ - Line **doh; - Line *x, *y; - - doh = (Line **) a; - x = *doh; - doh = (Line **) b; - y = *doh; - - // fprintf(stdout, "> %p: %s< %p: %s", x, x->data, y, y->data); - return APPLY_REVERSE(strcmp(x->data, y->data)); -} - -/* numeric order */ -static int compare_numeric(const void *a, const void *b) -{ - Line **doh; - Line *x, *y; - int xint, yint; - - doh = (Line **) a; - x = *doh; - doh = (Line **) b; - y = *doh; - - xint = strtoul(x->data, NULL, 10); - yint = strtoul(y->data, NULL, 10); - - return APPLY_REVERSE(xint - yint); -} - - -/* List */ - -/* */ -static List *list_init(List * self) -{ - self->len = 0; - self->sorted = NULL; - self->head = NULL; - self->current = NULL; - return self; -} - -/* for simplicity, the List gains ownership of the line */ -static List *list_insert(List * self, Line * line) -{ - if (line == NULL) { - return NULL; + /* Key with explicit separator starts after separator */ + if(key_separator && str[start]==key_separator) start++; + /* Strip leading whitespace if necessary */ + if(flags&FLAG_b) while(isspace(str[start])) start++; + /* Strip trailing whitespace if necessary */ + if(flags&FLAG_bb) while(end>start && isspace(str[end-1])) end--; + /* Handle offsets on start and end */ + if(key->range[3]) { + end+=key->range[3]-1; + if(end>len) end=len; } - - /* first insertion */ - if (self->head == NULL) { - self->head = line; - self->current = line; - - /* all subsequent insertions */ - } else { - self->current->next = line; - self->current = line; + if(key->range[1]) { + start+=key->range[1]-1; + if(start>len) start=len; } - self->len++; - return self; -} - -/* order the list according to compare() */ -static List *list_sort(List * self, Compare * compare) -{ - int i; - Line *line; - - /* mallocate array of Line*s */ - self->sorted = (Line **) malloc(self->len * sizeof(Line *)); - if (self->sorted == NULL) { - return NULL; + /* Make the copy */ + if(endhead; - while (line) { - self->sorted[i++] = line; - line = line->next; + /* Handle -i */ + if(flags&FLAG_i) { + for(start=end=0;str[end];end++) + if(isprint(str[end])) str[start++]=str[end]; + str[start]=0; } + /* Handle -f */ + if(flags*FLAG_f) for(i=0;str[i];i++) str[i]=toupper(str[i]); - /* apply qsort */ - qsort(self->sorted, self->len, sizeof(Line *), compare); - return self; + return str; } -/* precondition: list must be sorted */ -static List *list_writeToFile(List * self, FILE * dst) +static struct sort_key *add_key(void) { - int i; - Line **line = self->sorted; - - if (self->sorted == NULL) { - return NULL; - } - for (i = 0; i < self->len; i++) { - fprintf(dst, "%s", line[i]->data); - } - return self; + struct sort_key **pkey=&key_list; + while(*pkey) pkey=&((*pkey)->next_key); + return *pkey=xcalloc(1,sizeof(struct sort_key)); } -/* deallocate */ -static void list_release(List * self) +#define GET_LINE(fp) (global_flags&FLAG_z) ? bb_get_chunk_from_file(fp,NULL) \ + : bb_get_chomped_line_from_file(fp) +#else +#define GET_LINE(fp) bb_get_chomped_line_from_file(fp) +#endif + +/* Iterate through keys list and perform comparisons */ +static int compare_keys(const void *xarg, const void *yarg) { - Line *i; - Line *die; + int flags=global_flags,retval=0; + char *x,*y; + +#ifdef CONFIG_FEATURE_SORT_BIG + struct sort_key *key; - i = self->head; - while (i) { - die = i; - i = die->next; - line_release(die); + for(key=key_list;!retval && key;key=key->next_key) { + flags=(key->flags) ? key->flags : global_flags; + /* Chop out and modify key chunks, handling -dfib */ + x=get_key(*(char **)xarg,key,flags); + y=get_key(*(char **)yarg,key,flags); +#else + /* This curly bracket serves no purpose but to match the nesting + level of the for() loop we're not using */ + { + x=*(char **)xarg; + y=*(char **)yarg; +#endif + /* Perform actual comparison */ + switch(flags&7) { + default: + bb_error_msg_and_die("Unknown sort type."); + break; + /* Ascii sort */ + case 0: + retval=strcmp(x,y); + break; +#ifdef CONFIG_FEATURE_SORT_BIG + case FLAG_g: + { + char *xx,*yy; + double dx=strtod(x,&xx), dy=strtod(y,&yy); + /* not numbers < NaN < -infinity < numbers < +infinity) */ + if(x==xx) retval=(y==yy ? 0 : -1); + else if(y==yy) retval=1; + /* Check for isnan */ + else if(dx != dx) retval = (dy != dy) ? 0 : -1; + else if(dy != dy) retval = 1; + /* Check for infinity. Could underflow, but it avoids libm. */ + else if(1.0/dx == 0.0) { + if(dx<0) retval=((1.0/dy == 0.0 && dy<0) ? 0 : -1); + else retval=((1.0/dy == 0.0 && dy>0) ? 0 : 1); + } else if(1.0/dy == 0.0) retval=dy<0 ? 1 : -1; + else retval=dx>dy ? 1 : (dxdy ? 1 : (dx0) { + line=strchr(optlist,c); + if(!line) bb_show_usage(); + switch(*line) { +#ifdef CONFIG_FEATURE_SORT_BIG + case 'o': + if(outfile) bb_error_msg_and_die("Too many -o."); + outfile=bb_xfopen(optarg,"w"); break; - case 'n': - /* numeric comparison */ - compare = compare_numeric; + case 't': + if(key_separator || optarg[1]) + bb_error_msg_and_die("Too many -t."); + key_separator=*optarg; break; -#ifdef BB_FEATURE_SORT_REVERSE - case 'r': - /* reverse */ - reverse = 1; + /* parse sort key */ + case 'k': + { + struct sort_key *key=add_key(); + char *temp, *temp2; + + temp=optarg; + for(i=0;*temp;) { + /* Start of range */ + key->range[2*i]=(unsigned short)strtol(temp,&temp,10); + if(*temp=='.') + key->range[(2*i)+1]=(unsigned short)strtol(temp+1,&temp,10); + for(;*temp;temp++) { + if(*temp==',' && !i++) { + temp++; + break; + } /* no else needed: fall through to syntax error + because comma isn't in optlist */ + temp2=strchr(optlist,*temp); + flag=(1<<(temp2-optlist)); + if(!temp2 || (flag>FLAG_M && flagflags|=flag; + } + } break; + } #endif default: - fprintf(stderr, "sort: invalid option -- %c\n", opt); - usage(sort_usage); - } - } else { - break; + global_flags|=(1<<(line-optlist)); + /* global b strips leading and trailing spaces */ + if(global_flags&FLAG_b) global_flags|=FLAG_bb; + break; } } - - /* this could be factored better */ - - /* work w/ stdin */ - if (i >= argc) { - while ((l = line_newFromFile(stdin))) { - list_insert(&list, l); + /* Open input files and read data */ + for(i=argv[optind] ? optind : optind-1;argv[i];i++) { + if(irange[0]=1; + /* handle -c */ + if(global_flags&FLAG_c) { + int j=(global_flags&FLAG_u) ? -1 : 0; + for(i=1;ij) { + fprintf(stderr,"Check line %d\n",i); + return 1; } - fclose(src); + return 0; + } +#endif + /* Perform the actual sort */ + qsort(lines,linecount,sizeof(char *),compare_keys); + /* handle -u */ + if(global_flags&FLAG_u) { + for(flag=0,i=1;i