540e359080
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>
383 lines
9.2 KiB
C
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|