Compare commits

..

1 Commits

Author SHA1 Message Date
Jan Vidar Krey
e8957db819 Minor work on getting history command to be configurable and also
display a certain number of main chat history lines after login.
2010-05-19 20:51:13 +02:00
17 changed files with 109 additions and 234 deletions

View File

@@ -1,13 +1,3 @@
0.3.2:
- Fixed bugs in the kqueue network backend (OSX/BSD)
- Rewrote the configuration backend code.
- Added support for escaping characters in the configuration files.
- Updated the !broadcast command to send private messages instead of main chat messages.
- Adding support for redirecting clients to other hubs when they fail to log in.
- Fix some out of memory related crashes.
- Fixed minor memory leaks.
0.3.1: 0.3.1:
- Fixed bug where !getip did not work. - Fixed bug where !getip did not work.
- Added flood control configuration options. - Added flood control configuration options.

6
debian/changelog vendored
View File

@@ -1,9 +1,3 @@
uhub (0.3.2-1) unstable; urgency=low
* Updated upstream version.
-- Jan Vidar Krey <janvidar@extatic.org> Mon 30 May 2010 18:00:00 +0200
uhub (0.3.1-1) unstable; urgency=low uhub (0.3.1-1) unstable; urgency=low
* Updated version number. * Updated version number.

View File

@@ -1,6 +1,6 @@
Summary: High performance ADC p2p hub. Summary: High performance ADC p2p hub.
Name: uhub Name: uhub
Version: 0.3.2 Version: 0.3.1
Release: 3 Release: 3
License: GPLv3 License: GPLv3
Group: Networking/File transfer Group: Networking/File transfer

View File

@@ -756,7 +756,7 @@ char* adc_msg_get_argument(struct adc_message* cmd, int offset)
else else
{ {
argument = hub_strdup(&start[1]); argument = hub_strdup(&start[1]);
if (argument && *argument && argument[strlen(argument)-1] == '\n') if (argument && argument[strlen(argument)-1] == '\n')
argument[strlen(argument)-1] = 0; argument[strlen(argument)-1] = 0;
} }

View File

