10f66f451Sopenharmony_ci/* sort.c - put input lines into order 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2004, 2008 Rob Landley <rob@landley.net> 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * See http://opengroup.org/onlinepubs/007904975/utilities/sort.html 60f66f451Sopenharmony_ci * 70f66f451Sopenharmony_ci * Deviations from POSIX: Lots. 80f66f451Sopenharmony_ci * We invented -x 90f66f451Sopenharmony_ci 100f66f451Sopenharmony_ciUSE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) 110f66f451Sopenharmony_ci 120f66f451Sopenharmony_ciconfig SORT 130f66f451Sopenharmony_ci bool "sort" 140f66f451Sopenharmony_ci default y 150f66f451Sopenharmony_ci help 160f66f451Sopenharmony_ci usage: sort [-runbcdfiMsz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE] 170f66f451Sopenharmony_ci 180f66f451Sopenharmony_ci Sort all lines of text from input files (or stdin) to stdout. 190f66f451Sopenharmony_ci 200f66f451Sopenharmony_ci -r Reverse 210f66f451Sopenharmony_ci -u Unique lines only 220f66f451Sopenharmony_ci -n Numeric order (instead of alphabetical) 230f66f451Sopenharmony_ci -b Ignore leading blanks (or trailing blanks in second part of key) 240f66f451Sopenharmony_ci -c Check whether input is sorted 250f66f451Sopenharmony_ci -d Dictionary order (use alphanumeric and whitespace chars only) 260f66f451Sopenharmony_ci -f Force uppercase (case insensitive sort) 270f66f451Sopenharmony_ci -i Ignore nonprinting characters 280f66f451Sopenharmony_ci -M Month sort (jan, feb, etc) 290f66f451Sopenharmony_ci -x Hexadecimal numerical sort 300f66f451Sopenharmony_ci -s Skip fallback sort (only sort with keys) 310f66f451Sopenharmony_ci -z Zero (null) terminated lines 320f66f451Sopenharmony_ci -k Sort by "key" (see below) 330f66f451Sopenharmony_ci -t Use a key separator other than whitespace 340f66f451Sopenharmony_ci -o Output to FILE instead of stdout 350f66f451Sopenharmony_ci -V Version numbers (name-1.234-rc6.5b.tgz) 360f66f451Sopenharmony_ci 370f66f451Sopenharmony_ci Sorting by key looks at a subset of the words on each line. -k2 uses the 380f66f451Sopenharmony_ci second word to the end of the line, -k2,2 looks at only the second word, 390f66f451Sopenharmony_ci -k2,4 looks from the start of the second to the end of the fourth word. 400f66f451Sopenharmony_ci -k2.4,5 starts from the fourth character of the second word, to the end 410f66f451Sopenharmony_ci of the fifth word. Specifying multiple keys uses the later keys as tie 420f66f451Sopenharmony_ci breakers, in order. A type specifier appended to a sort key (such as -2,2n) 430f66f451Sopenharmony_ci applies only to sorting that key. 440f66f451Sopenharmony_ci 450f66f451Sopenharmony_ciconfig SORT_FLOAT 460f66f451Sopenharmony_ci bool 470f66f451Sopenharmony_ci default y 480f66f451Sopenharmony_ci depends on TOYBOX_FLOAT 490f66f451Sopenharmony_ci help 500f66f451Sopenharmony_ci usage: sort [-g] 510f66f451Sopenharmony_ci 520f66f451Sopenharmony_ci -g General numeric sort (double precision with nan and inf) 530f66f451Sopenharmony_ci*/ 540f66f451Sopenharmony_ci 550f66f451Sopenharmony_ci#define FOR_sort 560f66f451Sopenharmony_ci#include "toys.h" 570f66f451Sopenharmony_ci 580f66f451Sopenharmony_ciGLOBALS( 590f66f451Sopenharmony_ci char *t; 600f66f451Sopenharmony_ci struct arg_list *k; 610f66f451Sopenharmony_ci char *o, *T, S; 620f66f451Sopenharmony_ci 630f66f451Sopenharmony_ci void *key_list; 640f66f451Sopenharmony_ci int linecount; 650f66f451Sopenharmony_ci char **lines, *name; 660f66f451Sopenharmony_ci) 670f66f451Sopenharmony_ci 680f66f451Sopenharmony_ci// The sort types are n, g, and M. 690f66f451Sopenharmony_ci// u, c, s, and z apply to top level only, not to keys. 700f66f451Sopenharmony_ci// b at top level implies bb. 710f66f451Sopenharmony_ci// The remaining options can be applied to search keys. 720f66f451Sopenharmony_ci 730f66f451Sopenharmony_ci#define FLAG_bb (1<<31) // Ignore trailing blanks 740f66f451Sopenharmony_ci 750f66f451Sopenharmony_cistruct sort_key 760f66f451Sopenharmony_ci{ 770f66f451Sopenharmony_ci struct sort_key *next_key; // linked list 780f66f451Sopenharmony_ci unsigned range[4]; // start word, start char, end word, end char 790f66f451Sopenharmony_ci int flags; 800f66f451Sopenharmony_ci}; 810f66f451Sopenharmony_ci 820f66f451Sopenharmony_ci// Copy of the part of this string corresponding to a key/flags. 830f66f451Sopenharmony_ci 840f66f451Sopenharmony_cistatic char *get_key_data(char *str, struct sort_key *key, int flags) 850f66f451Sopenharmony_ci{ 860f66f451Sopenharmony_ci int start = 0, end, len, i, j; 870f66f451Sopenharmony_ci 880f66f451Sopenharmony_ci // Special case whole string, so we don't have to make a copy 890f66f451Sopenharmony_ci 900f66f451Sopenharmony_ci if(key->range[0]==1 && !key->range[1] && !key->range[2] && !key->range[3] 910f66f451Sopenharmony_ci && !(flags&(FLAG_b|FLAG_d|FLAG_i|FLAG_bb))) return str; 920f66f451Sopenharmony_ci 930f66f451Sopenharmony_ci // Find start of key on first pass, end on second pass 940f66f451Sopenharmony_ci 950f66f451Sopenharmony_ci len = strlen(str); 960f66f451Sopenharmony_ci for (j=0; j<2; j++) { 970f66f451Sopenharmony_ci if (!key->range[2*j]) end=len; 980f66f451Sopenharmony_ci 990f66f451Sopenharmony_ci // Loop through fields 1000f66f451Sopenharmony_ci else { 1010f66f451Sopenharmony_ci end = 0; 1020f66f451Sopenharmony_ci for (i = 1; i < key->range[2*j]+j; i++) { 1030f66f451Sopenharmony_ci 1040f66f451Sopenharmony_ci // Skip leading blanks 1050f66f451Sopenharmony_ci if (str[end] && !TT.t) while (isspace(str[end])) end++; 1060f66f451Sopenharmony_ci 1070f66f451Sopenharmony_ci // Skip body of key 1080f66f451Sopenharmony_ci for (; str[end]; end++) { 1090f66f451Sopenharmony_ci if (TT.t) { 1100f66f451Sopenharmony_ci if (str[end]==*TT.t) { 1110f66f451Sopenharmony_ci end++; 1120f66f451Sopenharmony_ci break; 1130f66f451Sopenharmony_ci } 1140f66f451Sopenharmony_ci } else if (isspace(str[end])) break; 1150f66f451Sopenharmony_ci } 1160f66f451Sopenharmony_ci } 1170f66f451Sopenharmony_ci } 1180f66f451Sopenharmony_ci if (!j) start=end; 1190f66f451Sopenharmony_ci } 1200f66f451Sopenharmony_ci 1210f66f451Sopenharmony_ci // Key with explicit separator starts after the separator 1220f66f451Sopenharmony_ci if (TT.t && str[start]==*TT.t) start++; 1230f66f451Sopenharmony_ci 1240f66f451Sopenharmony_ci // Strip leading and trailing whitespace if necessary 1250f66f451Sopenharmony_ci if ((flags&FLAG_b) || (!TT.t && !key->range[3])) 1260f66f451Sopenharmony_ci while (isspace(str[start])) start++; 1270f66f451Sopenharmony_ci if (flags&FLAG_bb) while (end>start && isspace(str[end-1])) end--; 1280f66f451Sopenharmony_ci 1290f66f451Sopenharmony_ci // Handle offsets on start and end 1300f66f451Sopenharmony_ci if (key->range[3]) { 1310f66f451Sopenharmony_ci end += key->range[3]-1; 1320f66f451Sopenharmony_ci if (end>len) end=len; 1330f66f451Sopenharmony_ci } 1340f66f451Sopenharmony_ci if (key->range[1]) { 1350f66f451Sopenharmony_ci start += key->range[1]-1; 1360f66f451Sopenharmony_ci if (start>len) start=len; 1370f66f451Sopenharmony_ci } 1380f66f451Sopenharmony_ci 1390f66f451Sopenharmony_ci // Make the copy 1400f66f451Sopenharmony_ci if (end<start) end = start; 1410f66f451Sopenharmony_ci str = xstrndup(str+start, end-start); 1420f66f451Sopenharmony_ci 1430f66f451Sopenharmony_ci // Handle -d 1440f66f451Sopenharmony_ci if (flags&FLAG_d) { 1450f66f451Sopenharmony_ci for (start = end = 0; str[end]; end++) 1460f66f451Sopenharmony_ci if (isspace(str[end]) || isalnum(str[end])) str[start++] = str[end]; 1470f66f451Sopenharmony_ci str[start] = 0; 1480f66f451Sopenharmony_ci } 1490f66f451Sopenharmony_ci 1500f66f451Sopenharmony_ci // Handle -i 1510f66f451Sopenharmony_ci if (flags&FLAG_i) { 1520f66f451Sopenharmony_ci for (start = end = 0; str[end]; end++) 1530f66f451Sopenharmony_ci if (isprint(str[end])) str[start++] = str[end]; 1540f66f451Sopenharmony_ci str[start] = 0; 1550f66f451Sopenharmony_ci } 1560f66f451Sopenharmony_ci 1570f66f451Sopenharmony_ci return str; 1580f66f451Sopenharmony_ci} 1590f66f451Sopenharmony_ci 1600f66f451Sopenharmony_ci// append a sort_key to key_list. 1610f66f451Sopenharmony_ci 1620f66f451Sopenharmony_cistatic struct sort_key *add_key(void) 1630f66f451Sopenharmony_ci{ 1640f66f451Sopenharmony_ci void **stupid_compiler = &TT.key_list; 1650f66f451Sopenharmony_ci struct sort_key **pkey = (struct sort_key **)stupid_compiler; 1660f66f451Sopenharmony_ci 1670f66f451Sopenharmony_ci while (*pkey) pkey = &((*pkey)->next_key); 1680f66f451Sopenharmony_ci return *pkey = xzalloc(sizeof(struct sort_key)); 1690f66f451Sopenharmony_ci} 1700f66f451Sopenharmony_ci 1710f66f451Sopenharmony_ci// Perform actual comparison 1720f66f451Sopenharmony_cistatic int compare_values(int flags, char *x, char *y) 1730f66f451Sopenharmony_ci{ 1740f66f451Sopenharmony_ci if (CFG_SORT_FLOAT && (flags & FLAG_g)) { 1750f66f451Sopenharmony_ci char *xx,*yy; 1760f66f451Sopenharmony_ci double dx = strtod(x,&xx), dy = strtod(y,&yy); 1770f66f451Sopenharmony_ci int xinf, yinf; 1780f66f451Sopenharmony_ci 1790f66f451Sopenharmony_ci // not numbers < NaN < -infinity < numbers < +infinity 1800f66f451Sopenharmony_ci 1810f66f451Sopenharmony_ci if (x==xx) return y==yy ? 0 : -1; 1820f66f451Sopenharmony_ci if (y==yy) return 1; 1830f66f451Sopenharmony_ci 1840f66f451Sopenharmony_ci // Check for isnan 1850f66f451Sopenharmony_ci if (dx!=dx) return (dy!=dy) ? 0 : -1; 1860f66f451Sopenharmony_ci if (dy!=dy) return 1; 1870f66f451Sopenharmony_ci 1880f66f451Sopenharmony_ci // Check for infinity. (Could underflow, but avoids needing libm.) 1890f66f451Sopenharmony_ci xinf = (1.0/dx == 0.0); 1900f66f451Sopenharmony_ci yinf = (1.0/dy == 0.0); 1910f66f451Sopenharmony_ci if (xinf) { 1920f66f451Sopenharmony_ci if(dx<0) return (yinf && dy<0) ? 0 : -1; 1930f66f451Sopenharmony_ci return (yinf && dy>0) ? 0 : 1; 1940f66f451Sopenharmony_ci } 1950f66f451Sopenharmony_ci if (yinf) return dy<0 ? 1 : -1; 1960f66f451Sopenharmony_ci 1970f66f451Sopenharmony_ci return dx>dy ? 1 : (dx<dy ? -1 : 0); 1980f66f451Sopenharmony_ci } else if (flags & FLAG_M) { 1990f66f451Sopenharmony_ci struct tm thyme; 2000f66f451Sopenharmony_ci int dx; 2010f66f451Sopenharmony_ci char *xx,*yy; 2020f66f451Sopenharmony_ci 2030f66f451Sopenharmony_ci xx = strptime(x,"%b",&thyme); 2040f66f451Sopenharmony_ci dx = thyme.tm_mon; 2050f66f451Sopenharmony_ci yy = strptime(y,"%b",&thyme); 2060f66f451Sopenharmony_ci if (!xx) return !yy ? 0 : -1; 2070f66f451Sopenharmony_ci else if (!yy) return 1; 2080f66f451Sopenharmony_ci else return dx==thyme.tm_mon ? 0 : dx-thyme.tm_mon; 2090f66f451Sopenharmony_ci 2100f66f451Sopenharmony_ci } else if (flags & FLAG_x) return strtol(x, NULL, 16)-strtol(y, NULL, 16); 2110f66f451Sopenharmony_ci else if (flags & FLAG_V) { 2120f66f451Sopenharmony_ci while (*x && *y) { 2130f66f451Sopenharmony_ci while (*x && *x == *y) x++, y++; 2140f66f451Sopenharmony_ci if (isdigit(*x) && isdigit(*y)) { 2150f66f451Sopenharmony_ci long long xx = strtoll(x, &x, 10), yy = strtoll(y, &y, 10); 2160f66f451Sopenharmony_ci 2170f66f451Sopenharmony_ci if (xx<yy) return -1; 2180f66f451Sopenharmony_ci if (xx>yy) return 1; 2190f66f451Sopenharmony_ci } else { 2200f66f451Sopenharmony_ci char xx = *x ? *x : x[-1], yy = *y ? *y : y[-1]; 2210f66f451Sopenharmony_ci 2220f66f451Sopenharmony_ci // -rc/-pre hack so abc-123 > abc-123-rc1 (other way already - < 0-9) 2230f66f451Sopenharmony_ci if (xx != yy) { 2240f66f451Sopenharmony_ci if (xx<yy && !strstart(&y, "-rc") && !strstart(&y, "-pre")) return -1; 2250f66f451Sopenharmony_ci else return 1; 2260f66f451Sopenharmony_ci } 2270f66f451Sopenharmony_ci } 2280f66f451Sopenharmony_ci } 2290f66f451Sopenharmony_ci return *x ? !!*y : -1; 2300f66f451Sopenharmony_ci } else if (flags & FLAG_n) { 2310f66f451Sopenharmony_ci // Full floating point version of -n 2320f66f451Sopenharmony_ci if (CFG_SORT_FLOAT) { 2330f66f451Sopenharmony_ci double dx = atof(x), dy = atof(y); 2340f66f451Sopenharmony_ci 2350f66f451Sopenharmony_ci return dx>dy ? 1 : (dx<dy ? -1 : 0); 2360f66f451Sopenharmony_ci // Integer version of -n for tiny systems 2370f66f451Sopenharmony_ci } else return atoi(x)-atoi(y); 2380f66f451Sopenharmony_ci 2390f66f451Sopenharmony_ci // Ascii sort 2400f66f451Sopenharmony_ci } else return ((flags&FLAG_f) ? strcasecmp : strcmp)(x, y); 2410f66f451Sopenharmony_ci} 2420f66f451Sopenharmony_ci 2430f66f451Sopenharmony_ci// Callback from qsort(): Iterate through key_list and perform comparisons. 2440f66f451Sopenharmony_cistatic int compare_keys(const void *xarg, const void *yarg) 2450f66f451Sopenharmony_ci{ 2460f66f451Sopenharmony_ci int flags = toys.optflags, retval = 0; 2470f66f451Sopenharmony_ci char *x, *y, *xx = *(char **)xarg, *yy = *(char **)yarg; 2480f66f451Sopenharmony_ci struct sort_key *key; 2490f66f451Sopenharmony_ci 2500f66f451Sopenharmony_ci for (key=(struct sort_key *)TT.key_list; !retval && key; key = key->next_key){ 2510f66f451Sopenharmony_ci flags = key->flags ? key->flags : toys.optflags; 2520f66f451Sopenharmony_ci 2530f66f451Sopenharmony_ci // Chop out and modify key chunks, handling -dfib 2540f66f451Sopenharmony_ci 2550f66f451Sopenharmony_ci x = get_key_data(xx, key, flags); 2560f66f451Sopenharmony_ci y = get_key_data(yy, key, flags); 2570f66f451Sopenharmony_ci 2580f66f451Sopenharmony_ci retval = compare_values(flags, x, y); 2590f66f451Sopenharmony_ci 2600f66f451Sopenharmony_ci // Free the copies get_key_data() made. 2610f66f451Sopenharmony_ci 2620f66f451Sopenharmony_ci if (x != xx) free(x); 2630f66f451Sopenharmony_ci if (y != yy) free(y); 2640f66f451Sopenharmony_ci 2650f66f451Sopenharmony_ci if (retval) break; 2660f66f451Sopenharmony_ci } 2670f66f451Sopenharmony_ci 2680f66f451Sopenharmony_ci // Perform fallback sort if necessary (always case insensitive, no -f, 2690f66f451Sopenharmony_ci // the point is to get a stable order even for -f sorts) 2700f66f451Sopenharmony_ci if (!retval && !FLAG(s)) { 2710f66f451Sopenharmony_ci flags = toys.optflags; 2720f66f451Sopenharmony_ci retval = strcmp(xx, yy); 2730f66f451Sopenharmony_ci } 2740f66f451Sopenharmony_ci 2750f66f451Sopenharmony_ci return retval * ((flags&FLAG_r) ? -1 : 1); 2760f66f451Sopenharmony_ci} 2770f66f451Sopenharmony_ci 2780f66f451Sopenharmony_ci// Read each line from file, appending to a big array. 2790f66f451Sopenharmony_cistatic void sort_lines(char **pline, long len) 2800f66f451Sopenharmony_ci{ 2810f66f451Sopenharmony_ci char *line; 2820f66f451Sopenharmony_ci 2830f66f451Sopenharmony_ci if (!pline) return; 2840f66f451Sopenharmony_ci line = *pline; 2850f66f451Sopenharmony_ci if (!FLAG(z) && len && line[len-1]=='\n') line[--len] = 0; 2860f66f451Sopenharmony_ci *pline = 0; 2870f66f451Sopenharmony_ci 2880f66f451Sopenharmony_ci // handle -c here so we don't allocate more memory than necessary. 2890f66f451Sopenharmony_ci if (FLAG(c)) { 2900f66f451Sopenharmony_ci int j = FLAG(u) ? -1 : 0; 2910f66f451Sopenharmony_ci 2920f66f451Sopenharmony_ci if (TT.lines && compare_keys((void *)&TT.lines, &line)>j) 2930f66f451Sopenharmony_ci error_exit("%s: Check line %d\n", TT.name, TT.linecount); 2940f66f451Sopenharmony_ci free(TT.lines); 2950f66f451Sopenharmony_ci TT.lines = (void *)line; 2960f66f451Sopenharmony_ci } else { 2970f66f451Sopenharmony_ci if (!(TT.linecount&63)) 2980f66f451Sopenharmony_ci TT.lines = xrealloc(TT.lines, sizeof(char *)*(TT.linecount+64)); 2990f66f451Sopenharmony_ci TT.lines[TT.linecount] = line; 3000f66f451Sopenharmony_ci } 3010f66f451Sopenharmony_ci TT.linecount++; 3020f66f451Sopenharmony_ci} 3030f66f451Sopenharmony_ci 3040f66f451Sopenharmony_ci// Callback from loopfiles to handle input files. 3050f66f451Sopenharmony_cistatic void sort_read(int fd, char *name) 3060f66f451Sopenharmony_ci{ 3070f66f451Sopenharmony_ci TT.name = name; 3080f66f451Sopenharmony_ci do_lines(fd, FLAG(z) ? '\0' : '\n', sort_lines); 3090f66f451Sopenharmony_ci} 3100f66f451Sopenharmony_ci 3110f66f451Sopenharmony_civoid sort_main(void) 3120f66f451Sopenharmony_ci{ 3130f66f451Sopenharmony_ci int idx, fd = 1; 3140f66f451Sopenharmony_ci 3150f66f451Sopenharmony_ci // Parse -k sort keys. 3160f66f451Sopenharmony_ci if (TT.k) { 3170f66f451Sopenharmony_ci struct arg_list *arg; 3180f66f451Sopenharmony_ci 3190f66f451Sopenharmony_ci for (arg = TT.k; arg; arg = arg->next) { 3200f66f451Sopenharmony_ci struct sort_key *key = add_key(); 3210f66f451Sopenharmony_ci char *temp; 3220f66f451Sopenharmony_ci int flag; 3230f66f451Sopenharmony_ci 3240f66f451Sopenharmony_ci idx = 0; 3250f66f451Sopenharmony_ci temp = arg->arg; 3260f66f451Sopenharmony_ci while (*temp) { 3270f66f451Sopenharmony_ci // Start of range 3280f66f451Sopenharmony_ci key->range[2*idx] = (unsigned)strtol(temp, &temp, 10); 3290f66f451Sopenharmony_ci if (*temp=='.') 3300f66f451Sopenharmony_ci key->range[(2*idx)+1] = (unsigned)strtol(temp+1, &temp, 10); 3310f66f451Sopenharmony_ci 3320f66f451Sopenharmony_ci // Handle flags appended to a key type. 3330f66f451Sopenharmony_ci for (;*temp;temp++) { 3340f66f451Sopenharmony_ci char *temp2, *optlist; 3350f66f451Sopenharmony_ci 3360f66f451Sopenharmony_ci // Note that a second comma becomes an "Unknown key" error. 3370f66f451Sopenharmony_ci 3380f66f451Sopenharmony_ci if (*temp==',' && !idx++) { 3390f66f451Sopenharmony_ci temp++; 3400f66f451Sopenharmony_ci break; 3410f66f451Sopenharmony_ci } 3420f66f451Sopenharmony_ci 3430f66f451Sopenharmony_ci // Which flag is this? 3440f66f451Sopenharmony_ci 3450f66f451Sopenharmony_ci optlist = toys.which->options; 3460f66f451Sopenharmony_ci temp2 = strchr(optlist, *temp); 3470f66f451Sopenharmony_ci flag = (1<<(optlist-temp2+strlen(optlist)-1)); 3480f66f451Sopenharmony_ci 3490f66f451Sopenharmony_ci // Was it a flag that can apply to a key? 3500f66f451Sopenharmony_ci 3510f66f451Sopenharmony_ci if (!temp2 || flag>FLAG_x 3520f66f451Sopenharmony_ci || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z))) 3530f66f451Sopenharmony_ci { 3540f66f451Sopenharmony_ci toys.exitval = 2; 3550f66f451Sopenharmony_ci error_exit("Unknown key option."); 3560f66f451Sopenharmony_ci } 3570f66f451Sopenharmony_ci // b after , means strip _trailing_ space, not leading. 3580f66f451Sopenharmony_ci if (idx && flag==FLAG_b) flag = FLAG_bb; 3590f66f451Sopenharmony_ci key->flags |= flag; 3600f66f451Sopenharmony_ci } 3610f66f451Sopenharmony_ci } 3620f66f451Sopenharmony_ci } 3630f66f451Sopenharmony_ci } 3640f66f451Sopenharmony_ci 3650f66f451Sopenharmony_ci // global b flag strips both leading and trailing spaces 3660f66f451Sopenharmony_ci if (FLAG(b)) toys.optflags |= FLAG_bb; 3670f66f451Sopenharmony_ci 3680f66f451Sopenharmony_ci // If no keys, perform alphabetic sort over the whole line. 3690f66f451Sopenharmony_ci if (!TT.key_list) add_key()->range[0] = 1; 3700f66f451Sopenharmony_ci 3710f66f451Sopenharmony_ci // Open input files and read data, populating TT.lines[TT.linecount] 3720f66f451Sopenharmony_ci loopfiles(toys.optargs, sort_read); 3730f66f451Sopenharmony_ci 3740f66f451Sopenharmony_ci // The compare (-c) logic was handled in sort_read(), 3750f66f451Sopenharmony_ci // so if we got here, we're done. 3760f66f451Sopenharmony_ci if (FLAG(c)) goto exit_now; 3770f66f451Sopenharmony_ci 3780f66f451Sopenharmony_ci // Perform the actual sort 3790f66f451Sopenharmony_ci qsort(TT.lines, TT.linecount, sizeof(char *), compare_keys); 3800f66f451Sopenharmony_ci 3810f66f451Sopenharmony_ci // handle unique (-u) 3820f66f451Sopenharmony_ci if (FLAG(u)) { 3830f66f451Sopenharmony_ci int jdx; 3840f66f451Sopenharmony_ci 3850f66f451Sopenharmony_ci for (jdx=0, idx=1; idx<TT.linecount; idx++) { 3860f66f451Sopenharmony_ci if (!compare_keys(&TT.lines[jdx], &TT.lines[idx])) 3870f66f451Sopenharmony_ci free(TT.lines[idx]); 3880f66f451Sopenharmony_ci else TT.lines[++jdx] = TT.lines[idx]; 3890f66f451Sopenharmony_ci } 3900f66f451Sopenharmony_ci if (TT.linecount) TT.linecount = jdx+1; 3910f66f451Sopenharmony_ci } 3920f66f451Sopenharmony_ci 3930f66f451Sopenharmony_ci // Open output file if necessary. We can't do this until we've finished 3940f66f451Sopenharmony_ci // reading in case the output file is one of the input files. 3950f66f451Sopenharmony_ci if (TT.o) fd = xcreate(TT.o, O_CREAT|O_TRUNC|O_WRONLY, 0666); 3960f66f451Sopenharmony_ci 3970f66f451Sopenharmony_ci // Output result 3980f66f451Sopenharmony_ci for (idx = 0; idx<TT.linecount; idx++) { 3990f66f451Sopenharmony_ci char *s = TT.lines[idx]; 4000f66f451Sopenharmony_ci unsigned i = strlen(s); 4010f66f451Sopenharmony_ci 4020f66f451Sopenharmony_ci if (!FLAG(z)) s[i] = '\n'; 4030f66f451Sopenharmony_ci xwrite(fd, s, i+1); 4040f66f451Sopenharmony_ci if (CFG_TOYBOX_FREE) free(s); 4050f66f451Sopenharmony_ci } 4060f66f451Sopenharmony_ci 4070f66f451Sopenharmony_ciexit_now: 4080f66f451Sopenharmony_ci if (CFG_TOYBOX_FREE) { 4090f66f451Sopenharmony_ci if (fd != 1) close(fd); 4100f66f451Sopenharmony_ci free(TT.lines); 4110f66f451Sopenharmony_ci } 4120f66f451Sopenharmony_ci} 413