diff options
Diffstat (limited to 'scoop.c')
-rw-r--r-- | scoop.c | 96 |
1 files changed, 77 insertions, 19 deletions
diff --git a/scoop.c b/scoop.c index 8c113d5..73aeb89 100644 --- a/scoop.c +++ b/scoop.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019 June McEnroe <june@causal.agency> * * 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 @@ -245,6 +245,7 @@ static void regexpFree(void *_regex) { free(regex); } +static int regexpFlags = REG_EXTENDED | REG_NOSUB; static void regexp(sqlite3_context *ctx, int n, sqlite3_value *args[]) { assert(n == 2); if (sqlite3_value_type(args[0]) == SQLITE_NULL) { @@ -267,7 +268,7 @@ static void regexp(sqlite3_context *ctx, int n, sqlite3_value *args[]) { int error = regcomp( regex, (const char *)sqlite3_value_text(args[0]), - REG_EXTENDED | REG_NOSUB + regexpFlags ); if (error) { char msg[256]; @@ -283,7 +284,9 @@ static void regexp(sqlite3_context *ctx, int n, sqlite3_value *args[]) { sqlite3_result_int(ctx, !error); } -static char select[4096] = SQL( +enum { QueryCap = 4096 }; + +static char select[QueryCap] = SQL( SELECT events.event, contexts.network, @@ -306,8 +309,6 @@ static char select[4096] = SQL( events.target, ); -enum { QueryCap = 4096 }; - static char from[QueryCap] = SQL( FROM events JOIN contexts USING (context) @@ -344,6 +345,14 @@ static enum Type parseType(const char *input) { errx(EX_USAGE, "no such type %s", input); } +static int parseTypes(char *list) { + int mask = 0; + while (list) { + mask |= 1 << parseType(strsep(&list, ",")); + } + return mask; +} + int main(int argc, char *argv[]) { bool tty = isatty(STDOUT_FILENO); @@ -353,20 +362,33 @@ int main(int argc, char *argv[]) { bool sort = false; bool group = false; + bool reverse = false; const char *limit = NULL; int n = 0; - struct Bind binds[argc + 2]; - const char *Opts = "D:F:LN:ST:a:b:c:d:f:gh:l:m:n:pqst:u:vw:"; + struct Bind *binds = calloc(argc + 2, sizeof(*binds)); + if (!binds) err(EX_OSERR, "calloc"); + + const char *Opts = "D:F:LN:ST:a:b:c:d:f:gh:il:m:n:pqrst:u:vw:"; for (int opt; 0 < (opt = getopt(argc, argv, Opts));) { switch (opt) { break; case 'D': { append( where, SQL( - AND events.time >= strftime('%s', :date, 'start of day') - AND events.time - < strftime('%s', :date, 'start of day', '+1 day') + AND events.time >= + CASE WHEN :local THEN + strftime('%s', :date, 'start of day', 'utc') + ELSE + strftime('%s', :date, 'start of day') + END + AND events.time < + CASE WHEN :local THEN + strftime('%s', :date, 'start of day', '+1 day', + 'utc') + ELSE + strftime('%s', :date, 'start of day', '+1 day') + END ) ); binds[n++] = Bind(":date", optarg, 0); @@ -389,11 +411,29 @@ int main(int argc, char *argv[]) { binds[n++] = Bind(":target", optarg, 0); } break; case 'a': { - append(where, SQL(AND events.time >= strftime('%s', :after))); + append( + where, + SQL( + AND events.time >= + CASE WHEN :local + THEN strftime('%s', :after, 'utc') + ELSE strftime('%s', :after) + END + ) + ); binds[n++] = Bind(":after", optarg, 0); } break; case 'b': { - append(where, SQL(AND events.time < strftime('%s', :before))); + append( + where, + SQL( + AND events.time < + CASE WHEN :local + THEN strftime('%s', :before, 'utc') + ELSE strftime('%s', :before) + END + ) + ); binds[n++] = Bind(":before", optarg, 0); } break; case 'c': { @@ -414,6 +454,9 @@ int main(int argc, char *argv[]) { append(where, SQL(AND names.host = :host)); binds[n++] = Bind(":host", optarg, 0); } + break; case 'i': { + regexpFlags |= REG_ICASE; + } break; case 'l': { limit = optarg; sort = true; @@ -434,12 +477,15 @@ int main(int argc, char *argv[]) { append(where, SQL(AND contexts.query = :query)); binds[n++] = Bind(":query", NULL, 1); } + break; case 'r': { + reverse = true; + } break; case 's': { sort = true; } break; case 't': { - append(where, SQL(AND events.type = :type)); - binds[n++] = Bind(":type", NULL, parseType(optarg)); + append(where, SQL(AND (1 << events.type) & :types)); + binds[n++] = Bind(":types", NULL, parseTypes(optarg)); } break; case 'u': { append(where, SQL(AND names.user = :user)); @@ -456,7 +502,8 @@ int main(int argc, char *argv[]) { } } - if (optind < argc) { + bool search = (optind < argc); + if (search) { append(select, SQL(highlight(search, 6, :open, :close))); append(from, SQL(JOIN search ON search.rowid = events.event)); append(where, SQL(AND search MATCH :search)); @@ -474,11 +521,15 @@ int main(int argc, char *argv[]) { } if (limit) { - append(where, SQL(ORDER BY time DESC, event DESC LIMIT :limit)); + if (search) { + append(where, SQL(ORDER BY search.rowid DESC LIMIT :limit)); + } else { + append(where, SQL(ORDER BY event DESC LIMIT :limit)); + } binds[n++] = Bind(":limit", limit, 0); } - dbFind(path, SQLITE_OPEN_READWRITE); + dbFind(path, SQLITE_OPEN_READONLY); if (dbVersion() != DatabaseVersion) { errx(EX_CONFIG, "database out of date; migrate with litterbox -m"); } @@ -503,9 +554,15 @@ int main(int argc, char *argv[]) { SQL( WITH results AS (%s %s %s) SELECT * FROM results - ORDER BY %s time, event; + ORDER BY %s time %s, event %s; ), - select, from, where, (group ? "network, context," : "") + select, from, where, (group ? "network, context," : ""), + (reverse ? "DESC" : ""), (reverse ? "DESC" : "") + ); + } else if (reverse) { + len = asprintf( + &query, "%s %s %s ORDER BY %s DESC;", + select, from, where, (search ? "search.rowid" : "event") ); } else { len = asprintf(&query, "%s %s %s;", select, from, where); @@ -522,6 +579,7 @@ int main(int argc, char *argv[]) { dbBindInt(stmt, binds[i].param, binds[i].value); } } + free(binds); if (verbose) { char *expand = sqlite3_expanded_sql(stmt); |