/* * This file is part of uIRC. (https://git.redxen.eu/caskd/uIRC) * Copyright (c) 2019, 2020 Alex-David Denes * * uIRC is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * uIRC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with uIRC. If not, see . */ #include "memory.h" // uirc_memory_*() #include "tokenizers.h" // uirc_tokenizer_*() #include "types.h" // IRC_Message #include // assert() #include // malloc_string() strtok_mr() #include // isalnum() #include // NULL #include // malloc() #include // strlen() static void free_ctx(IRC_Message* msg, char* str); static void skip_spaces(char** addr); IRC_Message* uirc_tokenizer_message(const char* str) { assert(str != NULL); IRC_Message* const m = malloc(sizeof(IRC_Message)); size_t len = strlen(str); // Ignore CRLF at the end, irrelevant for parsing if (str[len - 1] == '\n') { len--; if (str[len - 1] == '\r') len--; } char* const ws = malloc_string(str, len); // NOTE: Some compilers might warn you about modifying contents of p because it points to ws which // is "immutable". This is safe to ignore as the purpose of it is to not use ws for tokenizing and // to save the original pointer which can be freed char* p = ws; if (ws == NULL || m == NULL) { free(ws); free(m); return NULL; } memset(m, 0, sizeof(IRC_Message)); #ifdef UIRC_FEATURE_IRCV3 if (*p == '@') { p++; const char* tmp = NULL; if ((tmp = strtok_mr(&p, " ")) == NULL || (m->tag_list = uirc_tokenizer_tag_list(tmp)) == NULL) { free_ctx(m, ws); return NULL; } } skip_spaces(&p); #endif /* UIRC_FEATURE_IRCV3 */ if (*p == ':') { const char* tmp = NULL; p++; if ((tmp = strtok_mr(&p, " ")) == NULL || (m->source = uirc_tokenizer_user(tmp)) == NULL) { free_ctx(m, ws); return NULL; } skip_spaces(&p); } if (isalnum(*p)) { const char* tmp = NULL; if ((tmp = strtok_mr(&p, " ")) == NULL || (m->command = malloc_string(tmp, strlen(tmp))) == NULL) { free_ctx(m, ws); return NULL; } } else { free_ctx(m, ws); return NULL; } skip_spaces(&p); short i; for (i = 0; i < IRC_MAXARGS && *p;) { if (i == IRC_MAXARGS - 1 || *p == ':') { if (*p == ':') { p++; // trailing is supposed to represent a early ending of spaced arguments, not if there's any arguments at all m->trailing = true; } if ((m->args[i++] = malloc_string(p, strlen(p))) == NULL) { free_ctx(m, ws); return NULL; } break; } else { const char* tmp = NULL; if ((tmp = strtok_mr(&p, " ")) == NULL || (m->args[i++] = malloc_string(tmp, strlen(tmp))) == NULL) { free_ctx(m, ws); return NULL; } skip_spaces(&p); } m->args[i] = NULL; } free(ws); return m; } static void free_ctx(IRC_Message* msg, char* str) { uirc_free_message(msg); free(msg); free(str); } static void skip_spaces(char** addr) { for (; **addr == ' '; (*addr)++) ; }