myadm.c (19188B)
1 /* See LICENSE file for copyright and license details. 2 * 3 * myadm is a text-based TUI for MySQL. It emulates the mutt interface through 4 * the STFL library and talk with the SQL server using libmysqlclient. 5 * 6 * Each piece of information displayed is called an item. Items are organized 7 * in a linked items list on each view. A view contains an STFL form where all 8 * graphical elements are drawn along with all related informations. Each item 9 * contains a bit array to indicate tags of an item. 10 * 11 * To understand everything else, start reading main(). 12 */ 13 14 #include <stdio.h> 15 #include <stdarg.h> 16 #include <string.h> 17 #include <stdlib.h> 18 #include <signal.h> 19 #include <ctype.h> 20 #include <mysql.h> 21 #include <stfl.h> 22 #include <langinfo.h> 23 #include <locale.h> 24 #include <curses.h> 25 26 #include <unistd.h> 27 #include <sys/types.h> 28 #include <sys/wait.h> 29 #include <sys/stat.h> 30 #include <fcntl.h> 31 32 /* STFL fragments (generated via stflfrag) */ 33 #include "fragments.h" 34 35 #include "arg.h" 36 char *argv0; 37 38 #define QUOTE(S) (stfl_ipool_fromwc(ipool, stfl_quote(stfl_ipool_towc(ipool, S)))) 39 #define ISCURVIEW(N) !(N && selview && strcmp(selview->name, N)) 40 #define LENGTH(X) (sizeof X / sizeof X[0]) 41 42 #define MYSQLIDLEN 64 43 #define MAXQUERYLEN 4096 44 45 typedef union { 46 int i; 47 unsigned int ui; 48 float f; 49 const void *v; 50 } Arg; 51 52 typedef struct { 53 void (*cmd)(void); 54 } Action; 55 56 typedef struct Item Item; 57 struct Item { 58 char **cols; 59 int *lens; 60 int ncols; 61 Item *next; 62 }; 63 64 typedef struct Field Field; 65 struct Field { 66 char name[MYSQLIDLEN]; 67 int len; 68 Field *next; 69 }; 70 71 typedef struct { 72 const char *view; 73 const int code; 74 void (*func)(const Arg *); 75 const Arg arg; 76 } Key; 77 78 typedef struct View View; 79 struct View { 80 char name[16]; 81 void (*show)(void); 82 Item *items; 83 Item *choice; 84 Field *fields; 85 int cur; 86 int nitems; 87 int nfields; 88 struct stfl_form *form; 89 View *next; 90 }; 91 92 /* function declarations */ 93 void attach(View *v); 94 void attachfield(Field *f, Field **ff); 95 void attachitem(Item *i, Item **ii); 96 char ui_ask(const char *msg, char *opts); 97 void cleanup(void); 98 void cleanupfields(Field **fields); 99 void cleanupitems(Item **items); 100 void cleanupview(View *v); 101 void detach(View *v); 102 void detachfield(Field *f, Field **ff); 103 void detachitem(Item *i, Item **ii); 104 void die(const char *errstr, ...); 105 void *ecalloc(size_t nmemb, size_t size); 106 void editfile(char *file); 107 void editrecord(const Arg *arg); 108 void edittable(const Arg *arg); 109 int escape(char *esc, char *s, int sz, char c, char skip); 110 Item *getitem(int pos); 111 int *getmaxlengths(Item *items, Field *fields); 112 void itempos(const Arg *arg); 113 void mksql_alter_table(char *sql, char *tbl); 114 void mksql_update(char *sql, Item *item, Field *fields, char *tbl, char *uk); 115 int mysql_file_exec(char *file); 116 int mysql_exec(const char *sqlstr, ...); 117 int mysql_fields(MYSQL_RES *res, Field **fields); 118 void mysql_fillview(MYSQL_RES *res, int showfds); 119 int mysql_ukey(char *key, char *tbl, int sz); 120 int mysql_items(MYSQL_RES *res, Item **items); 121 void quit(const Arg *arg); 122 void reload(const Arg *arg); 123 void run(void); 124 void setview(const char *name, void (*func)(void)); 125 void setup(void); 126 void startup(void); 127 void ui_end(void); 128 struct stfl_form *ui_getform(wchar_t *code); 129 void ui_init(void); 130 void ui_modify(const char *name, const char *mode, const char *fmtstr, ...); 131 void ui_listview(Item *items, Field *fields); 132 void ui_putitem(Item *item, int *lens, int id); 133 void ui_refresh(void); 134 void ui_set(const char *key, const char *fmtstr, ...); 135 void ui_showfields(Field *fds, int *lens); 136 void ui_showitems(Item *items, int *lens); 137 void ui_sql_edit_exec(char *sql); 138 void usage(void); 139 void viewdb(const Arg *arg); 140 void viewdb_show(void); 141 void viewdblist(void); 142 void viewdblist_show(void); 143 void viewprev(const Arg *arg); 144 void viewtable(const Arg *arg); 145 void viewtable_show(void); 146 147 #if defined CTRL && defined _AIX 148 #undef CTRL 149 #endif 150 #ifndef CTRL 151 #define CTRL(k) ((k) & 0x1F) 152 #endif 153 #define CTRL_ALT(k) ((k) + (129 - 'a')) 154 155 #include "config.h" 156 157 /* variables */ 158 static int running = 1; 159 static MYSQL *mysql; 160 static View *views, *selview = NULL; 161 static struct stfl_ipool *ipool; 162 static int fldseplen; 163 164 /* function implementations */ 165 void 166 attach(View *v) { 167 v->next = views; 168 views = v; 169 } 170 171 void 172 attachfield(Field *f, Field **ff) { 173 Field **l; 174 175 for(l = ff; *l && (*l)->next; l = &(*l)->next); 176 if(!*l) 177 *l = f; 178 else 179 (*l)->next = f; 180 } 181 182 void 183 attachitem(Item *i, Item **ii) { 184 Item **l; 185 186 for(l = ii; *l && (*l)->next; l = &(*l)->next); 187 if(!*l) 188 *l = i; 189 else 190 (*l)->next = i; 191 } 192 193 char 194 ui_ask(const char *msg, char *opts) { 195 int c; 196 char *o = NULL; 197 198 ui_set("status", msg); 199 ui_refresh(); 200 while(!(o && *o) && (c = getch())) { 201 if(c == '\n') 202 o = &opts[0]; 203 else 204 for(o = opts; *o; ++o) 205 if(c == *o) 206 break; 207 } 208 ui_set("status", ""); 209 return *o; 210 } 211 212 void 213 cleanup(void) { 214 while(views) 215 cleanupview(views); 216 ui_end(); 217 mysql_close(mysql); 218 } 219 220 void 221 cleanupview(View *v) { 222 detach(v); 223 cleanupitems(&v->items); 224 cleanupfields(&v->fields); 225 if(v->form) 226 stfl_free(v->form); 227 free(v); 228 } 229 230 void 231 cleanupfields(Field **fields) { 232 Field *f; 233 234 while(*fields) { 235 f = *fields; 236 detachfield(f, fields); 237 free(f); 238 } 239 } 240 241 void 242 cleanupitems(Item **items) { 243 Item *i; 244 245 while(*items) { 246 i = *items; 247 detachitem(i, items); 248 while(--i->ncols >= 0) 249 free(i->cols[i->ncols]); 250 free(i->cols); 251 free(i->lens); 252 free(i); 253 } 254 } 255 256 void 257 detach(View *v) { 258 View **tv; 259 260 for(tv = &views; *tv && *tv != v; tv = &(*tv)->next); 261 *tv = v->next; 262 } 263 264 void 265 detachfield(Field *f, Field **ff) { 266 Field **tf; 267 268 for(tf = &(*ff); *tf && *tf != f; tf = &(*tf)->next); 269 *tf = f->next; 270 } 271 272 void 273 detachitem(Item *i, Item **ii) { 274 Item **ti; 275 276 for(ti = &(*ii); *ti && *ti != i; ti = &(*ti)->next); 277 *ti = i->next; 278 } 279 280 void 281 die(const char *errstr, ...) { 282 va_list ap; 283 284 va_start(ap, errstr); 285 vfprintf(stderr, errstr, ap); 286 va_end(ap); 287 exit(1); 288 } 289 290 void * 291 ecalloc(size_t nmemb, size_t size) { 292 void *p; 293 294 if (!(p = calloc(nmemb, size))) 295 die("Cannot allocate memory.\n"); 296 return p; 297 } 298 299 void 300 editfile(char *file) { 301 pid_t pid; 302 int rc = -1; 303 struct sigaction saold[4]; 304 305 /* take off ncurses signal handlers */ 306 struct sigaction sa = {.sa_flags = 0, .sa_handler = SIG_DFL}; 307 sigaction(SIGINT, &sa, &saold[0]); 308 sigaction(SIGTERM, &sa, &saold[1]); 309 sigaction(SIGTSTP, &sa, &saold[2]); 310 sigaction(SIGWINCH, &sa, &saold[3]); 311 312 if((pid = fork()) == 0) { 313 execl("/bin/sh", "sh", "-c", "$EDITOR \"$0\"", file, NULL); 314 _exit(127); 315 } 316 else if(pid == -1) 317 return; 318 while(!WIFEXITED(rc)) 319 waitpid(pid, &rc, 0); 320 321 /* restore ncurses signal handlers */ 322 sigaction(SIGINT, &saold[0], NULL); 323 sigaction(SIGTERM, &saold[1], NULL); 324 sigaction(SIGTSTP, &saold[2], NULL); 325 sigaction(SIGWINCH, &saold[3], NULL); 326 327 /* reinitialize ncurses */ 328 endwin(); 329 refresh(); 330 } 331 332 void 333 editrecord(const Arg *arg) { 334 Item *item = getitem(0); 335 char *tbl = selview->choice->cols[0], uk[MYSQLIDLEN+1], sql[MAXQUERYLEN+1]; 336 337 if(!item) { 338 ui_set("status", "No item selected."); 339 return; 340 } 341 if(mysql_ukey(uk, tbl, sizeof uk)) { 342 ui_set("status", "Cannot edit records in `%s`, no unique key found.", tbl); 343 return; 344 } 345 mksql_update(sql, item, selview->fields, tbl, uk); 346 ui_sql_edit_exec(sql); 347 } 348 349 void 350 edittable(const Arg *arg) { 351 Item *item = getitem(0); 352 char sql[MAXQUERYLEN+1]; 353 354 if(!item->cols[0]) { 355 ui_set("status", "No table selected."); 356 return; 357 } 358 /* XXX check alter table permissions */ 359 mksql_alter_table(sql, item->cols[0]); 360 ui_sql_edit_exec(sql); 361 } 362 363 int 364 escape(char *esc, char *s, int sz, char c, char skip) { 365 int i, ei = 0; 366 367 for(i = 0; i < sz; ++i) { 368 if(s[i] == c && (!skip || s[i+1] != skip)) 369 esc[ei++] = '\\'; 370 esc[ei++] = s[i]; 371 } 372 esc[ei] = '\0'; 373 return ei - sz; 374 } 375 376 Item * 377 getitem(int pos) { 378 Item *item; 379 int i; 380 381 if(!selview) 382 return NULL; 383 if(!pos) 384 pos = selview->cur; 385 for(item = selview->items, i = 0; item; item = item->next, ++i) 386 if(i == pos) 387 break; 388 return item; 389 } 390 391 int * 392 getmaxlengths(Item *items, Field *fields) { 393 Item *item; 394 Field *fld; 395 int i, *lens, ncols; 396 397 if(!(items || fields)) 398 return NULL; 399 if(items) 400 ncols = items->ncols; 401 else 402 for(fld = fields, ncols = 0; fld; fld = fld->next, ++ncols); 403 lens = ecalloc(ncols, sizeof(int)); 404 if(fields) 405 for(fld = fields, i = 0; fld; fld = fld->next, ++i) 406 lens[i] = (fld->len <= MAXCOLSZ ? fld->len : MAXCOLSZ); 407 if(items) 408 for(item = items; item; item = item->next) 409 for(i = 0; i < item->ncols; ++i) 410 if(lens[i] < item->lens[i]) 411 lens[i] = (item->lens[i] <= MAXCOLSZ ? item->lens[i] : MAXCOLSZ); 412 return lens; 413 } 414 415 void 416 itempos(const Arg *arg) { 417 int pos; 418 419 if(!selview || !selview->nitems) { 420 ui_set("info", "No items."); 421 return; 422 } 423 pos = selview->cur + arg->i; 424 if(pos < 0) 425 pos = 0; 426 else if(pos >= selview->nitems) 427 pos = selview->nitems - 1; 428 ui_set("pos", "%d", pos); 429 selview->cur = pos; 430 ui_set("info", "%d of %d item(s)", selview->cur+1, selview->nitems); 431 } 432 433 void 434 mksql_alter_table(char *sql, char *tbl) { 435 MYSQL_RES *res; 436 MYSQL_ROW row; 437 char *p; 438 int size = MAXQUERYLEN, len = 0, r; 439 440 *sql = '\0'; 441 r = mysql_exec("show create table `%s`", tbl); 442 if(r == -1 || !(res = mysql_store_result(mysql)) || !(row = mysql_fetch_row(res))) 443 return; 444 mysql_free_result(res); 445 len += snprintf(&sql[len], size - len + 1, "ALTER TABLE `%s`", tbl); 446 for(r = 0, p = &row[1][0]; row[1][r]; ++r) { 447 if(row[1][r] != '\n') 448 continue; 449 while(*p == ' ') 450 ++p; 451 if(*p == '`') { 452 row[1][r] = '\0'; 453 len += snprintf(&sql[len], size - len + 1, "\nMODIFY %s", p); 454 } 455 p = &row[1][r + 1]; 456 } 457 if(sql[len - 1] == ',') 458 sql[len - 1] = '\0'; 459 } 460 461 void 462 mksql_update(char *sql, Item *item, Field *fields, char *tbl, char *uk) { 463 Field *fld; 464 char *ukv = NULL, sqlfds[MAXQUERYLEN+1], col[MAXQUERYLEN]; 465 int size = MAXQUERYLEN, len = 0, i; 466 467 for(i = 0, fld = fields; fld; fld = fld->next, ++i) { 468 if(!ukv && !strncmp(uk, fld->name, fld->len)) 469 ukv = item->cols[i]; 470 escape(col, item->cols[i], item->lens[i], '\'', 0); 471 len += snprintf(&sqlfds[len], size - len + 1, "\n%c`%s` = '%s'", 472 len ? ',' : ' ', fld->name, col); 473 } 474 snprintf(sql, MAXQUERYLEN+1, "UPDATE `%s` SET%s\nWHERE `%s` = '%s'", 475 tbl, sqlfds, uk, ukv); 476 } 477 478 int 479 mysql_exec(const char *sqlstr, ...) { 480 va_list ap; 481 char sql[MAXQUERYLEN+1]; 482 int r, sqlen; 483 484 va_start(ap, sqlstr); 485 sqlen = vsnprintf(sql, sizeof sql, sqlstr, ap); 486 va_end(ap); 487 r = mysql_real_query(mysql, sql, sqlen); 488 return (r ? -1 : mysql_field_count(mysql)); 489 } 490 491 int 492 mysql_fields(MYSQL_RES *res, Field **fields) { 493 MYSQL_FIELD *fds; 494 Field *field; 495 int nfds, i; 496 497 nfds = mysql_num_fields(res); 498 if(!nfds) 499 return 0; 500 fds = mysql_fetch_fields(res); 501 for(i = 0; i < nfds; ++i) { 502 field = ecalloc(1, sizeof(Field)); 503 field->len = fds[i].name_length; 504 memcpy(field->name, fds[i].name, field->len); 505 attachfield(field, fields); 506 } 507 return nfds; 508 } 509 510 int 511 mysql_file_exec(char *file) { 512 char buf[MAXQUERYLEN+1], esc[MAXQUERYLEN*2+1]; 513 int fd, size, r; 514 515 fd = open(file, O_RDONLY); 516 if(fd == -1) 517 return -1; 518 size = read(fd, buf, MAXQUERYLEN); 519 if(size == -1) 520 return -2; 521 if(!size) 522 return 0; 523 buf[size] = '\0'; 524 /* We do not want flow control chars to be interpreted. */ 525 size += escape(esc, buf, size, '\\', '\''); 526 r = mysql_exec(esc); 527 if(r == -1) 528 return -3; 529 return size; 530 } 531 532 void 533 mysql_fillview(MYSQL_RES *res, int showfds) { 534 cleanupitems(&selview->items); 535 selview->nitems = mysql_items(res, &selview->items); 536 if(showfds) { 537 cleanupfields(&selview->fields); 538 selview->nfields = mysql_fields(res, &selview->fields); 539 } 540 } 541 542 int 543 mysql_ukey(char *key, char *tbl, int sz) { 544 MYSQL_RES *res; 545 MYSQL_ROW row; 546 int r; 547 548 r = mysql_exec("show keys from `%s` where Non_unique = 0", tbl); 549 if(r == -1 || !(res = mysql_store_result(mysql))) 550 return 1; 551 if(!(row = mysql_fetch_row(res))) { 552 mysql_free_result(res); 553 return 2; 554 } 555 snprintf(key, sz, "%s", row[4]); 556 mysql_free_result(res); 557 return 0; 558 } 559 560 int 561 mysql_items(MYSQL_RES *res, Item **items) { 562 MYSQL_ROW row; 563 Item *item; 564 int i, nfds, nrows; 565 unsigned long *lens; 566 567 nfds = mysql_num_fields(res); 568 nrows = mysql_num_rows(res); 569 *items = NULL; 570 while((row = mysql_fetch_row(res))) { 571 item = ecalloc(1, sizeof(Item)); 572 item->lens = ecalloc(nfds, sizeof(int)); 573 item->cols = ecalloc(nfds, sizeof(char *)); 574 lens = mysql_fetch_lengths(res); 575 item->ncols = nfds; 576 for(i = 0; i < nfds; ++i) { 577 item->cols[i] = ecalloc(1, lens[i]+1); 578 memcpy(item->cols[i], row[i], lens[i]); 579 item->lens[i] = lens[i]; 580 } 581 attachitem(item, items); 582 } 583 return nrows; 584 } 585 586 void 587 ui_listview(Item *items, Field *fields) { 588 int *lens; 589 590 if(!selview->form) 591 selview->form = ui_getform(FRAG_ITEMS); 592 lens = getmaxlengths(items, fields); 593 if(fields) 594 ui_showfields(fields, lens); 595 if(items) 596 ui_showitems(items, lens); 597 free(lens); 598 } 599 600 void 601 ui_showfields(Field *fds, int *lens) { 602 Field *fld; 603 char line[COLS + 1]; 604 int li = 0, i, j; 605 606 if(!(fds && lens)) 607 return; 608 line[0] = '\0'; 609 for(fld = fds, i = 0; fld && li < COLS; fld = fld->next, ++i) { 610 if(i) 611 for(j = 0; j < fldseplen && li < COLS; ++j) 612 line[li++] = FLDSEP[j]; 613 for(j = 0; li < COLS && j < fld->len && j < lens[i]; ++j) 614 line[li++] = fld->name[j]; 615 while(li < COLS && j++ < lens[i]) 616 line[li++] = ' '; 617 } 618 line[li] = '\0'; 619 ui_set("subtle", "%s", line); 620 ui_set("showsubtle", "%d", (line[0] ? 1 : 0)); 621 } 622 623 void 624 ui_showitems(Item *items, int *lens) { 625 Item *item; 626 int id = 0; 627 628 ui_modify("items", "replace_inner", "vbox"); /* empty items */ 629 for(item = selview->items; item; item = item->next) 630 ui_putitem(item, lens, ++id); 631 ui_set("pos", "0"); 632 } 633 634 void 635 ui_sql_edit_exec(char *sql) { 636 struct stat sb, sa; 637 int fd; 638 char tmpf[] = "/tmp/myadm.XXXXXX"; 639 640 fd = mkstemp(tmpf); 641 if(fd == -1) { 642 ui_set("status", "Cannot make a temporary file."); 643 return; 644 } 645 if(write(fd, sql, strlen(sql)) == -1) { 646 close(fd); 647 unlink(tmpf); 648 ui_set("status", "Cannot write into the temporary file."); 649 return; 650 } 651 close(fd); 652 stat(tmpf, &sb); 653 while(1) { 654 editfile(tmpf); 655 stat(tmpf, &sa); 656 if(!sa.st_size || sb.st_mtime == sa.st_mtime) { 657 ui_set("status", "No changes."); 658 break; 659 } 660 if(mysql_file_exec(tmpf) < 0) { 661 if(*mysql_error(mysql)) { 662 if(ui_ask("An error occurred. Continue editing ([y]/n)?", "yn") == 'y') 663 continue; 664 } 665 else 666 ui_set("status", "Something went wrong."); 667 break; 668 } 669 reload(NULL); 670 ui_set("status", "Updated."); 671 break; 672 } 673 unlink(tmpf); 674 } 675 676 void 677 quit(const Arg *arg) { 678 if(arg->i && ui_ask("Do you want to quit ([y]/n)?", "yn") != 'y') 679 return; 680 running = 0; 681 } 682 683 void 684 reload(const Arg *arg) { 685 if(!selview->show) 686 return; 687 selview->show(); 688 if(selview->cur) 689 ui_set("pos", "%d", selview->cur); 690 } 691 692 void 693 run(void) { 694 int code, i; 695 696 while(running) { 697 ui_refresh(); 698 code = getch(); 699 if(code < 0) 700 continue; 701 for(i = 0; i < LENGTH(keys); ++i) { 702 if(ISCURVIEW(keys[i].view) && keys[i].code == code) { 703 ui_set("status", ""); 704 keys[i].func(&keys[i].arg); 705 break; 706 } 707 } 708 } 709 } 710 711 void 712 setview(const char *name, void (*show)(void)) { 713 View *v; 714 715 v = ecalloc(1, sizeof(View)); 716 v->choice = getitem(0); 717 strncpy(v->name, name, sizeof v->name); 718 v->show = show; 719 attach(v); 720 selview = v; 721 show(); 722 } 723 724 void 725 setup(void) { 726 setlocale(LC_CTYPE, ""); 727 mysql = mysql_init(NULL); 728 if(mysql_real_connect(mysql, dbhost, dbuser, dbpass, NULL, 0, NULL, 0) == NULL) 729 die("Cannot connect to the database.\n"); 730 fldseplen = strlen(FLDSEP); 731 ui_init(); 732 } 733 734 void 735 startup(void) { 736 for(unsigned int i = 0; i < LENGTH(actions); i++) 737 actions[i].cmd(); 738 } 739 740 void 741 ui_end(void) { 742 stfl_reset(); 743 stfl_ipool_destroy(ipool); 744 } 745 746 struct stfl_form * 747 ui_getform(wchar_t *code) { 748 return stfl_create(code); 749 } 750 751 void 752 ui_init(void) { 753 struct stfl_form *f = ui_getform(L"label"); 754 755 stfl_run(f, -3); /* init ncurses */ 756 stfl_free(f); 757 nocbreak(); 758 raw(); 759 curs_set(0); 760 ipool = stfl_ipool_create(nl_langinfo(CODESET)); 761 } 762 763 void 764 ui_modify(const char *name, const char *mode, const char *fmtstr, ...) { 765 va_list ap; 766 char txt[1024]; 767 768 if(!selview->form) 769 return; 770 va_start(ap, fmtstr); 771 vsnprintf(txt, sizeof txt, fmtstr, ap); 772 va_end(ap); 773 stfl_modify(selview->form, 774 stfl_ipool_towc(ipool, name), 775 stfl_ipool_towc(ipool, mode), 776 stfl_ipool_towc(ipool, txt)); 777 } 778 779 void 780 ui_putitem(Item *item, int *lens, int id) { 781 char line[COLS + 1]; 782 int pad, li = 0, i, j; 783 784 if(!(item && lens)) 785 return; 786 line[0] = '\0'; 787 for(i = 0; i < item->ncols && li < COLS; ++i) { 788 if(i) 789 for(j = 0; j < fldseplen && li < COLS; ++j) 790 line[li++] = FLDSEP[j]; 791 pad = li; 792 for(j = 0; j < item->lens[i] && j < lens[i] && li < COLS; ++j) 793 line[li++] = (isprint(item->cols[i][j]) 794 ? item->cols[i][j] 795 : ' '); 796 pad = li - pad; 797 while(pad++ < lens[i] && li < COLS) 798 line[li++] = ' '; 799 } 800 line[li] = '\0'; 801 ui_modify("items", "append", "listitem[%d] text:%s", id, QUOTE(line)); 802 } 803 804 void 805 ui_refresh(void) { 806 if(selview && selview->form) 807 stfl_run(selview->form, -1); 808 } 809 810 void 811 ui_set(const char *key, const char *fmtstr, ...) { 812 va_list ap; 813 char val[256]; 814 815 if(!selview) 816 return; 817 818 va_start(ap, fmtstr); 819 vsnprintf(val, sizeof val, fmtstr, ap); 820 va_end(ap); 821 stfl_set(selview->form, stfl_ipool_towc(ipool, key), stfl_ipool_towc(ipool, val)); 822 } 823 824 void 825 usage(void) { 826 die("Usage: %s [-vhup <arg>]\n", argv0); 827 } 828 829 void 830 viewdb(const Arg *arg) { 831 Arg a = {.i = 0}; 832 Item *choice = getitem(0); 833 834 if(!choice) { 835 ui_set("status", "No database selected."); 836 return; 837 } 838 mysql_select_db(mysql, choice->cols[0]); 839 setview("tables", viewdb_show); 840 itempos(&a); 841 } 842 843 void 844 viewdb_show(void) { 845 MYSQL_RES *res; 846 847 if(mysql_exec("show tables") == -1 || !(res = mysql_store_result(mysql))) 848 die("show tables"); 849 mysql_fillview(res, 0); 850 mysql_free_result(res); 851 ui_listview(selview->items, NULL); 852 ui_set("title", "Tables in `%s`@%s", selview->choice->cols[0], dbhost); 853 } 854 855 void 856 viewdblist(void) { 857 Arg a = {.i = 0}; 858 859 setview("databases", viewdblist_show); 860 itempos(&a); 861 } 862 863 void 864 viewdblist_show(void) { 865 MYSQL_RES *res; 866 867 if(mysql_exec("show databases") == -1 || !(res = mysql_store_result(mysql))) 868 die("show databases"); 869 mysql_fillview(res, 0); 870 mysql_free_result(res); 871 ui_listview(selview->items, NULL); 872 ui_set("title", "Databases in `%s`", dbhost); 873 } 874 875 void 876 viewprev(const Arg *arg) { 877 View *v; 878 879 if(!(selview && selview->next)) 880 return; 881 v = selview->next; 882 cleanupview(selview); 883 selview = v; 884 } 885 886 void 887 viewtable(const Arg *arg) { 888 Arg a = {.i = 0}; 889 890 if(!getitem(0)) { 891 ui_set("status", "No table selected."); 892 return; 893 } 894 setview("records", viewtable_show); 895 itempos(&a); 896 } 897 898 void 899 viewtable_show(void) { 900 MYSQL_RES *res; 901 int r; 902 903 r = mysql_exec("select * from `%s`", selview->choice->cols[0]); 904 if(r == -1 || !(res = mysql_store_result(mysql))) 905 die("select from `%s`", selview->choice->cols[0]); 906 mysql_fillview(res, 1); 907 mysql_free_result(res); 908 ui_listview(selview->items, selview->fields); 909 ui_set("title", "Records in `%s`.`%s`@%s", 910 selview->next->choice->cols[0], selview->choice->cols[0], dbhost); 911 } 912 913 int 914 main(int argc, char **argv) { 915 ARGBEGIN { 916 case 'h': 917 dbhost = EARGF(usage()); 918 break; 919 case 'u': 920 dbuser = EARGF(usage()); 921 break; 922 case 'p': 923 dbpass = EARGF(usage()); 924 break; 925 case 'v': 926 die("%s-"VERSION, argv0); 927 default: 928 usage(); 929 } ARGEND; 930 setup(); 931 startup(); 932 run(); 933 cleanup(); 934 return 0; 935 }