/*
- * charybdis - an advanced ircd.
- * Copyright (c) 2016 William Pitcock <nenolod@dereferenced.org>.
+ * solanum - an advanced ircd.
+ * Copyright (c) 2016 Ariadne Conill <ariadne@dereferenced.org>.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
if (unescape) {
*out++ = unescape;
- *in++;
+ in++;
} else {
*out++ = *in++;
}
if (*ch == '\0')
return 2;
+ msgbuf->endp = &ch[strlen(ch)];
msgbuf->n_para = rb_string_to_array(ch, (char **)msgbuf->para, MAXPARA);
if (msgbuf->n_para == 0)
return 3;
return 0;
}
+/*
+ * Unparse the tail of a msgbuf perfectly, preserving framing details
+ * msgbuf->para[n] will reach to the end of the line
+ */
+
+void
+msgbuf_reconstruct_tail(struct MsgBuf *msgbuf, size_t n)
+{
+ if (msgbuf->endp == NULL || n > msgbuf->n_para)
+ return;
+
+ char *c;
+ const char *c_;
+
+ if (n == 0)
+ c_ = msgbuf->para[n];
+ else
+ c_ = msgbuf->para[n-1] + strlen(msgbuf->para[n-1]) + 1;
+
+ if (n == msgbuf->n_para && c_ == msgbuf->endp)
+ return;
+
+ msgbuf->para[n] = c_;
+ /* promote to non-const. msgbuf->endp witnesses that this is allowed */
+ c = msgbuf->endp - (msgbuf->endp - c_);
+
+ for ( ; c < msgbuf->endp; c++)
+ {
+ if (*c == '\0')
+ *c = ' ';
+ }
+}
+
+
/*
* Unparse msgbuf tags into a buffer
* returns the length of the tags written
return commit - buf;
}
+int
+msgbuf_unparse_linebuf_tags(char *buf, size_t buflen, void *data)
+{
+ struct MsgBuf_str_data *str_data = data;
+
+ return msgbuf_unparse_tags(buf, buflen, str_data->msgbuf, str_data->caps);
+}
+
/*
* unparse a MsgBuf and message prefix into a buffer
* if origin is NULL, me.name will be used.
* cmd should not be NULL.
* updates buflen to correctly allow remaining message data to be added
*/
-void
+int
msgbuf_unparse_prefix(char *buf, size_t *buflen, const struct MsgBuf *msgbuf, unsigned int capmask)
{
size_t tags_buflen;
- size_t tags_used = 0;
+ size_t used = 0;
+ int ret;
memset(buf, 0, *buflen);
tags_buflen = TAGSLEN + 1;
if (msgbuf->n_tags > 0)
- tags_used = msgbuf_unparse_tags(buf, tags_buflen, msgbuf, capmask);
+ used = msgbuf_unparse_tags(buf, tags_buflen, msgbuf, capmask);
- const size_t data_bufmax = (tags_used + DATALEN + 1);
+ const size_t data_bufmax = (used + DATALEN + 1);
if (*buflen > data_bufmax)
*buflen = data_bufmax;
- rb_snprintf_append(buf, *buflen, ":%s ", msgbuf->origin != NULL ? msgbuf->origin : me.name);
+ ret = rb_snprintf_append(buf, *buflen, ":%s ", msgbuf->origin != NULL ? msgbuf->origin : me.name);
+ if (ret > 0)
+ used += ret;
+
+ if (msgbuf->cmd != NULL) {
+ ret = rb_snprintf_append(buf, *buflen, "%s ", msgbuf->cmd);
+ if (ret > 0)
+ used += ret;
+ }
+
+ if (msgbuf->target != NULL) {
+ ret = rb_snprintf_append(buf, *buflen, "%s ", msgbuf->target);
+ if (ret > 0)
+ used += ret;
+ }
- if (msgbuf->cmd != NULL)
- rb_snprintf_append(buf, *buflen, "%s ", msgbuf->cmd);
+ if (used > data_bufmax - 1)
+ used = data_bufmax - 1;
- if (msgbuf->target != NULL)
- rb_snprintf_append(buf, *buflen, "%s ", msgbuf->target);
+ return used;
}
/*
return res;
}
+
+void
+msgbuf_cache_init(struct MsgBuf_cache *cache, const struct MsgBuf *msgbuf, const rb_strf_t *message)
+{
+ cache->msgbuf = msgbuf;
+ cache->head = NULL;
+ cache->overall_capmask = 0;
+
+ for (size_t i = 0; i < msgbuf->n_tags; i++) {
+ cache->overall_capmask |= msgbuf->tags[i].capmask;
+ }
+
+ for (int i = 0; i < MSGBUF_CACHE_SIZE; i++) {
+ cache->entry[i].caps = 0;
+ cache->entry[i].next = NULL;
+ }
+
+ rb_fsnprint(cache->message, sizeof(cache->message), message);
+}
+
+void
+msgbuf_cache_initf(struct MsgBuf_cache *cache, const struct MsgBuf *msgbuf, const rb_strf_t *message, const char *format, ...)
+{
+ va_list va;
+ rb_strf_t strings = { .format = format, .format_args = &va, .next = message };
+
+ va_start(va, format);
+ msgbuf_cache_init(cache, msgbuf, &strings);
+ va_end(va);
+}
+
+buf_head_t*
+msgbuf_cache_get(struct MsgBuf_cache *cache, unsigned int caps)
+{
+ struct MsgBuf_cache_entry *entry = cache->head;
+ struct MsgBuf_cache_entry *prev = NULL;
+ struct MsgBuf_cache_entry *result = NULL;
+ struct MsgBuf_cache_entry *tail = NULL;
+ int n = 0;
+
+ caps &= cache->overall_capmask;
+
+ while (entry != NULL) {
+ if (entry->caps == caps) {
+ /* Cache hit */
+ result = entry;
+ break;
+ }
+
+ tail = prev;
+ prev = entry;
+ entry = entry->next;
+ n++;
+ }
+
+ if (result == NULL) {
+ if (n < MSGBUF_CACHE_SIZE) {
+ /* Cache miss, allocate a new entry */
+ result = &cache->entry[n];
+ prev = NULL;
+ } else {
+ /* Cache full, replace the last entry */
+ result = prev;
+ if (tail != NULL)
+ tail->next = NULL;
+ prev = NULL;
+
+ rb_linebuf_donebuf(&result->linebuf);
+ }
+
+ /* Construct the line using the tags followed by the no tags line */
+ struct MsgBuf_str_data msgbuf_str_data = { .msgbuf = cache->msgbuf, .caps = caps };
+ rb_strf_t strings[2] = {
+ { .func = msgbuf_unparse_linebuf_tags, .func_args = &msgbuf_str_data, .length = TAGSLEN + 1, .next = &strings[1] },
+ { .format = cache->message, .length = DATALEN + 1, .next = NULL }
+ };
+
+ result->caps = caps;
+ rb_linebuf_newbuf(&result->linebuf);
+ rb_linebuf_put(&result->linebuf, &strings[0]);
+ }
+
+ /* Move it to the top */
+ if (cache->head != result) {
+ if (prev != NULL) {
+ prev->next = result->next;
+ }
+ result->next = cache->head;
+ cache->head = result;
+ }
+
+ return &result->linebuf;
+}
+
+void
+msgbuf_cache_free(struct MsgBuf_cache *cache)
+{
+ struct MsgBuf_cache_entry *entry = cache->head;
+
+ while (entry != NULL) {
+ rb_linebuf_donebuf(&entry->linebuf);
+ entry = entry->next;
+ }
+
+ cache->head = NULL;
+}