struct channel *chan = findChannel(args[0]);
if (!chan)
return err_nosuchchannel(client, args[0]), sendbuf(client), 0;
- if (!map_get(&chan->members, client->key))
+ if (!chan->members.has(client))
return err_notonchannel(client, chan), sendbuf(client), 0;
- if (map_set(&chan->o, client->key, client, NULL) == -1)
- abort();
+ chan->o.add(client);
copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +o %s\r\n", chan->name, client->nick);
sendbuf_chan(chan);
} else if(strcmp(command, "JOIN") == 0) {
if (!args[0])
return err_needmoreparams(client, command), sendbuf(client), 0;
if (!strcmp(args[0], "0")) {
- for (struct channel *chan; (chan = (struct channel *)map_pop(&client->channels)); )
+ for (auto chan : client->channels.pop_view())
channel_part(client, chan, "I fell for a classic prank", NULL);
return 0;
}
channel_sendtopic(client, chan, 0);
} else {
// TODO: membership check, maybe?
- if (chan->dmode & DMODE_TOPICPROT && !map_get(&chan->o, client->key))
+ if (chan->dmode & DMODE_TOPICPROT && !chan->o.has(client))
return 0; // FIXME: error 482
channel_topic(client, chan, args[1], client->nick, get_time());
}
if (!chan)
return err_nosuchchannel(client, args[1]), sendbuf(client), 0;
if (chan->dmode & DMODE_INVITEONLY) {
- if (!map_get(&chan->o, client->key))
+ if (!chan->o.has(client))
return err_chanoprivsneeded(client, chan), sendbuf(client), 0;
} else {
- if (!map_get(&chan->members, client->key))
+ if (!chan->members.has(client))
return err_notonchannel(client, chan), sendbuf(client), 0;
}
struct client_data *cl = findClient(args[0]);
if (!cl)
return err_nosuchnick(client, args[0]), sendbuf(client), 0;
- if (map_get(&chan->members, cl->key))
+ if (chan->members.has(cl))
return err_useronchannel(client, cl->nick, chan), sendbuf(client), 0;
- if (map_set(&chan->invitees, cl->nick, cl, NULL) == -1)
- abort();
- if (map_set(&cl->invites, chan->name, chan, NULL) == -1)
- abort();
+ chan->invitees.add(cl);
+ cl->invites.add(chan);
rpl_inviting(client, cl, chan);
sendbuf(client);
msg_invite(client, cl, chan);
struct channel *chan = findChannel(args[0]);
if (!chan)
return err_nosuchchannel(client, args[0]), sendbuf(client), 0;
- if (!map_get(&chan->o, client->key))
+ if (!chan->o.has(client))
return err_chanoprivsneeded(client, chan), sendbuf(client), 0;
- if (!map_get(&chan->members, client->key))
+ if (!chan->members.has(client))
return err_notonchannel(client, chan), sendbuf(client), 0;
char *p = args[1];
do {
if (match_list(client, chan->listb) && !match_list(client, chan->liste))
return err_cannotsendtochan(client, chan->name), sendbuf(client), 0;
if (chan->dmode & DMODE_INVITEONLY
- && !map_get(&chan->invitees, client->key) && !match_list(client, chan->listI))
+ && !chan->invitees.has(client) && !match_list(client, chan->listI))
return err_cannotsendtochan(client, chan->name), sendbuf(client), 0;
- if (chan->dmode & DMODE_NOEXTERNAL && !map_get(&chan->members, client->key))
+ if (chan->dmode & DMODE_NOEXTERNAL && !chan->members.has(client))
return err_cannotsendtochan(client, chan->name), sendbuf(client), 0;
- if (chan->dmode & DMODE_MODERATED && !map_get(&chan->v, client->key) && !map_get(&chan->o, client->key))
+ if (chan->dmode & DMODE_MODERATED && !chan->v.has(client) && !chan->o.has(client))
return err_cannotsendtochan(client, chan->name), sendbuf(client), 0;
#ifdef HAS_LUA
switch (admin_hook("sendtochannel", client, 0, chan, args[1])) {
struct channel *chan = findChannel(nick);
if (!chan)
return err_nosuchchannel(client, nick), sendbuf(client), 0;
- if (!map_get(&chan->o, client->key))
+ if (!chan->o.has(client))
return 0; // TODO: error
if (argc == 2) { // Type A channel modes
int all = !strcmp(args[1], "+0");
return err_notregistered(client), sendbuf(client), 0;
rpl_liststart(client), sendbuf(client);
for (auto chan : channel_map)
- if (client->umode & UMODE_OPER || !(chan->dmode & DMODE_SECRET) || map_get(&chan->members, client->key))
+ if (client->umode & UMODE_OPER || !(chan->dmode & DMODE_SECRET) || chan->members.has(client))
rpl_list(client, chan), sendbuf(client);
rpl_listend(client), sendbuf(client);
} else if(strcmp(command, "NAMES") == 0) {
if ((p = strchr(channel, ',')))
*p++ = '\0';
struct channel *chan = findChannel(channel);
- if (chan && (!(chan->dmode & DMODE_SECRET) || map_get(&chan->members, client->key))) {
+ if (chan && (!(chan->dmode & DMODE_SECRET) || chan->members.has(client))) {
channel_sendnames(client, chan);
} else {
rpl_endofnames(client, channel);
int visible = client->umode & UMODE_OPER || !(cl->umode & UMODE_INVISIBLE) || client == cl;
if (!visible) {
for (auto chan : cl->channels) {
- if (map_get(&client->channels, chan->key)) {
+ if (client->channels.has(chan)) {
visible = 1;
break;
}
if (cl->away)
rpl_away(client, cl), sendbuf(client);
for (auto chan : cl->channels) {
- if ((cl->umode & UMODE_INVISIBLE || chan->dmode & DMODE_SECRET) && !map_get(&chan->members, client->key))
+ if ((cl->umode & UMODE_INVISIBLE || chan->dmode & DMODE_SECRET) && !chan->members.has(client))
continue;
rpl_whoischannels(client, cl, chan), sendbuf(client);
}
*p++ = '\0';
if (client->monitor.add(nick))
monitor_count++;
- struct client_data *cl = (struct client_data *)map_get(&client_map, nick);
+ struct client_data *cl = client_map.get(nick);
if (cl)
rpl_mononline(client, cl), sendbuf(client);
else
break;
case 'S':
for (auto key : client->monitor) {
- struct client_data *cl = (struct client_data *)map_get(&client_map, key);
+ struct client_data *cl = client_map.get(key);
if (cl)
rpl_mononline(client, cl), sendbuf(client);
else
stringlower(key);
// TODO: make the cia nickname a define
- if (!strcmp(key, "cia") || map_get(&client_map, key)) {
+ if (!strcmp(key, "cia") || client_map.get(key)) {
err_nicknameinuse(client, nickname), sendbuf(client);
return;
}
sendbuf(client);
sendbuf_visible(client);
rpl_monoffline(0, client->key), sendbuf_monitor(client->key, 0, 0, 0);
- map_unset(&client_map, client->key, NULL);
+ client_map.remove(client);
for (auto chan : client->channels) {
- map_unset(&chan->members, client->key, NULL);
- if (map_set(&chan->members, key, client, NULL) == -1)
- abort();
- void *old;
- map_unset(&chan->o, client->key, &old);
- if (old)
- if (map_set(&chan->o, key, client, NULL) == -1)
- abort();
- map_unset(&chan->v, client->key, &old);
- if (old)
- if (map_set(&chan->v, key, client, NULL) == -1)
- abort();
+ chan->members.rename(client, key);
+ chan->o.rename(client, key);
+ chan->v.rename(client, key);
}
free(client->nick);
free(client->key);
client->key = mystrdup(key);
if (!client->key)
abort();
- if (map_set(&client_map, client->key, client, 0) == -1)
- abort();
+ client_map.add(client);
rpl_mononline(0, client), sendbuf_monitor(client->key, 0, 0, 0);
return;
}
sendbuf_list_sans_self_caps(client, chan->members, 0, 0, CAP_EXTENDEDJOIN|CAP_CHGHOST);
msg_join_extended(client, chan);
sendbuf_list_sans_self_caps(client, chan->members, 0, CAP_EXTENDEDJOIN, CAP_CHGHOST);
- if (map_get(&chan->v, client->key)) {
+ if (chan->v.has(client)) {
copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +v %s\r\n", chan->name, client->nick);
sendbuf_list_sans_self_caps(client, chan->members, 0, 0, CAP_CHGHOST);
}
- if (map_get(&chan->o, client->key)) {
+ if (chan->o.has(client)) {
copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +o %s\r\n", chan->name, client->nick);
sendbuf_list_sans_self_caps(client, chan->members, 0, 0, CAP_CHGHOST);
}
char key[33];
memcpy(key, channel, chanlen+1);
stringlower(key);
- struct channel *chan = (struct channel *)map_get(&channel_map, key);
+ struct channel *chan = channel_map.get(key);
int newchan = !chan;
if (!chan) {
chan = channel_new(channel, key, get_time());
chan->dmode &= ~DMODE_PERSIST;
- if (map_set(&channel_map, key, chan, NULL) == -1)
- abort();
- if (map_set(&chan->o, client->key, client, NULL) == -1)
- abort();
+ channel_map.add(chan);
+ chan->o.add(client);
} else {
- if (map_get(&chan->members, client->key))
+ if (chan->members.has(client))
return;
if (chan->chankey && (!chankey || strcmp(chan->chankey, chankey) == 0))
return err_badchannelkey(client, chan), sendbuf(client);
if (match_list(client, chan->listb) && !match_list(client, chan->liste))
return err_bannedfromchan(client, chan), sendbuf(client);
if (chan->dmode & DMODE_INVITEONLY
- && !map_get(&chan->invitees, client->key) && !match_list(client, chan->listI))
+ && !chan->invitees.has(client) && !match_list(client, chan->listI))
return err_inviteonlychan(client, chan), sendbuf(client);
}
- if (map_set(&chan->members, client->key, client, NULL) == -1)
- abort();
- if (map_set(&client->channels, key, chan, NULL) == -1)
- abort();
+ chan->members.add(client);
+ client->channels.add(chan);
chan->member_count++;
// send JOIN to everyone
msg_join(client, chan);
// TODO: send channel modes to the joining client
// apply +w modes
if (match_list(client, chan->listwo)) {
- if (map_set(&chan->o, client->key, client, NULL) == -1)
- abort();
+ chan->o.add(client);
copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +o %s\r\n", chan->name, client->nick);
sendbuf_chan(chan);
}
if (match_list(client, chan->listwv)) {
- if (map_set(&chan->v, client->key, client, NULL) == -1)
- abort();
+ chan->v.add(client);
copy_strf(&databuf, ":cia!cia@cia.gov MODE %s +v %s\r\n", chan->name, client->nick);
sendbuf_chan(chan);
}
}
void channel_part(struct client_data *client, struct channel *chan, const char *reason, struct client_data *kicker)
{
- if (!map_get(&chan->members, client->key))
+ if (!chan->members.has(client))
return;
if (kicker) {
msg_kick(kicker, client, chan, reason);
msg_part(client, chan, *reason ? reason : NULL);
sendbuf_chan(chan);
}
- map_unset(&client->channels, chan->key, NULL);
- map_unset(&chan->members, client->key, NULL);
- map_unset(&chan->o, client->key, NULL);
- map_unset(&chan->v, client->key, NULL);
+ client->channels.remove(chan);
+ chan->members.remove(client);
+ chan->o.remove(client);
+ chan->v.remove(client);
chan->member_count--;
if (!chan->member_count && !(chan->dmode & DMODE_PERSIST))
channel_kill(chan);
}
void channel_kill(struct channel *chan)
{
- map_unset(&channel_map, chan->key, NULL);
+ channel_map.remove(chan);
for (auto cl : chan->invitees)
- map_unset(&cl->invites, chan->key, NULL);
+ cl->invites.remove(chan);
free(chan->key);
free(chan->name);
free(chan->topic);
err_nosuchnick(client, nick), sendbuf(client);
break;
}
- if (!map_get(&chan->members, cl->key)) {
+ if (!chan->members.has(cl)) {
if (client)
err_usernotinchannel(client, cl->nick, chan), sendbuf(client);
break;
}
- if (map_set(*p == 'o' ? &chan->o : &chan->v, cl->key, is_plus ? cl : NULL, NULL) == -1)
- abort();
+ clientmap &clmap = *p == 'o' ? chan->o : chan->v;
+ if (is_plus)
+ clmap.add(cl);
+ else
+ clmap.remove(cl);
copy_strf(&databuf, ":%s!%s@%s MODE %s %s%s %s\r\n",
n, u, h, chan->name, is_plus?"+":"-", which, cl->nick);
sendbuf_chan(chan);
return NULL;
memcpy(key, nickname, len+1);
stringlower(key);
- return (struct client_data *)map_get(&client_map, key);
+ return client_map.get(key);
}
struct channel *findChannel(const char *channel)
{
char key[33];
memcpy(key, channel, chanlen+1);
stringlower(key);
- return (struct channel *)map_get(&channel_map, key);
+ return channel_map.get(key);
}
struct map_base {
struct map_value *ptr;
size_t len, cap;
- size_t size() {
- return len;
- }
};
typedef struct map_iter {
map_base *root;
/* Associate a new value with a given key.
* Returns 0 on success, -1 on allocation error.
* If oldvalue is not NULL, it receives the old value of key.
+ * No allocation failures on value = NULL.
* Dynarr complexity: O(n) */
NODISCARD int map_set(map_base *root, const char *key, const void *value, void **oldvalue);
-/* Remove a key.
- * Same as associating a key to NULL. No allocation failures. */
-#define map_unset(root, key, oldvalue) ((void)map_set((root), (key), 0, (oldvalue)))
/* Removes all keys from the map and frees any associated memory.
* The map_t is reset to zero-initialized state and can be used again.
* Dynarr complexity: O(n) */
* Returns 0 on end of iteration, 1 otherwise.
*/
NODISCARD int map_foriter(map_iter_t *f, const char **outkey, void *outvalue);
-#define map_foreach(rootp, outkey, outvalue) \
- for (map_iter_t map_itertmp = { .root = rootp, .index = 0 }; map_foriter(&map_itertmp, outkey, outvalue); )
-/* Return a value from the map.
- * No guarantees about which one!
- * You don't get a key and the association is not removed.
- * Returns 0 if the map is empty.
- *
- * Typical use:
- * for (value_type *v; v = map_pop(&map); )
- * map_unset(&map, v->key, 0);
- * free(v);
- * }
- * map_free(&map);
- */
-NODISCARD void *map_pop(map_base *root);
#include <utility>
+#include <iterator>
#include <stdlib.h>
-template<typename T>
-struct iterator_base {
- T *ptr;
- iterator_base &operator++() { ++ptr; return *this; }
- bool operator!=(iterator_base other) { return ptr != other.ptr; }
-};
struct client_data;
struct channel;
-struct clientmap : map_base {
- struct iterator : iterator_base<map_value> {
- auto operator*() { return (struct client_data *)ptr->value; }
+template<typename T, typename V>
+struct map_base2 : map_base {
+ using value_type = V;
+ struct iterator {
+ using iterator_category = std::bidirectional_iterator_tag;
+ using value_type = V;
+ using difference_type = ptrdiff_t;
+ using pointer = void;
+ using reference = V;
+ iterator &operator++() { ++ptr; return *this; }
+ iterator operator++(int) { auto it = *this; ++ptr; return it; }
+ iterator &operator--() { --ptr; return *this; }
+ iterator operator--(int) { auto it = *this; --ptr; return it; }
+ bool operator==(const iterator& other) const = default;
+ reference operator*() const { return T::to_value(ptr); }
+ map_value *ptr;
};
+ static_assert(std::bidirectional_iterator<iterator>);
auto begin() { return iterator{ptr}; }
auto end() { return iterator{ptr+len}; }
-};
-struct channelmap : map_base {
- struct iterator : iterator_base<map_value> {
- auto operator*() { return (struct channel *)ptr->value; }
+ struct reverse {
+ map_base2 &obj;
+ auto begin() { return std::make_reverse_iterator(obj.end()); }
+ auto end() { return std::make_reverse_iterator(obj.begin()); }
};
- auto begin() { return iterator{ptr}; }
- auto end() { return iterator{ptr+len}; }
-};
-struct stringset : map_base {
+ // Iterate over keys of the map in such a way that you can delete them.
+ auto pop_view() {
+ return reverse{*this};
+ }
void clear() {
map_free(this);
}
- bool has(const char *key) {
- return !!map_get(this, key);
+ size_t size() {
+ return len;
+ }
+protected:
+ static const char *to_key(V v) {
+ return v->key;
+ }
+ static V to_value(map_value *v) {
+ return static_cast<V>(v->value);
}
- bool add(const char *key) {
+public:
+ bool has(V v) {
+ return !!map_get(this, T::to_key(v));
+ }
+ bool add(V v) {
void *old;
- if (map_set(this, key, (void *)1, &old) == -1)
+ if (map_set(this, T::to_key(v), v, &old) == -1)
abort();
return !old;
}
- bool remove(const char *key) {
+ bool remove(V v) {
void *old;
- map_unset(this, key, &old);
+ (void)map_set(this, T::to_key(v), 0, &old);
return !!old;
}
- struct iterator : iterator_base<map_value> {
- auto operator*() { return (const char *)ptr->key; }
- };
- auto begin() { return iterator{ptr}; }
- auto end() { return iterator{ptr+len}; }
+
+ V get(const char *k) {
+ return (V)map_get(this, k);
+ }
+};
+
+struct clientmap : map_base2<clientmap, struct client_data *> {
+ void rename(struct client_data *v, const char *newk) {
+ if (remove(v) && map_set(this, newk, v, 0) == -1)
+ abort();
+ }
+};
+struct channelmap : map_base2<channelmap, struct channel *> {
+};
+struct stringset : map_base2<stringset, const char *> {
+protected:
+ friend map_base2;
+ static const char *to_key(const char *v) {
+ return v;
+ }
+ static const char *to_value(map_value *v) {
+ return v->key;
+ }
+public:
+ const char *get(const char *k) = delete;
};
#endif