From d66728a79328c85d8e8389e1e0f71dafb388e1be Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sun, 20 Mar 2022 14:29:32 +0100 Subject: [PATCH] utils: changes/more tests --- src/nsub/nsub.c | 21 ++- src/tests/utils/array.c | 8 +- src/tests/utils/cstring.c | 33 ++++- src/tests/utils/desktop.c | 91 ++++++++++++ src/tests/utils/main.c | 4 + src/tests/utils/main.h | 2 + src/tests/utils/test.desktop | 10 ++ src/utils/array.c | 192 ++++++------------------- src/utils/array.h | 160 +++++++-------------- src/utils/cstring.c | 84 +++++++++-- src/utils/cstring.h | 44 +++++- src/utils/desktop.c | 263 +++++++++++++---------------------- src/utils/desktop.h | 74 ++++++---- 13 files changed, 494 insertions(+), 492 deletions(-) create mode 100644 src/tests/utils/desktop.c create mode 100644 src/tests/utils/test.desktop diff --git a/src/nsub/nsub.c b/src/nsub/nsub.c index e0629fe..d5d39cb 100644 --- a/src/nsub/nsub.c +++ b/src/nsub/nsub.c @@ -21,7 +21,6 @@ #include #include "nsub.h" -#include "utils/array.h" #include "utils/utils.h" /* Public */ @@ -104,16 +103,18 @@ void song_add_meta(song_t *song, char *key, char *value) { } song_t *nsub_read(FILE *in, NSUB_FORMAT fmt) { - array_t *lines = new_array(sizeof(char *), 80); - array_readfiles(lines, in); - song_t *song = new_song(); - array_loop_i(lines, line, char, i) - { + + cstring_t *line = new_cstring(); + size_t i = 0; + while (cstring_readline(line, in)) { + i++; + switch (fmt) { case NSUB_FMT_LRC: - if (!nsub_read_lrc(song, line)) { - fprintf(stderr, "Read error on line %zu: <%s>\n", i, line); + if (!nsub_read_lrc(song, line->string)) { + fprintf(stderr, "Read error on line %zu: <%s>\n", i, + line->string); song = NULL; goto fail; } @@ -128,9 +129,7 @@ song_t *nsub_read(FILE *in, NSUB_FORMAT fmt) { fail: - array_loop(lines, line, void) - free(line); - + free_cstring(line); return song; } diff --git a/src/tests/utils/array.c b/src/tests/utils/array.c index 6d7d9c2..404c6a6 100644 --- a/src/tests/utils/array.c +++ b/src/tests/utils/array.c @@ -180,7 +180,7 @@ START(pop) ASSERT_EQUALS_INT("bad item popped", (int )'T', (int )*rep); if (a->count) - FAIL("popped 1-sized array still has %d items", a->count); + FAIL("popped 1-sized array still has %zu items", a->count); rep = array_new(a); *rep = 'T'; @@ -247,10 +247,6 @@ 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); @@ -272,8 +268,6 @@ Suite *test_array(const char title[]) { 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); diff --git a/src/tests/utils/cstring.c b/src/tests/utils/cstring.c index fb94c8f..267d695 100644 --- a/src/tests/utils/cstring.c +++ b/src/tests/utils/cstring.c @@ -390,9 +390,12 @@ START(rfind) ASSERT_EQUALS_INT("(a) find error", 0, cstring_rfind(str, "Une", 0)); ASSERT_EQUALS_INT("(b) find error", 0, cstring_rfind(str, "Une", 1)); ASSERT_EQUALS_INT("(c) find error", 4, cstring_rfind(str, "petite", 0)); - ASSERT_EQUALS_INT("(d) find error", 4, cstring_rfind(str, "petite", 11)); - ASSERT_EQUALS_INT("(e) find error", -1, cstring_rfind(str, "petite", 2)); - ASSERT_EQUALS_INT("(f) find error", 38, cstring_rfind(str, "choses", 0)); + ASSERT_EQUALS_INT("(d) find error", 4, + cstring_rfind(str, "petite", 11)); + ASSERT_EQUALS_INT("(e) find error", -1, + cstring_rfind(str, "petite", 2)); + ASSERT_EQUALS_INT("(f) find error", 38, + cstring_rfind(str, "choses", 0)); ASSERT_EQUALS_INT("(g) find error", -1, cstring_rfind(str, "Oops", 0)); ASSERT_EQUALS_INT("(h) find error", 42, cstring_rfind(str, "e", 0)); ASSERT_EQUALS_INT("(i) find error", 42, cstring_rfind(str, "e", -1)); @@ -774,6 +777,29 @@ START(dirname) END +START(concat) + char *cc; + + cc = cstring_concat(NULL); + if (cc) + FAIL("concat of NULL should return NULL, not: <%s>", cc); + + cc = cstring_concat("only", NULL); + ASSERT_EQUALS_STR("Single parameter", "only", cc); + free(cc); + + cc = cstring_concat("Only", "Fans", NULL); + ASSERT_EQUALS_STR("Test 2 params", "OnlyFans", cc); + free(cc); + + cc = cstring_concat("Fanfan", " ", "et", " Tulipe", + " entrent dans un bar", NULL); + ASSERT_EQUALS_STR("Test multiple params", + "Fanfan et Tulipe entrent dans un bar", cc); + free(cc); + + END + START(many_adds) size_t count = 10 * 1000 * 1000; for (size_t i = 0; i < count; i++) { @@ -815,6 +841,7 @@ Suite *test_cstring(const char title[]) { tcase_add_test(core, pop_path); tcase_add_test(core, basename); tcase_add_test(core, dirname); + tcase_add_test(core, concat); suite_add_tcase(suite, core); diff --git a/src/tests/utils/desktop.c b/src/tests/utils/desktop.c new file mode 100644 index 0000000..c219d8a --- /dev/null +++ b/src/tests/utils/desktop.c @@ -0,0 +1,91 @@ +/* + * 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/desktop.h" + +#include + +#include "../launcher.h" + +desktop_t *d; + +static void setup() { + d = new_desktop("utils/test.desktop", 24); +} + +static void teardown() { + free_desktop(d); +} + +static void reset() { + teardown(); + setup(); +} + +START(init) + if (!d) + FAIL("new_desktop returned NULL"); + + ASSERT_EQUALS_STR("Name", "IRC", d->name); + ASSERT_EQUALS_STR("Icon", "irssi", d->icon); + ASSERT_EQUALS_STR("Exec", "irssi", d->icon); + + END + + // TODO +START(NO_TEST_YET_submenu) + END +START(NO_TEST_YET_icons) + END +START(NO_TEST_YET_find_icon) + END +START(NO_TEST_YET_find_id) + END + +Suite *test_desktop(const char title[]) { + + d = new_desktop("test.desktop", 24); + free_desktop(d); + + Suite *suite = suite_create(title); + + TCase *core = tcase_create("core"); + tcase_add_checked_fixture(core, setup, teardown); + tcase_add_test(core, init); + tcase_add_test(core, NO_TEST_YET_submenu); + tcase_add_test(core, NO_TEST_YET_icons); + tcase_add_test(core, NO_TEST_YET_find_icon); + tcase_add_test(core, NO_TEST_YET_find_id); + + suite_add_tcase(suite, core); + + return suite; +} + +Suite *test_desktop_more(const char title[]) { + Suite *suite = suite_create(title); + + TCase *tmore = tcase_create("more"); + tcase_add_checked_fixture(tmore, setup, teardown); + // TODO + + suite_add_tcase(suite, tmore); + + return suite; +} diff --git a/src/tests/utils/main.c b/src/tests/utils/main.c index 474c431..4f2986a 100644 --- a/src/tests/utils/main.c +++ b/src/tests/utils/main.c @@ -36,6 +36,10 @@ SRunner *get_tests(int more) { if (more) add_test(test_base64_more("base64 -- more (longer)")); + add_test(test_desktop("desktop")); + if (more) + add_test(test_desktop_more("desktop -- more (longer)")); + return runner; } diff --git a/src/tests/utils/main.h b/src/tests/utils/main.h index 5fced56..f27dafa 100644 --- a/src/tests/utils/main.h +++ b/src/tests/utils/main.h @@ -28,5 +28,7 @@ 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[]); +Suite *test_desktop(const char title[]); +Suite *test_desktop_more(const char title[]); #endif /* SRC_TESTS_UTILS_MAIN_H_ */ diff --git a/src/tests/utils/test.desktop b/src/tests/utils/test.desktop new file mode 100644 index 0000000..234d390 --- /dev/null +++ b/src/tests/utils/test.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Name=IRC +Comment=Launch an IRC client +Exec=irssi +Icon=irssi +Terminal=true +Categories=Internet +Type=Application +StartupNotify=true diff --git a/src/utils/array.c b/src/utils/array.c index 6e192ef..c5299f0 100644 --- a/src/utils/array.c +++ b/src/utils/array.c @@ -43,46 +43,59 @@ 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 */ -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; - - if (initial) - me = malloc(sizeof(array_t)); - - if (me) { - 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; - } - } + if (!initial) + return NULL; + + array_t *me = malloc(sizeof(array_t)); + if (!init_array(me, elem_size, initial)) { + free(me); + me = NULL; } return me; } +int init_array(array_t *me, size_t elem_size, size_t initial) { + if (!initial) + return 0; + + strcpy(me->CNAME, "[CArray ]"); + + me->priv = malloc(sizeof(priv_t)); + if (!me->priv) + return 0; + + 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); + return 0; + } + + return 1; +} + void free_array(array_t *me) { - if (!me) - return; + if (me) + uninit_array(me); + free(me); +} + +void uninit_array(array_t *me) { priv_t *priv = (priv_t *) me->priv; me->count = 0; priv->buffer = 0; free(priv->data); + priv->data = NULL; free(priv); - free(me); + me->priv = NULL; + me->CNAME[0] = '!'; } void array_clear(array_t *me) { @@ -305,119 +318,6 @@ int array_setn(array_t *me, size_t i, void *data, size_t n) { return 1; } -size_t array_readfilei(array_t *me, FILE *in) { - return array_readfile(me, in, array_readfile_funci); -} - -size_t array_readfiles(array_t *me, FILE *in) { - return array_readfile(me, in, array_readfile_funcs); -} - -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++) - ; - if (i > start) { - array_pushn(mot, buffer + start, (i - start)); - } - - if (i == start || (i < n && buffer[i] == '\n')) { - array_push(mot, &zero); - doline(me, mot_priv->data); - count++; - array_clear(mot); - } - } - } - - if (mot->count) { - array_push(mot, &zero); - doline(me, mot_priv->data); - count++; - } - - free_array(mot); - return count; -} - -void array_print(array_t *me) { - array_print_fmt(me, NULL, NULL); -} - -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 **) priv->data)[i]); - fprintf(stdout, "> %zu: %s\n", i, (char *) d); - } -} - -void array_printi(array_t *me) { - priv_t *priv = (priv_t *) me->priv; - - array_print_fmt(me, NULL, NULL); - 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_t *me) { - priv_t *priv = (priv_t *) me->priv; - - array_print_fmt(me, NULL, NULL); - 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_t *me) { - priv_t *priv = (priv_t *) me->priv; - - array_print_fmt(me, NULL, NULL); - 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_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, priv->elem_size, priv->buffer); - if (display) { - 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); - } - } -} - /* Privates functions */ static int array_assure(array_t *me, size_t nb_elem) { @@ -438,15 +338,3 @@ static int array_assure(array_t *me, size_t nb_elem) { return 1; } - -static void array_readfile_funci(array_t *me, const char line[]) { - int i = atoi(line); - array_push(me, &i); -} - -static void array_readfile_funcs(array_t *me, const char line[]) { - char *myline = malloc((strlen(line) + 1) * sizeof(char)); - strcpy(myline, line); - char **n = array_new(me); - *n = myline; -} diff --git a/src/utils/array.h b/src/utils/array.h index caf8128..201c2a0 100644 --- a/src/utils/array.h +++ b/src/utils/array.h @@ -130,23 +130,51 @@ typedef struct { /** * Create a new array. * + * @note always identical to malloc + init_array + * * @param elem_size the size of one element - * @param initial the initial number of items the buffer can hold + * @param initial the initial number of items the buffer can hold (not 0) + * + * @see malloc() + * @see init_array(array_t *self) * * @return a new array (you must later call `free_array()` or `array_convert()`) */ array_t *new_array(size_t elem_size, size_t initial); +/** + * Create a new array. + * + * @param elem_size the size of one element + * @param initial the initial number of items the buffer can hold (not 0) + */ +int init_array(array_t *self, 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. * + * @note always equivalent to uninit_array + free + * + * @see uninit_array(array_t *self) + * @see free(void *data) * @see array_clear * @see array_loop */ void free_array(array_t *me); +/** + * 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. + * + * @see free_array(array_t *self) + * @see array_clear + * @see array_loop + */ +void uninit_array(array_t *me); + /** * Clear the array, that is, resets its current size to 0 (buffer unchanged). */ @@ -328,39 +356,6 @@ int array_push(array_t *me, void *data); */ int array_pushn(array_t *me, void *data, size_t n); -/** - * Set an element of the array to the given value. - * Can also append a new elements. - * Memory will be copied from the given data to the array. - * - * @param i the element index - * @param data the data that will replace the current value, or new data - * to append after the current elements (you can add items just at - * the end of the array (index = count), but it is not allowed to - * set items after that index, so not to create holes) - * - * @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_t *me, size_t i, void *data); - -/** - * Set elements of the array to the given value. - * Can also append some of them to the array if no values were present. - * Memory will be copied from the given data to the array. - * - * @param i the element index to start the insertion at - * @param data the data that will replace the current values, or new data - * to append after the current elements (you can add items just at - * the end of the array (index = count), but it is not allowed to - * set items with `i` after that index, so not to create holes) - * @param n the number of elements to copy from `data` and to insert at `i` - * - * @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_t *me, size_t i, void *data, size_t n); - /** * Retrieve the content of an item. * The item will be copied to the given address location if it exists. @@ -386,88 +381,37 @@ int array_copy(array_t *me, void *target, size_t i); int array_copyn(array_t *me, void *target, size_t i, size_t n); /** - * Read all the lines from a file. - * - * All the lines are supposed to be \n-terminated. - * - * @param in the file to read from - * @param doline a function that will receive the data for each line - * @param (parameters) - * * `me`: the array we work on (the one you passed to - * `array_readfile()` itself) - * * `line`: a read-only copy of the current line - * - * @return the number of elements in the array - */ -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. - * - * Note that the integer conversion will only convert the initial portion of - * each line if the rest is not considered numerical (also, if it fails to - * convert, it will simply store the value 0 for this line). - * - * @see array_readfile + * Set an element of the array to the given value. + * Can also append a new elements. + * Memory will be copied from the given data to the array. * - * @param in the file to read + * @param i the element index + * @param data the data that will replace the current value, or new data + * to append after the current elements (you can add items just at + * the end of the array (index = count), but it is not allowed to + * set items after that index, so not to create holes) * - * @return the number of elements in the array + * @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 */ -size_t array_readfilei(array_t *me, FILE *in); +int array_set(array_t *me, size_t i, void *data); /** - * 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 the array_loop macro. - * - * @see array_loop - * @see array_readfile - * - * @param in the file to read + * Set elements of the array to the given value. + * Can also append some of them to the array if no values were present. + * Memory will be copied from the given data to the array. * - * @return the number of elements in the array - */ -size_t array_readfiles(array_t *me, FILE *in); - -/** - * Print the array metadata to `stderr` (mostly for DEBUG). - */ -void array_print(array_t *me); - -/** - * Print the array and strings content to `stderr` (mostly for DEBUG). - */ -void array_prints(array_t *me); - -/** - * Print the array and integer content to `stderr` (mostly for DEBUG). - */ -void array_printi(array_t *me); - -/** - * Print the array and long content to `stderr` (mostly for DEBUG). - */ -void array_printl(array_t *me); - -/** - * Print the array and floats (%d) content to `stderr` (mostly for DEBUG). - */ -void array_printf(array_t *me); - -/** - * Print the array and content to `stderr` (mostly for DEBUG). + * @param i the element index to start the insertion at + * @param data the data that will replace the current values, or new data + * to append after the current elements (you can add items just at + * the end of the array (index = count), but it is not allowed to + * set items with `i` after that index, so not to create holes) + * @param n the number of elements to copy from `data` and to insert at `i` * - * @param display a function that gives a textual representation of an item - * @param (parameters) - * * `item`: the item to describe - * * `buffer`: the buffer to use for this (which is the one you pass) - * @param buffer a buffer that will be passed to `display` + * @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 */ -void array_print_fmt(array_t *me, void (*display)(char *buffer, void *item), - char *buffer); +int array_setn(array_t *me, size_t i, void *data, size_t n); #endif /* ARRAY_H */ diff --git a/src/utils/cstring.c b/src/utils/cstring.c index 5505f27..6dcdeba 100644 --- a/src/utils/cstring.c +++ b/src/utils/cstring.c @@ -66,27 +66,48 @@ static char *locale = NULL; // end of privates cstring_t *new_cstring() { - cstring_t *string; + cstring_t *self = malloc(sizeof(cstring_t)); + if (!init_cstring(self)) { + free(self); + self = NULL; + } + + return self; +} - string = malloc(sizeof(cstring_t)); - strcpy(string->CNAME, "[CString]"); - string->priv = malloc(sizeof(priv_t)); - string->length = 0; - ((priv_t *) string->priv)->buffer_length = BUFFER_SIZE; - string->string = malloc(sizeof(char) * BUFFER_SIZE); - string->string[0] = '\0'; +int init_cstring(cstring_t *self) { + strcpy(self->CNAME, "[CString]"); - return string; + self->priv = malloc(sizeof(priv_t)); + if (!self->priv) + return 0; + self->string = malloc(sizeof(char) * BUFFER_SIZE); + if (!self->string) { + free(self->priv); + return 0; + } + + self->length = 0; + ((priv_t *) self->priv)->buffer_length = BUFFER_SIZE; + self->string[0] = '\0'; + + return 1; } -void free_cstring(cstring_t *string) { - if (!string) - return; +void free_cstring(cstring_t *self) { + if (self) + uninit_cstring(self); - free(string->priv); - free(string->string); + free(self); +} - free(string); +void uninit_cstring(cstring_t *self) { + free(self->priv); + free(self->string); + self->priv = NULL; + self->string = NULL; + self->length = 0; + self->CNAME[0] = '!'; } void cstring_swap(cstring_t *a, cstring_t *b) { @@ -717,3 +738,36 @@ int cstring_is_utf8(cstring_t *self) { // -2 = invalid, -1 = not whole return (rep != (size_t) -2) && (rep != (size_t) -1); } + +char *cstring_concat(const char str1[], ...) { + if (!str1) + return NULL; + + va_list args; + size_t total; + size_t prec; + char *arg; + char *ptr; + char *rep; + + total = strlen(str1); + va_start(args, str1); + while ((arg = va_arg(args, char *))) { + total += strlen(arg); + } + va_end(args); + + rep = malloc(total * sizeof(char) + 1); + ptr = rep; + ptr = strcpy(ptr, str1); + prec = strlen(str1); + + va_start(args, str1); + while ((arg = va_arg(args, char *))) { + ptr = strcpy(ptr + prec, arg); + prec = strlen(arg); + } + va_end(args); + + return rep; +} diff --git a/src/utils/cstring.h b/src/utils/cstring.h index b38c610..98679dc 100644 --- a/src/utils/cstring.h +++ b/src/utils/cstring.h @@ -60,20 +60,48 @@ typedef struct { /** * Instantiate a new cstring. * + * @note always identical to malloc + init_cstring + * * Create (and allocate the memory for) a new cstring. * Do not forget to call cstring_free(cstring) when done. + * + * @see malloc() + * @see init_cstring(cstring_t *self) */ cstring_t *new_cstring(); +/** + * Instantiate a new cstring. + * + * Create (and allocate the memory for) a new cstring. + * Do not forget to call uninit_cstring(cstring_t *self) when done. + * + * @see new_cstring() + * @see uninit_cstring(cstring_t *self) + */ +int init_cstring(cstring_t *self); + /** * Free the given cstring. * * Free all the resources allocated for this cstring. * - * @param self the cstring to free, which MUST NOT be used again afterward + * @note always equivalent to uninit_cstring + free + * + * @see uninit_cstring(cstring_t *self) + * @see free(void *data) */ void free_cstring(cstring_t *self); +/** + * Free the given cstring. + * + * Free all the resources allocated for this cstring. + * + * @param self the cstring to free, which MUST NOT be used again afterward + */ +void uninit_cstring(cstring_t *self); + /** * Grow the cstring to accommodate that many characters in addition to those * already held, if needed. @@ -457,6 +485,20 @@ char *cstring_dirname(const char path[]); */ int cstring_is_utf8(cstring_t *self); +/** + * Concat all the given string and return the concatenation as a newly allocated + * string that you now own. + * + * @note the last parameter must be NULL + * + * @note if NULL is passed as first parameter, NULL will be returned + * + * @param str1 the first string + * + * @return the concatenated string or NULL if str1 is NULL + */ +char *cstring_concat(const char str1[], ...); + #endif #ifdef __cplusplus diff --git a/src/utils/desktop.c b/src/utils/desktop.c index ba353a7..d200555 100644 --- a/src/utils/desktop.c +++ b/src/utils/desktop.c @@ -29,23 +29,24 @@ #define EXT "desktop" -struct { - char *name; - char *icon; - char *icon_file; - char *exec; - array_t *children; - int id; -}typedef desktop_p; - /* Private functions */ static int desktop_compare(const void *a, const void* b); -static char *desktop_concat(const char str1[], ...); static int desktop_test_file(const char filename[]); /* */ -desktop *new_desktop(const char filename[], int best_size) { - desktop_p *me = malloc(sizeof(desktop_p)); +desktop_t *new_desktop(const char filename[], int best_size) { + desktop_t *me = malloc(sizeof(desktop_t)); + if (!init_desktop(me, filename, best_size)) { + free(me); + me = NULL; + } + + return me; +} + +int init_desktop(desktop_t *me, const char filename[], int best_size) { + strcpy(me->CNAME, "[Desktop]"); + me->name = NULL; me->exec = NULL; me->icon = NULL; @@ -94,80 +95,74 @@ desktop *new_desktop(const char filename[], int best_size) { me->icon_file = desktop_find_icon("folder", best_size); } - me->children = new_array(sizeof(desktop_p*), 32); + me->children = new_array(sizeof(desktop_t*), 32); for (struct dirent *ep = readdir(dp); ep; ep = readdir(dp)) { if (!strcmp(ep->d_name, ".")) continue; if (!strcmp(ep->d_name, "..")) continue; - char *childname = desktop_concat(filename, "/", ep->d_name, NULL); - desktop_p *child = (desktop_p*) new_desktop(childname, best_size); + + desktop_t *child = array_new(me->children); + char *childname = cstring_concat(filename, "/", ep->d_name, NULL); + if (!init_desktop(child, childname, best_size)) + array_pop(me->children); free(childname); - if (child) { - array_push(me->children, &child); - } } array_qsort(me->children, desktop_compare); closedir(dp); free(ext); - return (desktop*) me; + return 1; } // Only process ".desktop" files if (!ext || strcmp(ext, EXT)) { - free_desktop((desktop *) me); + free_desktop(me); free(ext); - return NULL; + return 0; } FILE *file; - array_t *tab; - - tab = new_array(sizeof(char *), 32); + cstring_t *line; + char *startsWith; file = fopen(filename, "r"); - array_readfiles(tab, file); - fclose(file); - - char *startsWith; - size_t n; - array_loop_i(tab, line, char, i) { - startsWith = "Name="; - n = strlen(startsWith); - if (!strncmp(line, startsWith, n)) { - free(me->name); - me->name = strdup(line + n); - } + if (file) { + line = new_cstring(); + while (cstring_readline(line, file)) { + startsWith = "Name="; + if (cstring_starts_with(line->string, startsWith, 0)) { + free(me->name); + me->name = strdup(line->string + strlen(startsWith)); + } - startsWith = "Exec="; - n = strlen(startsWith); - if (!strncmp(line, startsWith, n)) { - free(me->exec); - me->exec = strdup(line + n); - // TODO: %f %F %u %U %i %c %k: inject values instead - char *cars = "ifFuUck"; - for (char *ptr = index(me->exec, '%'); ptr; ptr = index(ptr, '%')) { - if (index(cars, ptr[1])) { - ptr[0] = ' '; - ptr[1] = ' '; + startsWith = "Exec="; + if (cstring_starts_with(line->string, startsWith, 0)) { + free(me->exec); + me->exec = strdup(line->string + strlen(startsWith)); + // TODO: %f %F %u %U %i %c %k: inject values instead + char *cars = "ifFuUck"; + for (char *ptr = index(me->exec, '%'); ptr; + ptr = index(ptr, '%')) { + if (index(cars, ptr[1])) { + ptr[0] = ' '; + ptr[1] = ' '; + } + ptr++; } - ptr++; } - } - startsWith = "Icon="; - n = strlen(startsWith); - if (!strncmp(line, startsWith, n)) { - free(me->icon); - me->icon = strdup(line + n); + startsWith = "Icon="; + if (cstring_starts_with(line->string, startsWith, 0)) { + free(me->icon); + me->icon = strdup(line->string + strlen(startsWith)); + } } + free_cstring(line); + fclose(file); } - array_loop(tab, item, void) - free(item); - // Find icon file linked to icon if (me->icon && !me->icon_file) { me->icon_file = desktop_find_icon(me->icon, best_size); @@ -178,66 +173,37 @@ desktop *new_desktop(const char filename[], int best_size) { } free(ext); - return (desktop *) me; + return 1; } -void free_desktop(desktop *app) { - desktop_p *me = (desktop_p*) app; +void free_desktop(desktop_t *me) { + if (me) + uninit_desktop(me); - if (!me) - return; + free(me); +} +void uninit_desktop(desktop_t *me) { free(me->name); free(me->exec); free(me->icon); free(me->icon_file); + me->name = NULL; + me->exec = NULL; + me->icon = NULL; + me->icon_file = NULL; free_array(me->children); - - free(me); -} - -const char *desktop_get_name(desktop *app) { - desktop_p *me = (desktop_p*) app; - return me->name; -} - -const char *desktop_get_exec(desktop *app) { - desktop_p *me = (desktop_p*) app; - return me->exec; -} - -const char *desktop_get_icon(desktop *app) { - desktop_p *me = (desktop_p*) app; - return me->icon; -} - -const char *desktop_get_icon_file(desktop *app) { - desktop_p *me = (desktop_p*) app; - return me->icon_file; -} - -int desktop_get_id(desktop *app) { - desktop_p *me = (desktop_p*) app; - return me->id; -} - -void desktop_set_id(desktop *app, int id) { - desktop_p *me = (desktop_p*) app; - me->id = id; -} - -array_t *desktop_get_children(desktop *app) { - desktop_p *me = (desktop_p*) app; - return me->children; + me->children = NULL; + me->CNAME[0] = '!'; } -desktop *desktop_find_id(array_t *children, int id) { - desktop *found = NULL; +desktop_t *desktop_find_id(array_t *children, int id) { + desktop_t *found = NULL; - array_loop(children, child, desktop_p) + array_loop(children, child, desktop_t) { if (child->id == id) { - found = (desktop*) child; + found = child; break; } @@ -251,39 +217,9 @@ desktop *desktop_find_id(array_t *children, int id) { /* Private functions */ -static char *desktop_concat(const char str1[], ...) { - va_list args; - size_t total; - size_t prec; - char *arg; - char *ptr; - char *rep; - - total = strlen(str1); - va_start(args, str1); - while ((arg = va_arg(args, char *))) { - total += strlen(arg); - } - va_end(args); - - rep = malloc(total * sizeof(char) + 1); - ptr = rep; - ptr = strcpy(ptr, str1); - prec = strlen(str1); - - va_start(args, str1); - while ((arg = va_arg(args, char *))) { - ptr = strcpy(ptr + prec, arg); - prec = strlen(arg); - } - va_end(args); - - return rep; -} - static int desktop_compare(const void *a, const void* b) { - desktop_p *me1 = ((desktop_p**) a)[0]; - desktop_p *me2 = ((desktop_p**) b)[0]; + desktop_t *me1 = ((desktop_t**) a)[0]; + desktop_t *me2 = ((desktop_t**) b)[0]; if (me1->children && !(me2->children)) return -1; @@ -313,7 +249,7 @@ static int desktop_test_file(const char filename[]) { #define TRY_DIR(a,b,c) \ do { \ - tmp = desktop_concat(a, b, c, basename, ".png", NULL); \ + tmp = cstring_concat(a, b, c, basename, ".png", NULL); \ if(desktop_test_file(tmp)) \ return tmp; \ free(tmp); \ @@ -328,44 +264,39 @@ char *desktop_find_icon(const char basename[], int icon_size) { sprintf(icon_size_str, "%dx%d", icon_size, icon_size); if (!theme) { - array_t *tab = new_array(sizeof(char *), 32); - - tmp = desktop_concat(home, "/.gtkrc-2.0", NULL); + tmp = cstring_concat(home, "/.gtkrc-2.0", NULL); FILE *file = fopen(tmp, "r"); free(tmp); if (file) { - array_readfiles(tab, file); + const char *startsWith = "gtk-icon-theme-name="; + size_t n = strlen(startsWith); + + cstring_t *line = new_cstring(); + while (cstring_readline(line, file)) { + if (cstring_starts_with(line->string, startsWith, 0)) { + free(theme); + if (line->string[n] == '"') { + theme = strdup(line->string + n + 1); + theme[strlen(theme) - 1] = '\0'; + } else { + theme = strdup(line->string + n); + } + } + } + free_cstring(line); fclose(file); - } - const char *startsWith = "gtk-icon-theme-name="; - size_t n = strlen(startsWith); - - array_loop(tab, line, char) - { - if (!strncmp(line, startsWith, n)) { - free(theme); - if (line[n] == '"') { - theme = strdup(line + n + 1); - theme[strlen(theme) - 1] = '\0'; - } else { - theme = strdup(line + n); - } + if (!theme || !theme[0]) { + theme = strdup(""); + ltheme = strdup(""); + } else { + tmp = theme; + theme = cstring_concat("/usr/share/icons/", tmp, "/", NULL); + ltheme = cstring_concat(home, "/", ".icons/", tmp, "/", NULL); + free(tmp); } - } - if (!theme || !theme[0]) { - theme = strdup(""); - ltheme = strdup(""); - } else { - tmp = theme; - theme = desktop_concat("/usr/share/icons/", tmp, "/", NULL); - ltheme = desktop_concat(home, "/", ".icons/", tmp, "/", NULL); - free(tmp); } - - array_loop(tab,item, void) - free(item); } // Allow NULL diff --git a/src/utils/desktop.h b/src/utils/desktop.h index 8e208ae..bf39398 100644 --- a/src/utils/desktop.h +++ b/src/utils/desktop.h @@ -52,55 +52,69 @@ extern "C" { /** * The structure used to represent desktop objects. */ -typedef struct desktop_p desktop; +struct { + char CNAME[10]; + /** The user name of the desktop object. */ + char *name; + /** The icon name, if any. */ + char *icon; + /** The icon file that corresponds, if any. */ + char *icon_file; + /** The EXEC command to start. */ + char *exec; + /** The submenu items of this desktop object (for a menu/submenu). */ + array_t *children; + /** A custom external ID for this desktop object, for your own use. */ + int id; +}typedef desktop_t; /** * Create a new desktop object from the given .desktop file. * + * @note always identical to malloc + init_desktop + * * @param filename the path to the actual .desktop file * @param best_size the default size for the icon (see icon selection in the * description of the {@see desktop} object * + * @see malloc() + * @see init_desktop(desktop_t *self, const char filename[], int best_size) + * @see free_desktop(desktop_t *self) + * * @return the desktop object */ -desktop *new_desktop(const char filename[], int best_size); - -/** - * Free the given desktop object - */ -void free_desktop(desktop *self); - -/** Return the name of the desktop object (you do not own it). */ -const char *desktop_get_name(desktop *self); -/** Return the exec command of the desktop object (you do not own it). */ -const char *desktop_get_exec(desktop *self); -/** Return the icon name of the desktop object (you do not own it). */ -const char *desktop_get_icon(desktop *self); -/** Return the icon file of the desktop object (you do not own it). */ -const char *desktop_get_icon_file(desktop *self); +desktop_t *new_desktop(const char filename[], int best_size); /** - * Return the external ID of this desktop object - * ({@see desktop_set_id(desktop*, int)}. + * Create a new desktop object from the given .desktop file. * - * @return the external ID + * @param filename the path to the actual .desktop file + * @param best_size the default size for the icon (see icon selection in the + * description of the {@see desktop} object + * + * @see new_desktop(const char filename[], int best_size) + * @see uninit_desktop(desktop_t *self) + * + * @return TRUE if success (could fail if the target is not a .desktop + * file */ -int desktop_get_id(desktop *self); +int init_desktop(desktop_t *self, const char filename[], int best_size); /** - * Set a custom external ID for this desktop object, for your own use. + * Free the given desktop object. * - * @param id the ID to set + * @note always equivalent to uninit_desktop + free + * + * @see uninit_desktop(desktop_t *self) + * @see free(void *data) */ -void desktop_set_id(desktop *self, int id); +void free_desktop(desktop_t *self); /** - * Return all the submenu items of this desktop objects (for a menu/submenu). - * - * TODO: switch to full objects - * @return an array of pointers to desktop objects + * Free the resources used by the given desktop object -- do not use it anymore + * after this call. */ -array_t *desktop_get_children(desktop *app); +void uninit_desktop(desktop_t *self); /** * Find a submenu item by the given ID ({@see desktop_set_id(desktop *, int)}). @@ -111,7 +125,7 @@ array_t *desktop_get_children(desktop *app); * * @return the given submenu if found, or NULL */ -desktop *desktop_find_id(array_t *children, int menu_id); +desktop_t *desktop_find_id(array_t *children, int menu_id); /** * Look for the icon file related to this basename. @@ -119,6 +133,8 @@ desktop *desktop_find_id(array_t *children, int menu_id); * @param basename the base name of the icon we want to look for * @param icon_size the best_size to use for the icon (see the description of * the {@desktop} object) + * + * @return the path to the best related icon we found (you own it), or NULL */ char *desktop_find_icon(const char basename[], int icon_size); -- 2.27.0