massive improvements all abound
This commit is contained in:
parent
9b4826f9cb
commit
b307906cf4
@ -57,4 +57,10 @@ bool dc_api_get_userinfo(dc_api_t api, dc_account_t login,
|
||||
bool dc_api_get_userguilds(dc_api_t api, dc_account_t login,
|
||||
GPtrArray **guilds);
|
||||
|
||||
/**
|
||||
* Fetch a list of friends of the login account "login". Returns a GPtrArray
|
||||
* of dc_account_t which automatically cleans itself up.
|
||||
*/
|
||||
bool dc_api_get_friends(dc_api_t api, dc_account_t login, GPtrArray **friends);
|
||||
|
||||
#endif
|
||||
|
@ -360,13 +360,40 @@ cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool dc_api_get_friends(dc_api_t api, dc_account_t login, GPtrArray **friends)
|
||||
{
|
||||
char *url = NULL;
|
||||
json_t *reply = NULL;
|
||||
bool ret = false;
|
||||
|
||||
return_if_true(api == NULL, false);
|
||||
return_if_true(login == NULL, false);
|
||||
|
||||
asprintf(&url, "users/%s/guilds", dc_account_id(login));
|
||||
|
||||
reply = dc_api_call_sync(api, "GET", dc_account_token(login), url, NULL);
|
||||
goto_if_true(reply == NULL, cleanup);
|
||||
|
||||
dc_util_dump_json(reply);
|
||||
ret = true;
|
||||
|
||||
cleanup:
|
||||
|
||||
json_decref(reply);
|
||||
reply = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool dc_api_get_userguilds(dc_api_t api, dc_account_t login, GPtrArray **out)
|
||||
{
|
||||
char *url = NULL;
|
||||
json_t *reply = NULL, *c = NULL, *val = NULL;
|
||||
size_t i = 0;
|
||||
bool ret = false;
|
||||
GPtrArray *guilds = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref);
|
||||
GPtrArray *guilds = g_ptr_array_new_with_free_func(
|
||||
(GDestroyNotify)dc_unref
|
||||
);
|
||||
|
||||
return_if_true(api == NULL, false);
|
||||
return_if_true(login == NULL, false);
|
||||
|
@ -2,20 +2,25 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
|
||||
|
||||
PKG_CHECK_MODULES(NCURSES REQUIRED ncursesw)
|
||||
PKG_CHECK_MODULES(PANEL REQUIRED panel)
|
||||
PKG_CHECK_MODULES(EDIT REQUIRED libedit)
|
||||
PKG_CHECK_MODULES(CONFUSE REQUIRED libconfuse)
|
||||
|
||||
SET(TARGET "ncdc")
|
||||
|
||||
SET(SOURCES
|
||||
"include/ncdc/cmds.h"
|
||||
"include/ncdc/config.h"
|
||||
"include/ncdc/input.h"
|
||||
"include/ncdc/mainwindow.h"
|
||||
"include/ncdc/ncdc.h"
|
||||
"include/ncdc/textview.h"
|
||||
"src/cmds.c"
|
||||
"src/config.c"
|
||||
"src/emacs.c"
|
||||
"src/input.c"
|
||||
"src/login.c"
|
||||
"src/mainwindow.c"
|
||||
"src/ncdc.c"
|
||||
"src/textview.c"
|
||||
"src/util.c"
|
||||
)
|
||||
|
||||
@ -28,7 +33,7 @@ INCLUDE_DIRECTORIES(
|
||||
${GLIB2_INCLUDE_DIRS}
|
||||
${NCURSES_INCLUDE_DIRS}
|
||||
${PANEL_INCLUDE_DIRS}
|
||||
${EDIT_INCLUDE_DIRS}
|
||||
${CONFUSE_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(${TARGET} ${SOURCES})
|
||||
@ -37,5 +42,5 @@ TARGET_LINK_LIBRARIES(${TARGET}
|
||||
${GLIB2_LIBRARIES}
|
||||
${NCURSES_LIBRARIES}
|
||||
${PANEL_LIBRARIES}
|
||||
${EDIT_LIBRARIES}
|
||||
${CONFUSE_LIBRARIES}
|
||||
)
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <ncdc/mainwindow.h>
|
||||
|
||||
typedef bool (*ncdc_command_t)(ncdc_mainwindow_t n,
|
||||
wchar_t const *s, size_t len);
|
||||
size_t argc, wchar_t **argv);
|
||||
|
||||
typedef struct {
|
||||
wchar_t const *name;
|
||||
@ -14,6 +14,9 @@ typedef struct {
|
||||
|
||||
extern ncdc_commands_t cmds[];
|
||||
|
||||
bool ncdc_cmd_quit(ncdc_mainwindow_t n, wchar_t const *s, size_t len);
|
||||
bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s);
|
||||
|
||||
bool ncdc_cmd_quit(ncdc_mainwindow_t n, size_t ac, wchar_t **av);
|
||||
bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av);
|
||||
|
||||
#endif
|
||||
|
13
ncdc/include/ncdc/config.h
Normal file
13
ncdc/include/ncdc/config.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef NCDC_CONFIG_H
|
||||
#define NCDC_CONFIG_H
|
||||
|
||||
#include <ncdc/ncdc.h>
|
||||
|
||||
struct ncdc_config_;
|
||||
typedef struct ncdc_config_ *ncdc_config_t;
|
||||
|
||||
ncdc_config_t ncdc_config_new(void);
|
||||
|
||||
dc_account_t ncdc_config_account(ncdc_config_t c, char const *name);
|
||||
|
||||
#endif
|
@ -2,12 +2,18 @@
|
||||
#define NCDC_MAINWINDOW_H
|
||||
|
||||
#include <ncdc/ncdc.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct ncdc_mainwindow_;
|
||||
typedef struct ncdc_mainwindow_ *ncdc_mainwindow_t;
|
||||
|
||||
ncdc_mainwindow_t ncdc_mainwindow_new(void);
|
||||
|
||||
/* holy shit stains I am lazy
|
||||
*/
|
||||
#define LOG(n, ...) ncdc_mainwindow_log(n, __VA_ARGS__)
|
||||
void ncdc_mainwindow_log(ncdc_mainwindow_t w, wchar_t const *fmt, ...);
|
||||
|
||||
void ncdc_mainwindow_refresh(ncdc_mainwindow_t n);
|
||||
void ncdc_mainwindow_input_ready(ncdc_mainwindow_t n);
|
||||
|
||||
|
@ -30,15 +30,32 @@
|
||||
#include <dc/refable.h>
|
||||
#include <dc/api.h>
|
||||
#include <dc/loop.h>
|
||||
#include <dc/account.h>
|
||||
|
||||
#define return_if_true(v,r) do { if (v) return r; } while(0)
|
||||
#define goto_if_true(v,l) do { if (v) goto l; } while(0)
|
||||
|
||||
struct ncdc_account_ {
|
||||
dc_account_t account;
|
||||
GPtrArray *friends;
|
||||
GPtrArray *guilds;
|
||||
};
|
||||
|
||||
typedef struct ncdc_account_ *ncdc_account_t;
|
||||
|
||||
extern GHashTable *accounts;
|
||||
extern char *ncdc_private_dir;
|
||||
extern void *config;
|
||||
|
||||
int strwidth(char const *string);
|
||||
char *read_char(FILE *stream);
|
||||
|
||||
char *w_convert(wchar_t const *w);
|
||||
wchar_t* wcsndup(const wchar_t* string, size_t maxlen);
|
||||
size_t w_strlenv(wchar_t **s);
|
||||
void w_strfreev(wchar_t **s);
|
||||
wchar_t **w_tokenise(wchar_t const *w);
|
||||
wchar_t *w_next_tok(wchar_t const *w);
|
||||
wchar_t const *w_next_word(wchar_t const *w, ssize_t len);
|
||||
|
||||
#endif
|
||||
|
15
ncdc/include/ncdc/textview.h
Normal file
15
ncdc/include/ncdc/textview.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef NCDC_TEXTVIEW_H
|
||||
#define NCDC_TEXTVIEW_H
|
||||
|
||||
#include <ncdc/ncdc.h>
|
||||
|
||||
struct ncdc_textview_;
|
||||
typedef struct ncdc_textview_ *ncdc_textview_t;
|
||||
|
||||
ncdc_textview_t ncdc_textview_new(void);
|
||||
|
||||
void ncdc_textview_append(ncdc_textview_t v, wchar_t const *w);
|
||||
wchar_t const *ncdc_textview_nthline(ncdc_textview_t v, size_t i);
|
||||
void ncdc_textview_render(ncdc_textview_t v, WINDOW *win, int lines, int cols);
|
||||
|
||||
#endif
|
@ -1,11 +1,36 @@
|
||||
#include <ncdc/cmds.h>
|
||||
|
||||
ncdc_commands_t cmds[] = {
|
||||
{ L"login", ncdc_cmd_login },
|
||||
{ L"quit", ncdc_cmd_quit },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
bool ncdc_cmd_quit(ncdc_mainwindow_t n, wchar_t const *s, size_t len)
|
||||
bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s)
|
||||
{
|
||||
wchar_t **tokens = NULL;
|
||||
size_t size = 0;
|
||||
size_t i = 0;
|
||||
bool ret = false;
|
||||
|
||||
tokens = w_tokenise(s);
|
||||
return_if_true(tokens == NULL, false);
|
||||
|
||||
size = w_strlenv(tokens);
|
||||
|
||||
for (i = 0; cmds[i].name != NULL; i++) {
|
||||
if (wcscmp(cmds[i].name, tokens[0]) == 0) {
|
||||
ret = cmds[i].handler(n, size, tokens);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
w_strfreev(tokens);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ncdc_cmd_quit(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
||||
{
|
||||
exit(0);
|
||||
}
|
||||
|
82
ncdc/src/config.c
Normal file
82
ncdc/src/config.c
Normal file
@ -0,0 +1,82 @@
|
||||
#include <ncdc/config.h>
|
||||
#include <confuse.h>
|
||||
|
||||
static cfg_opt_t account_opts[] = {
|
||||
CFG_STR("email", NULL, CFGF_NONE),
|
||||
CFG_STR("password", NULL, CFGF_NONE),
|
||||
CFG_END()
|
||||
};
|
||||
|
||||
static cfg_opt_t opts[] = {
|
||||
CFG_SEC("account", account_opts, CFGF_TITLE|CFGF_MULTI),
|
||||
CFG_END()
|
||||
};
|
||||
|
||||
struct ncdc_config_
|
||||
{
|
||||
dc_refable_t ref;
|
||||
|
||||
char *configpath;
|
||||
cfg_t *cfg;
|
||||
};
|
||||
|
||||
static void ncdc_config_free(ncdc_config_t c)
|
||||
{
|
||||
return_if_true(c == NULL,);
|
||||
|
||||
cfg_free(c->cfg);
|
||||
free(c->configpath);
|
||||
free(c);
|
||||
}
|
||||
|
||||
ncdc_config_t ncdc_config_new(void)
|
||||
{
|
||||
ncdc_config_t c = calloc(1, sizeof(struct ncdc_config_));
|
||||
return_if_true(c == NULL, NULL);
|
||||
|
||||
c->ref.cleanup = (dc_cleanup_t)ncdc_config_free;
|
||||
|
||||
c->cfg = cfg_init(opts, CFGF_NONE);
|
||||
if (c->cfg == NULL) {
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
asprintf(&c->configpath, "%s/config", ncdc_private_dir);
|
||||
if (cfg_parse(c->cfg, c->configpath) == CFG_PARSE_ERROR) {
|
||||
free(c->configpath);
|
||||
cfg_free(c->cfg);
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
dc_account_t ncdc_config_account(ncdc_config_t c, char const *name)
|
||||
{
|
||||
cfg_t *cfg = NULL;
|
||||
dc_account_t acc = NULL;
|
||||
size_t i = 0;
|
||||
|
||||
return_if_true(c == NULL || name == NULL, NULL);
|
||||
|
||||
for (i = 0; i < cfg_size(c->cfg, "account"); i++) {
|
||||
cfg = cfg_getnsec(c->cfg, "account", i);
|
||||
if (strcmp(cfg_title(cfg), name) == 0) {
|
||||
/* entry has been found
|
||||
*/
|
||||
char const *email = cfg_getstr(cfg, "email");
|
||||
char const *password = cfg_getstr(cfg, "password");
|
||||
|
||||
if (email == NULL || password == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
acc = dc_account_new2(email, password);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return acc;
|
||||
}
|
41
ncdc/src/login.c
Normal file
41
ncdc/src/login.c
Normal file
@ -0,0 +1,41 @@
|
||||
#include <ncdc/cmds.h>
|
||||
#include <ncdc/ncdc.h>
|
||||
#include <ncdc/config.h>
|
||||
|
||||
bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
||||
{
|
||||
char *arg = NULL;
|
||||
bool ret = false;
|
||||
ncdc_account_t ptr = NULL;
|
||||
dc_account_t acc = NULL;
|
||||
|
||||
goto_if_true(ac <= 1, cleanup);
|
||||
|
||||
arg = w_convert(av[1]);
|
||||
goto_if_true(arg == NULL, cleanup);
|
||||
|
||||
if (g_hash_table_lookup(accounts, arg) != NULL) {
|
||||
LOG(n, L"login: %ls: this account is already logged in", av[1]);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
acc = ncdc_config_account(config, arg);
|
||||
if (acc == NULL) {
|
||||
LOG(n, L"login: %ls: no such account in configuration", av[1]);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ptr = calloc(1, sizeof(struct ncdc_account_));
|
||||
goto_if_true(ptr == NULL, cleanup);
|
||||
|
||||
ptr->account = acc;
|
||||
g_hash_table_insert(accounts, arg, ptr);
|
||||
|
||||
ret = true;
|
||||
|
||||
cleanup:
|
||||
|
||||
free(arg);
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#include <ncdc/mainwindow.h>
|
||||
#include <ncdc/input.h>
|
||||
#include <ncdc/textview.h>
|
||||
#include <ncdc/cmds.h>
|
||||
#include <ncdc/ncdc.h>
|
||||
|
||||
@ -36,6 +37,7 @@ struct ncdc_mainwindow_
|
||||
WINDOW *sep2;
|
||||
|
||||
ncdc_input_t in;
|
||||
ncdc_textview_t log;
|
||||
|
||||
int focus;
|
||||
};
|
||||
@ -57,6 +59,7 @@ static void ncdc_mainwindow_free(ncdc_mainwindow_t n)
|
||||
delwin(n->sep2);
|
||||
|
||||
dc_unref(n->in);
|
||||
dc_unref(n->log);
|
||||
|
||||
free(n);
|
||||
}
|
||||
@ -71,6 +74,8 @@ ncdc_mainwindow_t ncdc_mainwindow_new(void)
|
||||
ptr->in = ncdc_input_new();
|
||||
ncdc_input_set_callback(ptr->in, ncdc_mainwindow_callback, ptr);
|
||||
|
||||
ptr->log = ncdc_textview_new();
|
||||
|
||||
ptr->guilds = newwin(5, 5, 1, 1);
|
||||
ptr->chat = newwin(5, 5, 4, 4);
|
||||
|
||||
@ -95,15 +100,11 @@ ncdc_mainwindow_callback(ncdc_input_t i, wchar_t const *s,
|
||||
ncdc_mainwindow_t mainwin = (ncdc_mainwindow_t)arg;
|
||||
|
||||
if (s[0] == '/') {
|
||||
size_t i = 0;
|
||||
wchar_t const *n = w_next_word(s, len);
|
||||
if (s[1] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (; cmds[i].name != NULL; i++) {
|
||||
if (wcsncmp(s+1, cmds[i].name, (n-s-1)) == 0) {
|
||||
cmds[i].handler(mainwin, s, len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return ncdc_dispatch(mainwin, s+1);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -135,9 +136,9 @@ static void ncdc_mainwindow_resize(ncdc_mainwindow_t n)
|
||||
wnoutrefresh(n->sep1);
|
||||
|
||||
n->chat_h = LINES - n->input_h - 1;
|
||||
n->chat_w = COLS - n->guilds_w - 1;
|
||||
n->chat_w = COLS - n->guilds_w - 2;
|
||||
n->chat_y = 0;
|
||||
n->chat_x = n->guilds_w + 1;
|
||||
n->chat_x = n->guilds_w + 2;
|
||||
|
||||
wresize(n->chat, n->chat_h, n->chat_w);
|
||||
mvwin(n->chat, n->chat_y, n->chat_x);
|
||||
@ -194,6 +195,8 @@ void ncdc_mainwindow_input_ready(ncdc_mainwindow_t n)
|
||||
void ncdc_mainwindow_refresh(ncdc_mainwindow_t n)
|
||||
{
|
||||
wnoutrefresh(n->guilds);
|
||||
|
||||
ncdc_textview_render(n->log, n->chat, n->chat_h, n->chat_w);
|
||||
wnoutrefresh(n->chat);
|
||||
|
||||
ncdc_input_draw(n->in, n->input);
|
||||
@ -205,6 +208,18 @@ void ncdc_mainwindow_refresh(ncdc_mainwindow_t n)
|
||||
wnoutrefresh(n->sep2);
|
||||
|
||||
ncdc_mainwindow_update_focus(n);
|
||||
|
||||
doupdate();
|
||||
}
|
||||
|
||||
void ncdc_mainwindow_log(ncdc_mainwindow_t w, wchar_t const *fmt, ...)
|
||||
{
|
||||
va_list lst;
|
||||
wchar_t buf[256] = {0};
|
||||
|
||||
return_if_true(w == NULL || fmt == NULL,);
|
||||
|
||||
va_start(lst, fmt);
|
||||
vswprintf(buf, 255, fmt, lst);
|
||||
va_end(lst);
|
||||
|
||||
ncdc_textview_append(w->log, buf);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <ncdc/ncdc.h>
|
||||
#include <ncdc/mainwindow.h>
|
||||
#include <ncdc/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
@ -16,14 +17,34 @@ ncdc_mainwindow_t mainwin = NULL;
|
||||
*/
|
||||
static bool done = false;
|
||||
|
||||
char *dc_private_dir = NULL;
|
||||
char *dc_config_file = NULL;
|
||||
/* all the accounts we have logged into
|
||||
*/
|
||||
GHashTable *accounts = NULL;
|
||||
|
||||
static GKeyFile *config = NULL;
|
||||
char *ncdc_private_dir = NULL;
|
||||
void *config = NULL;
|
||||
|
||||
dc_loop_t loop = NULL;
|
||||
dc_api_t api = NULL;
|
||||
|
||||
static void ncdc_account_free(void *ptr)
|
||||
{
|
||||
ncdc_account_t a = (ncdc_account_t)ptr;
|
||||
|
||||
return_if_true(ptr == NULL,);
|
||||
|
||||
if (a->friends != NULL) {
|
||||
g_ptr_array_unref(a->friends);
|
||||
}
|
||||
|
||||
if (a->guilds != NULL) {
|
||||
g_ptr_array_unref(a->guilds);
|
||||
}
|
||||
|
||||
dc_unref(a->account);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void sighandler(int sig)
|
||||
{
|
||||
endwin();
|
||||
@ -42,8 +63,16 @@ static void cleanup(void)
|
||||
|
||||
done = true;
|
||||
|
||||
if (accounts != NULL) {
|
||||
g_hash_table_unref(accounts);
|
||||
accounts = NULL;
|
||||
}
|
||||
|
||||
dc_unref(api);
|
||||
dc_unref(loop);
|
||||
|
||||
dc_unref(config);
|
||||
dc_unref(mainwin);
|
||||
}
|
||||
|
||||
static void stdin_handler(int sock, short what, void *data)
|
||||
@ -78,31 +107,17 @@ static bool init_everything(void)
|
||||
|
||||
dc_loop_add_api(loop, api);
|
||||
|
||||
config = g_key_file_new();
|
||||
config = ncdc_config_new();
|
||||
return_if_true(config == NULL, false);
|
||||
|
||||
g_key_file_load_from_file(config, dc_config_file, 0, NULL);
|
||||
accounts = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
g_free, ncdc_account_free
|
||||
);
|
||||
return_if_true(accounts == NULL, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
dc_account_t account_from_config(void)
|
||||
{
|
||||
char const *email = NULL;
|
||||
char const *password = NULL;
|
||||
void *ptr = NULL;
|
||||
|
||||
email = g_key_file_get_string(config, "account", "email", NULL);
|
||||
password = g_key_file_get_string(config, "account", "password", NULL);
|
||||
|
||||
return_if_true(email == NULL || password == NULL, NULL);
|
||||
|
||||
ptr = dc_account_new2(email, password);
|
||||
dc_account_set_id(ptr, "@me");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
bool done = false;
|
||||
@ -116,17 +131,15 @@ int main(int ac, char **av)
|
||||
return 3;
|
||||
}
|
||||
|
||||
asprintf(&dc_private_dir, "%s/.ndc", getenv("HOME"));
|
||||
if (mkdir(dc_private_dir, 0755) < 0) {
|
||||
asprintf(&ncdc_private_dir, "%s/.ncdc", getenv("HOME"));
|
||||
if (mkdir(ncdc_private_dir, 0755) < 0) {
|
||||
if (errno != EEXIST) {
|
||||
fprintf(stderr, "failed to make %s: %s\n", dc_private_dir,
|
||||
fprintf(stderr, "failed to make %s: %s\n", ncdc_private_dir,
|
||||
strerror(errno));
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
asprintf(&dc_config_file, "%s/config", dc_private_dir);
|
||||
|
||||
if (!init_everything()) {
|
||||
return 3;
|
||||
}
|
||||
|
82
ncdc/src/textview.c
Normal file
82
ncdc/src/textview.c
Normal file
@ -0,0 +1,82 @@
|
||||
#include <ncdc/textview.h>
|
||||
#include <ncdc/ncdc.h>
|
||||
|
||||
struct ncdc_textview_
|
||||
{
|
||||
dc_refable_t ref;
|
||||
GPtrArray *par;
|
||||
};
|
||||
|
||||
static void ncdc_textview_free(ncdc_textview_t v)
|
||||
{
|
||||
return_if_true(v == NULL,);
|
||||
|
||||
g_ptr_array_unref(v->par);
|
||||
free(v);
|
||||
}
|
||||
|
||||
ncdc_textview_t ncdc_textview_new(void)
|
||||
{
|
||||
ncdc_textview_t p = calloc(1, sizeof(struct ncdc_textview_));
|
||||
return_if_true(p == NULL, NULL);
|
||||
|
||||
p->ref.cleanup = (dc_cleanup_t)ncdc_textview_free;
|
||||
|
||||
p->par = g_ptr_array_new_with_free_func(free);
|
||||
if (p->par == NULL) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void ncdc_textview_append(ncdc_textview_t v, wchar_t const *w)
|
||||
{
|
||||
return_if_true(v == NULL || w == NULL,);
|
||||
|
||||
wchar_t const *p = w;
|
||||
wchar_t const *last = w;
|
||||
|
||||
while ((p = wcschr(p, '\n')) != NULL) {
|
||||
wchar_t *dup = wcsndup(p, p - last);
|
||||
g_ptr_array_add(v->par, dup);
|
||||
last = p;
|
||||
}
|
||||
|
||||
g_ptr_array_add(v->par, wcsdup(last));
|
||||
}
|
||||
|
||||
wchar_t const *ncdc_textview_nthline(ncdc_textview_t v, size_t idx)
|
||||
{
|
||||
return_if_true(v == NULL, NULL);
|
||||
return_if_true(idx >= v->par->len, NULL);
|
||||
return g_ptr_array_index(v->par, idx);
|
||||
}
|
||||
|
||||
void ncdc_textview_render(ncdc_textview_t v, WINDOW *win, int lines, int cols)
|
||||
{
|
||||
ssize_t i = 0, needed_lines = 0, atline = 0;
|
||||
|
||||
werase(win);
|
||||
|
||||
if (v->par->len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = v->par->len-1; i >= 0; i--) {
|
||||
wchar_t const *w = ncdc_textview_nthline(v, i);
|
||||
size_t sz = wcslen(w);
|
||||
|
||||
needed_lines += (sz / cols);
|
||||
if ((sz / cols) == 0) {
|
||||
needed_lines += 1;
|
||||
}
|
||||
atline = (lines - needed_lines);
|
||||
mvwaddwstr(win, atline, 0, ncdc_textview_nthline(v, i));
|
||||
|
||||
if (needed_lines >= lines) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
128
ncdc/src/util.c
128
ncdc/src/util.c
@ -14,40 +14,110 @@ wchar_t const *w_next_word(wchar_t const *w, ssize_t len)
|
||||
return w+i;
|
||||
}
|
||||
|
||||
char *read_char(FILE *stream)
|
||||
wchar_t* wcsndup(const wchar_t* string, size_t maxlen)
|
||||
{
|
||||
uint8_t str[7] = {0};
|
||||
int len = 0, i = 0;
|
||||
size_t n = wcsnlen(string, maxlen) + 1;
|
||||
wchar_t* r = calloc(n, sizeof(wchar_t));
|
||||
return r == NULL ? NULL : wmemcpy(r, string, n);
|
||||
}
|
||||
|
||||
/* check if we need more
|
||||
size_t w_strlenv(wchar_t **s)
|
||||
{
|
||||
size_t i = 0;
|
||||
for (; s[i] != NULL; i++)
|
||||
;
|
||||
return i;
|
||||
}
|
||||
|
||||
void w_strfreev(wchar_t **s)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
return_if_true(s == NULL,);
|
||||
|
||||
for (; s[i] != NULL; i++) {
|
||||
free(s[i]);
|
||||
}
|
||||
|
||||
free(s);
|
||||
}
|
||||
|
||||
wchar_t **w_tokenise(wchar_t const *w)
|
||||
{
|
||||
GPtrArray *array = g_ptr_array_new();
|
||||
wchar_t const *item = w;
|
||||
wchar_t *dup = NULL;
|
||||
size_t len = 0, origlen = 0;
|
||||
|
||||
while ((dup = w_next_tok(item)) != NULL) {
|
||||
len = origlen = wcslen(dup);
|
||||
|
||||
if (*dup == '"') {
|
||||
memmove(dup, dup+1, sizeof(wchar_t)*(len-1));
|
||||
--len;
|
||||
}
|
||||
|
||||
if (len > 0 && dup[len-1] == '"') {
|
||||
dup[len-1] = '\0';
|
||||
--len;
|
||||
}
|
||||
|
||||
g_ptr_array_add(array, dup);
|
||||
item += origlen;
|
||||
}
|
||||
|
||||
g_ptr_array_add(array, NULL);
|
||||
|
||||
return (wchar_t**)g_ptr_array_free(array, FALSE);
|
||||
}
|
||||
|
||||
char *w_convert(wchar_t const *w)
|
||||
{
|
||||
size_t sz = 0;
|
||||
char *ptr = NULL;
|
||||
|
||||
sz = wcstombs(NULL, w, 0);
|
||||
|
||||
ptr = calloc(sz+1, sizeof(char));
|
||||
return_if_true(ptr == NULL, NULL);
|
||||
|
||||
wcstombs(ptr, w, sz);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
wchar_t *w_next_tok(wchar_t const *w)
|
||||
{
|
||||
bool quotes = false;
|
||||
wchar_t const *start = NULL;
|
||||
|
||||
/* skip first white spaces if there are any
|
||||
*/
|
||||
str[0] = (uint8_t)fgetc(stream);
|
||||
len = g_utf8_skip[str[0]];
|
||||
for (; *w != '\0' && iswspace(*w); w++)
|
||||
;
|
||||
|
||||
for (i = 1; i < len; i++) {
|
||||
str[i] = (uint8_t)fgetc(stream);
|
||||
}
|
||||
str[len] = '\0';
|
||||
|
||||
return strdup((char const *)str);
|
||||
}
|
||||
|
||||
int strwidth(char const *string)
|
||||
{
|
||||
size_t needed = mbstowcs(NULL, string, 0) + 1;
|
||||
wchar_t *wcstring = calloc(needed, sizeof(wchar_t));
|
||||
size_t ret = 0;
|
||||
|
||||
return_if_true(wcstring == NULL, -1);
|
||||
|
||||
ret = mbstowcs(wcstring, string, needed);
|
||||
if (ret == (size_t)-1) {
|
||||
free(wcstring);
|
||||
return -1;
|
||||
if (*w == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int width = wcswidth(wcstring, needed);
|
||||
free(wcstring);
|
||||
start = w;
|
||||
quotes = (*w == '"');
|
||||
|
||||
return width;
|
||||
do {
|
||||
if (iswspace(*w) && !quotes) {
|
||||
--w;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*w == '"' && *(w-1) != '\\' && quotes) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*w == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
++w;
|
||||
} while (1);
|
||||
|
||||
return wcsndup(start, (w - start));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user