From 59a022b2e5e2bf77ac08639a47205d2ee6e2933d Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Thu, 10 Mar 2022 19:52:59 +0100 Subject: [PATCH] jdoc, more tests, some utils fixes/changes --- src/nsub/nsub.c | 94 ++++----- src/nsub/nsub.h | 78 ++++++- src/nsub/nsub_read_lrc.c | 10 +- src/nsub/nsub_write_lrc.c | 4 +- src/nsub/nsub_write_srt.c | 2 +- src/nsub/nsub_write_webvtt.c | 4 +- src/tests/launcher.c | 2 - src/tests/launcher.h | 12 +- src/tests/utils/array.c | 242 +++++++++++++++++++++- src/tests/utils/base64.c | 109 ++++++++++ src/tests/utils/cstring.c | 6 +- src/tests/utils/main.c | 4 + src/tests/utils/main.h | 2 + src/utils/array.c | 389 +++++++++++++++++++++-------------- src/utils/array.h | 251 +++++++++++++++------- src/utils/base64.h | 38 +++- src/utils/cstring.c | 86 ++++---- src/utils/cstring.h | 81 +++++--- src/utils/desktop.c | 45 ++-- src/utils/desktop.h | 9 +- src/utils/net.c | 4 +- src/utils/net.h | 74 ++++--- src/utils/print.h | 7 +- src/utils/timing.h | 10 +- src/utils/utils.h | 31 ++- 25 files changed, 1116 insertions(+), 478 deletions(-) create mode 100644 src/tests/utils/base64.c diff --git a/src/nsub/nsub.c b/src/nsub/nsub.c index ce88cf5..e0629fe 100644 --- a/src/nsub/nsub.c +++ b/src/nsub/nsub.c @@ -37,26 +37,18 @@ song_t *new_song() { } void free_song(song_t *song) { - size_t count; - if (!song) return; - count = array_count(song->metas); - meta_t *meta; - for (size_t i = 0; i < count; i++) { - meta = array_get_ptr(song->metas, i); + array_loop(song->metas, meta, meta_t) + { free(meta->key); free(meta->value); } free_array(song->metas); - count = array_count(song->lyrics); - lyric_t *lyric; - for (size_t i = 0; i < count; i++) { - lyric = array_get_ptr(song->lyrics, i); + array_loop(song->lyrics, lyric, lyric_t) free(lyric->text); - } free_array(song->lyrics); free(song->lang); @@ -64,66 +56,60 @@ void free_song(song_t *song) { } void song_add_unknown(song_t *song, char *text) { - lyric_t lyric; - lyric.type = NSUB_UNKNOWN; - lyric.num = 0; - lyric.start = 0; - lyric.stop = 0; - lyric.name = NULL; - lyric.text = text ? strdup(text) : NULL; - array_add(song->lyrics, &lyric); + lyric_t *lyric = array_new(song->lyrics); + lyric->type = NSUB_UNKNOWN; + lyric->num = 0; + lyric->start = 0; + lyric->stop = 0; + lyric->name = NULL; + lyric->text = text ? strdup(text) : NULL; } void song_add_empty(song_t *song) { - lyric_t lyric; - lyric.type = NSUB_EMPTY; - lyric.num = 0; - lyric.start = 0; - lyric.stop = 0; - lyric.name = NULL; - lyric.text = NULL; - array_add(song->lyrics, &lyric); + lyric_t *lyric = array_new(song->lyrics); + lyric->type = NSUB_EMPTY; + lyric->num = 0; + lyric->start = 0; + lyric->stop = 0; + lyric->name = NULL; + lyric->text = NULL; } void song_add_comment(song_t *song, char *comment) { - lyric_t lyric; - lyric.type = NSUB_COMMENT; - lyric.num = 0; - lyric.start = 0; - lyric.stop = 0; - lyric.name = NULL; - lyric.text = comment ? strdup(comment) : NULL; - array_add(song->lyrics, &lyric); + lyric_t *lyric = array_new(song->lyrics); + lyric->type = NSUB_COMMENT; + lyric->num = 0; + lyric->start = 0; + lyric->stop = 0; + lyric->name = NULL; + lyric->text = comment ? strdup(comment) : NULL; } void song_add_lyric(song_t *song, int start, int stop, char *name, char *text) { song->current_num = song->current_num + 1; - lyric_t lyric; - lyric.type = NSUB_LYRIC; - lyric.num = song->current_num; - lyric.start = start; - lyric.stop = stop; - lyric.name = name ? strdup(name) : NULL; - lyric.text = text ? strdup(text) : NULL; - array_add(song->lyrics, &lyric); + lyric_t *lyric = array_new(song->lyrics); + lyric->type = NSUB_LYRIC; + lyric->num = song->current_num; + lyric->start = start; + lyric->stop = stop; + lyric->name = name ? strdup(name) : NULL; + lyric->text = text ? strdup(text) : NULL; } void song_add_meta(song_t *song, char *key, char *value) { - meta_t meta; - meta.key = key ? strdup(key) : NULL; - meta.value = value ? strdup(value) : NULL; - array_add(song->metas, &meta); + meta_t *meta = array_new(song->metas); + meta->key = key ? strdup(key) : NULL; + meta->value = value ? strdup(value) : NULL; } song_t *nsub_read(FILE *in, NSUB_FORMAT fmt) { - array *lines = new_array(sizeof(char *), 80); - size_t count = array_readfiles(lines, in); + array_t *lines = new_array(sizeof(char *), 80); + array_readfiles(lines, in); song_t *song = new_song(); - char *line; - for (size_t i = 0; i < count; i++) { - array_get(lines, &line, i); + array_loop_i(lines, line, char, i) + { switch (fmt) { case NSUB_FMT_LRC: if (!nsub_read_lrc(song, line)) { @@ -142,7 +128,9 @@ song_t *nsub_read(FILE *in, NSUB_FORMAT fmt) { fail: - array_free_all(lines); + array_loop(lines, line, void) + free(line); + return song; } diff --git a/src/nsub/nsub.h b/src/nsub/nsub.h index 257135b..008b1cc 100644 --- a/src/nsub/nsub.h +++ b/src/nsub/nsub.h @@ -17,50 +17,120 @@ * along with this program. If not, see . */ +/** + * @file nsub.h + * @author Niki + * @date 2022 + * + * @brief Subtitle/Lyrics conversion program (webvtt/srt/lrc) + * + * A small program to convert between subtitles and lyrics formats. + * It supports webvtt, srt and lrc files. + * + * Use nsub --help for more information. + */ + #ifndef NSUB_H #define NSUB_H /* * Description of the lyrics of a song. - * Note: all timings are in milliseconds. + * + * @note all timings are in milliseconds. */ - #include "utils/array.h" +/** + * A subtitle or lyric format to import from/export to. + */ typedef int NSUB_FORMAT; +/** The format is not supported. */ #define NSUB_FMT_ERROR -1 +/** The format is (not yet) known. */ #define NSUB_FMT_UNKNOWN 0 +/** Lyrcis format (usually for music lyrics). */ #define NSUB_FMT_LRC 1 +/** The W3C standard format for video subtitles. */ #define NSUB_FMT_WEBVTT 2 +/** A de-facto standard for video subtitles (from the program SubRip). */ #define NSUB_FMT_SRT 3 +/** + * A type of lyric. + */ typedef int NSUB_TYPE; +/** Unknown type. */ #define NSUB_UNKNOWN 0 +/** Empty line (more or less a comment). */ #define NSUB_EMPTY 1 +/** A comment (with content). */ #define NSUB_COMMENT 2 +/** A lyric. */ #define NSUB_LYRIC 3 +/** + * A lyric (the text of a line from a dialogue or part of a song). + */ typedef struct { NSUB_TYPE type; + /** + * The sequential number of this lyric (only valid for NSUB_LYRIC). + * + * @note the numbering start at 1 + */ int num; + /** + * The time in milliseconds (total song/video play time) after which this + * lyric start. + */ int start; + /** + * The time in milliseconds (total song/video play time) after which this + * lyric stop. + */ int stop; + /** + * The name of this lyric (not the text content, but a name for this line). + * + * @note most format do not understand this, and make it a comment or ignore + * it altogether (example: « Chorus 1 ») + */ char *name; + /** + * The actual content of this lyric or comment. + */ char *text; } lyric_t; +/** + * Some piece of meta-data. + */ typedef struct { char *key; char *value; } meta_t; +/** + * A song (or video). + * + * The main goal of this structure is to group the lyrics. + */ typedef struct { - array *lyrics; - array *metas; + /** The actual lyrics. */ + array_t *lyrics; + /** The meta-data if any. */ + array_t *metas; + /** An offset to apply to every lyrics timings. */ int offset; + /** + * The last lyric number (start at 1, 0 means no lyric). + * + * @see lyric_t.num + */ int current_num; + /** The language of the lyrics. */ char *lang; } song_t; diff --git a/src/nsub/nsub_read_lrc.c b/src/nsub/nsub_read_lrc.c index 24938f8..35cbb15 100644 --- a/src/nsub/nsub_read_lrc.c +++ b/src/nsub/nsub_read_lrc.c @@ -53,9 +53,8 @@ int nsub_read_lrc(song_t *song, char *line) { int start = lrc_millisec(line); char *name = NULL; - size_t count = array_count(song->lyrics); - if (count) { - lyric_t *lyric = (lyric_t*) array_get_ptr(song->lyrics, count - 1); + array_loop(song->lyrics, lyric, lyric_t) + { if (lyric->type == NSUB_LYRIC) lyric->stop = start; if (lyric->type == NSUB_COMMENT) @@ -73,7 +72,8 @@ int nsub_read_lrc(song_t *song, char *line) { if (line[text_offset]) { if (name) { song->current_num--; - array_set_size(song->lyrics, count - 1); + // TODO: memory leak? + array_cut_at(song->lyrics, array_count(song->lyrics) - 1); } song_add_lyric(song, start, start + 5000, name, line + text_offset); } else { @@ -88,7 +88,7 @@ int nsub_read_lrc(song_t *song, char *line) { line[end] = '\0'; if (!strcmp("language", line + 1)) { song->lang = strdup(line + text_offset); - }else if (!strcmp("created_by", line + 1)) { + } else if (!strcmp("created_by", line + 1)) { // skip (we KNOW what program we are) } else { song_add_meta(song, line + 1, line + text_offset); diff --git a/src/nsub/nsub_write_lrc.c b/src/nsub/nsub_write_lrc.c index cb38ddd..b8bf438 100644 --- a/src/nsub/nsub_write_lrc.c +++ b/src/nsub/nsub_write_lrc.c @@ -38,7 +38,7 @@ int nsub_write_lrc(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset) { size_t count = array_count(song->metas); meta_t *meta; for (size_t i = 0; i < count; i++) { - meta = (meta_t *) array_get_ptr(song->metas, i); + meta = (meta_t *) array_get(song->metas, i); fprintf(out, "[%s: %s]\n", meta->key, meta->value); } } @@ -69,7 +69,7 @@ int nsub_write_lrc(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset) { size_t count = array_count(song->lyrics); lyric_t *lyric; for (size_t i = 0; i < count; i++) { - lyric = (lyric_t*) array_get_ptr(song->lyrics, i); + lyric = (lyric_t*) array_get(song->lyrics, i); nsub_write_lrc_lyric(out, lyric, offset); } } diff --git a/src/nsub/nsub_write_srt.c b/src/nsub/nsub_write_srt.c index cf16c01..742d582 100644 --- a/src/nsub/nsub_write_srt.c +++ b/src/nsub/nsub_write_srt.c @@ -47,7 +47,7 @@ int nsub_write_srt(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset) { size_t count = array_count(song->lyrics); lyric_t *lyric; for (size_t i = 0; i < count; i++) { - lyric = (lyric_t*) array_get_ptr(song->lyrics, i); + lyric = (lyric_t*) array_get(song->lyrics, i); nsub_write_srt_lyric(out, lyric, offset); } } diff --git a/src/nsub/nsub_write_webvtt.c b/src/nsub/nsub_write_webvtt.c index b856776..177d6ae 100644 --- a/src/nsub/nsub_write_webvtt.c +++ b/src/nsub/nsub_write_webvtt.c @@ -45,7 +45,7 @@ int nsub_write_webvtt(FILE *out, song_t *song, NSUB_FORMAT fmt, size_t count = array_count(song->metas); meta_t *meta; for (size_t i = 0; i < count; i++) { - meta = (meta_t *) array_get_ptr(song->metas, i); + meta = (meta_t *) array_get(song->metas, i); fprintf(out, "NOTE META %s: %s\n\n", meta->key, meta->value); } } @@ -66,7 +66,7 @@ int nsub_write_webvtt(FILE *out, song_t *song, NSUB_FORMAT fmt, size_t count = array_count(song->lyrics); lyric_t *lyric; for (size_t i = 0; i < count; i++) { - lyric = (lyric_t*) array_get_ptr(song->lyrics, i); + lyric = (lyric_t*) array_get(song->lyrics, i); nsub_write_webvtt_lyric(out, lyric, offset); } } diff --git a/src/tests/launcher.c b/src/tests/launcher.c index ebdfe83..79b9d2d 100644 --- a/src/tests/launcher.c +++ b/src/tests/launcher.c @@ -146,8 +146,6 @@ int main(int argc, char **argv) { test_failure(); cont = 0; } else if (!strcmp("--more", argv[i])) { - if ((i + 1) >= argc) - return 1; more = 1; } else if (!strcmp("--color", argv[i])) { launcher_color = 1; diff --git a/src/tests/launcher.h b/src/tests/launcher.h index 89a6ac4..b1e6e63 100644 --- a/src/tests/launcher.h +++ b/src/tests/launcher.h @@ -16,19 +16,19 @@ END_TEST\ ck_abort_msg(__VA_ARGS__)\ #define ASSERT_EQUALS_STR(title, expected, received) \ - if (strcmp(expected, received)) { \ + do { if (strcmp(expected, received)) { \ ck_abort_msg("%s\n\tExpected: <%s>\n\tReceived: <%s>", title, expected, received); \ -} +}} while(0) #define ASSERT_EQUALS_INT(title, expected, received) \ - if (expected != received) { \ + do { if (expected != received) { \ ck_abort_msg("%s\n\tExpected: %lld\n\tReceived: %lld", title, (long long)expected, (long long)received); \ -} +}} while(0) #define ASSERT_EQUALS_SIZE(title, expected, received) \ - if (expected != received) { \ + do { if (expected != received) { \ ck_abort_msg("%s\n\tExpected: %zu\n\tReceived: %zu", title, expected, received); \ -} +}} while(0) extern int launcher_color; diff --git a/src/tests/utils/array.c b/src/tests/utils/array.c index 956d04d..6d7d9c2 100644 --- a/src/tests/utils/array.c +++ b/src/tests/utils/array.c @@ -17,23 +17,28 @@ * along with this program. If not, see . */ -#include "launcher.h" #include "utils/array.h" -array *a; +#include +#include +#include + +#include "launcher.h" -void test_array_setup() { +static array_t *a; + +static void setup() { a = new_array(sizeof(char), 80); } -void test_array_teardown() { +static void teardown() { free_array(a); a = NULL; } -void array_reset() { - test_array_teardown(); - test_array_setup(); +static void reset() { + teardown(); + setup(); } START(init) @@ -42,14 +47,233 @@ START(init) if (array_count(a)) FAIL("empty array has a size of %zu", array_count(a)); + + END + +START(clear) + char car = 'T'; + + array_push(a, &car); + array_push(a, &car); + array_clear(a); + if (array_count(a)) + FAIL("empty array has a size of %zu", array_count(a)); + + END + +START(convert) + char car = 'T'; + char eos = '\0'; + char *str; + + array_push(a, &car); + array_push(a, &car); + array_push(a, &eos); + + str = (char*) array_convert(a); + ASSERT_EQUALS_STR("array of chars to string conversion", "TT", str); + + free(str); + a = NULL; + + END + +START(data) + char car = 'T'; + char eos = '\0'; + char *str; + + array_push(a, &car); + array_push(a, &car); + array_push(a, &eos); + + str = (char*) array_data(a); + ASSERT_EQUALS_STR("array of chars to string conversion", "TT", str); + + END + +START(count) + char car = 'T'; + char eos = '\0'; + + ASSERT_EQUALS_INT("empty array", 0, array_count(a)); + reset(); + + array_push(a, &car); + ASSERT_EQUALS_INT("1-char array", 1, array_count(a)); + reset(); + + array_push(a, &car); + array_push(a, &car); + array_push(a, &eos); + ASSERT_EQUALS_INT("array with a NUL at the end", 3, array_count(a)); + reset(); + + END + +START(pointers) + char *ptr; + char car; + + ptr = array_new(a); + *ptr = 'T'; + ASSERT_EQUALS_INT("add a new element via ptr", 1, array_count(a)); + + ptr = array_first(a); + car = *ptr; + if (car != 'T') + FAIL("first_ptr: 'T' was expected, but we got: '%c'", car); + + reset(); + + char *testy = "Testy"; + ptr = array_newn(a, strlen(testy) + 1); + for (size_t i = 0; i <= strlen(testy); i++) { + *ptr = testy[i]; + ptr++; + } + + ptr = array_first(a); + for (size_t i = 0; i < strlen(testy); i++) { + ASSERT_EQUALS_STR("next_ptr", testy + i, (char* )ptr); + ptr = array_next(a, ptr); + } + + array_loop_i(a, ptr2, char, i) { + if (i < strlen(testy)) + ASSERT_EQUALS_STR("array_loop", testy + i, ptr2); + else if (i == strlen(testy)) + ASSERT_EQUALS_STR("array_loop (last)", "", ptr2); + else + FAIL("array_loop went out of bounds"); + } + + END + +START(get) + char *str = "Testy"; + size_t sz = strlen(str); + + array_pushn(a, str, sz + 1); + + for (size_t i = 0; i < sz + 1; i++) { + char expected = str[i]; + char *ret = array_get(a, i); + ASSERT_EQUALS_INT("get each element", expected, *ret); + } + + END + +START(pop) + char *rep; + + if (array_pop(a)) + FAIL("popping an item from an empty array"); + + rep = array_new(a); + *rep = 'T'; + + rep = array_pop(a); + if (!rep) + FAIL("cannot pop item from 1-sized array"); + + ASSERT_EQUALS_INT("bad item popped", (int )'T', (int )*rep); + + if (a->count) + FAIL("popped 1-sized array still has %d items", a->count); + + rep = array_new(a); + *rep = 'T'; + rep = array_new(a); + *rep = 'T'; + + rep = array_pop(a); + ASSERT_EQUALS_INT("bad item 1 popped", (int )'T', (int )*rep); + ASSERT_EQUALS_INT("popping should remove 1 from count", 1, a->count); + + rep = array_pop(a); + ASSERT_EQUALS_INT("bad item 2 popped", (int )'T', (int )*rep); + ASSERT_EQUALS_INT("popping should remove 1 from count", 0, a->count); + + END + +START(cut_at) + char *rep; + + if (array_cut_at(a, 4)) + FAIL("cutting an empty array returned something"); + + rep = array_new(a); + *rep = 'T'; + rep = array_new(a); + *rep = 'T'; + + if (array_cut_at(a, 4)) + FAIL("cutting an array above its count returned something"); + + if (!array_cut_at(a, 1)) + FAIL("failed to cut an array"); + ASSERT_EQUALS_INT("cutting at 1 should get a 1-sized array", 1, + a->count); + + if (!array_cut_at(a, 0)) + FAIL("failed to cut an array"); + ASSERT_EQUALS_INT("cutting at 0 should get a 1-sized array", 0, + a->count); + + END + +START(compact) + char car = 'T'; + char eos = '\0'; + + array_compact(a); + + array_push(a, &car); + array_push(a, &car); + array_push(a, &eos); + + ASSERT_EQUALS_STR("compacted array has wrong data", "TT", + (char* )array_data(a)); + + END + + // TODO +START(NO_TEST_YET_qsort) + END +START(NO_TEST_YET_push) + END +START(NO_TEST_YET_set) + END +START(NO_TEST_YET_copy) + END +START(NO_TEST_YET_readfile) + END +START(NO_TEST_YET_print) 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_checked_fixture(core, setup, teardown); tcase_add_test(core, init); + tcase_add_test(core, clear); + tcase_add_test(core, convert); + tcase_add_test(core, data); + tcase_add_test(core, count); + tcase_add_test(core, pointers); // new, newn, first, next + tcase_add_test(core, get); + tcase_add_test(core, pop); + tcase_add_test(core, cut_at); + tcase_add_test(core, compact); + + tcase_add_test(core, NO_TEST_YET_qsort); + tcase_add_test(core, NO_TEST_YET_push); + tcase_add_test(core, NO_TEST_YET_set); + tcase_add_test(core, NO_TEST_YET_copy); + tcase_add_test(core, NO_TEST_YET_readfile); + tcase_add_test(core, NO_TEST_YET_print); suite_add_tcase(suite, core); @@ -60,7 +284,7 @@ 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); + tcase_add_checked_fixture(tmore, setup, teardown); // TODO suite_add_tcase(suite, tmore); diff --git a/src/tests/utils/base64.c b/src/tests/utils/base64.c new file mode 100644 index 0000000..18e567f --- /dev/null +++ b/src/tests/utils/base64.c @@ -0,0 +1,109 @@ +/* + * 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 . + */ + +#include "utils/base64.h" + +#include + +#include "launcher.h" + +static void setup() { +} + +static void teardown() { +} + +static char decoded[] = "This is Le Test, we will UTF-8 the String, too!"; +static char encoded[] = + "VGhpcyBpcyBMZSBUZXN0LCB3ZSB3aWxsIFVURi04IHRoZSBTdHJpbmcsIHRvbyE="; + +START(decode) + ASSERT_EQUALS_STR("decoding", decoded, base64_decode(encoded)); + END + +START(encode) + ASSERT_EQUALS_STR("encoding", encoded, base64_encode(decoded)); + END + +START(both_ways) + char *enc = base64_encode(decoded); + char *dec = base64_decode(enc); + ASSERT_EQUALS_STR("both ways", decoded, dec); + END + +START(big) + size_t sz = 10 * 1024 * 1024; + char *dec = malloc((sz + 1) * sizeof(char)); + for (int i = 0; i < sz; i++) { + dec[i] = '0' + (i % 10); + } + dec[sz] = '\0'; + + char *enc = base64_encode(dec); + char *dec2 = base64_decode(enc); + + ASSERT_EQUALS_STR("long encode/decode cycle", dec, dec2); + free(dec2); + free(enc); + free(dec); + + END + +START(lots) + size_t count = 1 * 1000 * 1000; + for (size_t i = 0; i < count; i++) { + char *enc = base64_encode(decoded); + char *dec = base64_decode(enc); + + if (strcmp(decoded, dec)) { + FAIL("Failed short encode/decode cycle at index %zu", i); + } + + free(enc); + free(dec); + } + + END + +Suite *test_base64(const char title[]) { + Suite *suite = suite_create(title); + + TCase *core = tcase_create("core"); + tcase_add_checked_fixture(core, setup, teardown); + tcase_add_test(core, decode); + tcase_add_test(core, encode); + + suite_add_tcase(suite, core); + + return suite; +} + +Suite *test_base64_more(const char title[]) { + Suite *suite = suite_create(title); + + TCase *tmore = tcase_create("more"); + tcase_add_checked_fixture(tmore, setup, teardown); + tcase_add_test(tmore, both_ways); + tcase_add_test(tmore, big); + tcase_add_test(tmore, lots); + + suite_add_tcase(suite, tmore); + + return suite; +} diff --git a/src/tests/utils/cstring.c b/src/tests/utils/cstring.c index b44aef2..fb94c8f 100644 --- a/src/tests/utils/cstring.c +++ b/src/tests/utils/cstring.c @@ -24,7 +24,7 @@ #define TEST_FILE_READLINE "utils/test_readline.txt" -cstring *s; +cstring_t *s; static void setup() { s = new_cstring(); @@ -435,7 +435,7 @@ START(convert) END START(clone) - cstring *clone; + cstring_t *clone; clone = cstring_clone(NULL); if (clone) @@ -775,7 +775,7 @@ START(dirname) END START(many_adds) - size_t count = 50000000; + size_t count = 10 * 1000 * 1000; for (size_t i = 0; i < count; i++) { cstring_add(s, "1234567890"); } diff --git a/src/tests/utils/main.c b/src/tests/utils/main.c index 182401d..474c431 100644 --- a/src/tests/utils/main.c +++ b/src/tests/utils/main.c @@ -32,6 +32,10 @@ SRunner *get_tests(int more) { if (more) add_test(test_array_more("array -- more (longer)")); + add_test(test_base64("base64")); + if (more) + add_test(test_base64_more("base64 -- more (longer)")); + return runner; } diff --git a/src/tests/utils/main.h b/src/tests/utils/main.h index 497bc2f..5fced56 100644 --- a/src/tests/utils/main.h +++ b/src/tests/utils/main.h @@ -26,5 +26,7 @@ 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[]); +Suite *test_base64(const char title[]); +Suite *test_base64_more(const char title[]); #endif /* SRC_TESTS_UTILS_MAIN_H_ */ diff --git a/src/utils/array.c b/src/utils/array.c index 0634839..6e192ef 100644 --- a/src/utils/array.c +++ b/src/utils/array.c @@ -24,250 +24,318 @@ #include "array.h" -struct array_p { +typedef struct { size_t elem_size; - size_t count; size_t buffer; void *data; -}; +} priv_t; /* make sure we have at least n elements in the buffer, grow if needed */ -int array_assure(array *me, size_t nb_elem); +static int array_assure(array_t *me, size_t nb_elem); /* for qsort operations */ -int array_qsorts_func(const void *a, const void *b); -int array_qsorti_func(const void *a, const void *b); -int array_qsortl_func(const void *a, const void *b); -int array_qsortf_func(const void *a, const void *b); -int array_qsorts_rfunc(const void *a, const void *b); -int array_qsorti_rfunc(const void *a, const void *b); -int array_qsortl_rfunc(const void *a, const void *b); -int array_qsortf_rfunc(const void *a, const void *b); +static int array_qsorts_func(const void *a, const void *b); +static int array_qsorti_func(const void *a, const void *b); +static int array_qsortl_func(const void *a, const void *b); +static int array_qsortf_func(const void *a, const void *b); +static int array_qsorts_rfunc(const void *a, const void *b); +static int array_qsorti_rfunc(const void *a, const void *b); +static int array_qsortl_rfunc(const void *a, const void *b); +static int array_qsortf_rfunc(const void *a, const void *b); /* for reafilei, readfiles */ -void array_readfile_funci(array *me, const char line[]); -void array_readfile_funcs(array *me, const char line[]); +static void array_readfile_funci(array_t *me, const char line[]); +static void array_readfile_funcs(array_t *me, const char line[]); + +array_t *new_array(size_t elem_size, size_t initial) { + array_t *me = NULL; -array *new_array(size_t elem_size, size_t initial) { - array *me = NULL; if (initial) - me = malloc(sizeof(array)); + me = malloc(sizeof(array_t)); + if (me) { - me->elem_size = elem_size; - me->count = 0; - me->buffer = initial; - me->data = malloc(elem_size * initial); - if (!me->data) { - free(me); - me = NULL; + me->priv = malloc(sizeof(priv_t)); + if (me->priv) { + priv_t *priv = (priv_t *) me->priv; + strcpy(me->CNAME, "[CArray ]"); + priv->elem_size = elem_size; + me->count = 0; + priv->buffer = initial; + priv->data = malloc(elem_size * initial); + if (!priv->data) { + free(me->priv); + free(me); + me = NULL; + } } } - + return me; } -void free_array(array *me) { +void free_array(array_t *me) { if (!me) return; + priv_t *priv = (priv_t *) me->priv; me->count = 0; - me->buffer = 0; - free(me->data); + priv->buffer = 0; + free(priv->data); + free(priv); free(me); } -void array_clear(array *me) { +void array_clear(array_t *me) { me->count = 0; } // convert to void * data (free the rest) -void *array_convert(array *me) { - void *data = me->data; +void *array_convert(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + void *data = priv->data; + free(priv); free(me); return data; } -void *array_data(array *me) { - return me->data; +void *array_data(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + return priv->data; } -size_t array_count(array *me) { +size_t array_count(array_t *me) { return me->count; } -void array_compact(array *me) { - int c = me->count > 0 ? me->count : 1; - me->data = realloc(me->data, c * me->elem_size); - me->buffer = c; +void *array_new(array_t *me) { + return array_newn(me, 1); +} + +void *array_newn(array_t *me, size_t how_many) { + if (!array_assure(me, me->count + how_many)) + return 0; + + me->count += how_many; + return array_get(me, me->count - how_many); } -void array_all(array *me, void(*func)()) { - for (size_t i = 0 ; i < me->count ; i++) { - func(((void **)me->data)[i]); +void *array_first(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + return priv->data; +} + +void *array_next(array_t *me, void *ptr) { + priv_t *priv = (priv_t *) me->priv; + + // cast to (char *) because we want 'byte' arithmetic + char *cptr = (char *) ptr; + char *cdata = (char*) priv->data; + + if (cptr) { + cptr += priv->elem_size; + char *last = cdata + ((me->count - 1) * priv->elem_size); + if (cptr <= last) { + return cptr; + } } + + return NULL; } -void array_free_all(array *me) { - array_all(me, free); - me->count = 0; +void *array_get(array_t *me, size_t i) { + priv_t *priv = (priv_t *) me->priv; + + // cast to (char *) because we want 'byte' arithmetic + return (void *) (((char *) priv->data) + (i * priv->elem_size)); +} + +void *array_pop(array_t *me) { + return array_cut_at(me, me->count - 1); +} + +void *array_cut_at(array_t *me, size_t n) { + if (n < me->count) { + void *item = array_get(me, n); + me->count = n; + return item; + } + + return NULL; } -void array_qsort(array *me, int (*compar)(const void *, const void *)) { - qsort(me->data, me->count, me->elem_size, compar); +void array_compact(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + + int c = me->count ? me->count : 1; + priv->data = realloc(priv->data, c * priv->elem_size); + priv->buffer = c; } -void array_qsorts(array *me, int rev) { +void array_qsort(array_t *me, int (*compar)(const void *, const void *)) { + priv_t *priv = (priv_t *) me->priv; + qsort(priv->data, me->count, priv->elem_size, compar); +} + +void array_qsorts(array_t *me, int rev) { array_qsort(me, rev ? array_qsorts_rfunc : array_qsorts_func); } -int array_qsorts_func(const void *a, const void *b) { - char *stra = ((char **)a)[0]; - char *strb = ((char **)b)[0]; +static int array_qsorts_func(const void *a, const void *b) { + char *stra = ((char **) a)[0]; + char *strb = ((char **) b)[0]; return strcmp(stra, strb); } -int array_qsorts_rfunc(const void *a, const void *b) { - char *stra = ((char **)a)[0]; - char *strb = ((char **)b)[0]; +static int array_qsorts_rfunc(const void *a, const void *b) { + char *stra = ((char **) a)[0]; + char *strb = ((char **) b)[0]; return strcmp(strb, stra); } -void array_qsorti(array *me, int rev) { +void array_qsorti(array_t *me, int rev) { array_qsort(me, rev ? array_qsorti_rfunc : array_qsorti_func); } -int array_qsorti_func(const void *a, const void *b) { +static int array_qsorti_func(const void *a, const void *b) { long ia, ib; - ia = ((int *)a)[0]; - ib = ((int *)b)[0]; - if (ia < ib) return -1; + ia = ((int *) a)[0]; + ib = ((int *) b)[0]; + if (ia < ib) + return -1; return !(ia == ib); } -int array_qsorti_rfunc(const void *a, const void *b) { +static int array_qsorti_rfunc(const void *a, const void *b) { long ia, ib; - ia = ((int *)a)[0]; - ib = ((int *)b)[0]; - if (ia > ib) return -1; + ia = ((int *) a)[0]; + ib = ((int *) b)[0]; + if (ia > ib) + return -1; return !(ia == ib); } -void array_qsortl(array *me, int rev) { +void array_qsortl(array_t *me, int rev) { array_qsort(me, rev ? array_qsortl_rfunc : array_qsortl_func); } -int array_qsortl_func(const void *a, const void *b) { +static int array_qsortl_func(const void *a, const void *b) { long la, lb; - la = ((long *)a)[0]; - lb = ((long *)b)[0]; - if (la < lb) return -1; + la = ((long *) a)[0]; + lb = ((long *) b)[0]; + if (la < lb) + return -1; return !(la == lb); } -int array_qsortl_rfunc(const void *a, const void *b) { +static int array_qsortl_rfunc(const void *a, const void *b) { long la, lb; - la = ((long *)a)[0]; - lb = ((long *)b)[0]; - if (la > lb) return -1; + la = ((long *) a)[0]; + lb = ((long *) b)[0]; + if (la > lb) + return -1; return !(la == lb); } -void array_qsortf(array *me, int rev) { +void array_qsortf(array_t *me, int rev) { array_qsort(me, rev ? array_qsortf_rfunc : array_qsortf_func); } -int array_qsortf_func(const void *a, const void *b) { +static int array_qsortf_func(const void *a, const void *b) { float fa, fb; - fa = ((float *)a)[0]; - fb = ((float *)b)[0]; + fa = ((float *) a)[0]; + fb = ((float *) b)[0]; // Also works: //memcpy(&fa, a, sizeof(float)); //memcpy(&fb, b, sizeof(float)); - if (fa < fb) return -1; + if (fa < fb) + return -1; return !(fa == fb); } -int array_qsortf_rfunc(const void *a, const void *b) { +static int array_qsortf_rfunc(const void *a, const void *b) { float fa, fb; - fa = ((float *)a)[0]; - fb = ((float *)b)[0]; + fa = ((float *) a)[0]; + fb = ((float *) b)[0]; // Also works: //memcpy(&fa, a, sizeof(float)); //memcpy(&fb, b, sizeof(float)); - if (fa > fb) return -1; + if (fa > fb) + return -1; return !(fa == fb); } -int array_add(array *me, void *data) { +int array_push(array_t *me, void *data) { return array_setn(me, me->count, data, 1); } -int array_addn(array *me, void *data, size_t n) { +int array_pushn(array_t *me, void *data, size_t n) { return array_setn(me, me->count, data, n); } -void *array_get_ptr(array *me, size_t i) { - // cast to (char *) because we want 'byte' arithmetic - return (void *)(((char *)me->data) + (i * me->elem_size)); +int array_copy(array_t *me, void *target, size_t i) { + return array_copyn(me, target, i, 1); } -void array_get(array *me, void *target, size_t i) { - array_getn(me, target, i, 1); -} +int array_copyn(array_t *me, void *target, size_t i, size_t n) { + priv_t *priv = (priv_t *) me->priv; -void array_getn(array *me, void *target, size_t i, size_t n) { - // cast to (char *) because we want 'byte' arithmetic - memcpy(target, ((char *)(me->data)) + (i * me->elem_size), n * me->elem_size); + if (i + n < me->count) { + // cast to (char *) because we want 'byte' arithmetic + memcpy(target, ((char *) (priv->data)) + (i * priv->elem_size), + n * priv->elem_size); + + return 1; + } + + return 0; } -int array_set(array *me, size_t i, void *data) { +int array_set(array_t *me, size_t i, void *data) { return array_setn(me, i, data, 1); } -int array_setn(array *me, size_t i, void *data, size_t n) { +int array_setn(array_t *me, size_t i, void *data, size_t n) { // allow new items BUT NOT holes in the array if (i > me->count) return 0; - + if (!array_assure(me, i + n)) return 0; - + + priv_t *priv = (priv_t *) me->priv; + // cast to (char *) because we want 'byte' arithmetic - memcpy(((char *)(me->data)) + (i * me->elem_size), data, n * me->elem_size); + memcpy(((char *) (priv->data)) + (i * priv->elem_size), data, + n * priv->elem_size); if ((i + n) > me->count) me->count = i + n; - return 1; -} - -int array_set_size(array *me, size_t n) { - if (n < me->count) { - me->count = n; - return 1; - } - return 0; + return 1; } -size_t array_readfilei(array *me, FILE *in) { +size_t array_readfilei(array_t *me, FILE *in) { return array_readfile(me, in, array_readfile_funci); } -size_t array_readfiles(array *me, FILE *in) { +size_t array_readfiles(array_t *me, FILE *in) { return array_readfile(me, in, array_readfile_funcs); } -size_t array_readfile(array *me, FILE *in, void(*doline)(array *me, const char line[])) { - array *mot = NULL; +size_t array_readfile(array_t *me, FILE *in, + void (*doline)(array_t *me, const char line[])) { + array_t *mot = NULL; char zero = '\0'; char buffer[4096]; size_t i, n, start, count; mot = new_array(sizeof(char), 80); + priv_t *mot_priv = (priv_t *) mot->priv; count = 0; for (n = fread(buffer, sizeof(char), sizeof(buffer), in) - ; n ; n = fread(buffer, sizeof(char), sizeof(buffer), in)) { - for (i = 0 ; i < n ; i++) { - for (start = i ; i < n && buffer[i] != '\n' ; i++); + ; n; n = fread(buffer, sizeof(char), sizeof(buffer), in)) { + for (i = 0; i < n; i++) { + for (start = i; i < n && buffer[i] != '\n'; i++) + ; if (i > start) { - array_addn(mot, buffer + start, (i - start)); + array_pushn(mot, buffer + start, (i - start)); } if (i == start || (i < n && buffer[i] == '\n')) { - array_add(mot, &zero); - doline(me, mot->data); + array_push(mot, &zero); + doline(me, mot_priv->data); count++; array_clear(mot); } @@ -275,8 +343,8 @@ size_t array_readfile(array *me, FILE *in, void(*doline)(array *me, const char l } if (mot->count) { - array_add(mot, &zero); - doline(me, mot->data); + array_push(mot, &zero); + doline(me, mot_priv->data); count++; } @@ -284,57 +352,66 @@ size_t array_readfile(array *me, FILE *in, void(*doline)(array *me, const char l return count; } -void array_print(array *me) { +void array_print(array_t *me) { array_print_fmt(me, NULL, NULL); } -void array_prints(array *me) { +void array_prints(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + array_print_fmt(me, NULL, NULL); - for (size_t i = 0 ; i < me->count ; i++) { - void *d = (void *)(((void **)me->data)[i]); - fprintf(stdout, "> %zu: %s\n", i, (char *)d); + for (size_t i = 0; i < me->count; i++) { + void *d = (void *) (((void **) priv->data)[i]); + fprintf(stdout, "> %zu: %s\n", i, (char *) d); } } -void array_printi(array *me) { +void array_printi(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + array_print_fmt(me, NULL, NULL); - int *ii = malloc(me->count * me->elem_size); - memcpy(ii, me->data, me->count * me->elem_size); - for (size_t i = 0 ; i < me->count ; i++) { + int *ii = malloc(me->count * priv->elem_size); + memcpy(ii, priv->data, me->count * priv->elem_size); + for (size_t i = 0; i < me->count; i++) { fprintf(stdout, "> %zu: %d\n", i, ii[i]); } free(ii); } -void array_printl(array *me) { +void array_printl(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + array_print_fmt(me, NULL, NULL); - long *l = malloc(me->count * me->elem_size); - memcpy(l, me->data, me->count * me->elem_size); - for (size_t i = 0 ; i < me->count ; i++) { + long *l = malloc(me->count * priv->elem_size); + memcpy(l, priv->data, me->count * priv->elem_size); + for (size_t i = 0; i < me->count; i++) { fprintf(stdout, "> %zu: %ld\n", i, l[i]); } free(l); } -void array_printf(array *me) { +void array_printf(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + array_print_fmt(me, NULL, NULL); - float *f = malloc(me->count * me->elem_size); - memcpy(f, me->data, me->count * me->elem_size); - for (size_t i = 0 ; i < me->count ; i++) { + float *f = malloc(me->count * priv->elem_size); + memcpy(f, priv->data, me->count * priv->elem_size); + for (size_t i = 0; i < me->count; i++) { fprintf(stdout, "> %zu: %f\n", i, f[i]); } free(f); } -void array_print_fmt(array *me, - void(*display)(char *buffer, void *), char *buffer) { +void array_print_fmt(array_t *me, void (*display)(char *buffer, void *), + char *buffer) { + priv_t *priv = (priv_t *) me->priv; fprintf(stdout, - "Array of %zu elements of %zu bytes (buffer of %zu elements)\n", - me->count, me->elem_size, me->buffer); + "Array of %zu elements of %zu bytes (buffer of %zu elements)\n", + me->count, priv->elem_size, priv->buffer); if (display) { - for (size_t i = 0 ; i < me->count ; i++) { - void *d = (void *)(((void **)me->data)[i]); + for (size_t i = 0; i < me->count; i++) { + void *d = (void *) (((void **) priv->data)[i]); display(d, buffer); fprintf(stdout, "> %zu: %s\n", i, buffer); } @@ -343,29 +420,33 @@ void array_print_fmt(array *me, /* Privates functions */ -int array_assure(array *me, size_t nb_elem) { - if (me->buffer < nb_elem) { - me->buffer *= 2; - if (me->buffer < nb_elem) { - me->buffer = nb_elem; +static int array_assure(array_t *me, size_t nb_elem) { + priv_t *priv = (priv_t *) me->priv; + + if (priv->buffer < nb_elem) { + priv->buffer *= 2; + if (priv->buffer < nb_elem) { + priv->buffer = nb_elem; } - - me->data = realloc(me->data, me->elem_size * me->buffer); - if (!me->data) { + + void *tmp = realloc(priv->data, priv->elem_size * priv->buffer); + if (!tmp) return 0; - } + + priv->data = tmp; } - + return 1; } -void array_readfile_funci(array *me, const char line[]) { +static void array_readfile_funci(array_t *me, const char line[]) { int i = atoi(line); - array_add(me, &i); + array_push(me, &i); } -void array_readfile_funcs(array *me, const char line[]) { +static void array_readfile_funcs(array_t *me, const char line[]) { char *myline = malloc((strlen(line) + 1) * sizeof(char)); strcpy(myline, line); - array_add(me, &myline); + char **n = array_new(me); + *n = myline; } diff --git a/src/utils/array.h b/src/utils/array.h index a8a9d1f..caf8128 100644 --- a/src/utils/array.h +++ b/src/utils/array.h @@ -20,7 +20,7 @@ /** * @file array.h * @author Niki - * @date 2020 + * @date 2020 - 2022 * * @brief A simple auto-growing array-list * @@ -43,17 +43,28 @@ * const char *l2 = "3. En réserve"; * const char *l3 = "1. Première ligne"; * - * array_add(lines, &l1); - * array_add(lines, &l2); - * array_add(lines, &l3); + * // push mode (usually used for int, char, long...) + * array_push(lines, &l1); + * array_push(lines, &l2); * - * array_qsorts(lines); + * // new mode (usually used for structures) + * char **tmp = array_new(lines); + * *tmp = l3; * - * char *last_line; - * array_get(lines, &last_line, array_count(lines) - 1); - * printf("Last line is now: %s\n", last_line); + * // sort as Strings (also possible with int, long and custom functions) + * array_qsorts(lines, 0); + * + * char **last_line = array_get(lines, array_count(lines) - 1); + * printf("Last line is now: %s\n", *last_line); * // -> last_line is now: 3. En réserve * + * array_loop(lines, line, char) { + * printf("Line: %s\n", line); + * } + * // -> Line: 1. Première ligne + * // -> Line: 2. En réserve + * // -> Line: 3. À l'arrière + * * free_array(lines); * ``` */ @@ -68,8 +79,53 @@ extern "C" { #include #include -/** The structure type to use for arrays. */ -typedef struct array_p array; +/** + * Declare a new TYPE * pointer and loop through the array with it. + * + * How to use: + * ```C + * array_loop(me, line, char) { + * printf("Item: %s\n", line); + * } + * ``` + */ +#define array_loop(me, ptr, TYPE) \ + for (TYPE *ptr = array_first(me); ptr; ptr = array_next(me, ptr)) + +/** + * Similar to array_loop, but add a counter i starting at 0. + * + * @see array_loop + * + * How to use: + * ```C + * array_loop_i(me, line, char, i) { + * printf("Item n°%d: %s\n", i, line); + * } + * ``` + * + * @note this macro does expand as 2 separate lines, surround with { } if needed + */ +#define array_loop_i(me, ptr, TYPE, i) \ + size_t i = 0; \ + for (TYPE *ptr = array_first(me); ptr; ptr = array_next(me, ptr), i++) + +/** + * @brief A simple auto-growing array-list + * + * The structure contains a private field (which you should not use) and the + * current count of how many items were added. You should probably not modify + * the count either (setting it higher is a bad idea and while it should be + * possible to set it lower, you are strongly advised to use + * array_cut_at instead). + * + * @see array_cut_at + */ +typedef struct { + char CNAME[10]; + size_t count; + void *priv; +} array_t; /** * Create a new array. @@ -79,28 +135,29 @@ typedef struct array_p array; * * @return a new array (you must later call `free_array()` or `array_convert()`) */ -array *new_array(size_t elem_size, size_t initial); +array_t *new_array(size_t elem_size, size_t initial); /** * Free the resources held for the given array: you must not use it any more. * Note that if you use pointers and not direct data, you may want to free - * those yourself first, or use `array_free_all()` if applicable. + * those yourself first. + * * @see array_clear - * @see array_free_all + * @see array_loop */ -void free_array(array *me); +void free_array(array_t *me); /** * Clear the array, that is, resets its current size to 0 (buffer unchanged). */ -void array_clear(array *me); +void array_clear(array_t *me); /** * Convert the array to a block of memory where all values are adjacent. * * @return the data (you must later call `free()` on it) */ -void *array_convert(array *me); +void *array_convert(array_t *me); /** * Return a pointer to the internal storage used by this array. @@ -111,40 +168,90 @@ void *array_convert(array *me); * * @return the internal storage area */ -void *array_data(array *me); +void *array_data(array_t *me); /** * Return the current number of elements in the array. * * @return the number of elements in the array */ -size_t array_count(array *me); +size_t array_count(array_t *me); -/** - * Compact the array (resize the buffer so it is equals to the current number - * of items in the array or size 1 if there are no items in the array). +/** + * Create a new element in the array and return a pointer to it. + * + * @return a pointer to the (newly allocated) last element of the array */ -void array_compact(array *me); +void *array_new(array_t *me); /** - * Walk through the array and call `func` on all the valid elements. - * - * @param func: a function that will be called on each element - * @param (parameters) - * * `data`: the element in question + * Create n elements in the array and return a pointer to the + * first one ({see array_next(void *)} to get the next ones). + * + * @param n how many elements to add + * + * @return a pointer to the (newly allocated) first new element of the array + */ +void *array_newn(array_t *me, size_t n); + +/** + * Return a pointer to the first element of the array (for instance, if you + * store integers, it will be (int *); if you store strings, it will + * be char **). + * + * @return a pointer to the first element + */ +void *array_first(array_t *me); + +/** + * Return the pointer to the next element, or NULL if it was the last. + * + * @param ptr a pointer from an array (the array must be valid) + * + * @return the next element, or NULL + */ +void *array_next(array_t *me, void *ptr); + +/** + * Retrieve the the pointer of an item. + * The address of the item will be returned. + * + * @param i the index of the element to retrieve + * + * @return the pointer to the i'th element + */ +void *array_get(array_t *me, size_t i); + +/** + * Return a pointer to the last element of this array and remove it from the + * array, if the array is not empty. + * + * @note be careful, the memory pointed to by the element will be reused the + * next time we add an element -- you should not use it after this; in + * short, the return value is mainly so you can call free on + * value pointed to by this pointer (not the pointer itself) if it + * is a pointer to memory you own, or use it locally before continuing to + * use the array + * @note in case this was not clear, do not call free on the + * returned value + * + * @return a pointer to the last (now removed) item, or NULL if no element */ -void array_all(array *me, void(*func)()); +void *array_pop(array_t *me); /** - * Walk through the array and call `free` on all the valid elements. - * This will not take any precautions, so only use it when you know that all - * the elements are indeed pointers you need to `free`. - * Note that the number of elements will be helfully set to 0 when done, but - * the array itself will **not** be `free`d. - * @see `free_array()` - * @see `array_clear()` + * Cut the array at the given size and return a pointer to the first element + * that was removed if any. + * + * @return a pointer to the first removed element, or NULL */ -void array_free_all(array *me); +void *array_cut_at(array_t *me, size_t n); + +/** + * Compact the array (resize the buffer so it is equals to the current number + * of items in the array or size 1 if there are no items in the array). + */ +void array_compact(array_t *me); /** * Sort the array with a call to `qsort()`. @@ -161,7 +268,7 @@ void array_free_all(array *me); * * `0`: if both elements are equals * * `1`: if element A is more than element B */ -void array_qsort(array *me, int (*compar)(const void *itm1, const void *itm2)); +void array_qsort(array_t *me, int (*compar)(const void *itm1, const void *itm2)); /** * Sort the array with `qsort()`, data is `char *`. @@ -170,7 +277,7 @@ void array_qsort(array *me, int (*compar)(const void *itm1, const void *itm2)); * * @see array_qsort */ -void array_qsorts(array *me, int rev); +void array_qsorts(array_t *me, int rev); /** * Sort the array with `qsort()`, data is `int`. @@ -179,7 +286,7 @@ void array_qsorts(array *me, int rev); * * @see array_qsort */ -void array_qsorti(array *me, int rev); +void array_qsorti(array_t *me, int rev); /** * Sort the array with `qsort()`, data is `long`. @@ -188,7 +295,7 @@ void array_qsorti(array *me, int rev); * * @see array_qsort */ -void array_qsortl(array *me, int rev); +void array_qsortl(array_t *me, int rev); /** * Sort the array with `qsort()`, data is `float`. @@ -197,7 +304,7 @@ void array_qsortl(array *me, int rev); * * @see array_qsort */ -void array_qsortf(array *me, int rev); +void array_qsortf(array_t *me, int rev); /** * Add an element to the array. @@ -207,7 +314,7 @@ void array_qsortf(array *me, int rev); * @return FALSE if the array is too short and we cannot allocate enough * contiguous memory for its needs */ -int array_add(array *me, void *data); +int array_push(array_t *me, void *data); /** * Add multiple elements to the array. @@ -219,7 +326,7 @@ int array_add(array *me, void *data); * @return FALSE if the array is too short and we cannot allocate enough * contiguous memory for its needs */ -int array_addn(array *me, void *data, size_t n); +int array_pushn(array_t *me, void *data, size_t n); /** * Set an element of the array to the given value. @@ -235,7 +342,7 @@ int array_addn(array *me, void *data, size_t n); * @return FALSE if the array is too short and we cannot allocate enough * contiguous memory for its needs, or if the index is out of bounds */ -int array_set(array *me, size_t i, void *data); +int array_set(array_t *me, size_t i, void *data); /** * Set elements of the array to the given value. @@ -252,39 +359,31 @@ int array_set(array *me, size_t i, void *data); * @return FALSE if the array is too short and we cannot allocate enough * contiguous memory for its needs, or if the index is out of bounds */ -int array_setn(array *me, size_t i, void *data, size_t n); - -// only work if new size < current size -int array_set_size(array *me, size_t n); - -/** - * Retrieve the the pointer of an item. - * The address of the item will be returned. - * - * @param i the index of the element to retrieve - * - * @return the pointer to the i'th element - */ -void *array_get_ptr(array *me, size_t i); +int array_setn(array_t *me, size_t i, void *data, size_t n); /** - * Retrieve the content of an item -- no bounds checking. - * The item will be copied to the given address location. + * Retrieve the content of an item. + * The item will be copied to the given address location if it exists. * * @param target an address where to write a copy of the item * @param i the index of the element to retrieve + * + * @return TRUE if the item exists (if i is an element of the array) */ -void array_get(array *me, void *target, size_t i); +int array_copy(array_t *me, void *target, size_t i); /** - * Retrieve the content of multiple items -- no bounds checking. + * Retrieve the content of multiple items if they exist. * The items will be copied in a sequence to the given address location. * * @param target an address where to write a copy of the items * @param i the index of the first element to retrieve * @param n the number of elements to retrieve + * + * @return TRUE if the item exists (if i to n are elements + * of the array) */ -void array_getn(array *me, void *target, size_t i, size_t n); +int array_copyn(array_t *me, void *target, size_t i, size_t n); /** * Read all the lines from a file. @@ -300,8 +399,8 @@ void array_getn(array *me, void *target, size_t i, size_t n); * * @return the number of elements in the array */ -size_t array_readfile(array *me, FILE *in, void(*doline)(array *me, - const char line[])); +size_t array_readfile(array_t *me, FILE *in, + void (*doline)(array_t *me, const char line[])); /** * Read all the lines from a file, converting them to integer values. @@ -316,47 +415,47 @@ size_t array_readfile(array *me, FILE *in, void(*doline)(array *me, * * @return the number of elements in the array */ -size_t array_readfilei(array *me, FILE *in); +size_t array_readfilei(array_t *me, FILE *in); /** * Read all the lines from a file, converting them to strings. * * Note that you will need to free them before freeing the array, - * for instance with `array_free_all()`. + * for instance with the array_loop macro. * + * @see array_loop * @see array_readfile - * @see array_free_all * * @param in the file to read * * @return the number of elements in the array */ -size_t array_readfiles(array *me, FILE *in); +size_t array_readfiles(array_t *me, FILE *in); /** * Print the array metadata to `stderr` (mostly for DEBUG). */ -void array_print(array *me); +void array_print(array_t *me); /** * Print the array and strings content to `stderr` (mostly for DEBUG). */ -void array_prints(array *me); +void array_prints(array_t *me); /** * Print the array and integer content to `stderr` (mostly for DEBUG). */ -void array_printi(array *me); +void array_printi(array_t *me); /** * Print the array and long content to `stderr` (mostly for DEBUG). */ -void array_printl(array *me); +void array_printl(array_t *me); /** * Print the array and floats (%d) content to `stderr` (mostly for DEBUG). */ -void array_printf(array *me); +void array_printf(array_t *me); /** * Print the array and content to `stderr` (mostly for DEBUG). @@ -367,8 +466,8 @@ void array_printf(array *me); * * `buffer`: the buffer to use for this (which is the one you pass) * @param buffer a buffer that will be passed to `display` */ -void array_print_fmt(array *me, - void(*display)(char *buffer, void *item), char *buffer); +void array_print_fmt(array_t *me, void (*display)(char *buffer, void *item), + char *buffer); #endif /* ARRAY_H */ diff --git a/src/utils/base64.h b/src/utils/base64.h index 84226ab..b6c3f25 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -17,6 +17,22 @@ * along with this program. If not, see . */ +/** + * @file base64.h + * @author Niki + * @date 2013 - 2022 + * + * @brief Base64 encode and decode + * + * This file only provides 2 functions, base64_encode and + * base64_decode, which works on NUL-terminated strings and do what + * you expect them to. + * + * @see base64_encode(const char data[]) + * @see base64_decode(const char data[]) + */ + + #ifdef __cplusplus extern "C" { #endif @@ -26,12 +42,26 @@ extern "C" { #include -typedef struct { - char *table; -} base64; - +/** + * Encode the given data to Base64. + * + * @note can return NULL if there is not enough memory to allocated the answer + * + * @param data the data to encode + * + * @return a newly-allocated string for which you are responsible, or NULL + */ char *base64_encode(const char data[]); +/** + * Decode the given data to Base64. + * + * @note can return NULL if there is not enough memory to allocated the answer + * + * @param data the data to decode + * + * @return a newly-allocated string for which you are responsible, or NULL + */ char *base64_decode(const char data[]); #endif diff --git a/src/utils/cstring.c b/src/utils/cstring.c index 7c06927..5505f27 100644 --- a/src/utils/cstring.c +++ b/src/utils/cstring.c @@ -53,11 +53,11 @@ typedef struct { } priv_t; /** Swap the data */ -static void cstring_swap(cstring *a, cstring *b); +static void cstring_swap(cstring_t *a, cstring_t *b); /** Change the case to upper -or- lower case (UTF8-compatible) */ -static void cstring_change_case(cstring *self, int up); +static void cstring_change_case(cstring_t *self, int up); /** For path-related functions */ -static void normalize_path(cstring *self); +static void normalize_path(cstring_t *self); // Private variables @@ -65,10 +65,10 @@ static char *locale = NULL; // end of privates -cstring *new_cstring() { - cstring *string; +cstring_t *new_cstring() { + cstring_t *string; - string = malloc(sizeof(cstring)); + string = malloc(sizeof(cstring_t)); strcpy(string->CNAME, "[CString]"); string->priv = malloc(sizeof(priv_t)); string->length = 0; @@ -79,7 +79,7 @@ cstring *new_cstring() { return string; } -void free_cstring(cstring *string) { +void free_cstring(cstring_t *string) { if (!string) return; @@ -89,7 +89,7 @@ void free_cstring(cstring *string) { free(string); } -void cstring_swap(cstring *a, cstring *b) { +void cstring_swap(cstring_t *a, cstring_t *b) { void *tmp_p; char *tmp_s; size_t tmp_l; @@ -107,14 +107,18 @@ void cstring_swap(cstring *a, cstring *b) { b->priv = tmp_p; } -int cstring_grow(cstring *self, int min_extra) { +int cstring_grow(cstring_t *self, int min_extra) { priv_t *priv = ((priv_t *) self->priv); size_t sz = priv->buffer_length; size_t req = self->length + min_extra; if (req > sz) { - sz += BUFFER_SIZE; + if (sz > BUFFER_SIZE) + sz *= 2; + else + sz += BUFFER_SIZE; + if (req > sz) sz = req; @@ -124,7 +128,7 @@ int cstring_grow(cstring *self, int min_extra) { return 1; } -int cstring_grow_to(cstring *self, int min_buffer) { +int cstring_grow_to(cstring_t *self, int min_buffer) { priv_t *priv = ((priv_t *) self->priv); if (min_buffer > priv->buffer_length) { @@ -140,7 +144,7 @@ int cstring_grow_to(cstring *self, int min_buffer) { return 1; } -void cstring_compact(cstring *self) { +void cstring_compact(cstring_t *self) { if (self != NULL) { priv_t *priv = ((priv_t *) self->priv); @@ -149,7 +153,7 @@ void cstring_compact(cstring *self) { } } -int cstring_add_car(cstring *self, char source) { +int cstring_add_car(cstring_t *self, char source) { if (!cstring_grow(self, 1)) return 0; @@ -160,19 +164,19 @@ int cstring_add_car(cstring *self, char source) { return 1; } -int cstring_add(cstring *self, const char source[]) { +int cstring_add(cstring_t *self, const char source[]) { return cstring_addf(self, source, 0); } -int cstring_addf(cstring *self, const char source[], size_t idx) { +int cstring_addf(cstring_t *self, const char source[], size_t idx) { return cstring_addfn(self, source, idx, 0); } -int cstring_addn(cstring *self, const char source[], size_t n) { +int cstring_addn(cstring_t *self, const char source[], size_t n) { return cstring_addfn(self, source, 0, n); } -int cstring_addfn(cstring *self, const char source[], size_t idx, size_t n) { +int cstring_addfn(cstring_t *self, const char source[], size_t idx, size_t n) { size_t ss; ss = strlen(source); @@ -196,7 +200,7 @@ int cstring_addfn(cstring *self, const char source[], size_t idx, size_t n) { return 1; } -int cstring_addp(cstring *self, const char *fmt, ...) { +int cstring_addp(cstring_t *self, const char *fmt, ...) { va_list ap; char empty = '\0'; @@ -218,16 +222,16 @@ int cstring_addp(cstring *self, const char *fmt, ...) { return ok; } -void cstring_cut_at(cstring *self, size_t size) { +void cstring_cut_at(cstring_t *self, size_t size) { if (self->length > size) { self->string[size] = '\0'; self->length = size; } } -cstring *cstring_substring(const char self[], size_t start, size_t length) { +cstring_t *cstring_substring(const char self[], size_t start, size_t length) { size_t sz = strlen(self); - cstring * sub = new_cstring(); + cstring_t * sub = new_cstring(); if (start <= sz) { const char *source = (self + start); @@ -339,8 +343,8 @@ void cstring_reverse(char *self) { } } -int cstring_replace(cstring *self, const char from[], const char to[]) { - cstring *buffer; +int cstring_replace(cstring_t *self, const char from[], const char to[]) { + cstring_t *buffer; size_t i; size_t step; int occur; @@ -440,12 +444,12 @@ long cstring_rfind(char self[], const char find[], long rstart_index) { return -1; } -void cstring_clear(cstring *self) { +void cstring_clear(cstring_t *self) { self->length = 0; self->string[0] = '\0'; } -char *cstring_convert(cstring *self) { +char *cstring_convert(cstring_t *self) { char *string; if (!self) @@ -462,17 +466,17 @@ char *cstring_convert(cstring *self) { return string; } -cstring *cstring_clone(const char self[]) { +cstring_t *cstring_clone(const char self[]) { if (self == NULL) return NULL; - cstring *clone = new_cstring(); + cstring_t *clone = new_cstring(); cstring_add(clone, self); return clone; } -void cstring_rtrim(cstring *self, char car) { +void cstring_rtrim(cstring_t *self, char car) { for (size_t i = self->length - 1; i >= 0; i--) { if (self->string[i] != car) break; @@ -481,7 +485,7 @@ void cstring_rtrim(cstring *self, char car) { } } -void cstring_trim(cstring *self, char car) { +void cstring_trim(cstring_t *self, char car) { if (car == '\0') return; @@ -492,7 +496,7 @@ void cstring_trim(cstring *self, char car) { i++; if (i) { - cstring *tmp = new_cstring(); + cstring_t *tmp = new_cstring(); cstring_add(tmp, self->string + i); cstring_swap(self, tmp); @@ -512,15 +516,15 @@ size_t cstring_remove_crlf(char *self) { return sz; } -void cstring_toupper(cstring *self) { +void cstring_toupper(cstring_t *self) { cstring_change_case(self, 1); } -void cstring_tolower(cstring *self) { +void cstring_tolower(cstring_t *self) { cstring_change_case(self, 0); } -void cstring_change_case(cstring *self, int up) { +void cstring_change_case(cstring_t *self, int up) { // Change LC_ALL to LANG if not found // TODO: only take part we need (also, this is still bad practise) if (!locale) { @@ -535,7 +539,7 @@ void cstring_change_case(cstring *self, int up) { } } - cstring *rep; + cstring_t *rep; mbstate_t state_from, state_to; wchar_t wide; char tmp[10]; @@ -592,7 +596,7 @@ void cstring_change_case(cstring *self, int up) { free_cstring(rep); } -int cstring_readline(cstring *self, FILE *file) { +int cstring_readline(cstring_t *self, FILE *file) { char buffer[BUFFER_SIZE]; size_t size = 0; int full_line; @@ -635,13 +639,13 @@ int cstring_readline(cstring *self, FILE *file) { return 0; } -static void normalize_path(cstring *self) { +static void normalize_path(cstring_t *self) { while (self->length && self->string[self->length - 1] == CSTRING_SEP) self->length--; self->string[self->length] = '\0'; } -void cstring_add_path(cstring *self, const char subpath[]) { +void cstring_add_path(cstring_t *self, const char subpath[]) { while (self->length && self->string[self->length - 1] == CSTRING_SEP) self->length--; cstring_add_car(self, CSTRING_SEP); @@ -652,7 +656,7 @@ void cstring_add_path(cstring *self, const char subpath[]) { normalize_path(self); } -int cstring_pop_path(cstring *self, int how_many) { +int cstring_pop_path(cstring_t *self, int how_many) { int count = 0; size_t tmp; char first = '\0'; @@ -687,7 +691,7 @@ char *cstring_basename(const char path[], const char ext[]) { while (i && path[i] != CSTRING_SEP) i--; - cstring *rep; + cstring_t *rep; if (path[i] != CSTRING_SEP) { rep = cstring_clone(path); } else { @@ -703,12 +707,12 @@ char *cstring_basename(const char path[], const char ext[]) { } char *cstring_dirname(const char path[]) { - cstring *rep = cstring_clone(path); + cstring_t *rep = cstring_clone(path); cstring_pop_path(rep, 1); return cstring_convert(rep); } -int cstring_is_utf8(cstring *self) { +int cstring_is_utf8(cstring_t *self) { size_t rep = mbstowcs(NULL, self->string, 0); // -2 = invalid, -1 = not whole return (rep != (size_t) -2) && (rep != (size_t) -1); diff --git a/src/utils/cstring.h b/src/utils/cstring.h index ecd34d6..b38c610 100644 --- a/src/utils/cstring.h +++ b/src/utils/cstring.h @@ -17,6 +17,18 @@ * along with this program. If not, see . */ +/** + * @file cstring.h + * @author Niki + * @date 2013 - 2022 + * + * @brief Some string utility functions + * + * This file implements some basic functions of a string, most often by working + * directly with char * (but when needed with the provided + * cstring object). + */ + #ifndef CSTRING_H #define CSTRING_H @@ -27,20 +39,23 @@ extern "C" { #include /** - * This is a cstring. It contains a suite of characters terminated by a NUL byte + * @brief A NUL-byte terminated string, with a known length + * + * The structure 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*). + * 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 said char *). + * + * @see cstring_convert */ -typedef struct cstring_struct cstring; - -struct cstring_struct { - char CNAME[32]; +typedef struct { + char CNAME[10]; char *string; size_t length; void *priv; -}; +} cstring_t; /** * Instantiate a new cstring. @@ -48,7 +63,7 @@ struct cstring_struct { * Create (and allocate the memory for) a new cstring. * Do not forget to call cstring_free(cstring) when done. */ -cstring *new_cstring(); +cstring_t *new_cstring(); /** * Free the given cstring. @@ -57,7 +72,7 @@ cstring *new_cstring(); * * @param self the cstring to free, which MUST NOT be used again afterward */ -void free_cstring(cstring *self); +void free_cstring(cstring_t *self); /** * Grow the cstring to accommodate that many characters in addition to those @@ -69,7 +84,7 @@ void free_cstring(cstring *self); * @return TRUE if success (FALSE means it was unable to grow due to memory * pressure) */ -int cstring_grow(cstring *self, int min_extra); +int cstring_grow(cstring_t *self, int min_extra); /** * Grow the cstring to accommodate that many characters in total, if needed. @@ -80,14 +95,14 @@ int cstring_grow(cstring *self, int min_extra); * @return TRUE if success (FALSE means it was unable to grow due to memory * pressure) */ -int cstring_grow_to(cstring *self, int min_buffer); +int cstring_grow_to(cstring_t *self, int min_buffer); /** * Compact the memory used by this string. * * @param self the string to work on */ -void cstring_compact(cstring *self); +void cstring_compact(cstring_t *self); /** * Add a char at the end of the given cstring. @@ -98,7 +113,7 @@ void cstring_compact(cstring *self); * @return TRUE if success (FALSE means it was unable to grow due to memory * pressure) */ -int cstring_add_car(cstring *self, char source); +int cstring_add_car(cstring_t *self, char source); /** * Add a string (a sequence of char that MUST end with '\0') at the end of the @@ -110,7 +125,7 @@ int cstring_add_car(cstring *self, char source); * @return TRUE if success (FALSE means it was unable to grow due to memory * pressure) */ -int cstring_add(cstring *self, const char source[]); +int cstring_add(cstring_t *self, const char source[]); /** * Add a string (a sequence of char that MUST end with '\0') at the end of the @@ -123,7 +138,7 @@ int cstring_add(cstring *self, const char source[]); * @return TRUE if success (FALSE means it was unable to grow due to memory * pressure) */ -int cstring_addf(cstring *self, const char source[], size_t idx); +int cstring_addf(cstring_t *self, const char source[], size_t idx); /** * Add a string (a sequence of char that MAY end with '\0') at the end of the @@ -137,7 +152,7 @@ int cstring_addf(cstring *self, const char source[], size_t idx); * @return TRUE if success (FALSE means it was unable to grow due to memory * pressure) */ -int cstring_addn(cstring *self, const char source[], size_t n); +int cstring_addn(cstring_t *self, const char source[], size_t n); /** * Add a string (a sequence of char that MAY end with '\0') at the end of the @@ -152,7 +167,7 @@ int cstring_addn(cstring *self, const char source[], size_t n); * @return TRUE if success (FALSE means it was unable to grow due to memory * pressure) */ -int cstring_addfn(cstring *self, const char source[], size_t idx, size_t n); +int cstring_addfn(cstring_t *self, const char source[], size_t idx, size_t n); /** * Add a string via the usual printf formatters. @@ -164,7 +179,7 @@ int cstring_addfn(cstring *self, const char source[], size_t idx, size_t n); * @return TRUE if success (FALSE means it was unable to grow due to memory * pressure) */ -int cstring_addp(cstring *self, const char *fmt, ...); +int cstring_addp(cstring_t *self, const char *fmt, ...); /** * Cut the string at the given size if it is greater. @@ -176,7 +191,7 @@ int cstring_addp(cstring *self, const char *fmt, ...); * @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); +void cstring_cut_at(cstring_t *self, size_t size); /** * Create a substring of this one. @@ -187,7 +202,7 @@ void cstring_cut_at(cstring *self, size_t size); * * @return a newly allocated cstring */ -cstring *cstring_substring(const char self[], size_t start, size_t length); +cstring_t *cstring_substring(const char self[], size_t start, size_t length); /** * Split a cstring into "smaller" cstrings every time the given separator is found. @@ -238,7 +253,7 @@ void cstring_reverse(char *self); * * @return the number of occurrences changed */ -int cstring_replace(cstring *self, const char from[], const char to[]); +int cstring_replace(cstring_t *self, const char from[], const char to[]); /** * Replace all occurrences of a char inside the given string by another. @@ -306,7 +321,7 @@ long cstring_rfind(char self[], const char find[], long rstart_index); * * @param self the string to work on */ -void cstring_clear(cstring *self); +void cstring_clear(cstring_t *self); /** * Convert this cstring into a string @@ -315,7 +330,7 @@ void cstring_clear(cstring *self); * * @param self the cstring to work on */ -char *cstring_convert(cstring *self); +char *cstring_convert(cstring_t *self); /** * Clone this string. @@ -323,7 +338,7 @@ char *cstring_convert(cstring *self); * * @param self the string to clone */ -cstring *cstring_clone(const char self[]); +cstring_t *cstring_clone(const char self[]); /** * Trim this cstring of all trailing 'car' instances. @@ -333,7 +348,7 @@ cstring *cstring_clone(const char self[]); * * @return a right trimmed cstring */ -void cstring_rtrim(cstring *self, char car); +void cstring_rtrim(cstring_t *self, char car); /** * Trim this cstring of all 'car' instances from the start and/or the @@ -344,7 +359,7 @@ void cstring_rtrim(cstring *self, char car); * * @return a trimmed cstring */ -void cstring_trim(cstring *self, char car); +void cstring_trim(cstring_t *self, char car); /** * Remove the \r and \n sequence (or one OR the other) at the end of the string. @@ -364,7 +379,7 @@ size_t cstring_remove_crlf(char *self); * * @param self the cstring to work on */ -void cstring_toupper(cstring *self); +void cstring_toupper(cstring_t *self); /** * Change the case to lower-case (UTF-8 compatible, but the string MUST be @@ -375,7 +390,7 @@ void cstring_toupper(cstring *self); * * @param self the cstring to work on */ -void cstring_tolower(cstring *self); +void cstring_tolower(cstring_t *self); /** * Read a whole line (CR, LN or CR+LN terminated) from the given file stream. @@ -385,7 +400,7 @@ void cstring_tolower(cstring *self); * * @return 1 if a line was read, 0 if not */ -int cstring_readline(cstring *self, FILE *file); +int cstring_readline(cstring_t *self, FILE *file); /** * Add a path to the given cstring (if it is currently empty, it @@ -397,7 +412,7 @@ int cstring_readline(cstring *self, FILE *file); * @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[]); +void cstring_add_path(cstring_t *self, const char subpath[]); /** * Remove the how_many components of the path described by this @@ -409,7 +424,7 @@ void cstring_add_path(cstring *self, const char subpath[]); * @param how_many how many path components to remove (for instance, to go from * /some/path/to/file to /some/path you would need 2) */ -int cstring_pop_path(cstring *self, int how_many); +int cstring_pop_path(cstring_t *self, int how_many); /** * Return the basename component of this path (for instance, @@ -440,7 +455,7 @@ char *cstring_dirname(const char path[]); * * @return TRUE if it is UTF-8 */ -int cstring_is_utf8(cstring *self); +int cstring_is_utf8(cstring_t *self); #endif diff --git a/src/utils/desktop.c b/src/utils/desktop.c index bcdff7d..ba353a7 100644 --- a/src/utils/desktop.c +++ b/src/utils/desktop.c @@ -34,7 +34,7 @@ struct { char *icon; char *icon_file; char *exec; - array *children; + array_t *children; int id; }typedef desktop_p; @@ -104,7 +104,7 @@ desktop *new_desktop(const char filename[], int best_size) { desktop_p *child = (desktop_p*) new_desktop(childname, best_size); free(childname); if (child) { - array_add(me->children, &child); + array_push(me->children, &child); } } @@ -123,21 +123,17 @@ desktop *new_desktop(const char filename[], int best_size) { } FILE *file; - array *tab; - size_t n_lines; + array_t *tab; tab = new_array(sizeof(char *), 32); file = fopen(filename, "r"); - n_lines = array_readfiles(tab, file); + array_readfiles(tab, file); fclose(file); - char *line; char *startsWith; size_t n; - for (size_t i = 0; i < n_lines; i++) { - array_get(tab, &line, i); - + array_loop_i(tab, line, char, i) { startsWith = "Name="; n = strlen(startsWith); if (!strncmp(line, startsWith, n)) { @@ -169,7 +165,8 @@ desktop *new_desktop(const char filename[], int best_size) { } } - array_free_all(tab); + array_loop(tab, item, void) + free(item); // Find icon file linked to icon if (me->icon && !me->icon_file) { @@ -229,27 +226,24 @@ void desktop_set_id(desktop *app, int id) { me->id = id; } -array *desktop_get_children(desktop *app) { +array_t *desktop_get_children(desktop *app) { desktop_p *me = (desktop_p*) app; return me->children; } -desktop *desktop_find_id(array *children, int id) { +desktop *desktop_find_id(array_t *children, int id) { desktop *found = NULL; - size_t n = 0; - - if (children) - n = array_count(children); - desktop_p *child; - for (size_t i = 0; !found && i < n; i++) { - array_get(children, &child, i); + array_loop(children, child, desktop_p) + { if (child->id == id) { found = (desktop*) child; break; } found = desktop_find_id(child->children, id); + if (found) + break; } return found; @@ -334,23 +328,21 @@ char *desktop_find_icon(const char basename[], int icon_size) { sprintf(icon_size_str, "%dx%d", icon_size, icon_size); if (!theme) { - array *tab = new_array(sizeof(char *), 32); - size_t n_lines = 0; + array_t *tab = new_array(sizeof(char *), 32); tmp = desktop_concat(home, "/.gtkrc-2.0", NULL); FILE *file = fopen(tmp, "r"); free(tmp); if (file) { - n_lines = array_readfiles(tab, file); + array_readfiles(tab, file); fclose(file); } - char *line; const char *startsWith = "gtk-icon-theme-name="; size_t n = strlen(startsWith); - for (size_t i = 0; i < n_lines; i++) { - array_get(tab, &line, i); + array_loop(tab, line, char) + { if (!strncmp(line, startsWith, n)) { free(theme); if (line[n] == '"') { @@ -372,7 +364,8 @@ char *desktop_find_icon(const char basename[], int icon_size) { free(tmp); } - array_free_all(tab); + array_loop(tab,item, void) + free(item); } // Allow NULL diff --git a/src/utils/desktop.h b/src/utils/desktop.h index e6857f3..8e208ae 100644 --- a/src/utils/desktop.h +++ b/src/utils/desktop.h @@ -17,9 +17,10 @@ * along with this program. If not, see . */ -/* @file desktop.h +/** + * @file desktop.h * @author Niki - * @date 2021 + * @date 2021 - 2022 * * @brief Manipulate .desktop files (as described by * FreeDesktop.org) @@ -99,7 +100,7 @@ void desktop_set_id(desktop *self, int id); * TODO: switch to full objects * @return an array of pointers to desktop objects */ -array *desktop_get_children(desktop *app); +array_t *desktop_get_children(desktop *app); /** * Find a submenu item by the given ID ({@see desktop_set_id(desktop *, int)}). @@ -110,7 +111,7 @@ array *desktop_get_children(desktop *app); * * @return the given submenu if found, or NULL */ -desktop *desktop_find_id(array *children, int menu_id); +desktop *desktop_find_id(array_t *children, int menu_id); /** * Look for the icon file related to this basename. diff --git a/src/utils/net.c b/src/utils/net.c index f9fe50d..10937fb 100644 --- a/src/utils/net.c +++ b/src/utils/net.c @@ -105,7 +105,7 @@ int net_connect(const char server[], int port) { int sockfd; struct addrinfo hints, *servinfo, *p; int rv; - cstring *str; + cstring_t *str; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -167,7 +167,7 @@ int net_listen(int port, int backlog) { struct addrinfo hints, *servinfo, *p; char yes = 1; int rv; - cstring *str; + cstring_t *str; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; diff --git a/src/utils/net.h b/src/utils/net.h index 9652a4d..73140f3 100644 --- a/src/utils/net.h +++ b/src/utils/net.h @@ -18,11 +18,14 @@ */ /** - * @file + * @file net.h * @author niki - * @date November 2011 + * @date 2011 - 2022 * - * Allows you to make connections to/from a server, and to send/receive data. + * @brief Send/receive data from the network + * + * Allows you to make connections to/from a server, and to send/receive data + * through those connections. */ #ifndef NET_H @@ -34,54 +37,48 @@ extern "C" { // WHY ?? #ifdef _WIN32 - #ifndef WIN32 - #define WIN32 - #endif +#ifndef WIN32 +#define WIN32 +#endif #endif #ifdef WIN32 - #ifndef _WIN32_WINNT - #define _WIN32_WINNT 0x501 - #endif - #include - #include - 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"); +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x501 +#endif +#include +#include +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 - #include - #include - #include - #include - #include +#include +#include +#include +#include +#include +#include #endif /* for ssize_t */ #include -/** - * 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). + * (It is a noop on all other platforms.) * - * @return 0 when the Windows host does not support WinSock and thus, + * @return FALSE 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). + * (It is a noop on all other platforms.) */ void net_cleanup(); @@ -90,9 +87,9 @@ void net_cleanup(); * 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 + * @param block TRUE to block, FALSE not to block * - * @return 1 if success + * @return TRUE if success */ int net_set_blocking(int fd, int block); @@ -103,17 +100,17 @@ int net_set_blocking(int fd, int block); * @param server the server to connect to * @param port the port to connect on * - * @return the server socket or -1 if error + * @return the server socket or a negative value on error */ int net_connect(const char server[], int port); /** * Open a port and returns a (server) socket descriptor from which you can - * accept connection. + * accept connections. * * @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 + * this socket until you handle them * * @return the server socket, or a negative value on error */ @@ -145,8 +142,7 @@ void net_close_socketd(int socketd); * @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 + * @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); @@ -164,5 +160,5 @@ ssize_t net_read(int fd, void *buf, size_t nbytes); #endif #ifdef __cplusplus -extern } +extern} #endif diff --git a/src/utils/print.h b/src/utils/print.h index 30d59e6..f0872a6 100644 --- a/src/utils/print.h +++ b/src/utils/print.h @@ -3,9 +3,7 @@ * @author Samantaz Fox * @date 2019 * - * @brief print.h - * - * print.h + * @brief File name and line numbers * * Small utility header designed to print file name and line number * along with debug/info messages. @@ -14,7 +12,7 @@ * ERROR("program exited with code %d\n", getErrorCode() ); * * Known issues: - * - Must take at least one argument in addtion to format string + * - Must take at least one argument in addition to format string * * * Copyright 2019 (C) Samantaz Fox @@ -22,7 +20,6 @@ * * This file is in the public domain. * Feel free to copy, modify or redistribute it! - * */ #ifndef __PRINT_H__ diff --git a/src/utils/timing.h b/src/utils/timing.h index b1eba25..1dd0cae 100644 --- a/src/utils/timing.h +++ b/src/utils/timing.h @@ -20,9 +20,9 @@ /** * @file timing.h * @author Niki - * @date 2020 + * @date 2020 - 2022 * - * @brief Timing macro START and STOP + * @brief Timing macros START and STOP * * 2 macro are provided to print the elapsed time between the 2 to stdout. */ @@ -36,11 +36,17 @@ extern "C" { #include +/** + * Start the timer. + */ #define START struct timeval TIMING_start, TIMING_stop; \ /* 1 usec = 0.000001 s */ \ char cusec[7]; \ gettimeofday(&TIMING_start, NULL); +/** + * Stop the timer and print the elapsed time to stdout. + */ #define STOP gettimeofday(&TIMING_stop, NULL); \ TIMING_stop.tv_sec = TIMING_stop.tv_sec - TIMING_start.tv_sec; \ TIMING_stop.tv_usec = TIMING_stop.tv_usec - TIMING_start.tv_usec; \ diff --git a/src/utils/utils.h b/src/utils/utils.h index 667407c..90d4a6a 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -20,10 +20,10 @@ /** * @file utils.h * @author Niki - * @date 2020 + * @date 2020 - 2022 * - * @brief Include all the other .h as well as a C99-compatible strdup/strnlen - * functions. + * @brief Include all the other .h as well as C99-compatible + * strdup/strnlen functions if they are not already defined */ #ifndef UTILS_H #define UTILS_H @@ -40,10 +40,31 @@ extern "C" { /* Helps with C99 compatibility for code that is not */ -#ifndef strnlen +#if _POSIX_C_SOURCE < 200809L && _XOPEN_SOURCE < 500 +#ifndef _GNU_SOURCE +/** + * The strnlen() function returns the number of bytes in the string pointed to + * by s, excluding the terminating null byte ('\0'), but at most maxlen. + * In doing this, strnlen() looks only at the first maxlen characters in the + * string pointed to by s and never beyond s[maxlen-1]. + * + * @return The strnlen() function returns strlen(s), if that is less than + * maxlen, or maxlen if there is no null terminating ('\0') among the first + * maxlen characters pointed to by s + */ size_t strnlen(const char *s, size_t maxlen); #endif -#ifndef strdup +#endif +#if _POSIX_C_SOURCE < 200809L && _XOPEN_SOURCE < 500 +/** + * The strndup() function is similar, but copies at most n bytes. + * If s is longer than n, only n bytes are copied, and a terminating null byte + * ('\0') is added. + * + * @return On success, the strndup() function returns a pointer to the + * duplicated string. It returns NULL if insufficient memory was + * available, with errno set to indicate the error + */ char *strdup(const char *source); #endif -- 2.27.0