summaryrefslogtreecommitdiff
path: root/common.c
diff options
context:
space:
mode:
authorPřemysl Janouch <p.janouch@gmail.com>2015-05-04 02:17:55 +0200
committerPřemysl Janouch <p.janouch@gmail.com>2015-05-04 02:17:55 +0200
commitc9a02141f936a1b887b7aebce860016a64fc25f5 (patch)
tree38616693124860dc872ae37ea1b76e1107e5a748 /common.c
parent56a67d56e0ac5da860756418014f7f1ba2586a33 (diff)
downloadxK-c9a02141f936a1b887b7aebce860016a64fc25f5.tar.gz
xK-c9a02141f936a1b887b7aebce860016a64fc25f5.tar.xz
xK-c9a02141f936a1b887b7aebce860016a64fc25f5.zip
degesch: move some code to common.c
Diffstat (limited to 'common.c')
-rw-r--r--common.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/common.c b/common.c
index 936b840..4e87904 100644
--- a/common.c
+++ b/common.c
@@ -487,6 +487,170 @@ split_str (const char *s, char delimiter, struct str_vector *out)
str_vector_add (out, begin);
}
+static ssize_t
+str_vector_find (const struct str_vector *v, const char *s)
+{
+ for (size_t i = 0; i < v->len; i++)
+ if (!strcmp (v->vector[i], s))
+ return i;
+ return -1;
+}
+
+// --- CTCP decoding -----------------------------------------------------------
+
+#define CTCP_M_QUOTE '\020'
+#define CTCP_X_DELIM '\001'
+#define CTCP_X_QUOTE '\\'
+
+struct ctcp_chunk
+{
+ LIST_HEADER (struct ctcp_chunk)
+
+ bool is_extended; ///< Is this a tagged extended message?
+ struct str tag; ///< The tag, if any
+ struct str text; ///< Message contents
+};
+
+static struct ctcp_chunk *
+ctcp_chunk_new (void)
+{
+ struct ctcp_chunk *self = xcalloc (1, sizeof *self);
+ str_init (&self->tag);
+ str_init (&self->text);
+ return self;
+}
+
+static void
+ctcp_chunk_destroy (struct ctcp_chunk *self)
+{
+ str_free (&self->tag);
+ str_free (&self->text);
+ free (self);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static void
+ctcp_low_level_decode (const char *message, struct str *output)
+{
+ bool escape = false;
+ for (const char *p = message; *p; p++)
+ {
+ if (escape)
+ {
+ switch (*p)
+ {
+ case '0': str_append_c (output, '\0'); break;
+ case 'r': str_append_c (output, '\r'); break;
+ case 'n': str_append_c (output, '\n'); break;
+ default: str_append_c (output, *p);
+ }
+ escape = false;
+ }
+ else if (*p == CTCP_M_QUOTE)
+ escape = true;
+ else
+ str_append_c (output, *p);
+ }
+}
+
+static void
+ctcp_intra_decode (const char *chunk, size_t len, struct str *output)
+{
+ bool escape = false;
+ for (size_t i = 0; i < len; i++)
+ {
+ char c = chunk[i];
+ if (escape)
+ {
+ if (c == 'a')
+ str_append_c (output, CTCP_X_DELIM);
+ else
+ str_append_c (output, c);
+ escape = false;
+ }
+ else if (c == CTCP_X_QUOTE)
+ escape = true;
+ else
+ str_append_c (output, c);
+ }
+}
+
+static void
+ctcp_parse_tagged (const char *chunk, size_t len, struct ctcp_chunk *output)
+{
+ // We may search for the space before doing the higher level decoding,
+ // as it doesn't concern space characters at all
+ size_t tag_end = len;
+ for (size_t i = 0; i < len; i++)
+ if (chunk[i] == ' ')
+ {
+ tag_end = i;
+ break;
+ }
+
+ output->is_extended = true;
+ ctcp_intra_decode (chunk, tag_end, &output->tag);
+ if (tag_end++ != len)
+ ctcp_intra_decode (chunk + tag_end, len - tag_end, &output->text);
+}
+
+static struct ctcp_chunk *
+ctcp_parse (const char *message)
+{
+ struct str m;
+ str_init (&m);
+ ctcp_low_level_decode (message, &m);
+
+ struct ctcp_chunk *result = NULL, *result_tail = NULL;
+
+ size_t start = 0;
+ bool in_ctcp = false;
+ for (size_t i = 0; i < m.len; i++)
+ {
+ char c = m.str[i];
+ if (c != CTCP_X_DELIM)
+ continue;
+
+ // Remember the current state
+ size_t my_start = start;
+ bool my_is_ctcp = in_ctcp;
+
+ start = i + 1;
+ in_ctcp = !in_ctcp;
+
+ // Skip empty chunks
+ if (my_start == i)
+ continue;
+
+ struct ctcp_chunk *chunk = ctcp_chunk_new ();
+ if (my_is_ctcp)
+ ctcp_parse_tagged (m.str + my_start, i - my_start, chunk);
+ else
+ ctcp_intra_decode (m.str + my_start, i - my_start, &chunk->text);
+ LIST_APPEND_WITH_TAIL (result, result_tail, chunk);
+ }
+
+ // Finish the last text part. We ignore unended tagged chunks.
+ // TODO: don't ignore them, e.g. a /me may get cut off
+ if (!in_ctcp && start != m.len)
+ {
+ struct ctcp_chunk *chunk = ctcp_chunk_new ();
+ ctcp_intra_decode (m.str + start, m.len - start, &chunk->text);
+ LIST_APPEND_WITH_TAIL (result, result_tail, chunk);
+ }
+
+ str_free (&m);
+ return result;
+}
+
+static void
+ctcp_destroy (struct ctcp_chunk *list)
+{
+ LIST_FOR_EACH (struct ctcp_chunk, iter, list)
+ ctcp_chunk_destroy (iter);
+}
+
// --- Advanced configuration --------------------------------------------------
// This is a new configuration format, superseding the one currently present