This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
magicka/src/bbs_list.c
Dan Cross 540e359080 Cleanups and pointer vectors.
A repeated pattern in Magicka is to append to dynamically
sized arrays via malloc()/realloc().  Introduce the notion
of a "pointer vector": that is, a growable vector of
pointers, that can be reused to implement that logic more
safely and efficiently (this implementation uses power-of-two
growing).

Many malloc()/realloc() calls were not checked; these
assert() that the return value from realloc() is not NULL.

Add a method to consume the pointer vector: that is, realloc()
it to the current length and return the underlying pointers.

Make the `fmt` argument to dolog() const.
Include <sys/wait.h> in bluewave.c to squash a warning.

Signed-off-by: Dan Cross <patchdev@fat-dragon.org>
2018-10-10 10:25:42 +10:00

383 lines
9.2 KiB
C

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <ctype.h>
#include "bbs.h"
extern struct bbs_config conf;
extern struct user_record *gUser;
struct bbs_list_entry_t {
int id;
char *bbsname;
char *sysopname;
char *telnet;
int owner;
};
int add_bbs(struct bbs_list_entry_t *new_entry) {
char *create_sql = "CREATE TABLE IF NOT EXISTS bbslist ("
"id INTEGER PRIMARY KEY,"
"bbsname TEXT,"
"sysop TEXT,"
"telnet TEXT,"
"owner INTEGER);";
char *insert_sql = "INSERT INTO bbslist (bbsname, sysop, telnet, owner) VALUES(?,?, ?, ?)";
char bbsname[19];
char sysop[17];
char telnet[39];
char buffer[PATH_MAX];
char c;
char *err_msg = 0;
sqlite3 *db;
sqlite3_stmt *res;
int rc;
int id;
s_printf("\e[2J\e[1;1H");
s_printf(get_string(28));
s_readstring(bbsname, 18);
s_printf(get_string(29));
s_readstring(sysop, 16);
s_printf(get_string(30));
s_readstring(telnet, 38);
s_printf(get_string(31));
s_printf(get_string(32));
s_printf(get_string(33), bbsname);
s_printf(get_string(34), sysop);
s_printf(get_string(35), telnet);
s_printf(get_string(36));
s_printf(get_string(37));
c = s_getc();
if (tolower(c) == 'y') {
snprintf(buffer, PATH_MAX, "%s/bbslist.sq3", conf.bbs_path);
rc = sqlite3_open(buffer, &db);
if (rc != SQLITE_OK) {
dolog("Cannot open database: %s", sqlite3_errmsg(db));
return 0;
}
sqlite3_busy_timeout(db, 5000);
rc = sqlite3_exec(db, create_sql, 0, 0, &err_msg);
if (rc != SQLITE_OK) {
dolog("SQL error: %s", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 0;
}
rc = sqlite3_prepare_v2(db, insert_sql, -1, &res, 0);
if (rc == SQLITE_OK) {
sqlite3_bind_text(res, 1, bbsname, -1, 0);
sqlite3_bind_text(res, 2, sysop, -1, 0);
sqlite3_bind_text(res, 3, telnet, -1, 0);
sqlite3_bind_int(res, 4, gUser->id);
} else {
dolog("Failed to execute statement: %s", sqlite3_errmsg(db));
sqlite3_close(db);
return 0;
}
rc = sqlite3_step(res);
if (rc != SQLITE_DONE) {
dolog("execution failed: %s", sqlite3_errmsg(db));
sqlite3_finalize(res);
sqlite3_close(db);
return 0;
}
id = sqlite3_last_insert_rowid(db);
sqlite3_finalize(res);
sqlite3_close(db);
s_printf(get_string(38));
if (new_entry != NULL) {
new_entry->id = id;
new_entry->bbsname = strdup(bbsname);
new_entry->sysopname = strdup(sysop);
new_entry->telnet = strdup(telnet);
new_entry->owner = gUser->id;
}
return 1;
} else {
s_printf(get_string(39));
return 0;
}
}
int delete_bbs(int id) {
char buffer[PATH_MAX];
sqlite3 *db;
sqlite3_stmt *res;
int rc;
char *sql = "SELECT bbsname FROM bbslist WHERE id=? and owner=?";
char *dsql = "DELETE FROM bbslist WHERE id=?";
char c;
s_printf("\e[2J\e[1;1H");
snprintf(buffer, PATH_MAX, "%s/bbslist.sq3", conf.bbs_path);
rc = sqlite3_open(buffer, &db);
if (rc != SQLITE_OK) {
return 0;
}
sqlite3_busy_timeout(db, 5000);
rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
if (rc == SQLITE_OK) {
sqlite3_bind_int(res, 1, id);
sqlite3_bind_int(res, 2, gUser->id);
} else {
sqlite3_close(db);
s_printf(get_string(41));
return 0;
}
if (sqlite3_step(res) == SQLITE_ROW) {
s_printf(get_string(42), sqlite3_column_text(res, 0));
sqlite3_finalize(res);
c = s_getc();
if (tolower(c) == 'y') {
rc = sqlite3_prepare_v2(db, dsql, -1, &res, 0);
if (rc == SQLITE_OK) {
sqlite3_bind_int(res, 1, id);
} else {
sqlite3_close(db);
s_printf(get_string(41));
return 0;
}
sqlite3_step(res);
s_printf(get_string(43));
sqlite3_finalize(res);
sqlite3_close(db);
return 1;
} else {
s_printf(get_string(39));
sqlite3_close(db);
return 0;
}
} else {
sqlite3_finalize(res);
s_printf(get_string(44));
sqlite3_close(db);
return 0;
}
}
void bbs_list() {
int i;
int redraw = 1;
int start = 0;
int selected = 0;
char c;
char buffer[PATH_MAX];
sqlite3 *db;
sqlite3_stmt *res;
int rc;
char *sql = "SELECT id,bbsname,sysop,telnet FROM bbslist";
struct ptr_vector entries;
int entrycount;
struct bbs_list_entry_t *newentry;
init_ptr_vector(&entries);
while (1) {
entrycount = 0;
snprintf(buffer, PATH_MAX, "%s/bbslist.sq3", conf.bbs_path);
rc = sqlite3_open(buffer, &db);
if (rc != SQLITE_OK) {
dolog("Cannot open database: %s", sqlite3_errmsg(db));
return;
}
sqlite3_busy_timeout(db, 5000);
rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
if (rc != SQLITE_OK) {
sqlite3_close(db);
} else {
while (sqlite3_step(res) == SQLITE_ROW) {
struct bbs_list_entry_t *entry = malloz(sizeof(struct bbs_list_entry_t));
entry->id = sqlite3_column_int(res, 0);
entry->bbsname = strdup(sqlite3_column_text(res, 1));
entry->sysopname = strdup(sqlite3_column_text(res, 2));
entry->telnet = strdup(sqlite3_column_text(res, 3));
ptr_vector_append(&entries, entry);
}
sqlite3_finalize(res);
sqlite3_close(db);
}
entrycount = ptr_vector_len(&entries);
if (entrycount > 0) {
while (1) {
if (redraw) {
s_printf("\e[2J\e[1;1H");
s_printf(get_string(270));
s_printf(get_string(271));
for (i = start; i < start + 22 && i < entrycount; i++) {
struct bbs_list_entry_t *entry = ptr_vector_get(&entries, i);
int strn = (i == selected) ? 269 : 268;
s_printf(get_string(strn), i - start + 2, i, entry->bbsname, entry->sysopname, entry->telnet);
}
s_printf("\e[%d;5H", selected - start + 2);
redraw = 0;
}
c = s_getchar();
if (tolower(c) == 'q') {
for (i = 0; i < entrycount; i++) {
struct bbs_list_entry_t *entry = ptr_vector_get(&entries, i);
free(entry->bbsname);
free(entry->sysopname);
free(entry->telnet);
free(entry);
}
destroy_ptr_vector(&entries);
return;
} else if (tolower(c) == 'a') {
newentry = (struct bbs_list_entry_t *)malloz(sizeof(struct bbs_list_entry_t));
if (add_bbs(newentry)) {
ptr_vector_append(&entries, newentry);
entrycount++;
} else {
free(newentry);
}
redraw = 1;
} else if (tolower(c) == 'd') {
struct bbs_list_entry_t *entry = ptr_vector_del(&entries, selected);
if (delete_bbs(entry->id)) {
free(entry->bbsname);
free(entry->sysopname);
free(entry->telnet);
free(entry);
entrycount--;
if (entrycount == 0) {
return;
}
if (selected >= entrycount) {
selected = entrycount - 1;
}
}
redraw = 1;
} else if (c == 27) {
c = s_getchar();
if (c == 91) {
c = s_getchar();
if (c == 66) {
// down
if (selected + 1 >= start + 22) {
start += 22;
if (start >= entrycount) {
start = entrycount - 22;
}
redraw = 1;
}
selected++;
if (selected >= entrycount) {
selected = entrycount - 1;
} else {
if (!redraw) {
struct bbs_list_entry_t *before = ptr_vector_get(&entries, selected - 1);
struct bbs_list_entry_t *entry = ptr_vector_get(&entries, selected);
s_printf(get_string(268), selected - start + 1, selected - 1, before->bbsname, before->sysopname, before->telnet);
s_printf(get_string(269), selected - start + 2, selected, entry->bbsname, entry->sysopname, entry->telnet);
s_printf("\e[%d;4H", selected - start + 2);
}
}
} else if (c == 65) {
// up
if (selected - 1 < start) {
start -= 22;
if (start < 0) {
start = 0;
}
redraw = 1;
}
selected--;
if (selected < 0) {
selected = 0;
} else {
if (!redraw) {
struct bbs_list_entry_t *entry = ptr_vector_get(&entries, selected);
struct bbs_list_entry_t *after = ptr_vector_get(&entries, selected + 1);
s_printf(get_string(269), selected - start + 2, selected, entry->bbsname, entry->sysopname, entry->telnet);
s_printf(get_string(268), selected - start + 3, selected + 1, after->bbsname, after->sysopname, after->telnet);
s_printf("\e[%d;4H", selected - start + 2);
}
}
} else if (c == 75) {
// END KEY
selected = entrycount - 1;
start = entrycount - 22;
if (start < 0) {
start = 0;
}
redraw = 1;
} else if (c == 72) {
// HOME KEY
selected = 0;
start = 0;
redraw = 1;
} else if (c == 86 || c == '5') {
if (c == '5') {
s_getchar();
}
// PAGE UP
selected = selected - 22;
if (selected < 0) {
selected = 0;
}
start = selected;
redraw = 1;
} else if (c == 85 || c == '6') {
if (c == '6') {
s_getchar();
}
// PAGE DOWN
selected = selected + 22;
if (selected >= entrycount) {
selected = entrycount - 1;
}
start = selected;
redraw = 1;
}
}
}
}
} else {
// no entries
s_printf("\e[2J\e[1;1H");
s_printf(get_string(270));
s_printf(get_string(271));
s_printf(get_string(272));
s_printf(get_string(273));
while (1) {
c = s_getchar();
if (tolower(c) == 'a') {
add_bbs(NULL);
break;
} else if (tolower(c) == 'q') {
return;
}
}
}
}
}