Fix CRLF removal bug, change some of the assembly and parsing logic and add tests back

This commit is contained in:
Alex D. 2021-03-05 11:26:31 +00:00
parent e392076e19
commit 7109ae6b70
Signed by: caskd
GPG Key ID: F92BA85F61F4C173
12 changed files with 357 additions and 37 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16)
project(
uIRC
VERSION 0.2.4
VERSION 0.3.0
DESCRIPTION "Simple and lightweight IRC protocol helper"
LANGUAGES C
)
@ -11,11 +11,12 @@ include(GNUInstallDirs)
set(UIRC_VERSION "${PROJECT_VERSION}")
add_compile_definitions(UIRC_VERSION="${UIRC_VERSION}")
OPTION(BUILD_IRCV3 "Build IRCv3 components" OFF)
OPTION(BUILD_VALIDATORS "Build message validators" OFF)
OPTION(BUILD_DOCS "Build documentation with doxygen" OFF)
OPTION(CODE_ANALYZER "Analyze the code statically" OFF)
OPTION(CODE_COVERAGE "Build with coverage tools" OFF)
OPTION(BUILD_IRCV3 "Build IRCv3 components" OFF)
OPTION(BUILD_VALIDATORS "Build message validators" OFF)
OPTION(BUILD_DOCS "Build documentation with doxygen" OFF)
OPTION(BUILD_TESTS "Build tests that check the library compliance" OFF)
OPTION(CODE_ANALYZER "Analyze the code statically" OFF)
OPTION(CODE_COVERAGE "Build with coverage tools" OFF)
set(UIRC_SOURCE
src/assemblers/message.c
@ -24,6 +25,7 @@ set(UIRC_SOURCE
src/tokenizers/user.c
src/memory/allocators.c
src/memory/free.c
src/memory/listmgrs.c
)
set(UIRC_HEADERS
@ -84,7 +86,12 @@ if(BUILD_DOCS)
)
endif()
endif()
if (BUILD_TESTS)
message(STATUS "Tests are going to be built.")
add_subdirectory(tests)
enable_testing()
add_test(NAME "FullLoop" COMMAND "fullloop")
endif()
add_library(uirc ${UIRC_SOURCE})
target_link_libraries(uirc ${UIRC_LIBS})

View File

@ -32,6 +32,13 @@ IRC_Message* uirc_malloc_message(const char* command, ...);
IRC_Buffer* uirc_malloc_buffer(const char* name, const char* topic, const char* key);
IRC_Network* uirc_malloc_network(const char* addr, const char* svc, const char* pass);
bool uirc_list_add_buffer(IRC_Network* net, IRC_Buffer* buf);
bool uirc_list_add_user(IRC_Buffer* buf, IRC_User* usr);
bool uirc_list_add_message(IRC_Buffer* buf, IRC_Message* msg);
bool uirc_list_remove_buffer(IRC_Network* net, IRC_Buffer* buf);
bool uirc_list_remove_user(IRC_Buffer* buf, IRC_User* usr);
bool uirc_list_remove_message(IRC_Buffer* buf, IRC_Message* msg);
#ifdef UIRC_FEATURE_IRCV3
void uirc_free_tag(IRC_Tag* t);
void uirc_free_capability(IRC_Capability* c);

View File

@ -33,12 +33,25 @@ uirc_assembler_message(char* buf, const IRC_Message* m, size_t len)
#ifdef UIRC_FEATURE_IRCV3
if (m->tag_list != NULL) {
if (len > 1) {
*(buf++) = '@';
len--;
} else
return -1;
ssize_t ret;
if ((ret = uirc_assembler_tag_list(buf, m->tag_list, len)) >= 0) {
buf += (size_t) ret;
len -= (size_t) ret;
} else
return ret;
if (len > 1) {
*(buf++) = ' ';
len--;
} else
return -1;
}
assert(sv <= buf);
#endif /* UIRC_FEATURE_IRCV3 */
@ -67,7 +80,7 @@ uirc_assembler_message(char* buf, const IRC_Message* m, size_t len)
if (m->command != NULL) {
if (len < 2) return -1;
int ret;
if ((ret = snprintf(buf, len, "%s ", m->command)) >= 0) {
if ((ret = snprintf(buf, len, "%s", m->command)) >= 0) {
buf += (size_t) ret;
len -= (size_t) ret;
} else
@ -79,7 +92,7 @@ uirc_assembler_message(char* buf, const IRC_Message* m, size_t len)
for (unsigned int i = 0; i < IRC_MAXARGS && m->args[i] != NULL; i++) {
if (len < 2) return -1;
int ret;
if ((ret = snprintf(buf, len, (i < IRC_MAXARGS - 1 && m->args[i + 1] == NULL && m->trailing) ? ":%s" : "%s ", m->args[i])) >= 0) {
if ((ret = snprintf(buf, len, (m->args[i + 1] == NULL && m->trailing) ? " :%s" : " %s", m->args[i])) >= 0) {
buf += (size_t) ret;
len -= (size_t) ret;
} else

View File

@ -68,14 +68,8 @@ uirc_assembler_tag_list(char* buf, const llist_t* tl, size_t len)
assert(buf != NULL);
const char* const sv = buf;
for (; tl->content != NULL && ((IRC_Tag*) tl->content)->key != NULL;) {
if (buf == sv) {
if (len > 1) {
*(buf++) = '@';
len--;
} else
return -1;
} else {
for (; tl != NULL && tl->content != NULL && ((IRC_Tag*) tl->content)->key != NULL; tl = tl->next) {
if (buf != sv) {
if (len > 1) {
*(buf++) = ';';
len--;
@ -89,15 +83,6 @@ uirc_assembler_tag_list(char* buf, const llist_t* tl, size_t len)
len -= (size_t) ret;
} else
return -1;
if ((tl = tl->next) == NULL) {
if (len > 1) {
*(buf++) = ' ';
len--;
} else
return -1;
break;
}
}
return buf - sv;
}

View File

@ -20,7 +20,6 @@
#include "types.h" // IRC_*
#include <assert.h> // assert()
#include <corelibs/llist.h> // llist_t remove_linked_list_elem()
#include <corelibs/stringext.h> // malloc_string()
#include <stdarg.h> // va_*() va_list
#include <stdlib.h> // free()
@ -183,4 +182,3 @@ uirc_malloc_network(const char* addr, const char* svc, const char* pass)
}
return NULL;
}

View File

@ -63,7 +63,7 @@ uirc_free_message(IRC_Message* m)
}
free(m->command);
for (unsigned short i = 0; m->args[i] != NULL; i++) free(m->args[i]);
for (unsigned short i = 0; i < IRC_MAXARGS && m->args[i] != NULL; i++) free(m->args[i]);
#ifdef UIRC_FEATURE_IRCV3
for (llist_t* t = m->tag_list; t != NULL; t = t->next) {

120
src/memory/listmgrs.c Normal file
View File

@ -0,0 +1,120 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "memory.h"
#include "types.h" // IRC_*
#include <assert.h> // assert()
#include <corelibs/llist.h> // llist_t remove_linked_list_elem()
#include <stdbool.h> // bool
#include <stdlib.h> // free()
// NOTE: yes, i know these repeat a FUCKTON, need to find a way to use the preprocessor to merge them or create a generalized function
bool
uirc_list_add_message(IRC_Buffer* buf, IRC_Message* msg)
{
assert(buf != NULL);
if (msg == NULL) return 0;
llist_t* tmp = NULL;
if ((tmp = allocate_linked_list_elem(0)) == NULL) return 0;
if (buf->message_list != NULL) connect_linked_list_elem(buf->message_list, tmp);
else
buf->message_list = tmp;
tmp->content = buf;
return 1;
}
bool
uirc_list_add_buffer(IRC_Network* net, IRC_Buffer* buf)
{
assert(net != NULL);
if (buf == NULL) return 0;
llist_t* tmp = NULL;
if ((tmp = allocate_linked_list_elem(0)) == NULL) return 0;
if (net->buf_list != NULL) connect_linked_list_elem(net->buf_list, tmp);
else
net->buf_list = tmp;
tmp->content = buf;
return 1;
}
bool
uirc_list_add_user(IRC_Buffer* buf, IRC_User* usr)
{
assert(buf != NULL);
if (usr == NULL) return 0;
llist_t* tmp = NULL;
if ((tmp = allocate_linked_list_elem(0)) == NULL) return 0;
if (buf->user_list != NULL) connect_linked_list_elem(buf->user_list, tmp);
else
buf->user_list = tmp;
tmp->content = usr;
return 1;
}
bool
uirc_list_remove_buffer(IRC_Network* net, IRC_Buffer* buf)
{
assert(net != NULL);
assert(buf != NULL);
for (llist_t* cur = net->buf_list; cur != NULL;) {
if (cur->content == buf) {
if (cur == net->buf_list) net->buf_list = (cur->prev != NULL) ? cur->prev : cur->next;
uirc_free_buffer(buf);
remove_linked_list_elem(cur);
break;
}
if ((cur = cur->next) == NULL) return 0;
}
return 1;
}
bool
uirc_list_remove_user(IRC_Buffer* buf, IRC_User* usr)
{
assert(buf != NULL);
assert(usr != NULL);
for (llist_t* cur = buf->user_list; cur != NULL;) {
if (cur->content == usr) {
if (cur == buf->user_list) buf->user_list = (cur->prev != NULL) ? cur->prev : cur->next;
uirc_free_user(usr);
remove_linked_list_elem(cur);
break;
}
if ((cur = cur->next) == NULL) return 0;
}
return 1;
}
bool
uirc_list_remove_message(IRC_Buffer* buf, IRC_Message* msg)
{
assert(buf != NULL);
assert(msg != NULL);
for (llist_t* cur = buf->user_list; cur != NULL;) {
if (cur->content == msg) {
if (cur == buf->user_list) buf->user_list = (cur->prev != NULL) ? cur->prev : cur->next;
uirc_free_message(msg);
remove_linked_list_elem(cur);
break;
}
if ((cur = cur->next) == NULL) return 0;
}
return 1;
}

View File

@ -35,18 +35,17 @@ uirc_tokenizer_message(const char* str)
{
assert(str != NULL);
IRC_Message* m = malloc(sizeof(IRC_Message));
IRC_Message* const m = malloc(sizeof(IRC_Message));
size_t len = strlen(str);
// Ignore CRLF at the end, irrelevant for parsing
// NOTE: len can be used directly as it's the lenght of the string without terminating null byte
// this is also the same as the last element in a array (off by one)
if (str[len] == '\n') {
if (str[len - 1] == '\n') {
len--;
if (str[len] == '\r') 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 and it's purpose is to not use ws for tokenizing
// 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;
@ -95,12 +94,15 @@ uirc_tokenizer_message(const char* str)
short i;
for (i = 0; i < IRC_MAXARGS && *p;) {
if (i == IRC_MAXARGS - 1 || *p == ':') {
if (*p == ':') 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;
}
m->trailing = true;
break;
} else {
const char* tmp = NULL;

5
tests/CMakeLists.txt Normal file
View File

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.16)
include_directories(${uIRC_SOURCE_DIR}/include)
add_executable(fullloop fullloop.c common.c)
target_link_libraries(fullloop uirc)

82
tests/common.c Normal file
View File

@ -0,0 +1,82 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "uirc.h" // IRC_Message
#include <stdio.h> // printf()
#include <stdlib.h> // exit()
#include <string.h> // strcmp()
#define DELIMITER "-------------\n"
#define PRINTVARORNULL(VAR) ((VAR != NULL) ? VAR : "NULL")
void
expect(const char* var, const char* exp)
{
if (strcmp(var, exp) != 0) {
printf("Expected \"%s\" but got \"%s\" instead.\n", exp, var);
exit(EXIT_FAILURE);
}
}
void
print_irc_message(const IRC_Message* ptr)
{
#ifdef UIRC_FEATURE_IRCV3
printf(DELIMITER);
llist_t* tmp = ptr->tag_list;
for (; tmp != NULL; tmp = tmp->next) { print_irc_tag((IRC_Tag*) tmp->content); }
#endif /* UIRC_FEATURE_IRCV3 */
if (ptr->source != NULL) { print_irc_user(ptr->source); }
if (ptr->command != NULL) printf("mc: %s\n", ptr->command);
for (short i = 0; i < IRC_MAXARGS && ptr->args[i] != NULL; i++) { printf("marg[%i]: %s\n", i, ptr->args[i]); }
printf(DELIMITER);
}
#ifdef UIRC_FEATURE_IRCV3
void
print_irc_tag(const IRC_Tag* tag)
{
printf(DELIMITER "tn: %s\n"
"tv: %s\n"
"tc: %i\n" DELIMITER,
PRINTVARORNULL(tag->key),
PRINTVARORNULL(tag->value),
tag->clientbound);
}
#endif /* UIRC_FEATURE_IRCV3 */
void
print_irc_user(const IRC_User* user)
{
printf(DELIMITER "un: %s\n"
"uu: %s\n"
"ur: %s\n"
"uh: %s\n" DELIMITER,
PRINTVARORNULL(user->nick),
PRINTVARORNULL(user->user),
PRINTVARORNULL(user->real),
PRINTVARORNULL(user->host));
}

40
tests/common.h Normal file
View File

@ -0,0 +1,40 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "uirc.h" // IRC_*
#include <stdio.h> // printf()
#include <stdlib.h> // exit()
#include <string.h> // strcmp()
#ifndef UIRC_GUARD_PRIVATE_COMMONTESTS
#define UIRC_GUARD_PRIVATE_COMMONTESTS
#define ERRIFNULL(VAR, ...) \
if (VAR == NULL) fprintf(stderr, __VA_ARGS__)
void expect(const char* var, const char* exp);
void print_irc_message(const IRC_Message* ptr);
#ifdef UIRC_FEATURE_IRCV3
void print_irc_tag(const IRC_Tag* tag);
#endif /* UIRC_FEATURE_IRCV3 */
void print_irc_user(const IRC_User* user);
#endif /* UIRC_GUARD_PRIVATE_COMMONTESTS */

61
tests/fullloop.c Normal file
View File

@ -0,0 +1,61 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "common.h" // expect() print_irc_message()
#include "uirc.h" // uirc_* IRC_*
#include <assert.h> // assert()
#include <stdio.h> // printf()
#include <stdlib.h> // EXIT_SUCCESS
#include <string.h> // strlen()
int
main(void)
{
const char* str[] = { ":buddy@hostnet.com PRIVMSG #general :login hunter12\r\n",
"NOTICE * :nice cock bro\r\n",
"@day=13;willtolive=0 PRIVMSG you :helo\r\n",
":mom!mother@localhost NOTICE #netadmin :johnny, your pizza is done\r\n",
"NOTICE after 14 arguments the trailing starts early so there's no need for a colon hmm interesting really\r\n" };
for (unsigned long i = 0; i < sizeof(str) / sizeof(*str); i++) {
IRC_Message* m = NULL;
if ((m = uirc_tokenizer_message(str[i])) == NULL) {
fprintf(stderr, "Failed to convert string to IRC_Message struct.\n");
return EXIT_FAILURE;
}
print_irc_message(m);
ssize_t ret;
char buf[513];
if ((ret = uirc_assembler_message(buf, m, sizeof(buf) / sizeof(char))) <= 0) {
fprintf(stderr, "Failed to convert IRC_Message struct to a string. (%li)\n", ret);
return EXIT_FAILURE;
}
size_t s1 = strlen(str[i]), s2 = strlen(buf);
if (s1 != s2) {
fprintf(stderr, "String lenght doesn't match after parsing and assembly. Expected %li but got %li\n", s1, s2);
fprintf(stderr, "%s\n", buf);
return EXIT_FAILURE;
}
expect(buf, str[i]);
uirc_free_message(m);
free(m);
}
return EXIT_SUCCESS;
}