CFLAGS += -Wall -pedantic -I./ -I ../ -std=c99
CXXFLAGS += -Wall -pedantic -I./ -I ../
-LDFLAGS += -lcheck
ifdef DEBUG
CFLAGS += -ggdb -O0
+/*
+ * CUtils: some small C utilities
+ *
+ * Copyright (C) 2022 Niki Roo
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
#include <check.h>
#include <string.h>
#include <stdio.h>
#define FAIL(...) \
ck_abort_msg(__VA_ARGS__)\
-#define ASSERT_EQUALS_STR(title, un, deux) \
- if (strcmp(un, deux)) { \
-ck_abort_msg("%s\n\tExpected: <%s>\n\tReceived: <%s>", title, un, deux); \
+#define ASSERT_EQUALS_STR(title, expected, received) \
+ if (strcmp(expected, received)) { \
+ck_abort_msg("%s\n\tExpected: <%s>\n\tReceived: <%s>", title, expected, received); \
}
-#define ASSERT_EQUALS_INT(title, un, deux) \
- if (un != deux) { \
-ck_abort_msg("%s\n\tExpected: %d\n\tReceived: %d", title, un, deux); \
+#define ASSERT_EQUALS_INT(title, expected, received) \
+ if (expected != received) { \
+ck_abort_msg("%s\n\tExpected: %d\n\tReceived: %d", title, expected, received); \
}
-#define ASSERT_EQUALS_SIZE(title, un, deux) \
- if (un != deux) { \
-ck_abort_msg("%s\n\tExpected: %zu\n\tReceived: %zu", title, un, deux); \
+#define ASSERT_EQUALS_SIZE(title, expected, received) \
+ if (expected != received) { \
+ck_abort_msg("%s\n\tExpected: %zu\n\tReceived: %zu", title, expected, received); \
}
SRunner *get_tests(int more);
CFLAGS += -Wall -pedantic -I./ -I ../ -std=c99
CXXFLAGS += -Wall -pedantic -I./ -I ../
LDFLAGS += -lcheck
+ifdef DEBIAN
+LDFLAGS += -lsubunit -lm -lpthread -lrt
+endif
ifdef DEBUG
CFLAGS += -ggdb -O0
--- /dev/null
+/*
+ * CUtils: some small C utilities
+ *
+ * Copyright (C) 2022 Niki Roo
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "launcher.h"
+#include "utils/array.h"
+
+array *a;
+
+void test_array_setup() {
+ a = new_array(sizeof(char), 80);
+}
+
+void test_array_teardown() {
+ free_array(a);
+ a = NULL;
+}
+
+void array_reset() {
+ test_array_teardown();
+ test_array_setup();
+}
+
+START(init)
+ if (!a)
+ FAIL("new_array returned NULL");
+
+ if (array_count(a))
+ FAIL("empty array has a size of %zu", array_count(a));
+ END
+
+Suite *test_array(const char title[]) {
+ Suite *suite = suite_create(title);
+
+ TCase *core = tcase_create("core");
+ tcase_add_checked_fixture(core, test_array_setup, test_array_teardown);
+ tcase_add_test(core, init);
+
+ suite_add_tcase(suite, core);
+
+ return suite;
+}
+
+Suite *test_array_more(const char title[]) {
+ Suite *suite = suite_create(title);
+
+ TCase *tmore = tcase_create("more");
+ tcase_add_checked_fixture(tmore, test_array_setup, test_array_teardown);
+ // TODO
+
+ suite_add_tcase(suite, tmore);
+
+ return suite;
+}
--- /dev/null
+/*
+ * CUtils: some small C utilities
+ *
+ * Copyright (C) 2022 Niki Roo
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "launcher.h"
+#include "utils/cstring.h"
+
+#define TEST_FILE_READLN "utils/test_readln.txt"
+
+cstring *s;
+
+void test_cstring_setup() {
+ s = new_cstring();
+}
+
+void test_cstring_teardown() {
+ free_cstring(s);
+ s = NULL;
+}
+
+void cstring_reset() {
+ test_cstring_teardown();
+ test_cstring_setup();
+}
+
+START(init)
+ if (!s)
+ FAIL("new_cstring returned NULL");
+
+ if (s->length)
+ FAIL("empty cstring has a size of %zu", s->length);
+ END
+
+START(adds)
+ char *str;
+
+ str = "testy";
+ cstring_add(s, str);
+ ASSERT_EQUALS_STR("Short string", str, s->string);
+ cstring_reset();
+
+ str = "Fanfan entre dans un bar";
+ cstring_add(s, str);
+ ASSERT_EQUALS_STR("Medium string", str, s->string);
+ cstring_reset();
+
+ str = "Ligne 1\nLigne 2\nLigne 3, attention 4 = vide\n";
+ cstring_add(s, str);
+ ASSERT_EQUALS_STR("Multi-line", str, s->string);
+ cstring_reset();
+
+ str =
+ "Les accents en français sont bien là et se retrouvent avec une fréquence élevée";
+ cstring_add(s, str);
+ ASSERT_EQUALS_STR("accents", str, s->string);
+ cstring_reset();
+
+ str = "cents: ¢, copyright: ©, arrows: →↓↑←";
+ cstring_add(s, str);
+ ASSERT_EQUALS_STR("UTF-8", str, s->string);
+ cstring_reset();
+
+ str = "Os iusti meditabitur sapientiam,\n"
+ "Et lingua\n"
+ "eius loquetur\n"
+ "indicium.\n"
+ "\n"
+ "Beatus\n"
+ "vir qui\n"
+ "suffert tentationem, Quoniqm\n"
+ "cum probates\n"
+ "fuerit accipient\n"
+ "coronam vitae\n"
+ "\n"
+ "Kyrie, fons bonitatis.\n"
+ "Kyrie, ignis divine, eleison.\n"
+ "\n"
+ "O quam sancta, quam serena,\n"
+ "Quam benigma, quam amoena esse Virgo creditur.\n"
+ "O quam sancta, quam serena,\n"
+ "Quam benigma, quam amoena,\n"
+ "O castitatis lilium.\n"
+ "\n"
+ "Kyrie, fons bonitatis.\n"
+ "Kyrie, ignis divine, eleison.\n"
+ "\n"
+ "O quam sancta, quam serena,\n"
+ "Quam benigma, quam amoena,\n"
+ "O castitatis lilium.\n";
+ cstring_add(s, str);
+ ASSERT_EQUALS_STR("Long, multi-line string", str, s->string);
+ cstring_reset();
+
+ END
+
+START(clear)
+ if (!s)
+ FAIL("new_cstring returned NULL");
+
+ if (s->length)
+ FAIL("empty cstring has a size of %zu", s->length);
+ END
+
+START(addp)
+ char *str;
+
+ cstring_addp(s, "%d", 42);
+ ASSERT_EQUALS_STR("Simple int", "42", s->string);
+ cstring_reset();
+
+ cstring_addp(s, "%02d", 1);
+ ASSERT_EQUALS_STR("Leading zero int", "01", s->string);
+ cstring_reset();
+
+ cstring_addp(s, "%d", 352646);
+ ASSERT_EQUALS_STR("Large int", "352646", s->string);
+ cstring_reset();
+
+ str = "Simple test string";
+ cstring_addp(s, "%s", str);
+ ASSERT_EQUALS_STR("Simple string", str, s->string);
+ cstring_reset();
+
+ cstring_addp(s, "%s", "String 1, ");
+ str = "String 2";
+ cstring_addp(s, "%s", "String 2");
+ ASSERT_EQUALS_STR("Cumulative strings", "String 1, String 2", s->string);
+ cstring_reset();
+
+ END
+
+START(readln)
+ int read;
+ FILE *testin = fopen(TEST_FILE_READLN, "r");
+ if (!testin)
+ FAIL("Test file not found: test_readln.txt");
+
+ read = cstring_readline(s, testin);
+ if (!read)
+ FAIL("first line should not be last");
+ ASSERT_EQUALS_STR("first line incorrect", "ligne 1", s->string);
+ cstring_reset();
+
+ read = cstring_readline(s, testin);
+ if (!read)
+ FAIL("second line should not be last");
+ ASSERT_EQUALS_STR("second line incorrect", "", s->string);
+ cstring_reset();
+
+ read = cstring_readline(s, testin);
+ if (!read)
+ FAIL("third line should not be last");
+ ASSERT_EQUALS_STR("third line incorrect", "ligne 3", s->string);
+ cstring_reset();
+
+ if (cstring_readline(s, testin)) {
+ FAIL("fourth line should not exist");
+ }
+
+ END
+
+START(ends_with)
+ char *end;
+
+ cstring_add(s, "fichier.ext");
+
+ end = ".ext";
+ if (!cstring_ends_with(s, end))
+ FAIL("fichier.ext does not end in %s", end);
+
+ end = ".ex";
+ if (cstring_ends_with(s, end))
+ FAIL("fichier.ext ends in %s", end);
+
+ end = "aext";
+ if (cstring_ends_with(s, end))
+ FAIL("fichier.ext does not end in %s", end);
+
+ end = "";
+ if (!cstring_ends_with(s, end))
+ FAIL("fichier.ext does not end with nothing");
+
+ END
+
+START(starts_with)
+ char *start;
+
+ cstring_add(s, "fichier.ext");
+
+ start = "fichier";
+ if (!cstring_starts_with(s, start, 0))
+ FAIL("fichier.ext does not start with %s", start);
+
+ start = "ichier";
+ if (cstring_starts_with(s, start, 0))
+ FAIL("fichier.ext starts with %s", start);
+
+ start = "afichier";
+ if (cstring_starts_with(s, start, 0))
+ FAIL("fichier.ext starts with %s", start);
+
+ start = "";
+ if (!cstring_starts_with(s, start, 0))
+ FAIL("fichier.ext does not start with nothing");
+
+ start = "chier";
+ if (!cstring_starts_with(s, start, 2))
+ FAIL("fichier.ext +2 does not start with %s", start);
+
+ END
+
+START(many_adds)
+ size_t count = 50000000;
+ for (size_t i = 0; i < count; i++) {
+ cstring_add(s, "1234567890");
+ }
+
+ ASSERT_EQUALS_SIZE("Lot of adds", count * 10, s->length);
+
+ END
+
+Suite *test_cstring(const char title[]) {
+ Suite *suite = suite_create(title);
+
+ TCase *core = tcase_create("core");
+ tcase_add_checked_fixture(core, test_cstring_setup, test_cstring_teardown);
+ tcase_add_test(core, init);
+ tcase_add_test(core, adds);
+ tcase_add_test(core, clear);
+ tcase_add_test(core, addp);
+ tcase_add_test(core, readln);
+ tcase_add_test(core, ends_with);
+ tcase_add_test(core, starts_with);
+
+ suite_add_tcase(suite, core);
+
+ return suite;
+}
+
+Suite *test_cstring_more(const char title[]) {
+ Suite *suite = suite_create(title);
+
+ TCase *tmore = tcase_create("more");
+ tcase_add_checked_fixture(tmore, test_cstring_setup, test_cstring_teardown);
+ tcase_add_test(tmore, many_adds);
+
+ suite_add_tcase(suite, tmore);
+
+ return suite;
+}
#include "main.h"
#include "launcher.h"
+SRunner *runner = NULL;
+void add_test(Suite *test);
+
SRunner *get_tests(int more) {
- //TODO: add tests (the code for those is not ready yet)
- /*
- SRunner *runner = srunner_create(test_cstring("cstring"));
+ add_test(test_cstring("cstring"));
+ if (more)
+ add_test(test_cstring_more("cstring -- more (longer)"));
+
+ add_test(test_array("array"));
if (more)
- srunner_add_suite(runner, test_cstring_more("more tests (longer)"));
+ add_test(test_array_more("array -- more (longer)"));
return runner;
- */
- return NULL;
+}
+
+void add_test(Suite *test) {
+ if (!runner) {
+ runner = srunner_create(test);
+ } else {
+ srunner_add_suite(runner, test);
+ }
}
#include <check.h>
-//TODO: add tests (the code for those is not ready yet)
-//Suite *test_cstring(const char title[]);
-//Suite *test_cstring_more(const char title[]);
+Suite *test_cstring(const char title[]);
+Suite *test_cstring_more(const char title[]);
+Suite *test_array(const char title[]);
+Suite *test_array_more(const char title[]);
#endif /* SRC_TESTS_UTILS_MAIN_H_ */
--- /dev/null
+ligne 1
+
+ligne 3
--- /dev/null
+/*
+ * CUtils: some small C utilities
+ *
+ * Copyright (C) 2013 Niki Roo
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base64.h"
+#include "cstring.h"
+
+cstring *base64_decodesi(base64 *self, char *data, size_t len);
+//cstring *base64_encodesi(base64 *self, char *data, size_t len);
+
+char *b64_encode(const char *data, const char *encoding_table,
+ size_t input_length,
+ size_t *output_length);
+
+char *b64_decode(const char *data, const char *decoding_table,
+ size_t input_length,
+ size_t *output_length);
+
+static char b64_encoding_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/'};
+
+static int b64_mod_table[] = {0, 2, 1};
+
+base64 *base64_new() {
+ base64 *self;
+ int i;
+
+ self = (base64 *)malloc(sizeof(base64));
+ self->table = malloc(256 * sizeof(char));
+
+ for (i = 0; i < 64; i++)
+ self->table[(unsigned char)b64_encoding_table[i]] = i;
+
+ return self;
+}
+
+void base64_free(base64 *self) {
+ if (!self)
+ return;
+
+ free(self->table);
+ free(self);
+}
+
+cstring *base64_encode(base64 *self, cstring *data) {
+ if (!data)
+ return NULL;
+
+ return base64_encodesi(self, data->string, data->length);
+}
+
+cstring *base64_decode(base64 *self, cstring *data) {
+ if (!data)
+ return NULL;
+
+ return base64_decodesi(self, data->string, data->length);
+}
+
+cstring *base64_encodes(base64 *self, char *data) {
+ size_t len;
+
+ if (!data)
+ return NULL;
+
+ len = strlen(data);
+
+ return base64_encodesi(self, data, len);
+}
+
+cstring *base64_decodes(base64 *self, char *data) {
+ size_t len;
+
+ if (data)
+ len = strlen(data);
+
+ return base64_decodesi(self, data, len);
+}
+
+cstring *base64_encodesi(base64 *self, char *data, size_t len) {
+ cstring *rep;
+ char *encoded;
+ size_t size;
+
+ rep = NULL;
+ size = 0;
+ encoded = b64_encode(data, b64_encoding_table, len, &size);
+
+ if (encoded) {
+ rep = new_cstring();
+ free(rep->string);
+ rep->string = encoded;
+ rep->length = size;
+ cstring_compact(rep);
+ }
+
+ return rep;
+}
+
+cstring *base64_decodesi(base64 *self, char *data, size_t len) {
+ cstring *rep;
+ char *decoded;
+ size_t size;
+
+ rep = NULL;
+ size = 0;
+ decoded = b64_decode(data, self->table, len, &size);
+
+ if (decoded) {
+ rep = new_cstring();
+ free(rep->string);
+ rep->string = decoded;
+ rep->length = size;
+ cstring_compact(rep);
+ }
+
+ return rep;
+}
+
+char *b64_encode(const char *data, const char *encoding_table,
+ size_t input_length,
+ size_t *output_length) {
+ unsigned int i, j;
+
+ *output_length = 4 * ((input_length + 2) / 3) + 1;
+
+ char *encoded_data = malloc(*output_length);
+ if (encoded_data == NULL)
+ return NULL;
+
+ for (i = 0, j = 0 ; i < input_length ; ) {
+ uint32_t octet_a = i < input_length ?
+ (unsigned char)data[i++] : 0;
+ uint32_t octet_b = i < input_length ?
+ (unsigned char)data[i++] : 0;
+ uint32_t octet_c = i < input_length ?
+ (unsigned char)data[i++] : 0;
+
+ uint32_t triple = (octet_a << 0x10)
+ + (octet_b << 0x08)
+ + octet_c;
+
+ encoded_data[j++] =
+ (char)encoding_table[(triple >> 3 * 6) & 0x3F];
+ encoded_data[j++] =
+ (char)encoding_table[(triple >> 2 * 6) & 0x3F];
+ encoded_data[j++] =
+ (char)encoding_table[(triple >> 1 * 6) & 0x3F];
+ encoded_data[j++] =
+ (char)encoding_table[(triple >> 0 * 6) & 0x3F];
+ }
+
+ for (i = 0 ; (int)i < b64_mod_table[input_length % 3] ; i++)
+ encoded_data[*output_length - 2 - i] = '=';
+ encoded_data[*output_length - 1] = '\0';
+
+ return encoded_data;
+}
+
+
+char *b64_decode(const char *data, const char *decoding_table,
+ size_t input_length,
+ size_t *output_length) {
+
+ unsigned int i, j;
+
+ if (input_length % 4 != 0)
+ return NULL;
+
+ *output_length = (input_length / 4 * 3) + 1;
+ if (data[input_length - 2] == '=') (*output_length)--;
+ if (data[input_length - 3] == '=') (*output_length)--;
+
+ char *decoded_data = malloc(*output_length);
+ if (decoded_data == NULL)
+ return NULL;
+
+ i = j = 0;
+ for ( ; i < input_length ; i += 4) {
+ uint32_t sextet_a = 0;
+ uint32_t sextet_b = 0;
+ uint32_t sextet_c = 0;
+ uint32_t sextet_d = 0;
+
+ if (data[i] != '=') {
+ sextet_a = decoding_table[(unsigned char)data[i + 0]];
+ sextet_b = decoding_table[(unsigned char)data[i + 1]];
+ sextet_c = decoding_table[(unsigned char)data[i + 2]];
+ sextet_d = decoding_table[(unsigned char)data[i + 3]];
+ }
+
+ uint32_t triple = (sextet_a << 3 * 6)
+ + (sextet_b << 2 * 6)
+ + (sextet_c << 1 * 6)
+ + (sextet_d << 0 * 6);
+
+ if (j + 1 < *output_length)
+ decoded_data[j++] = (char)((triple >> 2 * 8) & 0xFF);
+ if (j + 1 < *output_length)
+ decoded_data[j++] = (char)((triple >> 1 * 8) & 0xFF);
+ if (j + 1 < *output_length)
+ decoded_data[j++] = (char)((triple >> 0 * 8) & 0xFF);
+ }
+
+ decoded_data[*output_length - 1] = '\0';
+ return decoded_data;
+}
+
--- /dev/null
+/*
+ * CUtils: some small C utilities
+ *
+ * Copyright (C) 2013 Niki Roo
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef BASE64_H
+#define BASE64_H
+
+#include <stdlib.h>
+#include "cstring.h"
+
+typedef struct {
+ char *table;
+} base64;
+
+/**
+ * Create a new base64 codec.
+ *
+ * @return a new codec
+ */
+base64 *base64_new();
+
+/**
+ * Free the given code.
+ */
+void base64_free(base64 *self);
+
+cstring *base64_encode(base64 *self, cstring *data);
+
+cstring *base64_decode(base64 *self, cstring *data);
+
+cstring *base64_encodes(base64 *self, char *data);
+
+cstring *base64_encodesi(base64 *self, char *data, size_t size);
+
+cstring *base64_decodes(base64 *self, char *data);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
--- /dev/null
+/*
+ * CUtils: some small C utilities
+ *
+ * Copyright (C) 2013 Niki Roo
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ Name: cstring.c
+ Copyright: niki (cc-by-nc) 2011
+ Author: niki
+ Date: 2011-06-16
+ Description: cstring is a collection of helper functions to manipulate string of text
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+/* Windows (and maybe others?) doesn't know about strnlen */
+#ifndef strnlen
+size_t strnlen(const char *s, size_t maxlen);
+size_t strnlen(const char *s, size_t maxlen) {
+ size_t i;
+ for (i = 0; s[i]; i++) {
+ if (i >= maxlen)
+ return maxlen;
+ }
+
+ return i;
+}
+#endif
+
+#include "cstring.h"
+#include "net.h"
+#include "base64.h"
+
+#ifndef BUFFER_SIZE
+#define BUFFER_SIZE 81
+#endif
+
+#ifdef WIN32
+#define CSTRING_SEP '\\'
+#else
+#define CSTRING_SEP '/'
+#endif
+
+//start of private prototypes
+
+struct cstring_private_struct {
+ size_t buffer_length;
+};
+
+void cstring_change_case(cstring *self, int up);
+
+//end of private prototypes
+
+cstring *new_cstring() {
+ cstring *string;
+
+ string = malloc(sizeof(cstring));
+ strcpy(string->CNAME, "[CString]");
+ string->priv = malloc(sizeof(struct cstring_private_struct));
+ string->length = 0;
+ string->priv->buffer_length = BUFFER_SIZE;
+ string->string = malloc(sizeof(char) * BUFFER_SIZE);
+ string->string[0] = '\0';
+
+ return string;
+}
+
+void free_cstring(cstring *string) {
+ if (!string)
+ return;
+
+ free(string->priv);
+ free(string->string);
+
+ free(string);
+}
+
+void cstring_grow(cstring *self, int min) {
+ cstring_grow_to(self, self->length + min);
+}
+
+void cstring_grow_to(cstring *self, int buffer) {
+ if (buffer > self->priv->buffer_length) {
+ self->priv->buffer_length = buffer;
+ self->string = (char *) realloc(self->string,
+ sizeof(char) * self->priv->buffer_length);
+ }
+}
+
+char *cstring_convert(cstring *self) {
+ char *string;
+
+ if (!self)
+ return NULL;
+
+ // Note: this could be skipped.
+ cstring_compact(self);
+
+ free(self->priv);
+ string = (self->string);
+ free(self);
+
+ return string;
+}
+
+cstring *cstring_clone(cstring *self) {
+ if (self == NULL)
+ return NULL;
+
+ return cstring_clones(self->string);
+}
+
+char *cstring_sclone(cstring *self) {
+ if (self == NULL)
+ return NULL;
+
+ return cstring_convert(cstring_clone(self));
+}
+
+char *cstring_sclones(const char self[]) {
+ return cstring_convert(cstring_clones(self));
+}
+
+cstring *cstring_clones(const char self[]) {
+ cstring *clone;
+
+ if (self == NULL)
+ return NULL;
+
+ clone = new_cstring();
+ cstring_add(clone, self);
+
+ return clone;
+}
+
+void cstring_compact(cstring *self) {
+ if (self != NULL) {
+ self->priv->buffer_length = self->length + 1;
+ self->string = (char *) realloc(self->string, self->length + 1);
+ }
+}
+
+void cstring_cut_at(cstring *self, size_t size) {
+ if (self->length > size) {
+ self->string[size] = '\0';
+ self->length = size;
+ }
+}
+
+/*
+ clist *cstring_splitc(cstring *self, char delim, char quote) {
+ clist *list;
+ cstring *d, *q;
+
+ d = new_cstring();
+ q = new_cstring();
+
+ if (delim)
+ cstring_add_car(d, delim);
+ if (quote)
+ cstring_add_car(q, quote);
+
+ list = cstring_split(self, d, q);
+
+ free_cstring(d);
+ free_cstring(q);
+
+ return list;
+ }
+
+ clist *cstring_splits(cstring *self, const char delim[], const char quote[]) {
+ clist *list;
+ cstring *d, *q;
+
+ d = new_cstring();
+ q = new_cstring();
+
+ if (delim)
+ cstring_add(d, delim);
+ if (quote)
+ cstring_add(q, quote);
+
+ list = cstring_split(self, d, q);
+
+ free_cstring(d);
+ free_cstring(q);
+
+ return list;
+ }
+ clist *cstring_split(cstring *self, cstring *delim, cstring *quote) {
+ clist *list;
+ cstring *elem;
+ clist_node *node;
+ size_t i;
+ int in_quote;
+ int hasdelim;
+
+ hasdelim = delim && delim->length > 0;
+
+ list = clist_new();
+ in_quote = 0;
+ elem = NULL;
+ for (i = 0; i < self->length; i++) {
+ if (quote->length > 0 && cstring_starts_with(self, quote, i)) {
+ in_quote = !in_quote;
+ i += quote->length - 1;
+ } else {
+ if (elem == NULL) {
+ elem = new_cstring();
+ node = clist_node_new();
+ node->data = elem;
+ node->free_data = free_cstring;
+ clist_add(list, node);
+ }
+ if (!in_quote && hasdelim && cstring_starts_with(self, delim, i)) {
+ elem = new_cstring();
+ node = clist_node_new();
+ node->data = elem;
+ node->free_data = free_cstring;
+ clist_add(list, node);
+ i += delim->length - 1;
+ } else {
+ cstring_add_car(elem, self->string[i]);
+ }
+ }
+ }
+
+ return list;
+ }
+ */
+
+cstring *cstring_substring(cstring *self, size_t start, size_t length) {
+ cstring *sub;
+ char *source;
+
+ if (length == 0) {
+ length = self->length - start;
+ }
+
+ sub = new_cstring();
+ source = self->string;
+ source = source + start;
+
+ cstring_addn(sub, source, length);
+
+ return sub;
+}
+
+int cstring_starts_with(cstring *self, const char find[], size_t start_index) {
+ return cstring_sstarts_with(self->string, find, start_index);
+}
+
+int cstring_sstarts_with(const char string[], const char find[],
+ size_t start_index) {
+ size_t i;
+
+ for (i = 0;
+ string[start_index + i] == find[i]
+ && string[start_index + i] != '\0' && find[i] != '\0'; i++)
+ ;
+
+ return find[i] == '\0';
+}
+
+int cstring_ends_with(cstring *self, const char find[]) {
+ size_t sz_needle = strlen(find);
+ if (sz_needle <= self->length) {
+ if (!strcmp(self->string + (self->length - sz_needle), find))
+ return 1;
+ }
+
+ return 0;
+}
+
+int cstring_sends_with(const char self[], const char find[]) {
+ size_t sz = strlen(self);
+ size_t sz_needle = strlen(find);
+ if (sz_needle <= sz) {
+ if (!strcmp(self + (sz - sz_needle), find))
+ return 1;
+ }
+
+ return 0;
+}
+
+long cstring_find(const char self[], const char find[], size_t start_index) {
+ size_t sz = strlen(self);
+ if (sz > start_index) {
+ char *found = strstr(self + start_index, find);
+ if (found) {
+ return (long) (found - self);
+ }
+ }
+
+ return -1;
+}
+
+long cstring_rfind(char self[], const char find[], size_t rstart_index) {
+ size_t sz = strlen(self);
+ size_t sz_needle = strlen(find);
+
+ if (rstart_index <= 0)
+ rstart_index += (sz - 1);
+
+ if (sz > rstart_index && sz_needle <= sz) {
+ for (size_t i = sz - sz_needle; i; i--) {
+ if (cstring_sstarts_with(self, find, i))
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int cstring_replace_car(cstring *self, char from, char to) {
+ size_t i;
+ int occur = 0;
+
+ for (i = 0; i < self->length; i++) {
+ if (self->string[i] == from) {
+ self->string[i] = to;
+ occur++;
+ }
+ }
+
+ return occur;
+}
+
+int cstring_replace(cstring *self, const char from[], const char to[]) {
+ cstring *buffer;
+ size_t i;
+ size_t step;
+ char *swap;
+ int occur;
+
+ // easy optimization:
+ if (from && to && from[0] && to[0] && !from[1] && !to[1])
+ return cstring_replace_car(self, from[0], to[0]);
+
+ // optimize for same-size strings?
+
+ step = strlen(from) - 1;
+ buffer = new_cstring();
+ occur = 0;
+ for (i = 0; i < self->length; i++) {
+ if (cstring_starts_with(self, from, i)) {
+ cstring_add(buffer, to);
+ i += step;
+ occur++;
+ } else {
+ cstring_add_car(buffer, self->string[i]);
+ }
+ }
+
+ // not clean, but quicker:
+ swap = self->string;
+ self->string = buffer->string;
+ buffer->string = swap;
+ self->length = buffer->length;
+
+ free_cstring(buffer);
+ return occur;
+}
+
+void cstring_clear(cstring *self) {
+ self->length = 0;
+ self->string[0] = '\0';
+}
+
+void cstring_reverse(cstring *self) {
+ size_t i;
+ size_t last;
+ char tmp;
+
+ if (self->length > 0) {
+ last = self->length - 1;
+ for (i = 0; i <= (last / 2); i++) {
+ tmp = self->string[i];
+ self->string[i] = self->string[last - i];
+ self->string[last - i] = tmp;
+ }
+ }
+}
+
+void cstring_add_car(cstring *self, char source) {
+ char source2[2];
+
+ source2[0] = source;
+ source2[1] = '\0';
+
+ cstring_add(self, source2);
+}
+
+void cstring_addp(cstring *self, const char *fmt, ...) {
+ va_list ap;
+ char empty = '\0';
+
+ va_start(ap, fmt);
+ int sz = vsnprintf(&empty, 0, fmt, ap);
+ va_end(ap);
+
+ char *tmp = malloc((sz + 1) * sizeof(char));
+ cstring_grow(self, sz);
+
+ va_start(ap, fmt);
+ sz = vsnprintf(tmp, sz + 1, fmt, ap);
+ va_end(ap);
+
+ cstring_add(self, tmp);
+
+ free(tmp);
+}
+
+void cstring_add(cstring *self, const char source[]) {
+ cstring_addf(self, source, 0);
+}
+
+void cstring_addf(cstring *self, const char source[], size_t indexi) {
+ size_t ss, ptr;
+
+ if (source != NULL && strlen(source) > indexi) {
+ ss = strlen(source) - indexi;
+ while ((self->length + ss) >= (self->priv->buffer_length)) {
+ self->priv->buffer_length += BUFFER_SIZE;
+ }
+ self->string = (char *) realloc(self->string,
+ sizeof(char) * self->priv->buffer_length);
+
+ for (ptr = self->length; ptr <= (self->length + ss); ptr++) {
+ self->string[ptr] = source[ptr - self->length + indexi];
+ }
+ self->length += ss;
+ }
+}
+
+void cstring_addn(cstring *self, const char source[], size_t n) {
+ cstring_addfn(self, source, 0, n);
+}
+
+void cstring_addfn(cstring *self, const char source[], size_t indexi, size_t n) {
+ size_t i;
+ char *tmp;
+
+ for (i = indexi; i < (n + indexi) && source[i] != '\0'; i++)
+ ;
+ if (source[i] == '\0') {
+ cstring_addf(self, source, indexi);
+ } else {
+ tmp = (char *) malloc(sizeof(char) * (n + 1));
+ strncpy(tmp, source + indexi, n);
+ tmp[n] = '\0';
+ cstring_add(self, tmp);
+ free(tmp);
+ }
+}
+
+void cstring_rtrim(cstring *self, char car) {
+ for (size_t i = self->length - 1; i >= 0; i--) {
+ if (self->string[i] != car)
+ break;
+ self->string[i] = '\0';
+ self->length--;
+ }
+}
+
+void cstring_trim(cstring *self, char car) {
+ if (car == '\0')
+ return;
+
+ cstring_rtrim(self, car);
+
+ int i = 0;
+ while (self->string[i] == car)
+ i++;
+
+ if (i) {
+ cstring *tmp = new_cstring();
+ cstring_add(tmp, self->string + i);
+
+ free(self->string);
+ self->priv->buffer_length = tmp->priv->buffer_length;
+ self->string = tmp->string;
+ tmp->string = NULL;
+ free_cstring(tmp);
+ }
+}
+
+size_t cstring_remove_crlf(cstring *self) {
+ size_t removed;
+
+ removed = cstring_sremove_crlf(self->string, self->length);
+ self->length -= removed;
+
+ return removed;
+}
+
+size_t cstring_sremove_crlf(char data[], size_t n) {
+ size_t removed;
+
+ removed = n;
+ while (removed > 0
+ && (data[removed - 1] == '\r' || data[removed - 1] == '\n')) {
+ removed--;
+ }
+
+ data[removed] = '\0';
+
+ return removed;
+}
+
+void cstring_toupper(cstring *self) {
+ cstring_change_case(self, 1);
+}
+
+void cstring_tolower(cstring *self) {
+ cstring_change_case(self, 0);
+}
+
+void cstring_change_case(cstring *self, int up) {
+ wchar_t *wide;
+ char tmp[10];
+ const char *src = self->string;
+ size_t s, i;
+ mbstate_t state;
+
+// init the state (passing NULL is not thread-safe)
+ memset(&state, '\0', sizeof(mbstate_t));
+
+// won't contain MORE chars (but maybe less)
+ wide = (wchar_t *) malloc((self->length + 1) * sizeof(wchar_t));
+ s = mbsrtowcs(wide, &src, self->length, &state);
+ wide[s] = (wchar_t) '\0';
+ cstring_clear(self);
+ for (i = 0; i <= s; i++) {
+ if (up)
+ wide[i] = (wchar_t) towupper((wint_t) wide[i]);
+ else
+ wide[i] = (wchar_t) towlower((wint_t) wide[i]);
+ memset(&state, '\0', sizeof(mbstate_t));
+ wcrtomb(tmp, wide[i], &state);
+ cstring_add(self, tmp);
+ }
+ free(wide);
+}
+
+int cstring_readline(cstring *self, FILE *file) {
+ char buffer[BUFFER_SIZE];
+ size_t size = 0;
+ int full_line;
+
+// sanity check:
+ if (!file)
+ return 0;
+
+ buffer[BUFFER_SIZE - 1] = '\0'; // just in case
+
+ if (!feof(file)) {
+ cstring_clear(self);
+ buffer[0] = '\0';
+
+ // Note: strlen() could return 0 if the file contains \0
+ // at the start of a line
+ if (!fgets(buffer, (int) BUFFER_SIZE - 1, file))
+ return 0;
+ size = strlen(buffer);
+
+ full_line = ((file && feof(file)) || size == 0
+ || buffer[size - 1] == '\n');
+ size -= cstring_sremove_crlf(buffer, size);
+ cstring_add(self, buffer);
+
+ // No luck, we need to continue getting data
+ while (!full_line) {
+ if (!fgets(buffer, (int) BUFFER_SIZE - 1, file))
+ break;
+ size = strlen(buffer);
+
+ full_line = ((file && feof(file)) || size == 0
+ || buffer[size - 1] == '\n');
+ size -= cstring_sremove_crlf(buffer, size);
+ cstring_add(self, buffer);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void cstring_add_path(cstring *self, const char subpath[]) {
+ cstring_add_car(self, CSTRING_SEP);
+ cstring_add(self, subpath);
+}
+
+int cstring_pop_path(cstring *self, int how_many) {
+ char sep[] = { CSTRING_SEP };
+ int count = 0;
+
+ cstring_rtrim(self, CSTRING_SEP);
+ for (int i = 0; i < how_many; i++) {
+ size_t idx = cstring_rfind(self->string, sep, 0);
+ if (!idx)
+ break;
+
+ cstring_cut_at(self, idx - 1);
+ count++;
+ }
+
+ return count;
+}
+
+cstring *cstring_getdir(const char path[]) {
+ cstring *result;
+ size_t i;
+
+ size_t sz = strlen(path);
+
+ i = sz - 1;
+ if (i >= 0 && path[i] == CSTRING_SEP)
+ i--;
+ for (; i >= 0 && path[i] != CSTRING_SEP; i--)
+ ;
+
+ if (i < 0)
+ return new_cstring();
+
+ result = new_cstring();
+ cstring_addn(result, path, i);
+
+ return result;
+}
+
+cstring *cstring_getfile(cstring *path) {
+ cstring *result;
+ ssize_t i;
+
+ i = (ssize_t) path->length - 1;
+ if (i >= 0 && path->string[i] == CSTRING_SEP)
+ i--;
+ for (; i >= 0 && path->string[i] != CSTRING_SEP; i--)
+ ;
+
+ if (i < 0 || (size_t) (i + 1) >= path->length)
+ return new_cstring();
+
+ result = cstring_clones(path->string + i + 1);
+ return result;
+}
+
+cstring *cstring_getfiles(const char path[]) {
+ cstring *copy, *result;
+
+ copy = cstring_clones(path);
+ result = cstring_getfile(copy);
+ free_cstring(copy);
+ return result;
+}
+
+cstring *cstring_to_b64(cstring *self) {
+ static base64 *cstring_b64 = NULL;
+ if (!cstring_b64)
+ cstring_b64 = base64_new();
+
+ return base64_encode(cstring_b64, self);
+}
+
+char *cstring_to_sb64(cstring *self) {
+ static base64 *cstring_b64 = NULL;
+ if (!cstring_b64)
+ cstring_b64 = base64_new();
+
+ return cstring_convert(base64_encode(cstring_b64, self));
+}
+
+char *cstring_to_sb64s(char *self, size_t size) {
+ static base64 *cstring_b64 = NULL;
+ if (!cstring_b64)
+ cstring_b64 = base64_new();
+
+ return cstring_convert(base64_encodesi(cstring_b64, self, size));
+}
+
+cstring *cstring_from_b64(cstring *self) {
+ static base64 *cstring_b64 = NULL;
+ if (!cstring_b64)
+ cstring_b64 = base64_new();
+
+ return base64_decode(cstring_b64, self);
+}
+
+char *cstring_from_sb64(cstring *self) {
+ static base64 *cstring_b64 = NULL;
+ if (!cstring_b64)
+ cstring_b64 = base64_new();
+
+ return cstring_convert(base64_decode(cstring_b64, self));
+}
+
+char *cstring_from_sb64s(char *self) {
+ static base64 *cstring_b64 = NULL;
+ if (!cstring_b64)
+ cstring_b64 = base64_new();
+
+ return cstring_convert(base64_decodes(cstring_b64, self));
+}
+
+int cstring_is_whole(cstring *self) {
+ return mbstowcs(NULL, self->string, 0) != (size_t) -1;
+}
--- /dev/null
+/*
+ * CUtils: some small C utilities
+ *
+ * Copyright (C) 2013 Niki Roo
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef CSTRING_H
+#define CSTRING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+/**
+ * This is a cstring. It contains a suite of characters terminated by a NUL byte
+ * (or just a NUL byte), and a length.
+ * It is advised NOT to modify either of those directly.
+ * You can use cstring_convert to get a char*, though (in this case, the cstring
+ * MUST NOT be used again, and you are responsible for freeing the said char*).
+ */
+typedef struct cstring_struct cstring;
+typedef struct cstring_private_struct cstring_private;
+
+struct cstring_struct {
+ char CNAME[32];
+ char *string;
+ size_t length;
+ cstring_private *priv;
+};
+
+/**
+ * Instantiate a new cstring.
+ *
+ * Create (and allocate the memory for) a new cstring.
+ * Do not forget to call cstring_free(cstring) when done.
+ */
+cstring *new_cstring();
+
+/**
+ * Free the given cstring.
+ *
+ * Free all the resources allocated for this cstring.
+ *
+ * @param string the cstring to free, which MUST NOT be used again afterward
+ */
+void free_cstring(cstring *string);
+
+//TODO: desc: will make sure it has enough space for MIN more chars
+void cstring_grow(cstring *self, int min);
+
+//TODO: desc: will make sure it has enough space BUFFER chars (including NULL)
+void cstring_grow_to(cstring *self, int buffer);
+
+/**
+ * Add a char at the end of the given cstring.
+ *
+ * @param self the cstring to work on
+ * @param source the character to add
+ */
+void cstring_add_car(cstring *self, char source);
+
+/**
+ * Add a string (a sequence of char that MUST end with '\0') at the end of the given cstring.
+ *
+ * @param self the cstring to work on
+ * @param source the string to add
+ */
+void cstring_add(cstring *self, const char source[]);
+
+/**
+ * Add a string (a sequence of char that MUST end with '\0') at the end of the given cstring, starting from index.
+ *
+ * @param self the cstring to work on
+ * @param source the string to add
+ * @param index the starting index at which to copy from the source
+ */
+void cstring_addf(cstring *self, const char source[], size_t index);
+
+/**
+ * Add a string (a sequence of char that MAY end with '\0') at the end of the given cstring, up to N chars long.
+ *
+ * @param self the cstring to work on
+ * @param source the string to add
+ * @param n the maximum number of chars to add (excluding the NUL byte)
+ */
+void cstring_addn(cstring *self, const char source[], size_t n);
+
+/**
+ * Add a string (a sequence of char that MAY end with '\0') at the end of the given cstring, starting from index, up to N chars long.
+ *
+ * @param self the cstring to work on
+ * @param source the string to add
+ * @param index the starting index at which to copy from the source
+ * @param n the maximum number of chars to add (excluding the NUL byte)
+ */
+void cstring_addfn(cstring *self, const char source[], size_t index, size_t n);
+
+//TODO: desc + tests
+void cstring_addp(cstring *self, const char *fmt, ...);
+
+/**
+ * Cut the cstring at the given size if it is greater.
+ * E.g.: it will have (at most) this many characters (without counting NUL) in it after.
+ *
+ * @param self the cstring to work on
+ * @param size the size to cut at (the maximum size of the cstring after this operation, NUL excepted)
+ */
+void cstring_cut_at(cstring *self, size_t size);
+
+/**
+ * Create a substring of this cstring.
+ *
+ * @param self the cstring to work on
+ * @param start the index to start at
+ * @param length the number of characters to copy, 0 for 'up to the end'
+ *
+ * @return a newly allocated cstring
+ */
+cstring *cstring_substring(cstring *self, size_t start, size_t length);
+
+/**
+ * Split a cstring into "smaller" cstrings every time the given separator is found.
+ * Will also allow empty fields, ie: "a,b,,c" will return 4 cstrings, the third being empty).
+ *
+ * @param self the cstring to work on
+ * @param delim the separator, which can be longer than one character
+ *
+ * @return a list of cstring
+ */
+//TODO: use a []
+//clist *cstring_split(cstring *self, cstring *delim, cstring *quote);
+/**
+ * Split a cstring into "smaller" cstrings every time the given separator (which MUST end in \0) is found.
+ * Will also allow empty fields, ie: "a,b,,c" will return 4 cstrings, the third being empty).
+ *
+ * @param self the cstring to work on
+ * @param delim the separator, which can be longer than one character and MUST end with \0
+ *
+ * @return a list of cstring
+ */
+//TODO: use a []
+//clist *cstring_splits(cstring *self, const char delim[], const char quote[]);
+/**
+ * Split a cstring into "smaller" cstrings every time the given separator is found.
+ * Will also allow empty fields, ie: "a,b,,c" will return 4 cstrings, the third being empty).
+ *
+ * @param self the cstring to work on
+ * @param delim the separator
+ *
+ * @return a list of cstring
+ */
+//TODO: use a []
+//clist *cstring_splitc(cstring *self, char delim, char quote);
+/**
+ * Reverse the given cstring.
+ *
+ * @param self the cstring to work on
+ */
+void cstring_reverse(cstring *self);
+
+/**
+ * Replace all occurences of a string inside the given cstring by another.
+ *
+ * @param self the cstring to work on
+ * @param from the string to replace
+ * @param to the replacement string
+ *
+ * @return the number of occurences changed
+ */
+int cstring_replace(cstring *self, const char from[], const char to[]);
+
+/**
+ * Replace all occurences of a char inside the given cstring by another.
+ *
+ * @param self the cstring to work on
+ * @param from the char to replace
+ * @param to the replacement char
+ *
+ * @return the number of occurences changed
+ */
+int cstring_replace_car(cstring *self, char from, char to);
+
+/**
+ * Check if the cstring starts with the given pattern.
+ *
+ * @param self the cstring to work on
+ * @param find the string to find
+ * @param start_index the index at which to start the comparison
+ *
+ * @return 1 if it does
+ */
+int cstring_starts_with(cstring *self, const char find[], size_t start_index);
+
+/**
+ * Check if the string starts with the given pattern.
+ *
+ * @param self the string to work on
+ * @param find the string to find
+ * @param start_index the index at which to start the comparison
+ *
+ * @return 1 if it does
+ */
+int cstring_sstarts_with(const char string[], const char find[],
+ size_t start_index);
+
+/**
+ * Check if the cstring ends with the given pattern.
+ *
+ * @param self the cstring to work on
+ * @param find the string to find (if empty, will always be found)
+ * @param start_index the index at which to start the comparison
+ *
+ * @return 1 if it does
+ */
+int cstring_ends_with(cstring *self, const char find[]);
+
+/**
+ * Check if the string ends with the given pattern.
+ *
+ * @param self the string to work on
+ * @param find the string to find (if empty, will always be found)
+ * @param start_index the index at which to start the comparison
+ *
+ * @return 1 if it does
+ */
+int cstring_sends_with(const char self[], const char find[]);
+
+/**
+ * Find the given substring in this one.
+ *
+ * @param self the cstring to work on
+ * @param find the string to find
+ * @param start_index the index at which to start the search
+ *
+ * @return the start index of the found string if found, or a negative value
+ * if not
+ */
+long cstring_find(const char self[], const char find[], size_t rstart_index);
+
+/**
+ * Find the given substring in this one, but search in the reverse direction.
+ *
+ * @param self the cstring to work on
+ * @param find the string to find
+ * @param rstart_index the index at which to start the search, or 0 for
+ * "end of string" (remember that it is reverse, you would then never
+ * find anything with a real rstart_index of 0), or a negative value
+ * to count from the end of the string (-2 means 2 character before the
+ * end)
+ *
+ * @return the start index of the found string if found, or a negative value
+ * if not
+ */
+long cstring_rfind(char self[], const char find[], size_t rstart_index);
+
+/**
+ * Check if the given string is contained by this one.
+ *
+ * @param self the string to work on
+ * @param find the string to find
+ * @param start_index the index at which to start the comparison
+ *
+ * @return the start index of the found string if found, or a negative value if not
+ */
+long long cstring_sfind(const char self[], const char find[],
+ size_t start_index);
+
+/**
+ * Check if any of the given characters (in a char* which MUST end with '\0') is found.
+ *
+ * @param self the cstring to work on
+ * @param find the characters to find, which MUST be an array of char that ends with '\0'
+ * @param start_index the index at which to start the comparison
+ *
+ * @return the start index of the first found character if found, or a negative value if not
+ */
+long long cstring_find_any(cstring *self, const char find[], size_t start_index);
+
+/**
+ * Clear (truncate its size to 0) the given cstring.
+ *
+ * @param self the cstring to work on
+ */
+void cstring_clear(cstring *self);
+
+/**
+ * Convert this cstring into a string
+ * This means that you MUST NOT call cstring_free nor use the cstring anymore.
+ * NULL will return NULL.
+ *
+ * @param self the cstring to work on
+ */
+char *cstring_convert(cstring *self);
+
+/**
+ * Clone this cstring.
+ * NULL will return NULL.
+ *
+ * @param self the cstring to clone
+ */
+cstring *cstring_clone(cstring *self);
+
+/**
+ * Clone this string into a new cstring.
+ * NULL will return NULL.
+ *
+ * @param self the string to clone
+ */
+cstring *cstring_clones(const char self[]);
+
+/**
+ * Clone this cstring into a new string.
+ * NULL will return NULL.
+ *
+ * @param self the cstring to clone
+ */
+char *cstring_sclone(cstring *self);
+
+/**
+ * Clone this string into a new string.
+ * NULL will return NULL.
+ *
+ * @param self the string to clone
+ */
+char *cstring_sclones(const char self[]);
+
+/**
+ * Encode the string to BASE64.
+ *
+ * @param self the cstring to encode
+ */
+cstring *cstring_to_b64(cstring *self);
+
+/**
+ * Encode the string to BASE64.
+ *
+ * @param self the cstring to encode
+ */
+char *cstring_to_sb64(cstring *self);
+
+/**
+ * Encode the string to BASE64.
+ *
+ * @param self the string to encode
+ * @param size the size of the data (e.g., strlen(self))
+ */
+char *cstring_to_sb64s(char *self, size_t size);
+
+/**
+ * Decode the string to BASE64.
+ *
+ * @param self the cstring to decode
+ */
+cstring *cstring_from_b64(cstring *self);
+
+/**
+ * Decode the string to BASE64.
+ *
+ * @param self the cstring to decode
+ */
+char *cstring_from_sb64(cstring *self);
+
+/**
+ * Decode the string to BASE64.
+ *
+ * @param self the string to decode
+ */
+char *cstring_from_sb64s(char *self);
+
+/**
+ * Trim this cstring of all trailing 'car' instances.
+ *
+ * @param self the cstring to work on
+ * @param car the character to trim (right only)
+ *
+ * @return a right trimmed cstring
+ */
+void cstring_rtrim(cstring *self, char car);
+
+
+/**
+ * Trim this cstring of all 'car' instances from the start and/or the
+ * end of the string.
+ *
+ * @param self the cstring to work on
+ * @param car the character to trim
+ *
+ * @return a trimmed cstring
+ */
+void cstring_trim(cstring *self, char car);
+
+/**
+ * Compact the memory used by this cstring.
+ *
+ * @param self the cstring to work on
+ */
+void cstring_compact(cstring *self);
+
+/**
+ * Change the case to upper-case (UTF-8 compatible, but the string MUST be
+ * whole).
+ *
+ * @param self the cstring to work on
+ */
+void cstring_toupper(cstring *self);
+
+/**
+ * Change the case to lower-case (UTF-8 compatible, but the string MUST be
+ * whole).
+ *
+ * @param self the cstring to work on
+ */
+void cstring_tolower(cstring *self);
+
+/**
+ * Read a whole line (CR, LN or CR+LN terminated) from the given file stream.
+ *
+ * @param self the cstring to read into
+ * @param file the file to read
+ *
+ * @return 1 if a line was read, 0 if not
+ */
+int cstring_readline(cstring *self, FILE *file);
+
+/**
+ * Read a whole line (CR, LN or CR+LN terminated) from the given socket.
+ *
+ * @param self the cstring to read into
+ * @param fd the socket to read from
+ *
+ * @return 1 if a line was read, 0 if not
+ */
+int cstring_readnet(cstring *self, int fd);
+
+/**
+ * Add a path to the given cstring (if it is currently empty, it
+ * will result in a root path).
+ *
+ * Will be separated by a forward '/' except on non-standard systems
+ * that uses reverse slash (i.e., Windows).
+ *
+ * @param self the base cstring (empty for a root path)
+ * @param subpath the path component to add
+ */
+void cstring_add_path(cstring *self, const char subpath[]);
+
+//TODO: desc
+int cstring_pop_path(cstring *self, int how_many);
+
+/**
+ * Return the basename component of this path (for instance,
+ * '/home/user/file.ext' becomes 'file.ext').
+ *
+ * @param path the path to get the dir of (it can be a dir itself)
+ *
+ * @return a new string representing the parent directory
+ */
+char *cstring_basename(const char path[]);
+
+/**
+ * Return the dirname of this path (for instance,
+ * '/home/user/file.ext' becomes '/home/user').
+ *
+ * @param path the path to get the dir of (it can be a dir itself)
+ *
+ * @return a new string representing the parent directory
+ */
+char *cstring_dirname(const char path[]);
+
+/**
+ * Return the latest path component of this path (usually a FILE).
+ *
+ * @param path the path to get the basename of (it can be a dir itself)
+ *
+ * @return a new cstring representing the latest path component
+ */
+cstring *cstring_getfile(cstring *path);
+
+/**
+ * Return the latest path component of this path (usually a FILE).
+ *
+ * @param path the path to get the basename of (it can be a dir itself)
+ *
+ * @return a new string representing the latest path component
+ */
+cstring *cstring_getfiles(const char path[]);
+
+/**
+ * Remove all the \r and \n at the end of the given cstring.
+ *
+ * @param self the cstring to change
+ *
+ * @return how many removed characters
+ */
+size_t cstring_remove_crlf(cstring *self);
+
+/**
+ * Remove all the \r and \n at the end of the given string.
+ *
+ * @param self the string to change
+ * @param n the size of the string
+ *
+ * @return how many removed characters
+ */
+size_t cstring_sremove_crlf(char *self, size_t n);
+
+/**
+ * Check if the string is whole (i.e., it doesn't contain incomplete UTF-8
+ * sequences).
+ *
+ * @return TRUE if it is whole
+ */
+int cstring_is_whole(cstring *self);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
--- /dev/null
+/*
+ * CUtils: some small C utilities
+ *
+ * Copyright (C) 2012 Niki Roo
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#ifndef WIN32
+ #include <fcntl.h>
+#endif
+
+#include "net.h"
+#include "cstring.h"
+
+#define bool int
+#define true 1
+#define false 0
+
+/**
+ * Get the sockaddr, IPv4 or IPv6.
+ * @param sa the socket address is in this structure
+ * @return the sockaddr_in or sockaddr_in6 inside this socketaddr,
+ * depending if it is IPv4 or IPv6
+ */
+void *get_in_addr(struct sockaddr *sa);
+
+#ifndef WIN32
+/**
+ * Reap all zombie processes.
+ * This function will be called when a child process terminates, and
+ * will loop on all zombie processes to properly acknowledge them
+ * so they can die.
+ *
+ * @param s
+ */
+void sigchld_handler(int pid);
+
+void sigchld_handler(int pid) {
+ if (pid > 0 && pid < 0) pid = 0;
+
+ // Reap all zombie processes
+ while (waitpid(-1, NULL, WNOHANG) > 0);
+}
+#endif
+
+int net_init(){
+#if defined (WIN32)
+ WSADATA WSAData;
+ return !WSAStartup(MAKEWORD(2,2), &WSAData);
+#endif
+ return 1;
+}
+
+void net_cleanup(){
+#if defined (WIN32)
+ WSACleanup();
+#endif
+}
+
+int net_set_non_blocking(int fd) {
+ return net_set_blocking(fd, 0);
+}
+
+int net_set_blocking(int fd, int block) {
+ int flags;
+
+/* If they have O_NONBLOCK, use the POSIX way to do it */
+#if defined (O_NONBLOCK)
+ /* O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
+ flags = 0;
+ }
+ if (block) {
+ return fcntl(fd, F_SETFL, flags ^ O_NONBLOCK);
+ }
+ else {
+ return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ }
+#else
+ flags = block?0:1;
+ return ioctl(fd, FIONBIO, (int)(&flags));
+#endif
+}
+
+int net_connect(const char server[], int port) {
+ int sockfd;
+ struct addrinfo hints, *servinfo, *p;
+ int rv;
+ cstring *str;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ // convert the port number to a string
+ str = new_cstring();
+ cstring_addp(str, "%i", port);
+ rv = getaddrinfo(server, str->string, &hints, &servinfo);
+ free_cstring(str);
+ //
+
+ if (rv != 0) {
+ // DO NOT dirty the stderr
+ //fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+ return -1;
+ }
+
+ // loop through all the results and connect to the first we can
+ for (p = servinfo; p != NULL; p = p->ai_next) {
+ if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol))
+ == -1) {
+ //perror("client: socket");
+ continue;
+ }
+
+ if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+ close(sockfd);
+ //perror("client: connect");
+ continue;
+ }
+
+ break;
+ }
+
+ if (p == NULL) {
+ //fprintf(stderr, "client: failed to connect\n");
+ return -1;
+ }
+
+ freeaddrinfo(servinfo);
+
+ return sockfd;
+}
+
+int net_accept(int ssocketd) {
+ struct sockaddr_storage their_addr;
+ socklen_t sin_size;
+
+ sin_size = sizeof(their_addr);
+ return accept(ssocketd, (struct sockaddr *) &their_addr, &sin_size);
+}
+
+int net_listen(int port, int backlog) {
+#ifndef WIN32
+ struct sigaction sa;
+#endif
+ int sockfd;
+ struct addrinfo hints, *servinfo, *p;
+ char yes = 1;
+ int rv;
+ cstring *str;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE; // use my IP
+
+ // convert the port number to a string
+ str = new_cstring();
+ cstring_addp(str, "%d", port);
+ rv = getaddrinfo(NULL, str->string, &hints, &servinfo);
+ free_cstring(str);
+ //
+
+ if (rv != 0) {
+ // DO NOT dirty the stderr
+ //fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
+ return -1;
+ }
+
+ // loop through all the results and bind to the first we can
+ for (p = servinfo; p != NULL; p = p->ai_next) {
+ if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
+ //perror("server: socket");
+ continue;
+ }
+
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
+ //perror("setsockopt");
+ return -1;
+ }
+
+ if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+ close(sockfd);
+ //perror("server: bind");
+ continue;
+ }
+
+ break;
+ }
+
+ if (p == NULL) {
+ //fprintf(stderr, "server: failed to bind\n");
+ return -1;
+ }
+
+ // all done with this structure
+ freeaddrinfo(servinfo);
+
+ if (listen(sockfd, backlog) == -1) {
+ //perror("listen");
+ return -1;
+ }
+
+#ifndef WIN32
+ sa.sa_handler = sigchld_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ if (sigaction(SIGCHLD, &sa, NULL) == -1) {
+ //perror("sigaction");
+ return -1;
+ }
+#endif
+
+ return sockfd;
+}
+
+void net_close_socketd(int socketd) {
+ close(socketd);
+}
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+ssize_t net_write(int fd, const void *buf, size_t n) {
+ // In UNIX: send() with flag set to '0' == write()
+ // In WIN32: cannot write() to a socket
+ return send(fd, (char *)buf, n, MSG_NOSIGNAL);
+}
+
+ssize_t net_read(int fd, void *buf, size_t nbytes) {
+ // In UNIX: recv() with flag set to '0' == send()
+ // In WIN32: cannot read() from a socket
+ return recv(fd, (char *)buf, nbytes, 0);
+}
+
+void *get_in_addr(struct sockaddr *sa) {
+ if (sa->sa_family == AF_INET) {
+ return &(((struct sockaddr_in*) sa)->sin_addr);
+ }
+ else {
+ return &(((struct sockaddr_in6*) sa)->sin6_addr);
+ }
+}
+
--- /dev/null
+/*
+ * CUtils: some small C utilities
+ *
+ * Copyright (C) 2012 Niki Roo
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file
+ * @author niki
+ * @date November 2011
+ *
+ * Allows you to make connections to/from a server, and to send/receive data.
+ */
+
+#ifndef NET_H
+#define NET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// WHY ??
+#ifdef _WIN32
+ #ifndef WIN32
+ #define WIN32
+ #endif
+#endif
+
+#ifdef WIN32
+ #ifndef _WIN32_WINNT
+ #define _WIN32_WINNT 0x501
+ #endif
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ typedef SSIZE_T ssize_t;
+ #define close(s) closesocket(s)
+ #define ioctl(a,b,c) ioctlsocket(a,b,c)
+ #pragma comment(lib, "wsock32.lib");
+ #pragma comment(lib, "ws2_32.lib");
+#else
+ #include <unistd.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netdb.h>
+ #include <arpa/inet.h>
+ #include <sys/wait.h>
+#endif
+
+/* for ssize_t */
+#include <sys/types.h>
+
+/**
+ * Obsolete, see net_set_blocking(..)
+ */
+int net_set_non_blocking(int fd);
+
+/**
+ * You must call this function before doing any network related operation,
+ * because of some legacy WIN32 rule.
+ * (It is a noop on all other platforms).
+ *
+ * @return 0 when the Windows host does not support WinSock and thus,
+ * no network for you (you still need to call net_cleanup)
+ *
+ */
+int net_init();
+
+/**
+ * You must call this function after you are done using network related
+ * operations within this DLL, because of some legacy WIN32 rule.
+ * (It is a noop on all other platforms).
+ */
+void net_cleanup();
+
+/**
+ * Set the given socket to (non-)blocking I/O mode.
+ * This function can work with file sockets, too.
+ *
+ * @param fd the file descriptor or socket to change
+ * @param block 1 to block, 0 not to block
+ *
+ * @return 1 if success
+ */
+int net_set_blocking(int fd, int block);
+
+/**
+ * Connect to this server on this port, and return a socket descriptor
+ * to write to or read from it.
+ *
+ * @param server the server to connect to
+ * @param port the port to connect on
+ *
+ * @return the server socket or -1 if error
+ */
+int net_connect(const char server[], int port);
+
+/**
+ * Open a port and returns a (server) socket descriptor from which you can
+ * accept connection.
+ *
+ * @param port the port to connect on
+ * @param backlog the maximum number of client connections we will queue for
+ * this socket until you handle them
+ *
+ * @return the server socket, or a negative value on error
+ */
+int net_listen(int port, int backlog);
+
+/**
+ * Block (or not) and wait for a client to connect on this socket.
+ * When this is done, return a socket to this specific client/server
+ * connection. It can takes the connections from a queue,
+ * as defined in net_listen.
+ *
+ * @param ssocketd the server socket on which to accept a connection
+ *
+ * @return the socket, or a negative value on error
+ */
+int net_accept(int ssocketd);
+
+/**
+ * Close a socket (or a server socket).
+ *
+ * @param socketd the (server) socket to close
+ */
+void net_close_socketd(int socketd);
+
+/**
+ * Write to this socket, as you would with a file.
+ *
+ * @param fd the socket to write to
+ * @param buf the buffer to read from
+ * @param n the number of bytes to write
+ *
+ * @return the actual number of bytes written or a
+ * negative number if error
+ */
+ssize_t net_write(int fd, const void *buf, size_t n);
+
+/**
+ * Read from this socket, as you would with a file.
+ *
+ * @param fd the socket to read from
+ * @param buf the buffer to write to
+ * @param nbytes the number of bytes to read
+ *
+ * @return the actual number of bytes read
+ */
+ssize_t net_read(int fd, void *buf, size_t nbytes);
+
+#endif
+
+#ifdef __cplusplus
+extern }
+#endif