x11_draw_string(x, x->font, offset + 2, fheight, Palette[TagsFg], Query);
/* draw the scored and sorted results */
- for (int i = Offset, y = 2 * fheight + 4; ((size_t)i < vec_size(&Choices)) && ((size_t)i <= Offset+nlines); i++, y += fheight) {
- if ((size_t)i == ChoiceIdx)
- x11_draw_rect(x, Palette[EditSel], ScrollWidth+3, y - x->font->ascent, x->width, fheight);
- x11_draw_string(x, x->font, ScrollWidth+3, y, Palette[TagsFg], ((Choice*)vec_at(&Choices, i))->string);
+ if (vec_size(&Choices)) {
+ for (int i = Offset, y = 2 * fheight + 4; ((size_t)i < vec_size(&Choices)) && ((size_t)i <= Offset+nlines); i++, y += fheight) {
+ if ((size_t)i == ChoiceIdx)
+ x11_draw_rect(x, Palette[EditSel], ScrollWidth+3, y - x->font->ascent, x->width, fheight);
+ x11_draw_string(x, x->font, ScrollWidth+3, y, Palette[TagsFg], ((Choice*)vec_at(&Choices, i))->string);
+ }
+ } else {
+ x11_draw_string(x, x->font, ScrollWidth+3, 2 * fheight + 4, Palette[TagsFg], "Loading...");
}
x11_flip(x);
}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <regex.h>
+
+/* Line buffer flags */
+enum {
+ LB_NEWLINE = (1 << 0),
+ LB_DELETE = (1 << 1)
+};
+
+typedef struct {
+ int flags;
+ size_t capacity;
+ ssize_t length;
+ char* buffer;
+} LineBuf;
+
+/* Command flags */
+enum {
+ SUB_GLOBAL = (1 << 0),
+ SUB_PRINT = (1 << 1),
+ IN_RANGE = (1 << 2),
+};
+
+typedef struct {
+ enum { NONE, LINE, REGEX } type;
+ union {
+ regex_t* regex;
+ size_t line;
+ } u;
+} Addr;
+
+typedef struct {
+ int type;
+ int flags;
+ Addr addr[2];
+ regex_t* regex;
+ char* text;
+} Cmd;
+
+typedef struct {
+ size_t ncmds;
+ size_t line;
+ Cmd cmds[];
+} Prog;
+
+typedef void (*CmdFn)(Cmd* cmd, LineBuf* buf);
+
+static void cmd_d(Cmd* cmd, LineBuf* lbuf) {
+ (void)cmd;
+ lbuf->flags |= LB_DELETE;
+}
+
+static void cmd_s(Cmd* cmd, LineBuf* lbuf) {
+ regmatch_t match[10] = {0};
+ if (!regexec(cmd->regex, lbuf->buffer, 10, match, 0)) {
+// lbuf->flags |= LB_DELETE;
+ }
+}
+
+static const CmdFn Commands[] = {
+ ['d'] = cmd_d,
+ ['s'] = cmd_s
+};
+
+static int match_addr(Prog* prog, Addr* addr, LineBuf* lbuf) {
+ if (addr->type == NONE)
+ return 1;
+ else if (addr->type == LINE)
+ return (addr->u.line == prog->line);
+ else if (addr->type == REGEX)
+ return (regexec(addr->u.regex, lbuf->buffer, 0, 0, 0) == 0);
+ else
+ return 0;
+}
+
+static char* rdline(LineBuf* buf, FILE* file) {
+ buf->length = getline(&(buf->buffer), &(buf->capacity), file);
+ buf->flags = 0;
+ if (buf->length <= 0) {
+ free(buf->buffer);
+ buf->buffer = NULL;
+ } else if (buf->buffer[buf->length-1] == '\n') {
+ buf->flags |= LB_NEWLINE;
+ buf->buffer[buf->length-1] = '\0';
+ }
+ return buf->buffer;
+}
+
+static void parse_fail(char* msg) {
+ printf("parse error: %s\n", msg);
+ exit(1);
+}
+
+static char* parse_till(char* script, int term, char** str) {
+ if (!*script) return script;
+ char *sbeg = script, *send = script;
+ for (; *send && *send != term; send++)
+ if (*send == '\\') send++;
+ *str = strndup(sbeg, send - sbeg);
+ return (*send ? send+1 : send);
+}
+
+static char* parse_addr(char* script, Addr* addr) {
+ if (isdigit(*script)) {
+ addr->type = LINE;
+ for (; *script && isdigit(*script); script++)
+ addr->u.line = (addr->u.line * 10) + (*script - '0');
+ } else if (*script == '/') {
+ addr->type = REGEX;
+ char* rstr = NULL;
+ script = parse_till(++script, '/', &rstr);
+ addr->u.regex = calloc(1, sizeof(regex_t));
+ if (regcomp(addr->u.regex, rstr, REG_EXTENDED|REG_NOSUB) != 0)
+ parse_fail("failed to compile regex");
+ }
+ return script;
+}
+
+static char* parse_sub(char* script, Cmd* cmd) {
+ int sep = *script++;
+ if (!sep) return (script-1);
+
+ /* parse out the regex and template */
+ char *rstr = NULL, *tstr = NULL;
+ script = parse_till(script, sep, &rstr);
+ script = parse_till(script, sep, &tstr);
+
+ /* parse the flags */
+ for (;*script && !isspace(*script); script++) {
+ switch (*script) {
+ case 'g': cmd->flags |= SUB_GLOBAL; break;
+ case 'p': cmd->flags |= SUB_PRINT; break;
+ default: parse_fail("bad substitute flag");
+ }
+ }
+
+ /* compile the regex and setup the command */
+ cmd->text = tstr;
+ cmd->regex = calloc(1, sizeof(regex_t));
+ if (regcomp(cmd->regex, rstr, REG_EXTENDED|REG_NEWLINE) < 0)
+ parse_fail("failed to compile regex");
+ free(rstr);
+
+ return script;
+}
+
+static Prog* prog_grow(Prog* prog, Cmd** lastcmd) {
+ prog = realloc(prog, sizeof(Prog) + ((prog->ncmds + 1) * sizeof(Cmd)));
+ *lastcmd = &(prog->cmds[prog->ncmds]);
+ prog->ncmds++;
+ memset(*lastcmd, 0, sizeof(Cmd));
+ return prog;
+}
+
+Prog* prog_parse(char* script) {
+ Cmd* cmd = NULL;
+ Prog* prog = calloc(1, sizeof(Prog));
+ while (*script) {
+ while (isspace(*script)) script++;
+ if (!*script) break;
+
+ /* allocate a new command */
+ prog = prog_grow(prog, &cmd);
+
+ /* parse the addresses */
+ script = parse_addr(script, &(cmd->addr[0]));
+ if (*script == ',')
+ script = parse_addr(++script, &(cmd->addr[1]));
+
+ /* parse the command */
+ cmd->type = *script;
+ switch (*script++) {
+ case 'd':
+ /* handled above */
+ break;
+ case 's':
+ script = parse_sub(script, cmd);
+ break;
+ case '\0':
+ parse_fail("unexpected end of string");
+ break;
+ default:
+ parse_fail("unexpected char");
+ exit(1);
+ break;
+ }
+ }
+ return prog;
+}
+
+void prog_free(Prog* prog) {
+#define FREE_REGEX(regex) do { regfree(regex); free(regex); } while(0)
+ for (size_t i = 0; i < prog->ncmds; i++) {
+ if (prog->cmds[i].addr[0].type == REGEX) FREE_REGEX(prog->cmds[i].addr[0].u.regex);
+ if (prog->cmds[i].addr[1].type == REGEX) FREE_REGEX(prog->cmds[i].addr[1].u.regex);
+ if (prog->cmds[i].regex) FREE_REGEX(prog->cmds[i].regex);
+ if (prog->cmds[i].text) free(prog->cmds[i].text);
+ }
+#undef FREE_REGEX
+ free(prog);
+}
+
+void prog_exec(Prog* prog, LineBuf* lbuf) {
+ prog->line++;
+ for (size_t i = 0; i < prog->ncmds; i++) {
+ Cmd* cmd = &(prog->cmds[i]);
+ if ((cmd->flags & IN_RANGE) ||match_addr(prog, &(cmd->addr[0]), lbuf)) {
+ if (cmd->addr[1].type != NONE) {
+ cmd->flags |= IN_RANGE;
+ if (match_addr(prog, &(cmd->addr[1]), lbuf))
+ cmd->flags &= ~IN_RANGE;
+ }
+ Commands[cmd->type](cmd, lbuf);
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ if (argc < 2) return 1;
+
+ LineBuf buf = {0};
+ Prog* prog = prog_parse(argv[1]);
+ while (rdline(&buf, stdin)) {
+ prog_exec(prog, &buf);
+ if (buf.flags & LB_DELETE) continue;
+ fwrite(buf.buffer, 1u, buf.length, stdout);
+ if (buf.flags & LB_NEWLINE)
+ fwrite("\n", 1u, 1u, stdout);
+ }
+ prog_free(prog);
+
+ return 0;
+}