From: Niki Roo Date: Thu, 20 Jun 2024 22:38:40 +0000 (+0200) Subject: Working CUtils from nsub + net.c and timing.h fix X-Git-Url: http://git.nikiroo.be/?a=commitdiff_plain;h=0b3aed8ddf8730631238a19c2d69bd19078b718e;p=cutils.git Working CUtils from nsub + net.c and timing.h fix --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..48d3e3d --- /dev/null +++ b/Makefile @@ -0,0 +1,83 @@ +# Note: 99+ required for for-loop initial declaration (CentOS 6) +# Note: gnu99 (instead of c99) required for libcutils-net + +CFLAGS += -Wall -pedantic -I./ -std=gnu99 +CXXFLAGS += -Wall -pedantic -I./ +PREFIX = /usr/local + +ifdef DEBUG +CFLAGS += -ggdb -O0 +CXXFLAGS += -ggdb -O0 +endif + +.PHONY: all cutils check net install uninstall clean mrpropre mrpropre debug + +all: utils check net + +utils: ../../bin/libcutils.o + +check: ../../bin/libcutils-check.o + +net: ../../bin/libcutils-net.o + +SOURCES=$(wildcard *.c) +HEADERS=$(wildcard *.h) +OBJECTS=$(SOURCES:%.c=%.o) + +array.o: array.[ch] +desktop.o: desktop.[ch] array.h + +../../bin/libcutils.o: $(OBJECTS) + mkdir -p ../../bin + # note: -r = --relocatable, but former also works with Clang + $(LD) -r $(OBJECTS) -o $@ + +../../bin/libcutils-check.o: check/launcher.o + mkdir -p ../../bin + # note: -r = --relocatable, but former also works with Clang + $(LD) -r check/launcher.o -o $@ + +../../bin/libcutils-net.o: net/net.o + mkdir -p ../../bin + # note: -r = --relocatable, but former also works with Clang + $(LD) -r net/net.o -o $@ + +debug: + $(MAKE) -f makefile.d DEBUG=1 + +clean: + rm -f *.o check/*.o net/*.o + +mrproper: mrpropre + +mrpropre: clean + rm -f ../../bin/libcutils.o + rm -f ../../bin/libcutils-check.o + rm -f ../../bin/lubcutils-net.o + rmdir ../../bin 2>/dev/null || true + +install: + @echo "installing cutils to $(PREFIX)..." + mkdir -p "$(PREFIX)/lib/cutils/" + cp ../../bin/libcutils.o "$(PREFIX)/lib/cutils/" + cp ../../bin/libcutils-check.o "$(PREFIX)/lib/cutils/" + cp ../../bin/libcutils-net.o "$(PREFIX)/lib/cutils/" + mkdir -p "$(PREFIX)/include/cutils/check/" + mkdir -p "$(PREFIX)/include/cutils/net/" + cp *.h "$(PREFIX)/include/cutils/" + cp check/*.h "$(PREFIX)/include/cutils/check/" + cp net/*.h "$(PREFIX)/include/cutils/net/" + +uninstall: + @echo "uninstalling utils from $(PREFIX)..." + rm -f "$(PREFIX)/lib/cutils/libcutils.o" + rm -f "$(PREFIX)/lib/cutils/libcutils-check.o" + rm -f "$(PREFIX)/lib/cutils/libcutils-net.o" + rmdir "$(PREFIX)/lib/cutils/" 2>/dev/null || true + rm -f "$(PREFIX)/include/cutils/net/"*.h + rm -f "$(PREFIX)/include/cutils/check/"*.h + rm -f "$(PREFIX)/include/cutils/"*.h + rmdir "$(PREFIX)/include/cutils/net" 2>/dev/null || true + rmdir "$(PREFIX)/include/cutils/check" 2>/dev/null || true + rmdir "$(PREFIX)/include/cutils" 2>/dev/null || true + diff --git a/array.c b/array.c new file mode 100644 index 0000000..e5bb7ee --- /dev/null +++ b/array.c @@ -0,0 +1,382 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2020 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 +#include +#include +#include + +#include "array.h" + +typedef struct { + size_t elem_size; + size_t buffer; + void *data; +} priv_t; + +/* make sure we have at least n+1 elements in the buffer, grow if needed */ +/* +1 is so we can always end with a NULL value for array_data/convert() */ +static int array_assure(array_t *me, size_t nb_elem); + +/* for qsort operations */ +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); + +array_t *new_array(size_t elem_size, size_t initial) { + 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) + 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); + me->priv = NULL; + me->CNAME[0] = '!'; +} + +void array_clear(array_t *me) { + me->count = 0; +} + +// convert to void * data (free the rest) +void *array_convert(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + void *data = array_data(me); + free(priv); + free(me); + return data; +} + +void *array_data(array_t *me) { + priv_t *priv = (priv_t *) me->priv; + + // Note: this should be impossible + if (me->count >= priv->buffer) + array_assure(me, me->count + 1); + + // cast to (char *) because we want 'byte' arithmetic + void *after_end = (void *) (((char *) priv->data) + + (me->count * priv->elem_size)); + + // last item is always NULL + memset(after_end, '\0', priv->elem_size); + + return priv->data; +} + +size_t array_count(array_t *me) { + return me->count; +} + +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_first(array_t *me) { + if (!me->count) + return NULL; + + priv_t *priv = (priv_t *) me->priv; + return priv->data; +} + +void *array_last(array_t *me) { + if (!me->count) + return NULL; + + priv_t *priv = (priv_t *) me->priv; + // cast to (char *) because we want 'byte' arithmetic + return (void *) (((char *) priv->data) + ((me->count - 1) * priv->elem_size)); +} + +void *array_prev(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; + if (cptr >= cdata) { + return cptr; + } + } + + return NULL; +} + +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_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_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_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); +} +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); +} +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_t *me, int rev) { + array_qsort(me, rev ? array_qsorti_rfunc : array_qsorti_func); +} +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; + return !(ia == ib); +} +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; + return !(ia == ib); +} + +void array_qsortl(array_t *me, int rev) { + array_qsort(me, rev ? array_qsortl_rfunc : array_qsortl_func); +} +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; + return !(la == lb); +} +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; + return !(la == lb); +} + +void array_qsortf(array_t *me, int rev) { + array_qsort(me, rev ? array_qsortf_rfunc : array_qsortf_func); +} +static int array_qsortf_func(const void *a, const void *b) { + float fa, fb; + 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; + return !(fa == fb); +} +static int array_qsortf_rfunc(const void *a, const void *b) { + float fa, fb; + 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; + return !(fa == fb); +} + +int array_push(array_t *me, void *data) { + return array_setn(me, me->count, data, 1); +} + +int array_pushn(array_t *me, void *data, size_t n) { + return array_setn(me, me->count, data, n); +} + +int array_copy(array_t *me, void *target, size_t i) { + return array_copyn(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; + + 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_t *me, size_t i, void *data) { + return array_setn(me, i, data, 1); +} + +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 *) (priv->data)) + (i * priv->elem_size), data, + n * priv->elem_size); + if ((i + n) > me->count) + me->count = i + n; + + return 1; +} + +/* Privates functions */ + +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; + } + + void *tmp = realloc(priv->data, priv->elem_size * priv->buffer); + if (!tmp) + return 0; + + priv->data = tmp; + } + + return 1; +} diff --git a/array.h b/array.h new file mode 100644 index 0000000..3638562 --- /dev/null +++ b/array.h @@ -0,0 +1,465 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2020 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 . + */ + +/** + * @file array.h + * @author Niki + * @date 2020 - 2022 + * + * @brief A simple auto-growing array-list + * + * A simple auto-growing array-list, with a minimum buffer of 1. + * + * It has a link to `qsort`, too, so you don't need to retype all + * the parameters you already passed to the array. + * + * It is created with `new_array()` and must be freed with : + * - `free_array()` for normal operations + * - `array_convert()` if you want to free the array but keep the data + * (the data is now your responsibility to free or not, it was allocated + * with `malloc`/`realloc`) + * + * Example usage: + * ```C + * array lines = new_array(sizeof(char *), 100); + * + * const char *l1 = "2. À l'arrière"; + * const char *l2 = "3. En réserve"; + * const char *l3 = "1. Première ligne"; + * + * // push mode (usually used for int, char, long...) + * array_push(lines, &l1); + * array_push(lines, &l2); + * + * // new mode (usually used for structures) + * char **tmp = array_new(lines); + * *tmp = l3; + * + * // 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); + * ``` + */ + +#ifndef ARRAY_H +#define ARRAY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * 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. + * + * @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 (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); + +/** + * Initialise 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). + * + * @note if you hold custom structures with owned resources in the array, you + * should deallocate them properly before + */ +void array_clear(array_t *me); + +/** + * Convert the array to a block of memory where all values are adjacent. + * + * @note an extra NULL value is assured to be present as last element + * + * @return the data (you must later call `free()` on it) + */ +void *array_convert(array_t *me); + +/** + * Return a pointer to the internal storage used by this array. + * This is the same value as would return `array_convert()`, + * but the array is still valid. + * + * Be careful if you change the content (you should not). + * + * @note an extra NULL value is assured to be present as last element + * + * @return the internal storage area + */ +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_t *me); + +/** + * 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_new(array_t *me); + +/** + * 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, or NULL if no elements are + * present + */ +void *array_first(array_t *me); + +/** + * Return a pointer to the last 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 last element, or NULL if no elements are + * present + */ +void *array_last(array_t *me); + +/** + * Return the pointer to the previous element, or NULL if it was the first. + * + * @param ptr a pointer from an array (the array must be valid) + * + * @return the previous element, or NULL + */ +void *array_prev(array_t *me, void *ptr); + +/** + * 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 + * + * @note if the index is out of bounds, you will get invalid data + * + * @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_pop(array_t *me); + +/** + * Cut the array at the given size and return a pointer to the first element + * that was removed if any. + * + * @note be careful, the memory pointed to by the element(s) 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(s) 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(s) + * + * @return a pointer to the first removed element, or NULL + */ +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()`. + * All the appropriate parameters are passed, except the sorting function. + * + * @param compar a custom comparison function + * @param (parameters) + * * `itm1`: a **pointer** to one of your data element (for instance, + * if you store floats, it will be a `(float *)`, + * so you would need to cast it via `((float *)itm1)[0]` + * * `itm2`: another **pointer** to compare with the first one + * @param (returns) + * * `-1`: if element A is less than element B + * * `0`: if both elements are equals + * * `1`: if element A is more than element B + */ +void array_qsort(array_t *me, int (*compar)(const void *itm1, const void *itm2)); + +/** + * Sort the array with `qsort()`, data is `char *`. + * + * @param rev FALSE for normal order, TRUE for reverse order + * + * @see array_qsort + */ +void array_qsorts(array_t *me, int rev); + +/** + * Sort the array with `qsort()`, data is `int`. + * + * @param rev FALSE for normal order, TRUE for reverse order + * + * @see array_qsort + */ +void array_qsorti(array_t *me, int rev); + +/** + * Sort the array with `qsort()`, data is `long`. + * + * @param rev FALSE for normal order, TRUE for reverse order + * + * @see array_qsort + */ +void array_qsortl(array_t *me, int rev); + +/** + * Sort the array with `qsort()`, data is `float`. + * + * @param rev FALSE for normal order, TRUE for reverse order + * + * @see array_qsort + */ +void array_qsortf(array_t *me, int rev); + +/** + * Add an element to the array (will create a new item in the array and copy the + * data from the given element in it). + * + * @param data the memory position of the element to add + * + * @return FALSE if the array is too short and we cannot allocate enough + * contiguous memory for its needs + */ +int array_push(array_t *me, void *data); + +/** + * Add multiple elements to the array (will create new items in the array and + * copy the data from the given elements in them). + * + * @param data the memory position of the elements to add, adjacent to each + * other + * @param n the number of elements to copy from `data` + * + * @return FALSE if the array is too short and we cannot allocate enough + * contiguous memory for its needs + */ +int array_pushn(array_t *me, void *data, size_t n); + +/** + * 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) + */ +int array_copy(array_t *me, void *target, size_t i); + +/** + * 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) + */ +int array_copyn(array_t *me, void *target, size_t i, size_t n); + +/** + * Set an element of the array to the given value. + * Can also append an element at the end of the array (i.e., i can be + * the size of the array and this will result in an array with one more + * element). + * 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 elements at the end of the array (i.e., i can be + * the size of the array and this will result in an array with n more + * elements). + * 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); + +#endif /* ARRAY_H */ + +#ifdef __cplusplus +} +#endif + diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..d151fe3 --- /dev/null +++ b/base64.c @@ -0,0 +1,117 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2013 Niki Roo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "base64.h" + +#include +#include + +static char encoding_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', + 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; + +static int decoding_table_flg = 0; +static char decoding_table[256]; + +static void init_dtable() { + if (!decoding_table_flg) { + for (int i = 0; i < 64; i++) + decoding_table[(unsigned char) encoding_table[i]] = i; + decoding_table_flg = 1; + } +} + +char *base64_encode(const char *data) { + size_t input_length = strlen(data); + size_t output_length = 4 * ((input_length + 2) / 3); + + char *encoded_data = malloc(output_length + 1); + if (!encoded_data) + return NULL; + + for (unsigned int i = 0, j = 0; i < input_length;) { + unsigned int octet_a = i < input_length ? (unsigned char) data[i++] : 0; + unsigned int octet_b = i < input_length ? (unsigned char) data[i++] : 0; + unsigned int octet_c = i < input_length ? (unsigned char) data[i++] : 0; + + unsigned int triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + encoded_data[j++] = (char) encoding_table[(triple >> 3 * 6) & 0x3F]; + encoded_data[j++] = (char) encoding_table[(triple >> 2 * 6) & 0x3F]; + encoded_data[j++] = (char) encoding_table[(triple >> 1 * 6) & 0x3F]; + encoded_data[j++] = (char) encoding_table[(triple >> 0 * 6) & 0x3F]; + } + + if ((input_length % 3) > 0) + encoded_data[output_length - 1] = '='; + if ((input_length % 3) == 1) + encoded_data[output_length - 2] = '='; + + encoded_data[output_length] = '\0'; + + return encoded_data; +} + +char *base64_decode(const char *data) { + init_dtable(); + + size_t input_length = strlen(data); + if (input_length % 4 != 0) + return NULL; + + size_t output_length = ((input_length / 4) * 3); + if (data[input_length - 1] == '=') + output_length--; + if (data[input_length - 2] == '=') + output_length--; + + char *decoded_data = malloc(output_length + 1); + if (!decoded_data) + return NULL; + + for (unsigned int i = 0, j = 0; i < input_length; i += 4) { + unsigned int sextet_a = 0; + unsigned int sextet_b = 0; + unsigned int sextet_c = 0; + unsigned int sextet_d = 0; + + if (data[i] != '=') { + sextet_a = decoding_table[(unsigned char) data[i + 0]]; + sextet_b = decoding_table[(unsigned char) data[i + 1]]; + sextet_c = decoding_table[(unsigned char) data[i + 2]]; + sextet_d = decoding_table[(unsigned char) data[i + 3]]; + } + + unsigned int triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + + (sextet_c << 1 * 6) + (sextet_d << 0 * 6); + + if (j < output_length) + decoded_data[j++] = (char) ((triple >> 2 * 8) & 0xFF); + if (j < output_length) + decoded_data[j++] = (char) ((triple >> 1 * 8) & 0xFF); + if (j < output_length) + decoded_data[j++] = (char) ((triple >> 0 * 8) & 0xFF); + } + + decoded_data[output_length] = '\0'; + return decoded_data; +} + diff --git a/base64.h b/base64.h new file mode 100644 index 0000000..b6c3f25 --- /dev/null +++ b/base64.h @@ -0,0 +1,71 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2013 Niki Roo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @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 + +#ifndef BASE64_H +#define BASE64_H + +#include + +/** + * 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 + +#ifdef __cplusplus +} +#endif diff --git a/check/launcher.c b/check/launcher.c new file mode 100644 index 0000000..855bf25 --- /dev/null +++ b/check/launcher.c @@ -0,0 +1,197 @@ +/* + * 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 "launcher.h" + +#include +#include +#include +#include + +// Shared globals +int launcher_color = -1; + +// Private functions +static int has_colour(); +static char *waiting_color(); +static char *passed_color(); +static char *failed_color(); +static char *stop_color(); + +// Private vars +// static char BLEU[] = { (char) 27, '[', '3', '4', 'm', '\0' }; +// static char TEAL[] = { (char) 27, '[', '3', '6', 'm', '\0' }; +// static char GRIS[] = { (char) 27, '[', '3', '7', 'm', '\0' }; +static char VERT[] = { (char) 27, '[', '3', '2', 'm', '\0' }; +static char ROUGE[] = { (char) 27, '[', '3', '1', 'm', '\0' }; +static char ORANGE[] = { (char) 27, '[', '3', '3', 'm', '\0' }; +static char STOP[] = { (char) 27, '[', '0', 'm', '\0' }; +static char bs9[] = { 8, 8, 8, 8, 8, 8, 8, 8, 8, '\0' }; + +// COLORTERM : if it exists and is not empty, the system will default to +// colour mode (--color) as opposed to --no-color + +// CK_ENV : Gets the print mode from the environment variable CK_VERBOSITY, +// which can have the values "silent", "minimal", "normal", "verbose". If the +// variable is not found or the value is not recognised, the print mode is set +// to CK_NORMAL. + +// How to start the program: +// ======================== +// +// $0 +// test the code (normal tests only) +// $0 --more +// run extra tests (if available; usually, they are specified as extra if they +// take long to process) +// +// +// $0 --name NAME +// do not test, just format the name as if it was tested +// $0 --passed +// format the line as if it was passes +// $0 --failed +// format the line as if it was failed + +char *tests_name = NULL; + +void test_init(const char name[]) { + int i; + int cols; + struct winsize ws; + + ioctl(1, TIOCGWINSZ, &ws); + cols = ws.ws_col; + + if (!tests_name) { + tests_name = malloc(sizeof(char) * (cols + 1)); + } + + for (i = 0; i < (cols + 1); i++) { + if (i < 4) + tests_name[i] = ' '; + else + tests_name[i] = '.'; + } + + strcpy(tests_name + 4, name); + tests_name[strlen(name) + 4] = ' '; + tests_name[cols - 6 - 1 - 4] = '\0'; + + fprintf(stderr, "%s", tests_name); + fprintf(stderr, "%s[%s%s%s] ", " ", waiting_color(), " ?? ", + stop_color()); +} + +void test_success() { + fprintf(stderr, "%s[%s%s%s] ", bs9, passed_color(), " OK ", + stop_color()); +} + +void test_failure() { + fprintf(stderr, "%s[%s%s%s] ", bs9, failed_color(), "FAIL", + stop_color()); +} + +int test_start(int more) { + int failed; + SRunner *runner; + + runner = get_tests(more); + + failed = 0; + if (runner) { + srunner_run_all(runner, CK_ENV); + + failed = srunner_ntests_failed(runner); + srunner_free(runner); + } else { + printf(">>> No tests have been found <<<\n"); + } + + return failed; +} + +int main(int argc, char **argv) { + int more = 0; + int cont = 1; + + for (int i = 1; i < argc; i++) { + if (!strcmp("--name", argv[i])) { + if ((i + 1) >= argc) + return 1; + test_init(argv[++i]); + cont = 0; + } else if (!strcmp("--passed", argv[i])) { + test_success(); + cont = 0; + } else if (!strcmp("--failed", argv[i])) { + test_failure(); + cont = 0; + } else if (!strcmp("--more", argv[i])) { + more = 1; + } else if (!strcmp("--color", argv[i])) { + launcher_color = 1; + } else if (!strcmp("--no-color", argv[i])) { + launcher_color = 0; + } + } + + if (!cont) + return 0; + + return test_start(more); +} + +static int has_colour() { + if (launcher_color == -1) { + // TODO: could we do better? + const char *cterm = getenv("COLORTERM"); + if (cterm && cterm[0]) + launcher_color = 1; + else + launcher_color = 0; + } + + return launcher_color; +} + +static char *waiting_color() { + if (has_colour()) + return ORANGE; + return ""; +} + +static char *passed_color() { + if (has_colour()) + return VERT; + return ""; +} + +static char *failed_color() { + if (has_colour()) + return ROUGE; + return ""; +} + +static char *stop_color() { + if (has_colour()) + return STOP; + return ""; +} diff --git a/check/launcher.h b/check/launcher.h new file mode 100644 index 0000000..b1e6e63 --- /dev/null +++ b/check/launcher.h @@ -0,0 +1,46 @@ +#ifndef _LAUNCHER_H +#define _LAUNCHER_H + +#include + +#define START(name) \ +START_TEST(name) {\ + test_init(#name);\ + +#define END \ + test_success();\ +}\ +END_TEST\ + +#define FAIL(...) \ +ck_abort_msg(__VA_ARGS__)\ + +#define ASSERT_EQUALS_STR(title, 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) \ + 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) \ + do { if (expected != received) { \ +ck_abort_msg("%s\n\tExpected: %zu\n\tReceived: %zu", title, expected, received); \ +}} while(0) + +extern int launcher_color; + +SRunner *get_tests(int more); + +void test_init(const char name[]); + +int test_start(int more); + +void test_success(); + +void test_failure(); + +#endif + diff --git a/cstring.c b/cstring.c new file mode 100644 index 0000000..07215e7 --- /dev/null +++ b/cstring.c @@ -0,0 +1,773 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2011 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 . + */ + +/* + Name: cstring.c + Copyright: niki (gpl3 or later) 2011 + Author: niki + Date: 2011-06-16 + Description: cstring is a collection of helper functions to manipulate text + */ + +#include "cstring.h" + +#include +#include +#include +#include +#include + +// For upper/lowercase +#include + +#ifndef BUFFER_SIZE +#define BUFFER_SIZE 81 +#endif + +#ifdef WIN32 +#define CSTRING_SEP '\\' +#else +#define CSTRING_SEP '/' +#endif + +// Private functions + +typedef struct { + size_t buffer_length; +} priv_t; + +/** Swap the data */ +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_t *self, int up); +/** For path-related functions */ +static void normalize_path(cstring_t *self); + +// Private variables + +static char *locale = NULL; + +// end of privates + +cstring_t *new_cstring() { + cstring_t *self = malloc(sizeof(cstring_t)); + if (!init_cstring(self)) { + free(self); + self = NULL; + } + + return self; +} + +int init_cstring(cstring_t *self) { + strcpy(self->CNAME, "[CString]"); + + 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 *self) { + if (self) + uninit_cstring(self); + + free(self); +} + +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) { + void *tmp_p; + char *tmp_s; + size_t tmp_l; + + tmp_s = a->string; + tmp_l = a->length; + tmp_p = a->priv; + + a->string = b->string; + a->length = b->length; + a->priv = b->priv; + + b->string = tmp_s; + b->length = tmp_l; + b->priv = tmp_p; +} + +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) { + if (sz > BUFFER_SIZE) + sz *= 2; + else + sz += BUFFER_SIZE; + + if (req > sz) + sz = req; + + return cstring_grow_to(self, sz); + } + + return 1; +} + +int cstring_grow_to(cstring_t *self, int min_buffer) { + priv_t *priv = ((priv_t *) self->priv); + + if (min_buffer > priv->buffer_length) { + priv->buffer_length = min_buffer; + void *mem = realloc(self->string, sizeof(char) * priv->buffer_length); + + if (mem) + self->string = (char *) mem; + else + return 0; + } + + return 1; +} + +void cstring_compact(cstring_t *self) { + if (self != NULL) { + priv_t *priv = ((priv_t *) self->priv); + + priv->buffer_length = self->length + 1; + self->string = (char *) realloc(self->string, self->length + 1); + } +} + +int cstring_add_car(cstring_t *self, char source) { + if (!cstring_grow(self, 1)) + return 0; + + self->string[self->length] = source; + self->length++; + self->string[self->length] = '\0'; + + return 1; +} + +int cstring_add(cstring_t *self, const char source[]) { + return cstring_addf(self, source, 0); +} + +int cstring_addf(cstring_t *self, const char source[], size_t idx) { + return cstring_addfn(self, source, idx, 0); +} + +int cstring_addn(cstring_t *self, const char source[], size_t n) { + return cstring_addfn(self, source, 0, n); +} + +int cstring_addfn(cstring_t *self, const char source[], size_t idx, size_t n) { + size_t ss; + + ss = strlen(source); + if (source && ss > idx && idx >= 0) { + ss -= idx; + + if (n && n < ss) + ss = n; + + if (ss) { + // "+1" for the added '\0' + if (!cstring_grow(self, ss + 1)) + return 0; + + memcpy(self->string + self->length, source + idx, ss); + self->length += ss; + self->string[self->length] = '\0'; + } + } + + return 1; +} + +int cstring_addp(cstring_t *self, const char *fmt, ...) { + va_list ap; + char empty = '\0'; + + va_start(ap, fmt); + int sz = vsnprintf(&empty, 0, fmt, ap); + va_end(ap); + + char *tmp = malloc((sz + 1) * sizeof(char)); + if (!tmp) + return 0; + + va_start(ap, fmt); + sz = vsnprintf(tmp, sz + 1, fmt, ap); + va_end(ap); + + int ok = cstring_add(self, tmp); + free(tmp); + + return ok; +} + +void cstring_cut_at(cstring_t *self, size_t size) { + if (self->length > size) { + self->string[size] = '\0'; + self->length = size; + } +} + +cstring_t *cstring_substring(const char self[], size_t start, size_t length) { + size_t sz = strlen(self); + cstring_t * sub = new_cstring(); + + if (start <= sz) { + const char *source = (self + start); + + if (!length) + cstring_add(sub, source); + else + cstring_addn(sub, source, length); + } + + return sub; +} + +/* + clist *cstring_splitc(cstring *self, char delim, char quote) { + clist *list; + cstring *d, *q; + + d = new_cstring(); + q = new_cstring(); + + if (delim) + cstring_add_car(d, delim); + if (quote) + cstring_add_car(q, quote); + + list = cstring_split(self, d, q); + + free_cstring(d); + free_cstring(q); + + return list; + } + + clist *cstring_splits(cstring *self, const char delim[], const char quote[]) { + clist *list; + cstring *d, *q; + + d = new_cstring(); + q = new_cstring(); + + if (delim) + cstring_add(d, delim); + if (quote) + cstring_add(q, quote); + + list = cstring_split(self, d, q); + + free_cstring(d); + free_cstring(q); + + return list; + } + clist *cstring_split(cstring *self, cstring *delim, cstring *quote) { + clist *list; + cstring *elem; + clist_node *node; + size_t i; + int in_quote; + int hasdelim; + + hasdelim = delim && delim->length > 0; + + list = clist_new(); + in_quote = 0; + elem = NULL; + for (i = 0; i < self->length; i++) { + if (quote->length > 0 && cstring_starts_with(self, quote, i)) { + in_quote = !in_quote; + i += quote->length - 1; + } else { + if (elem == NULL) { + elem = new_cstring(); + node = clist_node_new(); + node->data = elem; + node->free_data = free_cstring; + clist_add(list, node); + } + if (!in_quote && hasdelim && cstring_starts_with(self, delim, i)) { + elem = new_cstring(); + node = clist_node_new(); + node->data = elem; + node->free_data = free_cstring; + clist_add(list, node); + i += delim->length - 1; + } else { + cstring_add_car(elem, self->string[i]); + } + } + } + + return list; + } + */ + +void cstring_reverse(char *self) { + size_t i; + size_t last; + char tmp; + + size_t sz = strlen(self); + if (sz) { + last = sz - 1; + for (i = 0; i <= (last / 2); i++) { + tmp = self[i]; + self[i] = self[last - i]; + self[last - i] = tmp; + } + } +} + +int cstring_replace(cstring_t *self, const char from[], const char to[]) { + cstring_t *buffer; + size_t i; + size_t step; + int occur; + + // easy optimisation: + if (!from || !from[0]) + return 0; + if (from && to && from[0] && to[0] && !from[1] && !to[1]) + return cstring_replace_car(self->string, from[0], to[0]); + + // optimise for same-size strings? + + step = strlen(from) - 1; + buffer = new_cstring(); + occur = 0; + for (i = 0; i < self->length; i++) { + if (cstring_starts_with(self->string, from, i)) { + cstring_add(buffer, to); + i += step; + occur++; + } else { + cstring_add_car(buffer, self->string[i]); + } + } + + cstring_swap(self, buffer); + free_cstring(buffer); + return occur; +} + +int cstring_replace_car(char *self, char from, char to) { + size_t i; + int occur = 0; + + for (i = 0; self[i]; i++) { + if (self[i] == from) { + self[i] = to; + occur++; + } + } + + return occur; +} + +int cstring_starts_with(const char string[], const char find[], + size_t start_idx) { + size_t i; + + for (i = 0; + string[start_idx + i] == find[i] && string[start_idx + i] != '\0' + && find[i] != '\0'; i++) + ; + + return find[i] == '\0'; +} + +int cstring_ends_with(const char self[], const char find[]) { + size_t sz = strlen(self); + size_t sz_needle = strlen(find); + if (sz_needle <= sz) { + if (!strcmp(self + (sz - sz_needle), find)) + return 1; + } + + return 0; +} + +long cstring_find(const char self[], const char find[], size_t start_index) { + size_t sz = strlen(self); + if (sz > start_index) { + char *found = strstr(self + start_index, find); + if (found) { + return (long) (found - self); + } + } + + return -1; +} + +long cstring_rfind(char self[], const char find[], long rstart_index) { + size_t sz = strlen(self); + size_t sz_needle = strlen(find); + + if (rstart_index <= 0) + rstart_index += (sz - 1); + + if (sz > rstart_index && sz_needle <= sz) { + for (size_t i = rstart_index;; i--) { + if (cstring_starts_with(self, find, i)) + return i; + + if (!i) + break; + } + } + + return -1; +} + +void cstring_clear(cstring_t *self) { + self->length = 0; + self->string[0] = '\0'; +} + +char *cstring_convert(cstring_t *self) { + char *string; + + if (!self) + return NULL; + + // Note: this could be skipped. + cstring_compact(self); + + string = (self->string); + self->string = NULL; + + free_cstring(self); + + return string; +} + +cstring_t *cstring_clone(const char self[]) { + if (self == NULL) + return NULL; + + cstring_t *clone = new_cstring(); + cstring_add(clone, self); + + return clone; +} + +void cstring_rtrim(cstring_t *self, char car) { + for (size_t i = self->length - 1; i >= 0; i--) { + if (self->string[i] != car) + break; + self->string[i] = '\0'; + self->length--; + } +} + +void cstring_trim(cstring_t *self, char car) { + if (car == '\0') + return; + + cstring_rtrim(self, car); + + int i = 0; + while (self->string[i] == car) + i++; + + if (i) { + cstring_t *tmp = new_cstring(); + cstring_add(tmp, self->string + i); + + cstring_swap(self, tmp); + free_cstring(tmp); + } +} + +size_t cstring_remove_crlf(char *self) { + size_t sz = strlen(self); + if (sz && self[sz - 1] == '\n') + sz--; + if (sz && self[sz - 1] == '\r') + sz--; + + self[sz] = '\0'; + + return sz; +} + +void cstring_toupper(cstring_t *self) { + cstring_change_case(self, 1); +} + +void cstring_tolower(cstring_t *self) { + cstring_change_case(self, 0); +} + +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) { + locale = setlocale(LC_ALL, NULL); + if (!locale || !locale[0] || !strcmp("C", locale)) { + char *lang = getenv("LANG"); + if (lang && lang[0]) { + locale = setlocale(LC_ALL, lang); + if (!locale) + locale = ""; + } + } + } + + cstring_t *rep; + mbstate_t state_from, state_to; + wchar_t wide; + char tmp[10]; + size_t count; + + // init the state (NULL = internal hidden state, not thread-safe) + memset(&state_from, '\0', sizeof(mbstate_t)); + memset(&state_to, '\0', sizeof(mbstate_t)); + + rep = new_cstring(); + + size_t i = 0; + while (i < self->length) { + count = mbrtowc(&wide, self->string + i, self->length - i, &state_from); + + //incomplete (should not happen) + if (count == (size_t) -2) { + // return; + cstring_add_car(rep, '_'); + i++; + continue; + } + // invalid multibyte sequence + if (count == (size_t) -1) { + // return; + cstring_add_car(rep, '_'); + i++; + continue; + } + + // End of String (should not happen, see WHILE condition) + if (!count) + break; + + // char is ok + i += count; + + if (up) + wide = (wchar_t) towupper((wint_t) wide); + else + wide = (wchar_t) towlower((wint_t) wide); + + count = wcrtomb(tmp, wide, &state_to); + if (count == (size_t) -1) { + // failed to convert :( + cstring_add_car(rep, '_'); + } else { + tmp[count] = '\0'; + cstring_add(rep, tmp); + } + } + + cstring_swap(self, rep); + free_cstring(rep); +} + +static char buffer[BUFFER_SIZE]; +int cstring_readline(cstring_t *self, FILE *file) { + size_t size = 0; + int full_line; + + // sanity check: + if (!file) + return 0; + + buffer[BUFFER_SIZE - 1] = '\0'; // just in case + + if (!feof(file)) { + cstring_clear(self); + buffer[0] = '\0'; + + // Note: fgets() could return NULL if EOF is reached + if (!fgets(buffer, (int) BUFFER_SIZE - 1, file)) + return 0; + + size = strlen(buffer); + full_line = ((file && feof(file)) || size == 0 + || buffer[size - 1] == '\n'); + size = cstring_remove_crlf(buffer); + cstring_add(self, buffer); + + // No luck, we need to continue getting data + while (!full_line) { + if (!fgets(buffer, (int) BUFFER_SIZE - 1, file)) + break; + + size = strlen(buffer); + full_line = ((file && feof(file)) || size == 0 + || buffer[size - 1] == '\n'); + size = cstring_remove_crlf(buffer); + cstring_add(self, buffer); + } + + return 1; + } + + return 0; +} + +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_t *self, const char subpath[]) { + while (self->length && self->string[self->length - 1] == CSTRING_SEP) + self->length--; + cstring_add_car(self, CSTRING_SEP); + if (subpath && subpath[0]) { + cstring_add(self, subpath); + } + + normalize_path(self); +} + +int cstring_pop_path(cstring_t *self, int how_many) { + int count = 0; + size_t tmp; + char first = '\0'; + + if (self->length) + first = self->string[0]; + + normalize_path(self); + for (int i = 0; i < how_many; i++) { + tmp = self->length; + while (self->length && self->string[self->length - 1] != CSTRING_SEP) + self->length--; + while (self->length && self->string[self->length - 1] == CSTRING_SEP) + self->length--; + if (self->length != tmp) + count++; + } + normalize_path(self); + + // Root is root of root + if (first == CSTRING_SEP && !self->length) + cstring_add_car(self, CSTRING_SEP); + + return count; +} + +char *cstring_basename(const char path[], const char ext[]) { + size_t i; + size_t sz = strlen(path); + + i = sz; + while (i && path[i] != CSTRING_SEP) + i--; + + cstring_t *rep; + if (path[i] != CSTRING_SEP) { + rep = cstring_clone(path); + } else { + rep = new_cstring(); + cstring_addf(rep, path, i + 1); + } + + if (ext && ext[0] && cstring_ends_with(rep->string, ext)) { + cstring_cut_at(rep, rep->length - strlen(ext)); + } + + return cstring_convert(rep); +} + +char *cstring_dirname(const char path[]) { + cstring_t *rep = cstring_clone(path); + cstring_pop_path(rep, 1); + return cstring_convert(rep); +} + +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); +} + +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/cstring.h b/cstring.h new file mode 100644 index 0000000..98679dc --- /dev/null +++ b/cstring.h @@ -0,0 +1,507 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2013 Niki Roo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * @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 said char *). + * + * @see cstring_convert + */ +typedef struct { + char CNAME[10]; + char *string; + size_t length; + void *priv; +} cstring_t; + +/** + * 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. + * + * @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. + * + * @param self the string to grow + * @param min the minimum number of extra characters it should be able to hold + * + * @return TRUE if success (FALSE means it was unable to grow due to memory + * pressure) + */ +int cstring_grow(cstring_t *self, int min_extra); + +/** + * Grow the cstring to accommodate that many characters in total, if needed. + * + * @param self the string to grow + * @param min the minimum number of characters it should be able to hold + * + * @return TRUE if success (FALSE means it was unable to grow due to memory + * pressure) + */ +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_t *self); + +/** + * Add a char at the end of the given cstring. + * + * @param self the cstring to work on + * @param source the character to add + * + * @return TRUE if success (FALSE means it was unable to grow due to memory + * pressure) + */ +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 + * given cstring. + * + * @param self the string to work on + * @param source the string to add + * + * @return TRUE if success (FALSE means it was unable to grow due to memory + * pressure) + */ +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 + * given cstring, starting from index. + * + * @param self the cstring to work on + * @param source the string to add + * @param idx the starting index at which to copy from the source + * + * @return TRUE if success (FALSE means it was unable to grow due to memory + * pressure) + */ +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 + * given string, up to N chars long. + * + * @param self the string to work on + * @param source the string to add + * @param n the maximum number of chars to add (excluding the NUL byte), or 0 + * to add the whole source (which MUST then end with a '\0') + * + * @return TRUE if success (FALSE means it was unable to grow due to memory + * pressure) + */ +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 + * given cstring, starting from index, up to N chars long. + * + * @param self the string to work on + * @param source the string to add + * @param idx the starting index at which to copy from the source + * @param n the maximum number of chars to add (excluding the NUL byte) or 0 + * to add the whole source (which MUST then end with a '\0') + * + * @return TRUE if success (FALSE means it was unable to grow due to memory + * pressure) + */ +int cstring_addfn(cstring_t *self, const char source[], size_t idx, size_t n); + +/** + * Add a string via the usual printf formatters. + * + * @param self the string to add to + * @param fmt the required format specifiers (@{see printf}) + * @param ... the printf-format parameters + * + * @return TRUE if success (FALSE means it was unable to grow due to memory + * pressure) + */ +int cstring_addp(cstring_t *self, const char *fmt, ...); + +/** + * Cut the string at the given size if it is greater. + * + * E.g.: it will have (at most) this many characters (without counting NULL) in + * it after. + * + * @param self the string to work on + * @param size the size to cut at (the maximum size of the cstring after this + * operation, NUL excepted) + */ +void cstring_cut_at(cstring_t *self, size_t size); + +/** + * Create a substring of this one. + * + * @param self the string to work on + * @param start the index to start at + * @param length the number of characters to copy, 0 for 'up to the end' + * + * @return a newly allocated cstring + */ +cstring_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. + * Will also allow empty fields, ie: "a,b,,c" will return 4 cstrings, the third being empty). + * + * @param self the cstring to work on + * @param delim the separator, which can be longer than one character + * + * @return a list of cstring + */ +//TODO: use a [] +//clist *cstring_split(cstring *self, cstring *delim, cstring *quote); +/** + * Split a cstring into "smaller" cstrings every time the given separator (which MUST end in \0) is found. + * Will also allow empty fields, ie: "a,b,,c" will return 4 cstrings, the third being empty). + * + * @param self the cstring to work on + * @param delim the separator, which can be longer than one character and MUST end with \0 + * + * @return a list of cstring + */ +//TODO: use a [] +//clist *cstring_splits(cstring *self, const char delim[], const char quote[]); +/** + * Split a cstring into "smaller" cstrings every time the given separator is found. + * Will also allow empty fields, ie: "a,b,,c" will return 4 cstrings, the third being empty). + * + * @param self the cstring to work on + * @param delim the separator + * + * @return a list of cstring + */ +//TODO: use a [] +//clist *cstring_splitc(cstring *self, char delim, char quote); +/** + * Reverse the given string. + * + * @param self the string to work on + */ +void cstring_reverse(char *self); + +/** + * Replace all occurrences of a string inside the given cstring by another. + * + * @param self the string to work on + * @param from the string to replace + * @param to the replacement string + * + * @return the number of occurrences changed + */ +int cstring_replace(cstring_t *self, const char from[], const char to[]); + +/** + * Replace all occurrences of a char inside the given string by another. + * + * @param self the string to work on + * @param from the char to replace + * @param to the replacement char + * + * @return the number of occurrences changed + */ +int cstring_replace_car(char *self, char from, char to); + +/** + * Check if the string starts with the given pattern. + * + * @param self the string to work on + * @param find the string to find + * @param start_idx the index at which to start the comparison + * + * @return 1 if it does + */ +int cstring_starts_with(const char self[], const char find[], size_t start_idx); + +/** + * Check if the string ends with the given pattern. + * + * @param self the string to work on + * @param find the string to find (if empty, will always be found) + * @param start_index the index at which to start the comparison + * + * @return 1 if it does + */ +int cstring_ends_with(const char self[], const char find[]); + +/** + * Find the given substring in this one. + * + * @param self the string to work on + * @param find the string to find + * @param start_index the index at which to start the search + * + * @return the start index of the found string if found, or a negative value + * if not + */ +long cstring_find(const char self[], const char find[], size_t rstart_index); + +/** + * Find the given substring in this one, but search in the reverse direction. + * + * @param self the string to work on + * @param find the string to find + * @param rstart_index the index at which to start the search, or 0 for + * "end of string" (remember that it is reverse, you would then never + * find anything with a real rstart_index of 0), or a negative value + * to count from the end of the string (-2 means 2 character before the + * end) + * + * @return the start index of the found string if found, or a negative value + * if not + */ +long cstring_rfind(char self[], const char find[], long rstart_index); + +/** + * Clear (truncate its size to 0) the given string. + * + * @param self the string to work on + */ +void cstring_clear(cstring_t *self); + +/** + * Convert this cstring into a string + * This means that you MUST NOT call cstring_free nor use the cstring anymore. + * NULL will return NULL. + * + * @param self the cstring to work on + */ +char *cstring_convert(cstring_t *self); + +/** + * Clone this string. + * NULL will return NULL. + * + * @param self the string to clone + */ +cstring_t *cstring_clone(const char self[]); + +/** + * Trim this cstring of all trailing 'car' instances. + * + * @param self the cstring to work on + * @param car the character to trim (right only) + * + * @return a right trimmed cstring + */ +void cstring_rtrim(cstring_t *self, char car); + +/** + * Trim this cstring of all 'car' instances from the start and/or the + * end of the string. + * + * @param self the cstring to work on + * @param car the character to trim + * + * @return a trimmed cstring + */ +void cstring_trim(cstring_t *self, char car); + +/** + * Remove the \r and \n sequence (or one OR the other) at the end of the string. + * + * @param self the string to change + * + * @return the new length of the string + */ +size_t cstring_remove_crlf(char *self); + +/** + * Change the case to upper-case (UTF-8 compatible, but the string MUST be + * whole). + * + * @note: if LC_ALL is not set or is set to C and a viable $LANG exists, it will + * set LC_ALL to $LANG + * + * @param self the cstring to work on + */ +void cstring_toupper(cstring_t *self); + +/** + * Change the case to lower-case (UTF-8 compatible, but the string MUST be + * whole). + * + * @note: if LC_ALL is not set or is set to C and a viable $LANG exists, it will + * set LC_ALL to $LANG + * + * @param self the cstring to work on + */ +void cstring_tolower(cstring_t *self); + +/** + * Read a whole line (CR, LN or CR+LN terminated) from the given file stream. + * + * @param self the cstring to read into + * @param file the file to read + * + * @return 1 if a line was read, 0 if not + */ +int cstring_readline(cstring_t *self, FILE *file); + +/** + * Add a path to the given cstring (if it is currently empty, it + * will result in a root path). + * + * Will be separated by a forward '/' except on non-standard systems + * that uses reverse slash (i.e., Windows). + * + * @param self the base cstring (empty for a root path) + * @param subpath the path component to add + */ +void cstring_add_path(cstring_t *self, const char subpath[]); + +/** + * Remove the how_many components of the path described by this + * cstring. Will ignore extra path separators and always trim it from the final + * result (i.e., some//path/ is identical to some/path). + * + * @note popping "0" path will simply make sure the string does not end in "/" + * + * @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_t *self, int how_many); + +/** + * Return the basename component of this path (for instance, + * '/home/user/file.ext' becomes 'file.ext'). + * + * @param path the path to get the dir of (it can be a dir itself) + * @param ext the extension to remove if any (can be empty or NULL for none) + * + * @note the extension should include the "." if any + * + * @return a new string representing the parent directory + */ +char *cstring_basename(const char path[], const char ext[]); + +/** + * Return the dirname of this path (for instance, + * '/home/user/file.ext' becomes '/home/user'). + * + * @param path the path to get the dir of (it can be a dir itself) + * + * @return a new string representing the parent directory + */ +char *cstring_dirname(const char path[]); + +/** + * Check if the string is a correct and whole UTF-8 string (i.e., it is indeed + * an UTF-8 string and doesn't contain incomplete UTF-8 sequences). + * + * @return TRUE if it is UTF-8 + */ +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 +} +#endif + diff --git a/cutils.c b/cutils.c new file mode 100644 index 0000000..1141c2c --- /dev/null +++ b/cutils.c @@ -0,0 +1,44 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2020 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 + +#include "cutils.h" +#include "cstring.h" + +#ifndef strnlen +size_t strnlen(const char *s, size_t maxlen) { + size_t i; + for (i = 0; s[i]; i++) { + if (i >= maxlen) + return maxlen; + } + + return i; +} +#endif + +#ifndef strdup +char *strdup(const char *source) { + size_t sz = strlen(source); + char *new = malloc((sz + 1) * sizeof(char)); + strcpy(new, source); + return new; +} +#endif diff --git a/cutils.h b/cutils.h new file mode 100644 index 0000000..0090a31 --- /dev/null +++ b/cutils.h @@ -0,0 +1,79 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2020 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 . + */ + +/** + * @file cutils.h + * @author Niki + * @date 2020 - 2022 + * + * @brief Include all the other .h as well as C99-compatible + * strdup/strnlen functions if they are not already defined + */ +#ifndef CUTILS_H +#define CUTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cstring.h" +#include "array.h" +#include "desktop.h" +#include "print.h" +#include "timing.h" + +/* Helps with C99 compatibility for code that is not */ + +#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 +#endif +#if _POSIX_C_SOURCE < 200809L && _XOPEN_SOURCE < 500 +/** + * The strdup() function returns a pointer to a new string which is a + * duplicate of the string s. Memory for the new string is obtained with + * malloc(3), and can be freed with free(3). + * + * + * @return On success, the strdup() 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 + +/* */ + +#endif // CUTILS_H + +#ifdef __cplusplus +} +#endif + diff --git a/desktop.c b/desktop.c new file mode 100644 index 0000000..1892811 --- /dev/null +++ b/desktop.c @@ -0,0 +1,339 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2021 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 +#include +#include +#include +#include +#include +#include + +#include "cutils.h" + +#define EXT "desktop" + +/* Private functions */ +static int desktop_compare(const void *a, const void* b); +static int desktop_test_file(const char filename[]); +/* */ + +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; + me->icon_file = NULL; + me->children = NULL; + me->id = 0; + + // Copy name + me->name = strdup(filename); + + // Get extension an remove ".desktop" from name + char *ext = rindex(me->name, '.'); + if (ext) { + size_t idot = (ext - me->name); + ext++; + if (!strcmp(ext, EXT)) + me->name[idot] = '\0'; + } + if (ext) + ext = strdup(ext); + + // If PNG of the same name, use as default icon + me->icon_file = desktop_find_icon(me->name, best_size); + + // Pretify default name (remove dir part) + char *slash = rindex(me->name, '/'); + if (slash && !slash[1]) { + slash[0] = '\0'; + slash = rindex(me->name, '/'); + } + if (slash) { + char *copy = strdup(slash + 1); + free(me->name); + me->name = copy; + } + + // Try with the base name, too + if (!me->icon_file) { + me->icon_file = desktop_find_icon(me->name, best_size); + } + + DIR *dp = opendir(filename); + if (dp) { + // Try to get the default folder icon if no icon yet + if (!me->icon_file) { + me->icon_file = desktop_find_icon("folder", best_size); + } + + 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; + + 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); + } + + array_qsort(me->children, desktop_compare); + + closedir(dp); + free(ext); + return 1; + } + + // Only process ".desktop" files + if (!ext || strcmp(ext, EXT)) { + uninit_desktop(me); + free(ext); + return 0; + } + + FILE *file; + cstring_t *line; + char *startsWith; + + file = fopen(filename, "r"); + 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="; + 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++; + } + } + + 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); + } + + // Find icon file linked to icon + if (me->icon && !me->icon_file) { + me->icon_file = desktop_find_icon(me->icon, best_size); + } + // ...or any we can find, actually + if (!me->icon_file) { + me->icon_file = desktop_find_icon(me->name, best_size); + } + + free(ext); + return 1; +} + +void free_desktop(desktop_t *me) { + if (me) + uninit_desktop(me); + + 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); + me->children = NULL; + me->CNAME[0] = '!'; +} + +desktop_t *desktop_find_id(array_t *children, int id) { + desktop_t *found = NULL; + + array_loop(children, child, desktop_t) + { + if (child->id == id) { + found = child; + break; + } + + if (child->children) { + found = desktop_find_id(child->children, id); + break; + } + } + + return found; +} + +/* Private functions */ + +static int desktop_compare(const void *a, const void* b) { + desktop_t *me1 = (desktop_t *)a; + desktop_t *me2 = (desktop_t *)b; + + if (me1->children && !(me2->children)) + return -1; + if (!(me1->children) && me2->children) + return 1; + + return strcmp(me1->name, me2->name); +} + +static int desktop_test_file(const char filename[]) { + FILE *test; + DIR *test_dir; + + test = fopen(filename, "r"); + if (test) { + fclose(test); + test_dir = opendir(filename); + if (test_dir) { + closedir(test_dir); + } else { + return 1; + } + } + + return 0; +} + +#define TRY_DIR(a,b,c) \ + do { \ + tmp = cstring_concat(a, b, c, basename, ".png", NULL); \ + if(desktop_test_file(tmp)) \ + return tmp; \ + free(tmp); \ + } while(0) + +static char *theme = NULL; +static char *ltheme = NULL; +char *desktop_find_icon(const char basename[], int icon_size) { + char *tmp = NULL; + char *home = getenv("HOME"); + char icon_size_str[100]; + sprintf(icon_size_str, "%dx%d", icon_size, icon_size); + + if (!theme) { + tmp = cstring_concat(home, "/.gtkrc-2.0", NULL); + FILE *file = fopen(tmp, "r"); + free(tmp); + if (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); + + 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); + } + + } + } + + // Allow NULL + if (!basename || !basename[0]) + return NULL; + + // exact match + tmp = strdup(basename); + if (desktop_test_file(tmp)) + return tmp; + free(tmp); + + // same name, with ".png" + TRY_DIR("", "", ""); + + // local icons + TRY_DIR(home, "/.local/share/icons/", ""); + + // theme icons + if (theme && theme[0]) { + // exact size (apps, places) + TRY_DIR(theme, icon_size_str, "/apps/"); + TRY_DIR(theme, icon_size_str, "/places/"); + + // scalable (apps, places) + TRY_DIR(ltheme, "scalable", "/apps/"); + TRY_DIR(ltheme, "scalable", "/places/"); + } + + // shared icons, exact size (apps, places) + TRY_DIR("/usr/share/icons/hicolors/", icon_size_str, "/apps/"); + TRY_DIR("/usr/share/icons/hicolors/", icon_size_str, "/places/"); + + // shared icons, scalable (apps, places) + TRY_DIR("/usr/share/icons/hicolors/scalable/apps/", "", ""); + TRY_DIR("/usr/share/icons/hicolors/scalable/places/", "", ""); + + return NULL; +} diff --git a/desktop.h b/desktop.h new file mode 100644 index 0000000..bf39398 --- /dev/null +++ b/desktop.h @@ -0,0 +1,146 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2021 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 . + */ + +/** + * @file desktop.h + * @author Niki + * @date 2021 - 2022 + * + * @brief Manipulate .desktop files (as described by + * FreeDesktop.org) + * + * This structure helps you manipulate .desktop files (as described by + * FreeDesktop.org). + * + * @note the desktop object can use icons; for the selection of those, an exact + * match will first be tried (same name as the desktop file, with + * a .png extension), then we will look into the local + * .local/share/icons and if we still haven't found an icon, into + * the theme (first by looking for a best_size sized icon and if + * not in scalable) + * + * @note we support the use of desktop objects for menu, too, and that includes + * submenu items support + */ +#ifndef DESKTOP_H +#define DESKTOP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "array.h" + +/** + * The structure used to represent desktop objects. + */ +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_t *new_desktop(const char filename[], int best_size); + +/** + * Create a new desktop object from the given .desktop file. + * + * @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 init_desktop(desktop_t *self, const char filename[], int best_size); + +/** + * Free the given desktop object. + * + * @note always equivalent to uninit_desktop + free + * + * @see uninit_desktop(desktop_t *self) + * @see free(void *data) + */ +void free_desktop(desktop_t *self); + +/** + * Free the resources used by the given desktop object -- do not use it anymore + * after this call. + */ +void uninit_desktop(desktop_t *self); + +/** + * Find a submenu item by the given ID ({@see desktop_set_id(desktop *, int)}). + * + * TODO: use full objects instead + * @param children the array of pointers to desktop objects to look through + * @param menu_id the ID of the submenu we want to find + * + * @return the given submenu if found, or NULL + */ +desktop_t *desktop_find_id(array_t *children, int menu_id); + +/** + * Look for the icon file related to this basename. + * + * @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); + +#endif /* DESKTOP_H */ + +#ifdef __cplusplus +} +#endif + diff --git a/net/net.c b/net/net.c new file mode 100644 index 0000000..7ce1cb2 --- /dev/null +++ b/net/net.c @@ -0,0 +1,263 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2012 Niki Roo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 + #include + #include +#else + #include +#endif + +#include "net.h" + +#define bool int +#define true 1 +#define false 0 + +/** + * Get the sockaddr, IPv4 or IPv6. + * @param sa the socket address is in this structure + * @return the sockaddr_in or sockaddr_in6 inside this socketaddr, + * depending if it is IPv4 or IPv6 + */ +void *get_in_addr(struct sockaddr *sa); + +#ifndef WIN32 +/** + * Reap all zombie processes. + * This function will be called when a child process terminates, and + * will loop on all zombie processes to properly acknowledge them + * so they can die. + * + * @param s + */ +void sigchld_handler(int pid); + +void sigchld_handler(int pid) { + if (pid > 0 || pid < 0) pid = 0; + + // Reap all zombie processes + while (waitpid(-1, NULL, WNOHANG) > 0); +} +#endif + +int net_init(){ +#if defined (WIN32) + WSADATA WSAData; + return !WSAStartup(MAKEWORD(2,2), &WSAData); +#endif + return 1; +} + +void net_cleanup(){ +#if defined (WIN32) + WSACleanup(); +#endif +} + +int net_set_non_blocking(int fd) { + return net_set_blocking(fd, 0); +} + +int net_set_blocking(int fd, int block) { + int flags; + +/* If they have O_NONBLOCK, use the POSIX way to do it */ +#if defined (O_NONBLOCK) + /* O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */ + if ((flags = fcntl(fd, F_GETFL, 0)) == -1) { + flags = 0; + } + if (block) { + return fcntl(fd, F_SETFL, flags ^ O_NONBLOCK); + } + else { + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); + } +#else + flags = block?0:1; + return ioctl(fd, FIONBIO, (int)(&flags)); +#endif +} + +int net_connect(const char server[], int port) { + int sockfd; + struct addrinfo hints, *servinfo, *p; + int rv; + char string[10]; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + // convert the port number to a string + sprintf(string, "%i%c", port, '\0'); + rv = getaddrinfo(server, string, &hints, &servinfo); + // + + if (rv != 0) { + // DO NOT dirty the stderr + //fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + // loop through all the results and connect to the first we can + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) + == -1) { + //perror("client: socket"); + continue; + } + + if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + //perror("client: connect"); + continue; + } + + break; + } + + if (p == NULL) { + //fprintf(stderr, "client: failed to connect\n"); + return -1; + } + + freeaddrinfo(servinfo); + + return sockfd; +} + +int net_accept(int ssocketd) { + struct sockaddr_storage their_addr; + socklen_t sin_size; + + sin_size = sizeof(their_addr); + return accept(ssocketd, (struct sockaddr *) &their_addr, &sin_size); +} + +int net_listen(int port, int backlog) { +#ifndef WIN32 + struct sigaction sa; +#endif + int sockfd; + struct addrinfo hints, *servinfo, *p; + char yes = 1; + int rv; + char string[10]; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; // use my IP + + // convert the port number to a string + sprintf(string, "%i%c", port, '\0'); + rv = getaddrinfo(NULL, string, &hints, &servinfo); + // + + if (rv != 0) { + // DO NOT dirty the stderr + //fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + // loop through all the results and bind to the first we can + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { + //perror("server: socket"); + continue; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + //perror("setsockopt"); + return -1; + } + + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + //perror("server: bind"); + continue; + } + + break; + } + + if (p == NULL) { + //fprintf(stderr, "server: failed to bind\n"); + return -1; + } + + // all done with this structure + freeaddrinfo(servinfo); + + if (listen(sockfd, backlog) == -1) { + //perror("listen"); + return -1; + } + +#ifndef WIN32 + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + if (sigaction(SIGCHLD, &sa, NULL) == -1) { + //perror("sigaction"); + return -1; + } +#endif + + return sockfd; +} + +void net_close_socketd(int socketd) { + close(socketd); +} + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif +ssize_t net_write(int fd, const void *buf, size_t n) { + // In UNIX: send() with flag set to '0' == write() + // In WIN32: cannot write() to a socket + return send(fd, (char *)buf, n, MSG_NOSIGNAL); +} + +ssize_t net_read(int fd, void *buf, size_t nbytes) { + // In UNIX: recv() with flag set to '0' == send() + // In WIN32: cannot read() from a socket + return recv(fd, (char *)buf, nbytes, 0); +} + +void *get_in_addr(struct sockaddr *sa) { + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*) sa)->sin_addr); + } + else { + return &(((struct sockaddr_in6*) sa)->sin6_addr); + } +} + diff --git a/net/net.h b/net/net.h new file mode 100644 index 0000000..73140f3 --- /dev/null +++ b/net/net.h @@ -0,0 +1,164 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2012 Niki Roo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file net.h + * @author niki + * @date 2011 - 2022 + * + * @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 +#define NET_H + +#ifdef __cplusplus +extern "C" { +#endif + +// WHY ?? +#ifdef _WIN32 +#ifndef WIN32 +#define WIN32 +#endif +#endif + +#ifdef WIN32 +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x501 +#endif +#include +#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 +#endif + +/* for ssize_t */ +#include + +/** + * You must call this function before doing any network related operation, + * because of some legacy WIN32 rule. + * (It is a noop on all other platforms.) + * + * @return 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.) + */ +void net_cleanup(); + +/** + * Set the given socket to (non-)blocking I/O mode. + * This function can work with file sockets, too. + * + * @param fd the file descriptor or socket to change + * @param block TRUE to block, FALSE not to block + * + * @return TRUE if success + */ +int net_set_blocking(int fd, int block); + +/** + * Connect to this server on this port, and return a socket descriptor + * to write to or read from it. + * + * @param server the server to connect to + * @param port the port to connect on + * + * @return the server socket or 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 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 + * + * @return the server socket, or a negative value on error + */ +int net_listen(int port, int backlog); + +/** + * Block (or not) and wait for a client to connect on this socket. + * When this is done, return a socket to this specific client/server + * connection. It can takes the connections from a queue, + * as defined in net_listen. + * + * @param ssocketd the server socket on which to accept a connection + * + * @return the socket, or a negative value on error + */ +int net_accept(int ssocketd); + +/** + * Close a socket (or a server socket). + * + * @param socketd the (server) socket to close + */ +void net_close_socketd(int socketd); + +/** + * Write to this socket, as you would with a file. + * + * @param fd the socket to write to + * @param buf the buffer to read from + * @param n the number of bytes to write + * + * @return the actual number of bytes written or a negative number if error + */ +ssize_t net_write(int fd, const void *buf, size_t n); + +/** + * Read from this socket, as you would with a file. + * + * @param fd the socket to read from + * @param buf the buffer to write to + * @param nbytes the number of bytes to read + * + * @return the actual number of bytes read + */ +ssize_t net_read(int fd, void *buf, size_t nbytes); + +#endif + +#ifdef __cplusplus +extern} +#endif diff --git a/print.h b/print.h new file mode 100644 index 0000000..f0872a6 --- /dev/null +++ b/print.h @@ -0,0 +1,62 @@ +/** + * @file print.h + * @author Samantaz Fox + * @date 2019 + * + * @brief File name and line numbers + * + * Small utility header designed to print file name and line number + * along with debug/info messages. + * + * Usage example: + * ERROR("program exited with code %d\n", getErrorCode() ); + * + * Known issues: + * - Must take at least one argument in addition to format string + * + * + * Copyright 2019 (C) Samantaz Fox + * https://github.com/SamantazFox/Micro-projects/raw/master/library/print.h + * + * This file is in the public domain. + * Feel free to copy, modify or redistribute it! +*/ + +#ifndef __PRINT_H__ +#define __PRINT_H__ + + +#ifdef __cplusplus + #include +#else + #include +#endif + + +#define TRACE(_format, ...) \ + do { \ + fprintf(stdout, "[Trace] %s:%d\t" _format, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) + +#define DEBUG(_format, ...) \ + do { \ + fprintf(stdout, "[Debug] %s:%d\t" _format, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) + +#define INFO(_format, ...) \ + do { \ + fprintf(stdout, "[Info ] %s:%d\t" _format, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) + +#define WARN(_format, ...) \ + do { \ + fprintf(stderr, "[Warn ] %s:%d\t" _format, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) + +#define ERROR(_format, ...) \ + do { \ + fprintf(stderr, "[Error] %s:%d\t" _format, __FILE__, __LINE__, __VA_ARGS__); \ + } while (0) + + +#endif /* !__PRINT_H__ */ diff --git a/timing.h b/timing.h new file mode 100644 index 0000000..3895e49 --- /dev/null +++ b/timing.h @@ -0,0 +1,62 @@ +/* + * CUtils: some small C utilities + * + * Copyright (C) 2020 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 . + */ + +/** + * @file timing.h + * @author Niki + * @date 2020 - 2024 + * + * @brief Timing macros START and STOP + * + * 2 macro are provided to print the elapsed time between the 2 to stdout. + */ + +#ifndef TIMING_H +#define TIMING_H + +#ifdef __cplusplus +extern "C" { +#endif + +#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; \ + sprintf(cusec, "%0.6d", TIMING_stop.tv_usec); \ + printf("TIME: %d.%s sec\n", TIMING_stop.tv_sec, cusec); \ + gettimeofday(&TIMING_start, NULL); + +#endif // TIMING_H + +#ifdef __cplusplus +} +#endif +