+static void Cbuf_LinkAdd(cbuf_cmd_t *add, cbuf_cmd_t **list)
+{
+ if(!*list)
+ *list = add;
+ else
+ {
+ cbuf_cmd_t *temp = add->prev;
+ add->prev->next = *list;
+ add->prev = (*list)->prev;
+ (*list)->prev->next = add;
+ (*list)->prev = temp;
+ }
+}
+
+static void Cbuf_LinkInsert(cbuf_cmd_t *insert, cbuf_cmd_t **list)
+{
+ // Same algorithm, but backwards
+ if(*list)
+ Cbuf_LinkAdd(*list, &insert);
+ *list = insert;
+}
+
+static cbuf_cmd_t *Cbuf_LinkPop(cbuf_cmd_t *node, cbuf_cmd_t **list)
+{
+ node = *list;
+ *list = node->next;
+ (*list)->prev = node->prev;
+ (*list)->prev->next = *list;
+ if(*list == node)
+ *list = NULL;
+ return node;
+}
+
+/*
+============
+Cbuf_ParseText
+
+Parses Quake console command-line
+Allocates a cyclic doubly linked list node
+for each individual command. Returns a
+pointer to the head.
+============
+*/
+static cbuf_cmd_t *Cbuf_ParseText(cmd_state_t *cmd, const char *text)
+{
+ int i = 0;
+ cbuf_t *cbuf = cmd->cbuf;
+ cbuf_cmd_t *head = NULL;
+ cbuf_cmd_t *current = NULL;
+ qboolean quotes = false;
+ qboolean comment = false;
+ qboolean escaped = false;
+ qboolean mark = false;
+ qboolean noalloc;
+ char *offset = NULL;
+ size_t cmdsize = 0;
+
+ if(cbuf->pending)
+ {
+ // If not from the same interpreter, weird things could happen.
+ if(cbuf->start->source != cmd)
+ cbuf->pending = false;
+ }
+
+ /*
+ * Allow escapes in quotes. Ignore newlines and
+ * comments. Return NULL if input consists solely
+ * of either of those, and ignore blank input.
+ */
+ while(text[i])
+ {
+ noalloc = cbuf->pending;
+
+ switch (text[i])
+ {
+ case '/':
+ if(!quotes && text[i+1] == '/' && (i == 0 || ISWHITESPACE(text[i-1])))
+ {
+ comment = true;
+ cbuf->pending = false;
+ mark = true;
+ }
+ break;
+ case '\r':
+ case '\n':
+ comment = false;
+ quotes = false;
+ cbuf->pending = false;
+ mark = true;
+ break;
+ }
+
+ if(!comment)
+ {
+ switch (text[i])
+ {
+ case ';':
+ if(!quotes)
+ {
+ cbuf->pending = false;
+ mark = true;
+ }
+ break;
+ case '"':
+ if (!escaped)
+ quotes = !quotes;
+ else
+ escaped = false;
+ break;
+ case '\\':
+ if (!escaped && quotes)
+ escaped = true;
+ else if (escaped)
+ escaped = false;
+ break;
+ }
+
+ if(!mark)
+ {
+ // If there's no trailing newline, mark it as pending
+ if(text[i+1] == 0)
+ {
+ cbuf->pending = true;
+ mark = true;
+ }
+
+ if(!offset)
+ // Allow i to run until the end of a comment
+ offset = (char *)&text[i];
+ cmdsize++;
+ }
+ }
+
+ if(!current)
+ {
+ if(noalloc)
+ current = cbuf->start;
+ else if(offset)
+ {
+ if(cbuf->free)
+ {
+ current = Cbuf_LinkPop(current, &cbuf->free);
+ current->size = 0;
+ }
+ else
+ {
+ current = (cbuf_cmd_t *)Z_Malloc(sizeof(cbuf_cmd_t));
+ current->size = 0;
+ }
+ }
+ }
+
+ // Create a cyclic doubly linked list.
+ if(mark)
+ {
+ if(offset)
+ {
+ // Data write stage
+ strlcpy(¤t->text[current->size], offset, cmdsize + 1);
+ current->size += cmdsize;
+ current->source = cmd;
+
+ if(!noalloc)
+ {
+ // Link stage
+ current->prev = current->next = current;
+ Cbuf_LinkAdd(current, &head);
+ }
+ cbuf->size += cmdsize;
+ cmdsize = 0;
+ }
+
+ // Reset stage
+ offset = NULL;
+ escaped = false;
+ mark = false;
+ current = NULL;
+ }
+ i++;
+ }
+
+ return head;
+}
+