myadm

Simple MySQL client for the terminal
git clone git://git.bitsmanent.org/myadm
Log | Files | Refs | README | LICENSE

commit 3400cb5bab00f8d69b1b0930c4e2aa5011908a44
Author: Claudio Alessi <smoppy@gmail.com>
Date:   Wed, 24 Feb 2016 00:07:29 +0100

Just in case.

Diffstat:
Acore.c | 388+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adatabases.stfl | 8++++++++
Atables.stfl | 8++++++++
Atest.c | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 631 insertions(+), 0 deletions(-)

diff --git a/core.c b/core.c @@ -0,0 +1,388 @@ +/* cc -D_BSD_SOURCE -std=c99 -O0 -Wall -pedantic -o core core.c $(mysql_config --cflags) -lmysqlclient -lstfl -lncursesw */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> + +#include <mysql.h> +#include <stfl.h> + +#include <langinfo.h> +#include <locale.h> + +#define LENGTH(X) (sizeof X / sizeof X[0]) + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct Item Item; +struct Item { + char *name; + int len; + unsigned int flags; + Item *next; +}; + +typedef struct { + const char *mode; + const wchar_t *modkey; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *mode; + const wchar_t *modkey; + const wchar_t *var; +} Stflkey; + +typedef struct { + const char *name; + void (*func)(void); +} Mode; + +typedef struct View View; +struct View { + Mode *mode; + Item *items; + struct stfl_form *form; + View *next; +}; + +/* function declarations */ +void die(const char *errstr, ...); +void databases(void); +void tables(void); +void records(void); +void text(void); +MYSQL_RES *mysql_exec(char *sql); +Item *mysql_items(MYSQL_RES *res); +void attach(View *v); +void detach(View *v); +void *ecalloc(size_t nmemb, size_t size); +void cleanupview(View *v); +void flagas(const Arg *arg); +void apply(const Arg *arg); +void quit(const Arg *arg); +void setmode(const Arg *arg); +void viewprev(const Arg *arg); +void usedb(const Arg *arg); +void usetable(const Arg *arg); +void userecord(const Arg *arg); + +/* config.h > */ + +/* XXX command line arguments */ +#define DBHOST "localhost" +#define DBUSER "root" +#define DBPASS "m0r3s3cur3" + +static Mode modes[] = { + /* name show function */ + { "databases", databases }, /* first entry is default */ + { "tables", tables }, + { "records", records }, + { "text", text }, +}; + +static Key keys[] = { + /* mode modkey function argument */ + { NULL, L"Q", quit, {.i = 0} }, + { NULL, L"q", viewprev, {0} }, + { NULL, L"D", setmode, {.v = &modes[0]} }, + { NULL, L"T", setmode, {.v = &modes[1]} }, + { NULL, L"R", setmode, {.v = &modes[2]} }, + { NULL, L"E", setmode, {.v = &modes[3]} }, + { "databases", L"q", quit, {.i = 1} }, + { "databases", L"ENTER", usedb, {.v = &modes[1]} }, + { "tables", L"ENTER", usetable, {0} }, + { "records", L"ENTER", userecord, {0} }, + { "records", L"d", flagas, {.v = "D"} }, + { "records", L"t", flagas, {.v = "*"} }, + { "records", L"$", apply, {0} }, +}; + +static Stflkey stflkeys[] = { + /* mode modkey stfl variable */ + { NULL, L"k UP", L"bind_up" }, + { NULL, L"j DOWN", L"bind_down" }, +}; + +/* < config.h */ + +/* variables */ +static int running = 1; +static MYSQL *mysql = NULL; +static View *views, *selview; +static struct stfl_ipool *ipool; + +/* function implementations */ +void +quit(const Arg *arg) { + if(arg->i) { + /* XXX ask for confirmation */ + } + running = 0; +} + +void +attach(View *v) { + v->next = views; + views = v; +} + +void +detach(View *v) { + View **tv; + + for (tv = &views; *tv && *tv != v; tv = &(*tv)->next); + *tv = v->next; +} + +void +setmode(const Arg *arg) { + const Mode *m = arg->v; + View *v; + unsigned int i; + + if(selview && !strcmp(arg->v, m->name)) + return; + + for(v = views; v; v = v->next) + if(!strcmp(v->mode->name, m->name)) + break; + if(!v) { + v = ecalloc(1, sizeof(View)); + for(i = 0; i < LENGTH(modes); ++i) + if(!strcmp(modes[i].name, m->name)) + v->mode = &modes[i]; + attach(v); + + selview = v; + selview->mode->func(); + + /* bind stfl keys */ + for(i = 0; i < LENGTH(stflkeys); ++i) + if(!(stflkeys[i].mode && strcmp(stflkeys[i].mode, selview->mode->name))) + stfl_set(selview->form, stflkeys[i].var, stflkeys[i].modkey); + } + else { + selview = v; + selview->mode->func(); + } + + /* XXX Throwing a visual error instead of die... */ + if(!selview->form) + die("%s: mode is broken.\n", m->name); +} + +void +cleanupview(View *v) { + detach(v); + + /* XXX + while(v->items) + cleanupitem(v->items); + */ + + /* XXX cleanup items */ + if(v->form) + stfl_free(v->form); + free(v); +} + +MYSQL_RES * +mysql_exec(char *sql) { + MYSQL_RES *res; + + if(mysql_real_query(mysql, sql, strlen(sql))) + return NULL; + res = mysql_store_result(mysql); + if(!res) + return NULL; /* XXX if(mysql_field_count(mysql)) error; */ + return res; +} + +Item * +mysql_items(MYSQL_RES *res) { + MYSQL_ROW row; + Item *items, *item; + int i, nfds; + + nfds = mysql_num_fields(res); + + items = NULL; + while((row = mysql_fetch_row(res))) { + item = ecalloc(1, sizeof(Item)); + item->name = ecalloc(256, sizeof(char)); + + for(i = 0; i < nfds; ++i) + snprintf(item->name, 22, "%s", row[i]); + + item->next = items; + items = item; + } + + return items; +} + +void +databases(void) { + MYSQL_RES *res; + Item *item; + char txt[256]; + int i; + + if(selview->form) + return; + + if(!(res = mysql_exec("show databases"))) + die("databases"); + + /* XXX Previously allocated items are never freed, only the LAST + * allocation gets freed by cleanupview(). */ + selview->items = mysql_items(res); + mysql_free_result(res); + + selview->form = stfl_create(L"<databases.stfl>"); + i = 0; + for(item = selview->items; item; item = item->next) { + snprintf(txt, sizeof txt, "listitem[%d] text:\"%s\"", i++, item->name); + stfl_modify(selview->form, L"databases", L"append", stfl_ipool_towc(ipool, txt)); + } +} + +void +tables(void) { + MYSQL_RES *res; + Item *item; + char txt[256]; + int i; + + if(!(res = mysql_exec("show tables"))) + die("tables\n"); + + /* + XXX + if(selview->items) + free items + */ + + selview->items = mysql_items(res); + mysql_free_result(res); + + if(!selview->form) + selview->form = stfl_create(L"<tables.stfl>"); + i = 0; + stfl_modify(selview->form, L"tables", L"replace_inner", L"vbox"); /* clear */ + for(item = selview->items; item; item = item->next) { + snprintf(txt, sizeof txt, "listitem[%d] text:\"%s\"", i++, item->name); + stfl_modify(selview->form, L"tables", L"append", stfl_ipool_towc(ipool, txt)); + } +} + +void +records(void) { +} + +void +text(void) { +} + +void +usedb(const Arg *arg) { + Item *item; + int i = 0; + + int pos = atoi(stfl_ipool_fromwc(ipool, stfl_get(selview->form, L"pos"))); + + for(item = selview->items; item; item = item->next) + if(i++ == pos) + break; + + mysql_select_db(mysql, item->name); + setmode(arg); +} + +void +usetable(const Arg *arg) { +} + +void +userecord(const Arg *arg) { +} + +void +viewprev(const Arg *arg) { + if(!selview->next) + return; + selview = selview->next; + selview->mode->func(); +} + +void +flagas(const Arg *arg) { +} + +void +apply(const Arg *arg) { +} + +void +die(const char *errstr, ...) { + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) { + void *p; + + if (!(p = calloc(nmemb, size))) + die("Cannot allocate memory."); + return p; +} + +int +main(int argc, char **argv) { + Arg a = {.v = &modes[0]}; + Key *k; + const wchar_t *ev; + unsigned int i; + + mysql = mysql_init(NULL); + if(mysql_real_connect(mysql, DBHOST, DBUSER, DBPASS, NULL, 0, NULL, 0) == NULL) + die("Cannot connect to the database.\n"); + + ipool = stfl_ipool_create(nl_langinfo(CODESET)); + setmode(&a); + + while(running) { + if(!(ev = stfl_run(selview->form, 0))) + continue; + + k = NULL; + for(i = 0; i < LENGTH(keys); ++i) + if(!((keys[i].mode && strcmp(selview->mode->name, keys[i].mode)) + || wcscmp(ev, keys[i].modkey))) + k = &keys[i]; + if(k) + k->func(&k->arg); + } + + while(views) + cleanupview(views); + + stfl_reset(); + stfl_ipool_destroy(ipool); + return 0; +} diff --git a/databases.stfl b/databases.stfl @@ -0,0 +1,8 @@ +vbox[main] + .expand:w + list[databases] + @style_normal:fg=white,bg=black + @style_focus:fg=blue,bg=black + @bind_down[bind_down]:** + @bind_up[bind_up]:** + pos[pos]:0 diff --git a/tables.stfl b/tables.stfl @@ -0,0 +1,8 @@ +vbox[main] + .expand:w + list[tables] + @style_normal:fg=white,bg=black + @style_focus:fg=blue,bg=black + @bind_down[bind_down]:** + @bind_up[bind_up]:** + pos[pos]:0 diff --git a/test.c b/test.c @@ -0,0 +1,227 @@ +/* cc -D_BSD_SOURCE -std=c99 -O0 -Wall -pedantic -o test test.c $(mysql_config --cflags) -lmysqlclient */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <mysql.h> + +typedef struct { + char *name; + int len; + unsigned int flags; +} Item; + +typedef struct { + char **fields; + Item **items; + int nfields; + int nvalues; +} List; + +typedef struct { + MYSQL_FIELD *fields; + MYSQL_RES *res; + int nfields; + int nrows; +} Query; + + +/* globals */ +static MYSQL *mysql = NULL; + +/* function declarations */ +/* ... */ + +/* function implementations */ +Item * +newitem(char *name, int len) { + Item *item; + + item = malloc(sizeof(Item)); + item->name = malloc(sizeof(char) * len + 1); + snprintf(item->name, len + 1, "%s", name); + item->len = len; + item->flags = 0; + return item; +} + +List * +newlist(int nfields, int nvalues, char **fields, Item **items) { + List *list; + + list = malloc(sizeof(List)); + if(!list) + return NULL; + list->fields = fields; + list->items = items; + list->nfields = nfields; + list->nvalues = nvalues; + return list; +} + +void +free_list(List *l) { + int i; + + for(i = 0; i < l->nvalues; ++i) + free(l->items[i]); + free(l); +} + +void +die(const char *errstr, ...) { + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(1); +} + +/* XXX to be implemented */ +Query * +mysql_vexec(char *sqlstr, ...) { + return NULL; +} + +Query * +mysql_exec(char *sql, Query *qry) { + MYSQL_RES *res; + int nfds; + + if(mysql_real_query(mysql, sql, strlen(sql))) + return NULL; + res = mysql_store_result(mysql); + if(!res) + return NULL; /* XXX if(mysql_field_count(mysql)) error; */ + nfds = mysql_num_fields(res); + if(!qry) + qry = malloc(sizeof(Query)); + qry->res = res; + qry->nfields = nfds; + qry->nrows = mysql_num_rows(res); + qry->fields = mysql_fetch_fields(res); + return qry; +} + +void +mysql_free(Query *q) { + mysql_free_result(q->res); + free(q); +} + +int +mysql_main() { + mysql = mysql_init(NULL); + if(mysql_real_connect(mysql, "localhost", "root", "m0r3s3cur3", NULL, 0, NULL, 0) == NULL) + return 1; + return 0; +} + +void +mysql_end() { + mysql_close(mysql); +} + +const char * +mysql_err() { + return mysql_error(mysql); +} + +List * +mysql_query_to_list(Query *q) { + MYSQL_ROW row; + unsigned long *lens; + Item **items; + char **fields; + int i, j, len; + + /* fields */ + fields = malloc(sizeof(char *) * q->nfields); + for(i = 0; i < q->nfields; ++i) { + len = strlen(q->fields[i].name) + 1; + fields[i] = malloc(sizeof(char) * len); + snprintf(fields[i], len, "%s", q->fields[i].name); + } + + /* items */ + items = malloc(sizeof(Item *) * q->nrows * q->nfields); + for(i = 0; i < q->nrows; ++i) { + row = mysql_fetch_row(q->res); + lens = mysql_fetch_lengths(q->res); + + for(j = 0; j < q->nfields; ++j) + items[i * q->nfields + j] = newitem(row[j], lens[j]); + } + + return newlist(q->nfields, q->nrows, fields, items); +} + +void +d_query(Query *q) { + MYSQL_ROW row; + int i, j, nfds; + + printf("%d field(s), %d row(s) queried.\n", q->nfields, q->nrows); + nfds = (q->nfields > 5 ? 5 : q->nfields); /* show max 5 cols */ + + for(i = 0; i < nfds; ++i) + printf("%18.16s", q->fields[i].name); + printf("\n"); + + mysql_data_seek(q->res, 0); + for(i = 0; i < q->nrows; ++i) { + row = mysql_fetch_row(q->res); + for(j = 0; j < nfds; ++j) + printf("%18.16s", row[j]); + printf("\n"); + } +} + +void +d_list(List *l) { + int i, j, nfds; + + printf("%d field(s), %d row(s) queried.\n", l->nfields, l->nvalues); + nfds = (l->nfields > 5 ? 5 : l->nfields); /* show max 5 cols */ + + for(i = 0; i < nfds; ++i) + printf("%18.16s", l->fields[i]); + printf("\n"); + + for(i = 0; i < l->nvalues; ++i) { + for(j = 0; j < nfds; ++j) + printf("%18.16s", l->items[i * l->nfields + j]->name); + printf("\n"); + } +} + +int +main(int argc, char *argv[]) { + List *l; + Query *q; + int i; + + if(mysql_main()) + die("mysql_main()"); + q = NULL; + for(i = 1; i < argc; ++i) { + if(!(q = mysql_exec(argv[i], q))) { + printf("%s\n", mysql_err()); + continue; + } + l = mysql_query_to_list(q); + //d_query(q); + if(!l) { + printf("Cannot convert to list.\n"); + continue; + } + d_list(l); + free_list(l); + } + if(q) + mysql_free(q); + mysql_end(); + return 0; +}