sbase/sort.c
FRIGN ec8246bbc6 Un-boolify sbase
It actually makes the binaries smaller, the code easier to read
(gems like "val == true", "val == false" are gone) and actually
predictable in the sense of that we actually know what we're
working with (one bitwise operator was quite adventurous and
should now be fixed).

This is also more consistent with the other suckless projects
around which don't use boolean types.
2014-11-14 10:54:20 +00:00

285 lines
5.3 KiB
C

/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "text.h"
#include "util.h"
struct keydef {
int start_column;
int end_column;
int start_char;
int end_char;
int flags;
};
enum {
MOD_N = 1 << 1,
MOD_STARTB = 1 << 2,
MOD_ENDB = 1 << 3,
MOD_R = 1 << 4,
};
struct kdlist {
struct keydef keydef;
struct kdlist *next;
};
static struct kdlist *head = NULL;
static struct kdlist *tail = NULL;
static void addkeydef(char *, int);
static void freelist(void);
static int linecmp(const char **, const char **);
static char *skipblank(char *);
static int parse_flags(char **, int *, int);
static int parse_keydef(struct keydef *, char *, int);
static char *nextcol(char *);
static char *columns(char *, const struct keydef *);
static int uflag = 0;
static char *fieldsep = NULL;
static void
usage(void)
{
enprintf(2, "usage: %s [-bnru] [-t delim] [-k def]... [file...]\n", argv0);
}
int
main(int argc, char *argv[])
{
long i;
FILE *fp;
struct linebuf linebuf = EMPTY_LINEBUF;
int global_flags = 0;
ARGBEGIN {
case 'b':
global_flags |= MOD_STARTB | MOD_ENDB;
break;
case 'k':
addkeydef(EARGF(usage()), global_flags);
break;
case 'n':
global_flags |= MOD_N;
break;
case 'r':
global_flags |= MOD_R;
break;
case 't':
fieldsep = EARGF(usage());
if (strlen(fieldsep) != 1)
usage();
break;
case 'u':
uflag = 1;
break;
default:
usage();
} ARGEND;
if (!head && global_flags)
addkeydef("1", global_flags);
addkeydef("1", global_flags & MOD_R);
if (argc == 0) {
getlines(stdin, &linebuf);
} else for (; argc > 0; argc--, argv++) {
if (!(fp = fopen(argv[0], "r"))) {
enprintf(2, "fopen %s:", argv[0]);
continue;
}
getlines(fp, &linebuf);
fclose(fp);
}
qsort(linebuf.lines, linebuf.nlines, sizeof *linebuf.lines,
(int (*)(const void *, const void *))linecmp);
for (i = 0; i < linebuf.nlines; i++) {
if (!uflag || i == 0 || linecmp((const char **)&linebuf.lines[i],
(const char **)&linebuf.lines[i-1])) {
fputs(linebuf.lines[i], stdout);
}
}
freelist();
return 0;
}
static void
addkeydef(char *def, int flags)
{
struct kdlist *node;
node = malloc(sizeof(*node));
if (!node)
enprintf(2, "malloc:");
if (!head)
head = node;
if (parse_keydef(&node->keydef, def, flags))
enprintf(2, "faulty key definition\n");
if (tail)
tail->next = node;
node->next = NULL;
tail = node;
}
static void
freelist(void)
{
struct kdlist *node;
struct kdlist *tmp;
for (node = head; node; node = tmp) {
tmp = node->next;
free(node);
}
}
static int
linecmp(const char **a, const char **b)
{
char *s1, *s2;
int res = 0;
struct kdlist *node;
for (node = head; node && res == 0; node = node->next) {
s1 = columns((char *)*a, &node->keydef);
s2 = columns((char *)*b, &node->keydef);
/* if -u is given, don't use default key definition
* unless it is the only one */
if (uflag && node == tail && head != tail)
res = 0;
else if (node->keydef.flags & MOD_N)
res = strtol(s1, 0, 10) - strtol(s2, 0, 10);
else
res = strcmp(s1, s2);
if (node->keydef.flags & MOD_R)
res = -res;
free(s1);
free(s2);
}
return res;
}
static int
parse_flags(char **s, int *flags, int bflag)
{
while (isalpha((int)**s))
switch (*((*s)++)) {
case 'b':
*flags |= bflag;
break;
case 'n':
*flags |= MOD_N;
break;
case 'r':
*flags |= MOD_R;
break;
default:
return -1;
}
return 0;
}
static int
parse_keydef(struct keydef *kd, char *s, int flags)
{
char *rest = s;
kd->start_column = 1;
kd->start_char = 1;
/* 0 means end of line */
kd->end_column = 0;
kd->end_char = 0;
kd->flags = flags;
kd->start_column = strtol(rest, &rest, 10);
if (kd->start_column < 1)
return -1;
if (*rest == '.')
kd->start_char = strtol(rest+1, &rest, 10);
if (kd->start_char < 1)
return -1;
if (parse_flags(&rest, &kd->flags, MOD_STARTB) == -1)
return -1;
if (*rest == ',') {
kd->end_column = strtol(rest+1, &rest, 10);
if (kd->end_column && kd->end_column < kd->start_column)
return -1;
if (*rest == '.') {
kd->end_char = strtol(rest+1, &rest, 10);
if (kd->end_char < 1)
return -1;
}
if (parse_flags(&rest, &kd->flags, MOD_ENDB) == -1)
return -1;
}
if (*rest != '\0')
return -1;
return 0;
}
static char *
skipblank(char *s)
{
while(*s && isblank(*s))
s++;
return s;
}
static char *
nextcol(char *s)
{
if (fieldsep == NULL) {
s = skipblank(s);
while(*s && !isblank(*s))
s++;
} else {
if (strchr(s, *fieldsep) == NULL)
s = strchr(s, '\0');
else
s = strchr(s, *fieldsep) + 1;
}
return s;
}
static char *
columns(char *line, const struct keydef *kd)
{
char *start, *end;
char *res;
int i;
for (i = 1, start = line; i < kd->start_column; i++)
start = nextcol(start);
if (kd->flags & MOD_STARTB)
start = skipblank(start);
start += MIN(kd->start_char, nextcol(start) - start) - 1;
if (kd->end_column) {
for (i = 1, end = line; i < kd->end_column; i++)
end = nextcol(end);
if (kd->flags & MOD_ENDB)
end = skipblank(end);
if (kd->end_char)
end += MIN(kd->end_char, nextcol(end) - end);
else
end = nextcol(end);
} else {
if ((end = strchr(line, '\n')) == NULL)
end = strchr(line, '\0');
}
if ((res = strndup(start, end - start)) == NULL)
enprintf(2, "strndup:");
return res;
}