massive improvements all abound

This commit is contained in:
Florian Stinglmayr 2019-07-02 21:53:29 +02:00
parent 9b4826f9cb
commit b307906cf4
15 changed files with 495 additions and 75 deletions

View File

@ -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, bool dc_api_get_userguilds(dc_api_t api, dc_account_t login,
GPtrArray **guilds); 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 #endif

View File

@ -360,13 +360,40 @@ cleanup:
return ret; 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) bool dc_api_get_userguilds(dc_api_t api, dc_account_t login, GPtrArray **out)
{ {
char *url = NULL; char *url = NULL;
json_t *reply = NULL, *c = NULL, *val = NULL; json_t *reply = NULL, *c = NULL, *val = NULL;
size_t i = 0; size_t i = 0;
bool ret = false; 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(api == NULL, false);
return_if_true(login == NULL, false); return_if_true(login == NULL, false);

View File

@ -2,20 +2,25 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
PKG_CHECK_MODULES(NCURSES REQUIRED ncursesw) PKG_CHECK_MODULES(NCURSES REQUIRED ncursesw)
PKG_CHECK_MODULES(PANEL REQUIRED panel) PKG_CHECK_MODULES(PANEL REQUIRED panel)
PKG_CHECK_MODULES(EDIT REQUIRED libedit) PKG_CHECK_MODULES(CONFUSE REQUIRED libconfuse)
SET(TARGET "ncdc") SET(TARGET "ncdc")
SET(SOURCES SET(SOURCES
"include/ncdc/cmds.h" "include/ncdc/cmds.h"
"include/ncdc/config.h"
"include/ncdc/input.h" "include/ncdc/input.h"
"include/ncdc/mainwindow.h" "include/ncdc/mainwindow.h"
"include/ncdc/ncdc.h" "include/ncdc/ncdc.h"
"include/ncdc/textview.h"
"src/cmds.c" "src/cmds.c"
"src/config.c"
"src/emacs.c" "src/emacs.c"
"src/input.c" "src/input.c"
"src/login.c"
"src/mainwindow.c" "src/mainwindow.c"
"src/ncdc.c" "src/ncdc.c"
"src/textview.c"
"src/util.c" "src/util.c"
) )
@ -28,7 +33,7 @@ INCLUDE_DIRECTORIES(
${GLIB2_INCLUDE_DIRS} ${GLIB2_INCLUDE_DIRS}
${NCURSES_INCLUDE_DIRS} ${NCURSES_INCLUDE_DIRS}
${PANEL_INCLUDE_DIRS} ${PANEL_INCLUDE_DIRS}
${EDIT_INCLUDE_DIRS} ${CONFUSE_INCLUDE_DIRS}
) )
ADD_EXECUTABLE(${TARGET} ${SOURCES}) ADD_EXECUTABLE(${TARGET} ${SOURCES})
@ -37,5 +42,5 @@ TARGET_LINK_LIBRARIES(${TARGET}
${GLIB2_LIBRARIES} ${GLIB2_LIBRARIES}
${NCURSES_LIBRARIES} ${NCURSES_LIBRARIES}
${PANEL_LIBRARIES} ${PANEL_LIBRARIES}
${EDIT_LIBRARIES} ${CONFUSE_LIBRARIES}
) )

View File