@@ -1,6 +1,6 @@
/* /*
* uhub - A tiny ADC p2p connection hub * uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2010, Jan Vidar Krey * Copyright (C) 2007-2009, Jan Vidar Krey
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -176,10 +176,14 @@ static int check_cmd_addr(const char* cmd, struct linked_list* list, char* line,
static int acl_parse_line(char* line, int line_count, void* ptr_data) static int acl_parse_line(char* line, int line_count, void* ptr_data)
{ {
char* pos;
struct acl_handle* handle = (struct acl_handle*) ptr_data; struct acl_handle* handle = (struct acl_handle*) ptr_data;
int ret; int ret;
strip_off_ini_line_comments(line, line_count); if ((pos = strchr(line, '#')) != NULL)
{
pos[0] = 0;
}
line = strip_white_space(line); line = strip_white_space(line);
if (!*line) if (!*line)

View File

@@ -1,6 +1,6 @@
/* /*
* uhub - A tiny ADC p2p connection hub * uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2010, Jan Vidar Krey * Copyright (C) 2007-2009, Jan Vidar Krey
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@@ -132,19 +132,40 @@ static int command_status_user_not_found(struct hub_info* hub, struct hub_user*
const char* command_get_syntax(struct commands_handler* handler) const char* command_get_syntax(struct commands_handler* handler)
{ {
static char args[128]; static char args[128];
int optional = 0;
size_t n = 0; size_t n = 0;
args[0] = 0; args[0] = 0;
if (handler->args) if (handler->args)
{ {
for (n = 0; n < strlen(handler->args); n++) for (n = 0; n < strlen(handler->args); n++)
{ {
// detect optionals
if (handler->args[n] == '?')
{
optional = 1;
continue;
}
if (n > 0) strcat(args, " "); if (n > 0) strcat(args, " ");
if (optional)
{
strcat(args, "[");
}
switch (handler->args[n]) switch (handler->args[n])
{ {
case 'n': strcat(args, "<nick>"); break; case 'n': strcat(args, "<nick>"); break;
case 'c': strcat(args, "<cid>"); break; case 'c': strcat(args, "<cid>"); break;
case 'a': strcat(args, "<addr>"); break; case 'a': strcat(args, "<addr>"); break;
case 'm': strcat(args, "<message>"); break; case 'm': strcat(args, "<message>"); break;
case 'i': strcat(args, "<number>"); break;
}
if (optional)
{
strcat(args, "]");
optional = 0;
} }
} }
} }
@@ -412,48 +433,11 @@ static int command_whoip(struct hub_info* hub, struct hub_user* user, struct hub
static int command_broadcast(struct hub_info* hub, struct hub_user* user, struct hub_command* cmd) static int command_broadcast(struct hub_info* hub, struct hub_user* user, struct hub_command* cmd)
{ {
size_t offset = 12; struct adc_message* command = adc_msg_construct(ADC_CMD_IMSG, strlen((cmd->message + 12)) + 6);
#if USE_OLD_BROADCAST_STYLE adc_msg_add_argument(command, (cmd->message + 12));
struct adc_message* command = adc_msg_construct(ADC_CMD_IMSG, strlen((cmd->message + offset)) + 6);
adc_msg_add_argument(command, (cmd->message + offset));
route_to_all(hub, command); route_to_all(hub, command);
adc_msg_free(command); adc_msg_free(command);
return 0; return 0;
#else
size_t message_len = strlen(cmd->message + offset);
struct adc_message* command = 0;
char pm_flag[7] = "PM";
char from_sid[5];
char buffer[128];
size_t recipients = 0;
memcpy(from_sid, sid_to_string(user->id.sid), sizeof(from_sid));
memcpy(pm_flag + 2, from_sid, sizeof(from_sid));
struct hub_user* target = (struct hub_user*) list_get_first(hub->users->list);
while (target)
{
if (target != user)
{
recipients++;
command = adc_msg_construct(ADC_CMD_DMSG, message_len + 23);
if (!command)
break;
adc_msg_add_argument(command, from_sid);
adc_msg_add_argument(command, sid_to_string(target->id.sid));
adc_msg_add_argument(command, (cmd->message + offset));
adc_msg_add_argument(command, pm_flag);
route_to_user(hub, target, command);
adc_msg_free(command);
}
target = (struct hub_user*) list_get_next(hub->users->list);
}
snprintf(buffer, sizeof(buffer), "*** %s: Delivered to " PRINTF_SIZE_T " user%s", cmd->prefix, recipients, (recipients != 1 ? "s" : ""));
return 0;
#endif
} }
static int command_history(struct hub_info* hub, struct hub_user* user, struct hub_command* cmd) static int command_history(struct hub_info* hub, struct hub_user* user, struct hub_command* cmd)
@@ -643,7 +627,7 @@ static struct commands_handler command_handlers[] = {
#endif #endif
{ "getip", 5, "n", cred_operator, command_getip, "Show IP address for a user" }, { "getip", 5, "n", cred_operator, command_getip, "Show IP address for a user" },
{ "help", 4, 0, cred_guest, command_help, "Show this help message." }, { "help", 4, 0, cred_guest, command_help, "Show this help message." },
{ "history", 7, 0, cred_guest, command_history, "Show the last chat messages." }, { "history", 7, "?i",cred_guest, command_history, "Show the last chat messages." },
{ "kick", 4, "n", cred_operator, command_kick, "Kick a user" }, { "kick", 4, "n", cred_operator, command_kick, "Kick a user" },
{ "log", 3, 0, cred_operator, command_log, "Display log" }, { "log", 3, 0, cred_operator, command_log, "Display log" },
{ "motd", 4, 0, cred_guest, command_motd, "Show the message of the day" }, { "motd", 4, 0, cred_guest, command_motd, "Show the message of the day" },

View File

@@ -85,7 +85,10 @@ static int config_parse_line(char* line, int line_count, void* ptr_data)
char* data; char* data;
struct hub_config* config = (struct hub_config*) ptr_data; struct hub_config* config = (struct hub_config*) ptr_data;
strip_off_ini_line_comments(line, line_count); if ((pos = strchr(line, '#')) != NULL)
{
pos[0] = 0;
}
if (!*line) return 0; if (!*line) return 0;

View File

@@ -211,6 +211,17 @@
<since>0.3.0</since> <since>0.3.0</since>
</option> </option>
<option name="history_on_login" type="int" default="0">
<check min="0" max="250" />
<short>Send chat message history when logging in</short>
<description><![CDATA[
This specifies the number of chat messages that are sent to all users when logging in.
Users can use the "!history" command to list these messages later.
]]></description>
<since>0.3.2</since>
</option>
<option name="max_logout_log" type="int" default="20"> <option name="max_logout_log" type="int" default="20">
<check min="0" max="2000" /> <check min="0" max="2000" />
<short>Number of log entries for people leaving the hub</short> <short>Number of log entries for people leaving the hub</short>

View File

@@ -257,15 +257,9 @@ int hub_handle_chat_message(struct hub_info* hub, struct hub_user* u, struct adc
int relay = 1; int relay = 1;
int offset; int offset;
if (!message) if (!message || !user_is_logged_in(u))
return 0; return 0;
if (!user_is_logged_in(u))
{
hub_free(message);
return 0;
}
if ((cmd->cache[0] == 'B') && (message[0] == '!' || message[0] == '+')) if ((cmd->cache[0] == 'B') && (message[0] == '!' || message[0] == '+'))
{ {
/* /*
@@ -486,11 +480,11 @@ void hub_send_flood_warning(struct hub_info* hub, struct hub_user* u, const char
adc_msg_add_argument(msg, "110"); adc_msg_add_argument(msg, "110");
adc_msg_add_argument(msg, tmp); adc_msg_add_argument(msg, tmp);
hub_free(tmp); hub_free(tmp);
route_to_user(hub, u, msg);
user_flag_set(u, flag_flood);
adc_msg_free(msg);
} }
route_to_user(hub, u, msg);
user_flag_set(u, flag_flood);
adc_msg_free(msg);
} }
static void hub_event_dispatcher(void* callback_data, struct event_data* message) static void hub_event_dispatcher(void* callback_data, struct event_data* message)
@@ -950,7 +944,7 @@ void hub_send_status(struct hub_info* hub, struct hub_user* user, enum status_me
struct adc_message* cmd = adc_msg_construct(ADC_CMD_ISTA, 6); struct adc_message* cmd = adc_msg_construct(ADC_CMD_ISTA, 6);
struct adc_message* qui = adc_msg_construct(ADC_CMD_IQUI, 512); struct adc_message* qui = adc_msg_construct(ADC_CMD_IQUI, 512);
char code[4]; char code[4];
char buf[256]; char buf[250];
const char* text = 0; const char* text = 0;
const char* flag = 0; const char* flag = 0;
char* escaped_text = 0; char* escaped_text = 0;

View File

@@ -92,10 +92,9 @@ static void probe_net_event(struct net_connection* con, int events, void *arg)
net_con_ssl_handshake(con, net_con_ssl_mode_server, probe->hub->ssl_ctx); net_con_ssl_handshake(con, net_con_ssl_mode_server, probe->hub->ssl_ctx);
return; return;
} }
#else #endif
probe_destroy(probe); probe_destroy(probe);
return; return;
#endif
} }
} }
} }

View File

@@ -79,8 +79,11 @@ int net_backend_init()
timeout_queue_initialize(&g_backend->timeout_queue, g_backend->now, 120); /* FIXME: max 120 secs! */ timeout_queue_initialize(&g_backend->timeout_queue, g_backend->now, 120); /* FIXME: max 120 secs! */
g_backend->cleaner = net_cleanup_initialize(g_backend->common.max); g_backend->cleaner = net_cleanup_initialize(g_backend->common.max);
for (n = 0; net_backend_init_funcs[n]; n++) for (n = 0; n < sizeof(net_backend_init_funcs); n++)
{ {
if (!net_backend_init_funcs[n])
break;
g_backend->data = net_backend_init_funcs[n](&g_backend->handler, &g_backend->common); g_backend->data = net_backend_init_funcs[n](&g_backend->handler, &g_backend->common);
if (g_backend->data) if (g_backend->data)
{ {

View File

@@ -32,29 +32,19 @@ struct net_connection_kqueue
NET_CON_STRUCT_COMMON NET_CON_STRUCT_COMMON
struct kevent ev_r; struct kevent ev_r;
struct kevent ev_w; struct kevent ev_w;
int change;
}; };
struct net_backend_kqueue struct net_backend_kqueue
{ {
int kqfd; int kqfd;
struct net_connection_kqueue** conns; struct net_connection_kqueue** conns;
struct kevent* changes; struct kevent** changes;
int* change_list; size_t nchanges;
size_t change_list_len;
struct kevent events[KQUEUE_EVBUFFER]; struct kevent events[KQUEUE_EVBUFFER];
struct net_backend_common* common; struct net_backend_common* common;
}; };
#define CHANGE_ACTION_ADD 0x0001
#define CHANGE_ACTION_MOD 0x0002
#define CHANGE_ACTION_DEL 0x0004
#define CHANGE_OP_WANT_READ 0x0100
#define CHANGE_OP_WANT_WRITE 0x0200
static void net_backend_set_handlers(struct net_backend_handler* handler); static void net_backend_set_handlers(struct net_backend_handler* handler);
static void add_change(struct net_backend_kqueue* backend, struct net_connection_kqueue* con, int actions);
static size_t create_change_list(struct net_backend_kqueue* backend);
const char* net_backend_name_kqueue() const char* net_backend_name_kqueue()
{ {
@@ -66,13 +56,12 @@ int net_backend_poll_kqueue(struct net_backend* data, int ms)
int res; int res;
struct timespec tspec = { 0, }; struct timespec tspec = { 0, };
struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data; struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data;
size_t changes;
tspec.tv_sec = (ms / 1000); tspec.tv_sec = (ms / 1000);
tspec.tv_nsec = ((ms % 1000) * 1000000); tspec.tv_nsec = ((ms % 1000) * 1000000);
changes = create_change_list(backend); res = kevent(backend->kqfd, *backend->changes, backend->nchanges, backend->events, KQUEUE_EVBUFFER, &tspec);
res = kevent(backend->kqfd, backend->changes, changes, backend->events, KQUEUE_EVBUFFER, &tspec); backend->nchanges = 0;
if (res == -1 && errno == EINTR) if (res == -1 && errno == EINTR)
return 0; return 0;
@@ -87,13 +76,11 @@ void net_backend_process_kqueue(struct net_backend* data, int res)
for (n = 0; n < res; n++) for (n = 0; n < res; n++)
{ {
struct net_connection_kqueue* con = (struct net_connection_kqueue*) backend->events[n].udata; struct net_connection_kqueue* con = (struct net_connection_kqueue*) backend->events[n].udata;
if (con && con->sd >= 0 && backend->conns[con->sd]) int ev = -1;
{ if (backend->events[n].filter == EVFILT_READ) ev = NET_EVENT_READ;
int ev = 0; else if (backend->events[n].filter == EVFILT_WRITE) ev = NET_EVENT_WRITE;
if (backend->events[n].filter == EVFILT_READ) ev = NET_EVENT_READ; if (con)
else if (backend->events[n].filter == EVFILT_WRITE) ev = NET_EVENT_WRITE;
net_con_callback((struct net_connection*) con, ev); net_con_callback((struct net_connection*) con, ev);
}
} }
} }
@@ -115,37 +102,58 @@ void net_con_initialize_kqueue(struct net_backend* data, struct net_connection*
void net_con_backend_add_kqueue(struct net_backend* data, struct net_connection* con_, int events) void net_con_backend_add_kqueue(struct net_backend* data, struct net_connection* con_, int events)
{ {
unsigned short flags_r = EV_ADD;
unsigned short flags_w = EV_ADD;
struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data; struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data;
struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_; struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_;
int operation;
backend->conns[con->sd] = con; backend->conns[con->sd] = con;
operation = CHANGE_ACTION_ADD;
if (events & NET_EVENT_READ) if (events & NET_EVENT_READ)
operation |= CHANGE_OP_WANT_READ; flags_r |= EV_ENABLE;
else
flags_r |= EV_DISABLE;
EV_SET(&con->ev_r, con->sd, EVFILT_READ, flags_r, 0, 0, con);
backend->changes[backend->nchanges++] = &con->ev_r;
if (events & NET_EVENT_WRITE) if (events & NET_EVENT_WRITE)
operation |= CHANGE_OP_WANT_WRITE; flags_w |= EV_ENABLE;
else
flags_w |= EV_DISABLE;
add_change(backend, con, operation); EV_SET(&con->ev_w, con->sd, EVFILT_WRITE, flags_w, 0, 0, con);
backend->changes[backend->nchanges++] = &con->ev_w;
} }
void net_con_backend_mod_kqueue(struct net_backend* data, struct net_connection* con_, int events) void net_con_backend_mod_kqueue(struct net_backend* data, struct net_connection* con_, int events)
{ {
unsigned short flags_r = 0;
unsigned short flags_w = 0;
struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data; struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data;
struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_; struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_;
int operation = CHANGE_ACTION_ADD;
if (events & NET_EVENT_READ) if (events & NET_EVENT_READ)
operation |= CHANGE_OP_WANT_READ; flags_r |= EV_ENABLE;
else
flags_r |= EV_DISABLE;
if (!(con->ev_r.flags & flags_r))
{
EV_SET(&con->ev_r, con->sd, EVFILT_READ, flags_r, 0, 0, con);
backend->changes[backend->nchanges++] = &con->ev_r;
}
if (events & NET_EVENT_WRITE) if (events & NET_EVENT_WRITE)
operation |= CHANGE_OP_WANT_WRITE; flags_r |= EV_ENABLE;
else
flags_r |= EV_DISABLE;
add_change(backend, con, operation); if (!(con->ev_w.flags & flags_w))
{
EV_SET(&con->ev_w, con->sd, EVFILT_WRITE, flags_w, 0, 0, con);
backend->changes[backend->nchanges++] = &con->ev_w;
}
} }
void net_con_backend_del_kqueue(struct net_backend* data, struct net_connection* con_) void net_con_backend_del_kqueue(struct net_backend* data, struct net_connection* con_)
@@ -154,8 +162,11 @@ void net_con_backend_del_kqueue(struct net_backend* data, struct net_connection*
struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_; struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_;
/* No need to remove it from the kqueue filter, the kqueue man page says /* No need to remove it from the kqueue filter, the kqueue man page says
it is automatically removed when the descriptor is closed... */ it is automatically removed when the descriptor is closed. */
add_change(backend, con, CHANGE_ACTION_DEL); EV_SET(&con->ev_r, con->sd, EVFILT_READ, EV_DELETE, 0, 0, 0);
backend->changes[backend->nchanges++] = &con->ev_r;
EV_SET(&con->ev_w, con->sd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
backend->changes[backend->nchanges++] = &con->ev_w;
// Unmap the socket descriptor. // Unmap the socket descriptor.
backend->conns[con->sd] = 0; backend->conns[con->sd] = 0;
@@ -167,7 +178,6 @@ void net_backend_shutdown_kqueue(struct net_backend* data)
close(backend->kqfd); close(backend->kqfd);
hub_free(backend->conns); hub_free(backend->conns);
hub_free(backend->changes); hub_free(backend->changes);
hub_free(backend->change_list);
hub_free(backend); hub_free(backend);
} }
@@ -187,8 +197,8 @@ struct net_backend* net_backend_init_kqueue(struct net_backend_handler* handler,
} }
backend->conns = hub_malloc_zero(sizeof(struct net_connection_kqueue*) * common->max); backend->conns = hub_malloc_zero(sizeof(struct net_connection_kqueue*) * common->max);
backend->changes = hub_malloc_zero(sizeof(struct kevent) * common->max * 2); backend->conns = hub_malloc_zero(sizeof(struct net_connection_kqueue*) * common->max);
backend->change_list = hub_malloc_zero(sizeof(int) * common->max); backend->changes = hub_malloc_zero(sizeof(struct kevent*) * common->max * 2);
backend->common = common; backend->common = common;
net_backend_set_handlers(handler); net_backend_set_handlers(handler);
@@ -208,71 +218,4 @@ static void net_backend_set_handlers(struct net_backend_handler* handler)
handler->con_del = net_con_backend_del_kqueue; handler->con_del = net_con_backend_del_kqueue;
} }
static void add_change(struct net_backend_kqueue* backend, struct net_connection_kqueue* con, int actions)
{
if (actions && !con->change)
{
backend->change_list[backend->change_list_len++] = con->sd;
con->change = actions;
}
}
static size_t create_change_list(struct net_backend_kqueue* backend)
{
size_t n = 0;
size_t changes = 0;
int sd;
struct net_connection_kqueue* con;
unsigned short flags_r = 0;
unsigned short flags_w = 0;
for (; n < backend->change_list_len; n++)
{
sd = backend->change_list[n];
con = backend->conns[sd];
if (con)
{
flags_r = 0;
flags_w = 0;
if (con->change & CHANGE_ACTION_ADD)
{
flags_r |= EV_ADD;
flags_w |= EV_ADD;
}
if (con->change & CHANGE_OP_WANT_READ)
flags_r |= EV_ENABLE;
else
flags_r |= EV_DISABLE;
if (con->change & CHANGE_OP_WANT_WRITE)
flags_w |= EV_ENABLE;
else
flags_w |= EV_DISABLE;
if (con->ev_r.flags != flags_r)
{
EV_SET(&con->ev_r, sd, EVFILT_READ, flags_r, 0, 0, con);
memcpy(&backend->changes[changes++], &con->ev_r, sizeof(struct kevent));
}
if (con->ev_w.flags != flags_w)
{
EV_SET(&con->ev_w, sd, EVFILT_WRITE, flags_w, 0, 0, con);
memcpy(&backend->changes[changes++], &con->ev_w, sizeof(struct kevent));
}
con->change = 0;
}
else
{
EV_SET(&backend->changes[changes++], sd, EVFILT_READ, EV_DELETE, 0, 0, 0);
EV_SET(&backend->changes[changes++], sd, EVFILT_READ, EV_DELETE, 0, 0, 0);
}
}
backend->change_list_len = 0;
return changes;
}
#endif /* USE_KQUEUE */ #endif /* USE_KQUEUE */

