1
0
forked from bip/bip

refactor nick management code

- drop struct nick.
- follow nick changes in a basic way.
  right now it does not work if one changes nick and somebody else takes
  the old unused nick.
This commit is contained in:
Arnaud Cornet 2008-12-20 17:59:16 +01:00
parent 4d231e8ddc
commit e8bb841e5f
5 changed files with 108 additions and 127 deletions

181
src/irc.c
View File

@ -78,7 +78,7 @@ struct channel *channel_new(const char *name)
struct channel *chan; struct channel *chan;
chan = bip_calloc(sizeof(struct channel), 1); chan = bip_calloc(sizeof(struct channel), 1);
chan->name = bip_strdup(name); chan->name = bip_strdup(name);
hash_init(&chan->nicks, HASH_NOCASE); hash_init(&chan->ovmasks, HASH_NOCASE);
return chan; return chan;
} }
@ -108,23 +108,21 @@ list_t *channel_name_list(struct channel *c)
{ {
list_t *ret; list_t *ret;
hash_iterator_t hi; hash_iterator_t hi;
size_t s = NAMESIZE;
ret = list_new(NULL);
size_t len = 0; size_t len = 0;
char *str = bip_malloc(NAMESIZE); char *str = bip_malloc(NAMESIZE + 1);
ret = list_new(NULL);
*str = 0; *str = 0;
for (hash_it_init(&c->nicks, &hi); hash_it_item(&hi); for (hash_it_init(&c->ovmasks, &hi); hash_it_item(&hi);
hash_it_next(&hi)){ hash_it_next(&hi)){
struct nick *n = hash_it_item(&hi); const char *nick = hash_it_key(&hi);
long int ovmask = (long int)hash_it_item(&hi);
if (strlen(n->name) + 2 >= NAMESIZE) assert(strlen(nick) + 2 < NAMESIZE);
fatal("nick too big for me"); /* FIXME */
if (len + strlen(n->name) + 2 + (n->ovmask ? 1 : 0) if (len + strlen(nick) + 2 + (ovmask ? 1 : 0) >= NAMESIZE) {
>= NAMESIZE) {
list_add_last(ret, str); list_add_last(ret, str);
str = bip_malloc(s); str = bip_malloc(NAMESIZE + 1);
*str = 0; *str = 0;
len = 0; len = 0;
} }
@ -132,20 +130,17 @@ list_t *channel_name_list(struct channel *c)
strncat(str, " ", NAMESIZE); strncat(str, " ", NAMESIZE);
len++; len++;
} }
if (n->ovmask & NICKOP) { if (ovmask & NICKOP)
strncat(str, "@", NAMESIZE); strncat(str, "@", NAMESIZE);
len++; else if (ovmask & NICKHALFOP)
} else if (n->ovmask & NICKHALFOP) {
strncat(str, "%", NAMESIZE); strncat(str, "%", NAMESIZE);
len++; else if (ovmask & NICKVOICED)
} else if (n->ovmask & NICKVOICED) {
strncat(str, "+", NAMESIZE); strncat(str, "+", NAMESIZE);
len++; len++;
}
strncat(str, n->name, NAMESIZE); strncat(str, nick, NAMESIZE);
len += strlen(n->name); len += strlen(nick);
if (len >= NAMESIZE) assert(len < NAMESIZE);
fatal("internal error 5");
} }
list_add_last(ret, str); list_add_last(ret, str);
return ret; return ret;
@ -542,7 +537,6 @@ static void irc_send_join(struct link_client *ic, struct channel *chan)
WRITE_LINE3(CONN(ic), P_SERV, "366", LINK(ic)->l_server->nick, WRITE_LINE3(CONN(ic), P_SERV, "366", LINK(ic)->l_server->nick,
chan->name, "End of /NAMES list."); chan->name, "End of /NAMES list.");
} }
static void write_init_string(connection_t *c, struct line *line, char *nick) static void write_init_string(connection_t *c, struct line *line, char *nick)
@ -666,7 +660,7 @@ static void irc_cli_backlog(struct link_client *ic)
user = LINK(ic)->user; user = LINK(ic)->user;
if (!user) if (!user)
fatal("irc_send_join: No user associated"); fatal("irc_cli_backlog: No user associated");
if (!user->backlog) { if (!user->backlog) {
mylog(LOG_DEBUG, "Backlog disabled for %s, not backlogging", mylog(LOG_DEBUG, "Backlog disabled for %s, not backlogging",
user->name); user->name);
@ -1254,7 +1248,6 @@ static int irc_join(struct link_server *server, struct line *line)
char *s_nick; char *s_nick;
const char *s_chan; const char *s_chan;
struct channel *channel; struct channel *channel;
struct nick *nick;
if (irc_line_count(line) != 2 && irc_line_count(line) != 3) if (irc_line_count(line) != 2 && irc_line_count(line) != 3)
return ERR_PROTOCOL; return ERR_PROTOCOL;
@ -1278,9 +1271,7 @@ static int irc_join(struct link_server *server, struct line *line)
return ERR_PROTOCOL; return ERR_PROTOCOL;
s_nick = nick_from_ircmask(line->origin); s_nick = nick_from_ircmask(line->origin);
nick = bip_calloc(sizeof(struct nick), 1); hash_insert(&channel->ovmasks, s_nick, 0);
nick->name = s_nick; /* not freeing s_nick */
hash_insert(&channel->nicks, s_nick, nick);
return OK_COPY; return OK_COPY;
} }
@ -1329,20 +1320,12 @@ static int irc_333(struct link_server *server, struct line *line)
return OK_COPY; return OK_COPY;
} }
static void nick_free(struct nick *nick)
{
if (nick->name)
free(nick->name);
free(nick);
}
static int irc_353(struct link_server *server, struct line *line) static int irc_353(struct link_server *server, struct line *line)
{ {
struct channel *channel; struct channel *channel;
struct nick *nick;
const char *names, *eon; const char *names, *eon;
size_t len; size_t len;
char *tmp; char *nick;
if (irc_line_count(line) != 5) if (irc_line_count(line) != 5)
return ERR_PROTOCOL; return ERR_PROTOCOL;
@ -1354,12 +1337,7 @@ static int irc_353(struct link_server *server, struct line *line)
if (!channel->running_names) { if (!channel->running_names) {
channel->running_names = 1; channel->running_names = 1;
hash_iterator_t hi; hash_clean(&channel->ovmasks);
for (hash_it_init(&channel->nicks, &hi); hash_it_item(&hi);
hash_it_next(&hi)) {
nick_free(hash_it_item(&hi));
}
hash_clean(&channel->nicks);
} }
/* TODO check that type is one of "=" / "*" / "@" */ /* TODO check that type is one of "=" / "*" / "@" */
@ -1368,7 +1346,7 @@ static int irc_353(struct link_server *server, struct line *line)
names = irc_line_elem(line, 4); names = irc_line_elem(line, 4);
while (*names) { while (*names) {
int ovmask = 0; long int ovmask = 0;
int flagchars = 1; int flagchars = 1;
/* some ircds (e.g. unreal) may display several flags for the /* some ircds (e.g. unreal) may display several flags for the
same nick */ same nick */
@ -1400,15 +1378,14 @@ static int irc_353(struct link_server *server, struct line *line)
eon++; eon++;
len = eon - names; len = eon - names;
tmp = bip_malloc(len + 1); nick = bip_malloc(len + 1);
memcpy(tmp, names, len); memcpy(nick, names, len);
tmp[len] = 0; nick[len] = 0;
nick = bip_malloc(sizeof(struct nick)); /* we just ignore names for nicks that are crazy long */
nick->name = tmp; if (len + 2 < NAMESIZE)
nick->ovmask = ovmask; hash_insert(&channel->ovmasks, nick, (void *)ovmask);
hash_insert(&channel->nicks, nick->name, nick);
while (*eon && *eon == ' ') while (*eon && *eon == ' ')
eon++; eon++;
names = eon; names = eon;
@ -1475,10 +1452,7 @@ static void channel_free(struct channel *c)
if (c->create_ts) if (c->create_ts)
free(c->create_ts); free(c->create_ts);
hash_iterator_t hi; hash_clean(&c->ovmasks);
for (hash_it_init(&c->nicks, &hi); hash_it_item(&hi); hash_it_next(&hi))
nick_free(hash_it_item(&hi));
hash_clean(&c->nicks);
free(c); free(c);
} }
@ -1487,7 +1461,6 @@ static int irc_part(struct link_server *server, struct line *line)
char *s_nick; char *s_nick;
const char *s_chan; const char *s_chan;
struct channel *channel; struct channel *channel;
struct nick *nick;
if (irc_line_count(line) != 2 && irc_line_count(line) != 3) if (irc_line_count(line) != 2 && irc_line_count(line) != 3)
return ERR_PROTOCOL; return ERR_PROTOCOL;
@ -1512,18 +1485,14 @@ static int irc_part(struct link_server *server, struct line *line)
if (!line->origin) if (!line->origin)
return ERR_PROTOCOL; return ERR_PROTOCOL;
s_nick = nick_from_ircmask(line->origin); s_nick = nick_from_ircmask(line->origin);
nick = hash_get(&channel->nicks, s_nick); if (!hash_includes(&channel->ovmasks, s_nick))
if (!nick) {
free(s_nick);
return ERR_PROTOCOL; return ERR_PROTOCOL;
} hash_remove(&channel->ovmasks, s_nick);
nick = hash_remove(&channel->nicks, s_nick);
free(s_nick);
log_part(LINK(server)->log, line->origin, s_chan, log_part(LINK(server)->log, line->origin, s_chan,
irc_line_count(line) == 3 ? irc_line_elem(line, 2) : NULL); irc_line_count(line) == 3 ?
irc_line_elem(line, 2) : NULL);
nick_free(nick);
return OK_COPY; return OK_COPY;
} }
@ -1579,10 +1548,8 @@ static int irc_mode(struct link_server *server, struct line *line)
const char *mode; const char *mode;
int add = 1; int add = 1;
unsigned cur_arg = 0; unsigned cur_arg = 0;
struct nick *nick;
array_t *mode_args = NULL; array_t *mode_args = NULL;
if (!irc_line_includes(line, 2)) if (!irc_line_includes(line, 2))
return ERR_PROTOCOL; return ERR_PROTOCOL;
@ -1621,6 +1588,8 @@ static int irc_mode(struct link_server *server, struct line *line)
* ^ ^ * ^ ^
* mode cur_arg * mode cur_arg
*/ */
const char *nick;
long int ovmask;
for (mode = irc_line_elem(line, 2); *mode; mode++) { for (mode = irc_line_elem(line, 2); *mode; mode++) {
switch (*mode) { switch (*mode) {
case '-': case '-':
@ -1637,43 +1606,48 @@ static int irc_mode(struct link_server *server, struct line *line)
case 'o': case 'o':
if (!irc_line_includes(line, cur_arg + 3)) if (!irc_line_includes(line, cur_arg + 3))
return ERR_PROTOCOL; return ERR_PROTOCOL;
nick = irc_line_elem(line, cur_arg + 3);
nick = hash_get(&channel->nicks, if (!hash_includes(&channel->ovmasks, nick))
irc_line_elem(line, cur_arg + 3));
if (!nick)
return ERR_PROTOCOL; return ERR_PROTOCOL;
ovmask = (long int)hash_remove(&channel->ovmasks, nick);
if (add) if (add)
nick->ovmask |= NICKOP; ovmask |= NICKOP;
else else
nick->ovmask &= ~NICKOP; ovmask &= ~NICKOP;
hash_insert(&channel->ovmasks, nick, (void *)ovmask);
cur_arg++; cur_arg++;
break; break;
case 'h': case 'h':
if (!irc_line_includes(line, cur_arg + 3)) if (!irc_line_includes(line, cur_arg + 3))
return ERR_PROTOCOL; return ERR_PROTOCOL;
nick = irc_line_elem(line, cur_arg + 3);
nick = hash_get(&channel->nicks, if (!hash_includes(&channel->ovmasks, nick))
irc_line_elem(line, cur_arg + 3));
if (!nick)
return ERR_PROTOCOL; return ERR_PROTOCOL;
ovmask = (long int)hash_get(&channel->ovmasks, nick);
if (add) if (add)
nick->ovmask |= NICKHALFOP; ovmask |= NICKHALFOP;
else else
nick->ovmask &= ~NICKHALFOP; ovmask &= ~NICKHALFOP;
hash_insert(&channel->ovmasks, nick, (void *)ovmask);
cur_arg++; cur_arg++;
break; break;
case 'v': case 'v':
if (!irc_line_includes(line, cur_arg + 3)) if (!irc_line_includes(line, cur_arg + 3))
return ERR_PROTOCOL; return ERR_PROTOCOL;
nick = irc_line_elem(line, cur_arg + 3);
nick = hash_get(&channel->nicks, if (!hash_includes(&channel->ovmasks, nick))
irc_line_elem(line, cur_arg + 3));
if (!nick)
return ERR_PROTOCOL; return ERR_PROTOCOL;
ovmask = (long int)hash_get(&channel->ovmasks, nick);
if (add) if (add)
nick->ovmask |= NICKVOICED; ovmask |= NICKVOICED;
else else
nick->ovmask &= ~NICKVOICED; ovmask &= ~NICKVOICED;
hash_insert(&channel->ovmasks, nick, (void *)ovmask);
cur_arg++; cur_arg++;
break; break;
case 'k': case 'k':
@ -1749,14 +1723,14 @@ static int irc_topic(struct link_server *server, struct line *line)
free(channel->create_ts); free(channel->create_ts);
channel->create_ts = irc_timestamp(); channel->create_ts = irc_timestamp();
log_topic(LINK(server)->log, line->origin, irc_line_elem(line, 1), topic); log_topic(LINK(server)->log, line->origin, irc_line_elem(line, 1),
topic);
return OK_COPY; return OK_COPY;
} }
static int irc_kick(struct link_server *server, struct line *line) static int irc_kick(struct link_server *server, struct line *line)
{ {
struct channel *channel; struct channel *channel;
struct nick *nick;
if (irc_line_count(line) != 3 && irc_line_count(line) != 4) if (irc_line_count(line) != 3 && irc_line_count(line) != 4)
return ERR_PROTOCOL; return ERR_PROTOCOL;
@ -1766,22 +1740,21 @@ static int irc_kick(struct link_server *server, struct line *line)
if (!channel) if (!channel)
return ERR_PROTOCOL; return ERR_PROTOCOL;
nick = hash_get(&channel->nicks, irc_line_elem(line, 2)); if (!hash_includes(&channel->ovmasks, irc_line_elem(line, 2)))
if (!nick)
return ERR_PROTOCOL; return ERR_PROTOCOL;
if (strcmp(nick->name, server->nick) == 0) { if (strcasecmp(irc_line_elem(line, 2), server->nick) == 0) {
log_kick(LINK(server)->log, line->origin, channel->name, log_kick(LINK(server)->log, line->origin, channel->name,
nick->name, irc_line_elem(line, 2),
irc_line_count(line) == 4 ? irc_line_elem(line, 3) : NULL); irc_line_count(line) == 4 ?
irc_line_elem(line, 3) : NULL);
hash_remove(&server->channels, channel->name); hash_remove(&server->channels, channel->name);
channel_free(channel); channel_free(channel);
return OK_COPY; return OK_COPY;
} }
hash_remove(&channel->nicks, nick->name); hash_remove(&channel->ovmasks, irc_line_elem(line, 2));
nick_free(nick);
log_kick(LINK(server)->log, line->origin, irc_line_elem(line, 1), log_kick(LINK(server)->log, line->origin, irc_line_elem(line, 1),
irc_line_elem(line, 2), irc_line_elem(line, 2),
irc_line_count(line) == 4 ? irc_line_elem(line, 3) : NULL); irc_line_count(line) == 4 ? irc_line_elem(line, 3) : NULL);
@ -1834,9 +1807,9 @@ static int irc_quit(struct link_server *server, struct line *line)
static int irc_nick(struct link_server *server, struct line *line) static int irc_nick(struct link_server *server, struct line *line)
{ {
struct channel *channel; struct channel *channel;
struct nick *nick;
hash_iterator_t hi; hash_iterator_t hi;
char *org_nick; char *org_nick;
const char *dst_nick;
if (irc_line_count(line) != 2) if (irc_line_count(line) != 2)
return ERR_PROTOCOL; return ERR_PROTOCOL;
@ -1845,24 +1818,20 @@ static int irc_nick(struct link_server *server, struct line *line)
return ERR_PROTOCOL; return ERR_PROTOCOL;
org_nick = nick_from_ircmask(line->origin); org_nick = nick_from_ircmask(line->origin);
dst_nick = irc_line_elem(line, 1);
for (hash_it_init(&server->channels, &hi); hash_it_item(&hi); for (hash_it_init(&server->channels, &hi); hash_it_item(&hi);
hash_it_next(&hi)) { hash_it_next(&hi)) {
channel = hash_it_item(&hi); channel = hash_it_item(&hi);
nick = hash_get(&channel->nicks, org_nick); if (!hash_includes(&channel->ovmasks, org_nick))
if (!nick)
continue; continue;
hash_remove(&channel->nicks, org_nick); hash_rename_key(&channel->ovmasks, org_nick, dst_nick);
free(nick->name); log_nick(LINK(server)->log, org_nick, channel->name, dst_nick);
nick->name = bip_strdup(irc_line_elem(line, 1));
hash_insert(&channel->nicks, nick->name, nick);
log_nick(LINK(server)->log, org_nick, channel->name,
irc_line_elem(line, 1));
} }
if (origin_is_me(line, server)) { if (origin_is_me(line, server)) {
free(server->nick); free(server->nick);
server->nick = bip_strdup(irc_line_elem(line, 1)); server->nick = bip_strdup(dst_nick);
if (LINK(server)->follow_nick && if (LINK(server)->follow_nick &&
(LINK(server)->away_nick == NULL || (LINK(server)->away_nick == NULL ||
strcmp(server->nick, LINK(server)->away_nick)) strcmp(server->nick, LINK(server)->away_nick))
@ -1879,7 +1848,6 @@ static int irc_nick(struct link_server *server, struct line *line)
static int irc_generic_quit(struct link_server *server, struct line *line) static int irc_generic_quit(struct link_server *server, struct line *line)
{ {
struct channel *channel; struct channel *channel;
struct nick *nick;
hash_iterator_t hi; hash_iterator_t hi;
char *s_nick; char *s_nick;
@ -1889,18 +1857,15 @@ static int irc_generic_quit(struct link_server *server, struct line *line)
if (!line->origin) if (!line->origin)
return ERR_PROTOCOL; return ERR_PROTOCOL;
s_nick = nick_from_ircmask(line->origin); s_nick = nick_from_ircmask(line->origin);
for (hash_it_init(&server->channels, &hi); hash_it_item(&hi); for (hash_it_init(&server->channels, &hi); hash_it_item(&hi);
hash_it_next(&hi)) { hash_it_next(&hi)) {
channel = hash_it_item(&hi); channel = hash_it_item(&hi);
nick = hash_get(&channel->nicks, s_nick); if (!hash_includes(&channel->ovmasks, s_nick))
if (!nick)
continue; continue;
hash_remove(&channel->nicks, s_nick); hash_remove(&channel->ovmasks, s_nick);
nick_free(nick);
log_quit(LINK(server)->log, line->origin, channel->name, log_quit(LINK(server)->log, line->origin, channel->name,
irc_line_includes(line, 1) ? irc_line_elem(line, 1) : NULL); irc_line_includes(line, 1) ?
irc_line_elem(line, 1) : NULL);
} }
free(s_nick); free(s_nick);
return OK_COPY; return OK_COPY;

View File

@ -15,7 +15,7 @@
#define IRC_H #define IRC_H
#include "connection.h" #include "connection.h"
#include "line.h" #include "line.h"
#include "log.h"
#define ERR_PROTOCOL (-1) #define ERR_PROTOCOL (-1)
#define ERR_AUTH (-2) #define ERR_AUTH (-2)
@ -40,11 +40,6 @@ struct server {
#define NICKHALFOP (1<<1) #define NICKHALFOP (1<<1)
#define NICKVOICED (1<<2) #define NICKVOICED (1<<2)
struct nick {
char *name;
int ovmask;
};
struct channel { struct channel {
char *name; char *name;
char *mode; char *mode;
@ -54,7 +49,7 @@ struct channel {
char type; char type;
char *creator; char *creator;
char *create_ts; char *create_ts;
hash_t nicks; hash_t ovmasks;
int running_names; int running_names;
}; };

View File

@ -415,6 +415,9 @@ void log_quit(log_t *logdata, const char *ircmask, const char *channel,
void log_nick(log_t *logdata, const char *ircmask, const char *channel, void log_nick(log_t *logdata, const char *ircmask, const char *channel,
const char *newnick) const char *newnick)
{ {
char *oldnick = nick_from_ircmask(ircmask);
if (hash_includes(&logdata->logfgs, oldnick))
hash_rename_key(&logdata->logfgs, oldnick, newnick);
snprintf(logdata->buffer, LOGLINE_MAXLEN, snprintf(logdata->buffer, LOGLINE_MAXLEN,
"%s -!- %s is now known as %s", "%s -!- %s is now known as %s",
timestamp(), ircmask, newnick); timestamp(), ircmask, newnick);
@ -559,6 +562,7 @@ void log_mode(log_t *logdata, const char *ircmask, const char *channel,
snprintf(tmpbuf, LOGLINE_MAXLEN, "%s -!- mode/%s [%s", timestamp(), snprintf(tmpbuf, LOGLINE_MAXLEN, "%s -!- mode/%s [%s", timestamp(),
channel, modes); channel, modes);
if (mode_args) {
for (i = 0; i < array_count(mode_args); i++) { for (i = 0; i < array_count(mode_args); i++) {
snprintf(tmpbuf2, LOGLINE_MAXLEN, "%s %s", tmpbuf, snprintf(tmpbuf2, LOGLINE_MAXLEN, "%s %s", tmpbuf,
(char *)array_get(mode_args, i)); (char *)array_get(mode_args, i));
@ -566,9 +570,9 @@ void log_mode(log_t *logdata, const char *ircmask, const char *channel,
tmpbuf = tmpbuf2; tmpbuf = tmpbuf2;
tmpbuf2 = tmp; tmpbuf2 = tmp;
} }
}
snprintf(logdata->buffer, LOGLINE_MAXLEN, "%s] by %s", tmpbuf, snprintf(logdata->buffer, LOGLINE_MAXLEN, "%s] by %s", tmpbuf, ircmask);
ircmask);
log_write(logdata, channel, logdata->buffer); log_write(logdata, channel, logdata->buffer);
free(tmpbuf); free(tmpbuf);

View File

@ -550,6 +550,14 @@ void hash_insert(hash_t *hash, const char *key, void *ptr)
list_add_first(&hash->lists[hash_func(key)], it); list_add_first(&hash->lists[hash_func(key)], it);
} }
int hash_includes(hash_t *hash, const char *key)
{
struct hash_item *hi;
list_t *list = &hash->lists[hash_func(key)];
hi = list_get(list, key);
return hi != NULL;
}
void *hash_get(hash_t *hash, const char *key) void *hash_get(hash_t *hash, const char *key)
{ {
struct hash_item *hi; struct hash_item *hi;
@ -667,6 +675,11 @@ list_t *hash_keys(hash_t *hash)
return ret; return ret;
} }
void hash_rename_key(hash_t *h, const char *oldk, const char *newk)
{
hash_insert(h, newk, hash_remove(h, oldk));
}
char *bip_strmaydup(char *s) char *bip_strmaydup(char *s)
{ {
if (!s) if (!s)

View File

@ -122,6 +122,7 @@ void hash_free(hash_t *h);
void hash_clean(hash_t *h); void hash_clean(hash_t *h);
hash_t *hash_new(int options); hash_t *hash_new(int options);
void hash_insert(hash_t *hash, const char *key, void *ptr); void hash_insert(hash_t *hash, const char *key, void *ptr);
int hash_includes(hash_t *hash, const char *key);
void *hash_get(hash_t *, const char *key); void *hash_get(hash_t *, const char *key);
void *hash_remove(hash_t *hash, const char *key); void *hash_remove(hash_t *hash, const char *key);
void *hash_remove_if_exists(hash_t *hash, const char *key); void *hash_remove_if_exists(hash_t *hash, const char *key);
@ -132,6 +133,7 @@ void *hash_it_item(hash_iterator_t *h);
const char *hash_it_key(hash_iterator_t *h); const char *hash_it_key(hash_iterator_t *h);
void *hash_it_remove(hash_iterator_t *li); void *hash_it_remove(hash_iterator_t *li);
list_t *hash_keys(hash_t *hash); list_t *hash_keys(hash_t *hash);
void hash_rename_key(hash_t *h, const char *oldk, const char *newk);
int is_valid_nick(char *str); int is_valid_nick(char *str);
int is_valid_username(char *str); int is_valid_username(char *str);
@ -183,8 +185,10 @@ static inline void *array_get(array_t *a, int index)
static inline void array_push(array_t *a, void *ptr) static inline void array_push(array_t *a, void *ptr)
{ {
array_ensure(a, a->elemc + 1); int idx = a->elemc;
a->elemv[a->elemc - 1] = ptr;
array_ensure(a, idx);
a->elemv[idx] = ptr;
} }
static inline void *array_pop(array_t *a) static inline void *array_pop(array_t *a)