@ -5,7 +5,7 @@
#include <ncdc/mainwindow.h> #include <ncdc/mainwindow.h>
typedef bool (*ncdc_command_t)(ncdc_mainwindow_t n, typedef bool (*ncdc_command_t)(ncdc_mainwindow_t n,
wchar_t const *s, size_t len); size_t argc, wchar_t **argv);
typedef struct { typedef struct {
wchar_t const *name; wchar_t const *name;
@ -14,6 +14,9 @@ typedef struct {
extern ncdc_commands_t cmds[]; 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 #endif

View 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

View File

@ -2,12 +2,18 @@
#define NCDC_MAINWINDOW_H #define NCDC_MAINWINDOW_H
#include <ncdc/ncdc.h> #include <ncdc/ncdc.h>
#include <stdarg.h>
struct ncdc_mainwindow_; struct ncdc_mainwindow_;
typedef struct ncdc_mainwindow_ *ncdc_mainwindow_t; typedef struct ncdc_mainwindow_ *ncdc_mainwindow_t;
ncdc_mainwindow_t ncdc_mainwindow_new(void); 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_refresh(ncdc_mainwindow_t n);
void ncdc_mainwindow_input_ready(ncdc_mainwindow_t n); void ncdc_mainwindow_input_ready(ncdc_mainwindow_t n);

View File

@ -30,15 +30,32 @@
#include <dc/refable.h> #include <dc/refable.h>
#include <dc/api.h> #include <dc/api.h>
#include <dc/loop.h> #include <dc/loop.h>
#include <dc/account.h>
#define return_if_true(v,r) do { if (v) return r; } while(0) #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) #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 char *ncdc_private_dir;
extern void *config;
int strwidth(char const *string); int strwidth(char const *string);
char *read_char(FILE *stream); 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); wchar_t const *w_next_word(wchar_t const *w, ssize_t len);
#endif #endif

View 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

View File

@ -1,11 +1,36 @@
#include <ncdc/cmds.h> #include <ncdc/cmds.h>
ncdc_commands_t cmds[] = { ncdc_commands_t cmds[] = {
{ L"login", ncdc_cmd_login },
{ L"quit", ncdc_cmd_quit }, { L"quit", ncdc_cmd_quit },
{ NULL, NULL } { 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); exit(0);
} }

82
ncdc/src/config.c Normal file
View 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
View 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;
}

View File

@ -1,5 +1,6 @@
#include <ncdc/mainwindow.h> #include <ncdc/mainwindow.h>
#include <ncdc/input.h> #include <ncdc/input.h>
#include <ncdc/textview.h>
#include <ncdc/cmds.h> #include <ncdc/cmds.h>
#include <ncdc/ncdc.h> #include <ncdc/ncdc.h>
@ -36,6 +37,7 @@ struct ncdc_mainwindow_
WINDOW *sep2; WINDOW *sep2;
ncdc_input_t in; ncdc_input_t in;
ncdc_textview_t log;
int focus; int focus;
}; };
@ -57,6 +59,7 @@ static void ncdc_mainwindow_free(ncdc_mainwindow_t n)
delwin(n->sep2); delwin(n->sep2);
dc_unref(n->in); dc_unref(n->in);
dc_unref(n->log);
free(n); free(n);
} }
@ -71,6 +74,8 @@ ncdc_mainwindow_t ncdc_mainwindow_new(void)
ptr->in = ncdc_input_new(); ptr->in = ncdc_input_new();
ncdc_input_set_callback(ptr->in, ncdc_mainwindow_callback, ptr); ncdc_input_set_callback(ptr->in, ncdc_mainwindow_callback, ptr);
ptr->log = ncdc_textview_new();
ptr->guilds = newwin(5, 5, 1, 1); ptr->guilds = newwin(5, 5, 1, 1);
ptr->chat = newwin(5, 5, 4, 4); 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; ncdc_mainwindow_t mainwin = (ncdc_mainwindow_t)arg;
if (s[0] == '/') { if (s[0] == '/') {
size_t i = 0; if (s[1] == '\0') {
wchar_t const *n = w_next_word(s, len); 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; return false;
@ -135,9 +136,9 @@ static void ncdc_mainwindow_resize(ncdc_mainwindow_t n)
wnoutrefresh(n->sep1); wnoutrefresh(n->sep1);
n->chat_h = LINES - n->input_h - 1; 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_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); wresize(n->chat, n->chat_h, n->chat_w);
mvwin(n->chat, n->chat_y, n->chat_x); 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) void ncdc_mainwindow_refresh(ncdc_mainwindow_t n)
{ {
wnoutrefresh(n->guilds); wnoutrefresh(n->guilds);
ncdc_textview_render(n->log, n->chat, n->chat_h, n->chat_w);
wnoutrefresh(n->chat); wnoutrefresh(n->chat);
ncdc_input_draw(n->in, n->input); ncdc_input_draw(n->in, n->input);
@ -205,6 +208,18 @@ void ncdc_mainwindow_refresh(ncdc_mainwindow_t n)
wnoutrefresh(n->sep2); wnoutrefresh(n->sep2);
ncdc_mainwindow_update_focus(n); 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);
} }

View File

@ -1,5 +1,6 @@
#include <ncdc/ncdc.h> #include <ncdc/ncdc.h>
#include <ncdc/mainwindow.h> #include <ncdc/mainwindow.h>
#include <ncdc/config.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
@ -16,14 +17,34 @@ ncdc_mainwindow_t mainwin = NULL;
*/ */
static bool done = false; static bool done = false;
char *dc_private_dir = NULL; /* all the accounts we have logged into
char *dc_config_file = NULL; */
GHashTable *accounts = NULL;
static GKeyFile *config = NULL; char *ncdc_private_dir = NULL;
void *config = NULL;
dc_loop_t loop = NULL; dc_loop_t loop = NULL;
dc_api_t api = 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) static void sighandler(int sig)
{ {
endwin(); endwin();
@ -42,8 +63,16 @@ static void cleanup(void)
done = true; done = true;
if (accounts != NULL) {
g_hash_table_unref(accounts);
accounts = NULL;
}
dc_unref(api); dc_unref(api);
dc_unref(loop); dc_unref(loop);
dc_unref(config);
dc_unref(mainwin);
} }
static void stdin_handler(int sock, short what, void *data) 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); dc_loop_add_api(loop, api);
config = g_key_file_new(); config = ncdc_config_new();
return_if_true(config == NULL, false); 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; 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) int main(int ac, char **av)
{ {
bool done = false; bool done = false;
@ -116,17 +131,15 @@ int main(int ac, char **av)
return 3; return 3;
} }
asprintf(&dc_private_dir, "%s/.ndc", getenv("HOME")); asprintf(&ncdc_private_dir, "%s/.ncdc", getenv("HOME"));
if (mkdir(dc_private_dir, 0755) < 0) { if (mkdir(ncdc_private_dir, 0755) < 0) {
if (errno != EEXIST) { 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)); strerror(errno));
return 3; return 3;
} }
} }
asprintf(&dc_config_file, "%s/config", dc_private_dir);
if (!init_everything()) { if (!init_everything()) {
return 3; return 3;
} }

82
ncdc/src/textview.c Normal file
View 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;
}
}
}

View File

@ -14,40 +14,110 @@ wchar_t const *w_next_word(wchar_t const *w, ssize_t len)
return w+i; return w+i;
} }
char *read_char(FILE *stream) wchar_t* wcsndup(const wchar_t* string, size_t maxlen)
{ {
uint8_t str[7] = {0}; size_t n = wcsnlen(string, maxlen) + 1;
int len = 0, i = 0; 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); for (; *w != '\0' && iswspace(*w); w++)
len = g_utf8_skip[str[0]]; ;
for (i = 1; i < len; i++) { if (*w == '\0') {
str[i] = (uint8_t)fgetc(stream); return NULL;
}
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;
} }
int width = wcswidth(wcstring, needed); start = w;
free(wcstring); 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));
} }