Audit sort(1) and mark it as finished

1) Remove the function prototypes. No need for them, as the
   functions are ordered.
2) Add fieldseplen, so the length of the field-separator is not
   calculated nearly each time skipcolumn() is called.
3) rename next_col to skip_to_next_col so the purpose is clear,
   also reorder the conditional accordingly.
4) Put parentheses around certain ternary expressions.
5) BUGFIX: Don't just exit() in check(), but make it return something,
   so we can cleanly fshut() everything.
6) OFF-POSIX: Posix for no apparent reason does not allow more than
   one file when the -c or -C flags are given.
   This can be problematic when you want to check multiple files.
   With the change 5), rewriting check() to return a value, I went
   off-posix after discussing this with Dimitris to just allow
   arbitrary numbers of files. Obviously, this does not break scripts
   and is convenient for everybody who wants to quickly check a big
   amount of files.
   As soon as 1 file is "unsorted", the return value is 1, as expected.
   For convenience reasons, check()'s warning now includes the filename.
7) BUGFIX: Set ret to 2 instead of 1 when the fshut(fp, *argv) fails.
8) BUGFIX: Don't forget to fshut stderr at the end. This would improperly
   return 1 in the following case:
   $ sort -c unsorted_file 2> /dev/full
9) Other style changes, line length, empty line before return.
This commit is contained in:
FRIGN 2015-08-04 12:45:59 +02:00 committed by sin
parent 0a2562e994
commit 51390a3c51
2 changed files with 37 additions and 29 deletions

2
README
View File

@ -69,7 +69,7 @@ The following tools are implemented:
=*|x sha256sum . =*|x sha256sum .
=*|x sha512sum . =*|x sha512sum .
=*|o sleep . =*|o sleep .
# sort (-d, -f, -i) #*|o sort (-d, -f, -i)
=*|o split . =*|o split .
=*|x sponge . =*|x sponge .
#*|o strings . #*|o strings .

64
sort.c
View File