View File

@@ -400,51 +400,3 @@ const char* get_timestamp(time_t now)
sprintf(ts, "[%02d:%02d]", t->tm_hour, t->tm_min); sprintf(ts, "[%02d:%02d]", t->tm_hour, t->tm_min);
return ts; return ts;
} }
void strip_off_ini_line_comments(char* line, int line_count)
{
char* p = line;
char* out = line;
int backslash = 0;
if (!*line)
return;
for (; *p; p++)
{
if (!backslash)
{
if (*p == '\\')
{
backslash = 1;
}
else if (*p == '#')
{
*out = '\0';
out++;
break;
}
else
{
*out = *p;
out++;
}
}
else
{
if (*p == '\\' || *p == '#' || *p == '\"')
{
*out = *p;
out++;
}
else
{
LOG_WARN("Invalid backslash escape on line %d", line_count);
*out = *p;
out++;
}
backslash = 0;
}
}
*out = '\0';
}

View File

@@ -33,12 +33,10 @@ extern int is_valid_base32_char(char c);
extern void base32_encode(const unsigned char* buffer, size_t len, char* result); extern void base32_encode(const unsigned char* buffer, size_t len, char* result);
extern void base32_decode(const char* src, unsigned char* dst, size_t len); extern void base32_decode(const char* src, unsigned char* dst, size_t len);
extern char* strip_white_space(char* string); extern char* strip_white_space(char* string);
extern void strip_off_ini_line_comments(char* line, int line_count);
extern int file_read_lines(const char* file, void* data, file_line_handler_t handler); extern int file_read_lines(const char* file, void* data, file_line_handler_t handler);
extern const char* uhub_itoa(int val); extern const char* uhub_itoa(int val);
extern const char* uhub_ulltoa(uint64_t val); extern const char* uhub_ulltoa(uint64_t val);

View File

@@ -5,7 +5,7 @@
#endif #endif
#ifndef VERSION #ifndef VERSION
#define VERSION "0.3.2" #define VERSION "0.3.1"
#endif #endif
#ifndef GIT_REVISION #ifndef GIT_REVISION