@ -27,17 +27,9 @@ enum {
static TAILQ_HEAD(kdhead, keydef) kdhead = TAILQ_HEAD_INITIALIZER(kdhead); static TAILQ_HEAD(kdhead, keydef) kdhead = TAILQ_HEAD_INITIALIZER(kdhead);
static char *skipblank(char *);
static char *skipnonblank(char *);
static char *skipcolumn(char *, char *, int);
static size_t columns(char *, const struct keydef *, char **, size_t *);
static int linecmp(const char **, const char **);
static void check(FILE *);
static int parse_flags(char **, int *, int);
static void addkeydef(char *, int);
static int Cflag = 0, cflag = 0, uflag = 0; static int Cflag = 0, cflag = 0, uflag = 0;
static char *fieldsep = NULL; static char *fieldsep = NULL;
static size_t fieldseplen = 0;
static char *col1, *col2; static char *col1, *col2;
static size_t col1siz, col2siz; static size_t col1siz, col2siz;
@ -46,6 +38,7 @@ skipblank(char *s)
{ {
while (*s == ' ' || *s == '\t') while (*s == ' ' || *s == '\t')
s++; s++;
return s; return s;
} }
@ -54,21 +47,25 @@ skipnonblank(char *s)
{ {
while (*s && *s != '\n' && *s != ' ' && *s != '\t') while (*s && *s != '\n' && *s != ' ' && *s != '\t')
s++; s++;
return s; return s;
} }
static char * static char *
skipcolumn(char *s, char *eol, int next_col) skipcolumn(char *s, char *eol, int skip_to_next_col)
{ {
if (fieldsep) { if (fieldsep) {
if ((s = strstr(s, fieldsep))) if ((s = strstr(s, fieldsep))) {
s += next_col ? strlen(fieldsep) : 0; if (skip_to_next_col)
else s += fieldseplen;
} else {
s = eol; s = eol;
}
} else { } else {
s = skipblank(s); s = skipblank(s);
s = skipnonblank(s); s = skipnonblank(s);
} }
return s; return s;
} }
@ -107,13 +104,14 @@ columns(char *line, const struct keydef *kd, char **col, size_t *colsiz)
} else { } else {
end = eol; end = eol;
} }
len = start > end ? 0 : end - start; len = (start > end) ? 0 : (end - start);
if (!*col || *colsiz < len) if (!*col || *colsiz < len)
*col = erealloc(*col, len + 1); *col = erealloc(*col, len + 1);
memcpy(*col, start, len); memcpy(*col, start, len);
(*col)[len] = '\0'; (*col)[len] = '\0';
if (*colsiz < len) if (*colsiz < len)
*colsiz = len; *colsiz = len;
return len; return len;
} }
@ -136,7 +134,7 @@ linecmp(const char **a, const char **b)
} else if (kd->flags & MOD_N) { } else if (kd->flags & MOD_N) {
x = strtold(col1, NULL); x = strtold(col1, NULL);
y = strtold(col2, NULL); y = strtold(col2, NULL);
res = (x < y) ? (-1) : (x > y); res = (x < y) ? -1 : (x > y);
} else { } else {
res = strcmp(col1, col2); res = strcmp(col1, col2);
} }
@ -150,23 +148,26 @@ linecmp(const char **a, const char **b)
return res; return res;
} }
static void static int
check(FILE *fp) check(FILE *fp, const char *fname)
{ {
static struct { char *buf; size_t size; } prev, cur, tmp; static struct { char *buf; size_t size; } prev, cur, tmp;
if (!prev.buf && getline(&prev.buf, &prev.size, fp) < 0) if (!prev.buf && getline(&prev.buf, &prev.size, fp) < 0)
eprintf("getline:"); eprintf("getline:");
while (getline(&cur.buf, &cur.size, fp) > 0) { while (getline(&cur.buf, &cur.size, fp) > 0) {
if (uflag > linecmp((const char **) &cur.buf, (const char **) &prev.buf)) { if (uflag > linecmp((const char **)&cur.buf,
(const char **)&prev.buf)) {
if (!Cflag) if (!Cflag)
weprintf("disorder: %s", cur.buf); weprintf("disorder %s: %s", fname, cur.buf);
exit(1); return 1;
} }
tmp = cur; tmp = cur;
cur = prev; cur = prev;
prev = tmp; prev = tmp;
} }
return 0;
} }
static int static int
@ -212,7 +213,8 @@ addkeydef(char *kdstr, int flags)
if (*kdstr == '.') { if (*kdstr == '.') {
if ((kd->start_char = strtol(kdstr + 1, &kdstr, 10)) < 1) if ((kd->start_char = strtol(kdstr + 1, &kdstr, 10)) < 1)
enprintf(2, "invalid start character in key definition\n"); enprintf(2, "invalid start character in key "
"definition\n");
} }
if (parse_flags(&kdstr, &kd->flags, MOD_STARTB) < 0) if (parse_flags(&kdstr, &kd->flags, MOD_STARTB) < 0)
enprintf(2, "invalid start flags in key definition\n"); enprintf(2, "invalid start flags in key definition\n");
@ -222,7 +224,8 @@ addkeydef(char *kdstr, int flags)
enprintf(2, "invalid end column in key definition\n"); enprintf(2, "invalid end column in key definition\n");
if (*kdstr == '.') { if (*kdstr == '.') {
if ((kd->end_char = strtol(kdstr + 1, &kdstr, 10)) < 0) if ((kd->end_char = strtol(kdstr + 1, &kdstr, 10)) < 0)
enprintf(2, "invalid end character in key definition\n"); enprintf(2, "invalid end character in key "
"definition\n");
} }
if (parse_flags(&kdstr, &kd->flags, MOD_ENDB) < 0) if (parse_flags(&kdstr, &kd->flags, MOD_ENDB) < 0)
enprintf(2, "invalid end flags in key definition\n"); enprintf(2, "invalid end flags in key definition\n");
@ -281,6 +284,7 @@ main(int argc, char *argv[])
break; break;
case 't': case 't':
fieldsep = EARGF(usage()); fieldsep = EARGF(usage());
fieldseplen = strlen(fieldsep);
break; break;
case 'u': case 'u':
uflag = 1; uflag = 1;
@ -296,7 +300,8 @@ main(int argc, char *argv[])
if (!argc) { if (!argc) {
if (Cflag || cflag) { if (Cflag || cflag) {
check(stdin); if (check(stdin, "<stdin>") && !ret)
ret = 1;
} else { } else {
getlines(stdin, &linebuf); getlines(stdin, &linebuf);
} }
@ -309,12 +314,13 @@ main(int argc, char *argv[])
continue; continue;
} }
if (Cflag || cflag) { if (Cflag || cflag) {
check(fp); if (check(fp, *argv) && !ret)
ret = 1;
} else { } else {
getlines(fp, &linebuf); getlines(fp, &linebuf);
} }
if (fp != stdin && fshut(fp, *argv)) if (fp != stdin && fshut(fp, *argv))
ret = 1; ret = 2;
} }
if (!Cflag && !cflag) { if (!Cflag && !cflag) {
@ -325,14 +331,16 @@ main(int argc, char *argv[])
(int (*)(const void *, const void *))linecmp); (int (*)(const void *, const void *))linecmp);
for (i = 0; i < linebuf.nlines; i++) { for (i = 0; i < linebuf.nlines; i++) {
if (!uflag || i == 0 || linecmp((const char **)&linebuf.lines[i], if (!uflag || i == 0 ||
(const char **)&linebuf.lines[i-1])) { linecmp((const char **)&linebuf.lines[i],
(const char **)&linebuf.lines[i - 1])) {
fputs(linebuf.lines[i], ofp); fputs(linebuf.lines[i], ofp);
} }
} }
} }
if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>")) if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>") |
fshut(stderr, "<stderr>"))
ret = 2; ret = 2;
return ret; return ret;