~crm/cstring

9cac873acc706b2a683ed1541c367e38768cb999 — Christos Margiolis 3 years ago 9ce1343
improved style, made indentation 8 spaces
9 files changed, 367 insertions(+), 343 deletions(-)

M LICENSE -rw-r--r-- => -rwxr-xr-x
M Makefile
M README.md
M cstring.3 -rw-r--r-- => -rwxr-xr-x
M cstring.c
M cstring.h
M tests/Makefile -rw-r--r-- => -rwxr-xr-x
M tests/test_basic.c
M tests/test_insert.c -rw-r--r-- => -rwxr-xr-x
M LICENSE => LICENSE +0 -0
M Makefile => Makefile +26 -24
@@ 1,3 1,5 @@
# See LICENSE file for copyright and license details.

LIB = cstring
VERSION = 0.1
DIST = ${LIB}-${VERSION}


@@ 31,40 33,40 @@ GZIP = gzip
all: options ${LIB}

options:
	@echo ${LIB} build options:
	@echo "CFLAGS   = ${CFLAGS}"
	@echo "LDFLAGS  = ${LDFLAGS}"
	@echo "CC       = ${CC}"
		@echo ${LIB} build options:
		@echo "CFLAGS   = ${CFLAGS}"
		@echo "LDFLAGS  = ${LDFLAGS}"
		@echo "CC       = ${CC}"

${LIB}: ${OBJ}
	${AR} ${ARFLAGS} lib${LIB}.a ${OBJ}
		${AR} ${ARFLAGS} lib${LIB}.a ${OBJ}

%.o: %.${EXT}
	${CC} ${CFLAGS} -c $< -o $@
${OBJ}: ${SRC}
		${CC} ${CFLAGS} -c $< -o $@

dist: clean
	${MKDIR} ${DIST}
	${CP} -R tests ${SRC} ${MAN3} LICENSE Makefile README.md ${DIST}
	${TAR} ${DIST}.tar ${DIST}
	${GZIP} ${DIST}.tar
	${RM_DIR} ${DIST}
		${MKDIR} ${DIST}
		${CP} -R tests ${SRC} ${MAN3} LICENSE Makefile README.md ${DIST}
		${TAR} ${DIST}.tar ${DIST}
		${GZIP} ${DIST}.tar
		${RM_DIR} ${DIST}

install: all
	${MKDIR} ${DESTDIR}${LIB_DIR} ${DESTDIR}${INC_DIR} ${DESTDIR}${MAN_DIR}
	${CP} ${LIB}.h ${DESTDIR}${INC_DIR}
	${CP} lib${LIB}.a ${DESTDIR}${LIB_DIR}
	${CP} ${MAN3} ${DESTDIR}${MAN_DIR}
	sed "s/VERSION/${VERSION}/g" < ${MAN3} > ${DESTDIR}${MAN_DIR}/${MAN3}
	chmod 755 ${DESTDIR}${INC_DIR}/${LIB}.h
	chmod 644 ${DESTDIR}${LIB_DIR}/lib${LIB}.a
	chmod 644 ${DESTDIR}${MAN_DIR}/${MAN3}
		${MKDIR} ${DESTDIR}${LIB_DIR} ${DESTDIR}${INC_DIR} ${DESTDIR}${MAN_DIR}
		${CP} ${LIB}.h ${DESTDIR}${INC_DIR}
		${CP} lib${LIB}.a ${DESTDIR}${LIB_DIR}
		${CP} ${MAN3} ${DESTDIR}${MAN_DIR}
		sed "s/VERSION/${VERSION}/g" < ${MAN3} > ${DESTDIR}${MAN_DIR}/${MAN3}
		chmod 755 ${DESTDIR}${INC_DIR}/${LIB}.h
		chmod 644 ${DESTDIR}${LIB_DIR}/lib${LIB}.a
		chmod 644 ${DESTDIR}${MAN_DIR}/${MAN3}

uninstall:
	${RM} ${DESTDIR}${INC_DIR}/${LIB}.h
	${RM} ${DESTDIR}${LIB_DIR}/lib${LIB}.a
	${RM} ${DESTDIR}${MAN_DIR}/${MAN3}
		${RM} ${DESTDIR}${INC_DIR}/${LIB}.h
		${RM} ${DESTDIR}${LIB_DIR}/lib${LIB}.a
		${RM} ${DESTDIR}${MAN_DIR}/${MAN3}

clean:
	${RM} ${LIB} ${OBJ} lib${LIB}.a ${DIST}.tar.gz
		${RM} ${LIB} ${OBJ} lib${LIB}.a ${DIST}.tar.gz

.PHONY: all options clean dist install uninstall

M README.md => README.md +4 -3
@@ 1,7 1,7 @@
# cstring

A simple and lightweight string library for C inspired by C++'s STL `string` class,
but with a lot of additions.
but with a many additions.

## Building



@@ 30,7 30,9 @@ When using this library, you must **always** call the `cstring_create` and `cstr
functions whenever you want to make a new instance of `cstring` and stop using it respectively,
in order not to cause any memory leaks, as there's no *constructor* and *destructor* to do it for you.  

The recommended way of initializing an empty string is by doing `cstring foo = cstring_create(CSTRING_INIT_EMPTY)`.
The recommended way of initializing an empty string is by doing `cstring foo = cstring_create(CSTRING_INIT_EMPTY)`.  

Read the `man` page for more detailed info.

## Functions



@@ 104,7 106,6 @@ The following macros can only be used in debug mode
See the test programs in `tests` for more.

```c
#include <stdio.h>
#include <cstring.h>

/* outputs "Foobar" to the screen */

M cstring.3 => cstring.3 +0 -0
M cstring.c => cstring.c +230 -212
@@ 1,23 1,25 @@
/* See LICENSE file for copyright and license details. */

#include "cstring.h"

#define CSTRING_EXCEEDS_CAPACITY(len, cap)  ((len) >= (cap))

#define CSTRING_FIND_OCCURENCE(cs, s, func) do {                            \
    char *_found;                                                           \
    if ((_found = func(cs->str, (s))) != NULL)                              \
        return (_found - cs->str);                                          \
#define CSTRING_FIND_OCCURENCE(cs, s, func) do {               \
        char *_found;                                          \
        if ((_found = func(cs->str, (s))) != NULL)             \
                return (_found - cs->str);                     \
} while (0)

#ifdef CSTRING_DBG
#define CSTRING_FREE(cs) do {                                               \
    CSTRING_DBG_LOG("Before CSTRING_FREE: %s\n", cs->str);                  \
    if (!cstring_empty(cs))                                                 \
        free(cs->str);                                                      \
#define CSTRING_FREE(cs) do {                                  \
        CSTRING_DBG_LOG("Before CSTRING_FREE: %s\n", cs->str); \
        if (!cstring_empty(cs))                                \
                free(cs->str);                                 \
} while (0)
#else /* !CSTRING_DBG */
#define CSTRING_FREE(cs) do {                                               \
    if (!cstring_empty(cs))                                                 \
        free(cs->str);                                                      \
#define CSTRING_FREE(cs) do {                                  \
        if (!cstring_empty(cs))                                \
                free(cs->str);                                 \
} while (0)
#endif /* CSTRING_DBG */



@@ 31,165 33,173 @@ static inline int cstring_cmp_char_less(const void *, const void *);
static int
cstring_is_one_of(char c, const char *s)
{
    for (; *s; s++)
        if (*s == c)
            return 1;
    return 0;
        for (; *s; s++)
                if (*s == c)
                        return 1;
        return 0;
}

static inline int
cstring_cmp_greater(const void *lhs, const void *rhs)
{
    return cstring_greater((cstring *)lhs, (cstring *)rhs);
        return cstring_greater((cstring *)lhs, (cstring *)rhs);
}

static inline int
cstring_cmp_less(const void *lhs, const void *rhs)
{
    return cstring_less((cstring *)lhs, (cstring *)rhs);
        return cstring_less((cstring *)lhs, (cstring *)rhs);
}

static inline int
cstring_cmp_char_greater(const void *lhs, const void *rhs)
{
    return (*(char *)lhs > *(char *)rhs);
        return (*(char *)lhs > *(char *)rhs);
}

static inline int
cstring_cmp_char_less(const void *lhs, const void *rhs)
{
    return (*(char *)lhs < *(char *)rhs);
        return (*(char *)lhs < *(char *)rhs);
}

/* externs */
cstring
cstring_create(const char *s)
{
    cstring cs;
    cs.len = strlen(s);
    cs.str = cstring_copy(s);
    cstring_resize(&cs, cs.len << 1);
        cstring cs;
        cs.len = strlen(s);
        cs.str = cstring_copy(s);
        cstring_resize(&cs, cs.len << 1);
#ifdef CSTRING_DBG
    CSTRING_DBG_LOG_STR_INFO(s, cs.len);
    CSTRING_DBG_LOG_CSTR_INFO_NPTR(cs);
        CSTRING_DBG_LOG_STR_INFO(s, cs.len);
        CSTRING_DBG_LOG_CSTR_INFO_NPTR(cs);
#endif /* CSTRING_DBG */
    return cs;
        return cs;
}

void
cstring_delete(cstring *cs)
{
    CSTRING_FREE(cs);
    cs->str = NULL;
    cs->len = 0;
    cs->capacity = 0;
        CSTRING_FREE(cs);
        cs->str = NULL;
        cs->len = 0;
        cs->capacity = 0;
}

void
cstring_assign(cstring *cs, const char *s)
{
    CSTRING_FREE(cs);
    cs->str = cstring_copy(s);
    cs->len = strlen(s);
    if (CSTRING_EXCEEDS_CAPACITY(cs->len, cs->capacity))
        cstring_resize(cs, cs->len << 1);
        CSTRING_FREE(cs);
        cs->str = cstring_copy(s);
        cs->len = strlen(s);
        if (CSTRING_EXCEEDS_CAPACITY(cs->len, cs->capacity))
                cstring_resize(cs, cs->len << 1);
#ifdef CSTRING_DBG
    CSTRING_DBG_LOG_STR_INFO(s, cs->len);
    CSTRING_DBG_LOG_CSTR_INFO(cs);
        CSTRING_DBG_LOG_STR_INFO(s, cs->len);
        CSTRING_DBG_LOG_CSTR_INFO(cs);
#endif /* CSTRING_DBG */
}

void
cstring_insert(cstring *cs, const char *s, size_t i)
{
    if (!CSTRING_OUT_OF_BOUNDS(cs, i) && s != NULL) {
        size_t slen = strlen(s);
        size_t newlen = cs->len + slen;
        size_t slen, newlen;
        char *tmp;
        CSTRING_MALLOC(tmp, newlen + 1);
        memcpy(tmp, cs->str, i);
        memcpy(tmp + i, s, slen);
        memcpy(tmp + i + slen, cs->str + i, newlen - slen - i + 1);
        CSTRING_FREE(cs);
        cs->len = newlen;
        cs->str = tmp;
        cs->str[cs->len] = '\0';
        if (CSTRING_EXCEEDS_CAPACITY(newlen, cs->capacity))
            cstring_resize(cs, newlen << 1);

        if (!CSTRING_OUT_OF_BOUNDS(cs, i) && s != NULL) {
                slen = strlen(s);
                newlen = cs->len + slen;
                CSTRING_MALLOC(tmp, newlen + 1);
                memcpy(tmp, cs->str, i);
                memcpy(tmp + i, s, slen);
                memcpy(tmp + i + slen, cs->str + i, newlen - slen - i + 1);
                CSTRING_FREE(cs);
                cs->len = newlen;
                cs->str = tmp;
                cs->str[cs->len] = '\0';
                if (CSTRING_EXCEEDS_CAPACITY(newlen, cs->capacity))
                        cstring_resize(cs, newlen << 1);
#ifdef CSTRING_DBG
        CSTRING_DBG_LOG_STR_INFO(s, slen);
        CSTRING_DBG_LOG_CSTR_INFO(cs);
                CSTRING_DBG_LOG_STR_INFO(s, slen);
                CSTRING_DBG_LOG_CSTR_INFO(cs);
#endif /* CSTRING_DBG */
    }
        }
}

#ifdef CSTRING_DBG
// FIX IT
#define CSTRING_DBG_EXPECTED_ERASE_STR(cs, pos, len) do {                   \
    CSTRING_DBG_LOG("%s", "CSTRING_DBG_EXPECTED_ERASE_STR: ");              \
    size_t _i;                                                              \
    for (_i = 0; _i < (pos); _i++)                                          \
        printf("%c", cs->str[_i]);                                          \
    for (_i = (pos) + (len); _i < cs->len; _i++)                            \
        printf("%c", cs->str[_i]);                                          \
    printf("\n");                                                           \
        CSTRING_DBG_LOG("%s", "CSTRING_DBG_EXPECTED_ERASE_STR: ");          \
        size_t _i;                                                          \
        for (_i = 0; _i < (pos); _i++)                                      \
                printf("%c", cs->str[_i]);                                  \
        for (_i = (pos) + (len); _i < cs->len; _i++)                        \
                printf("%c", cs->str[_i]);                                  \
        printf("\n");                                                       \
} while (0)

#define CSTRING_DBG_EXPECTED_ERASE_LEN(cs, len)                             \
    CSTRING_DBG_LOG("CSTRING_DBG_EXPECTED_ERASE_LEN: %ld\n", cs->len - len)
        CSTRING_DBG_LOG("CSTRING_DBG_EXPECTED_ERASE_LEN: %ld\n", cs->len - len)
#endif /* CSTRING_DBG */

void
cstring_erase(cstring *cs, size_t pos, size_t len)
{
    if (!cstring_empty(cs)
    && (!CSTRING_OUT_OF_BOUNDS(cs, pos)
    ||  !CSTRING_OUT_OF_BOUNDS(cs, len)))
    {
        size_t newlen;
        char *tmp;

        if (!cstring_empty(cs)
        && (!CSTRING_OUT_OF_BOUNDS(cs, pos)
        ||  !CSTRING_OUT_OF_BOUNDS(cs, len))) {
#ifdef CSTRING_DBG
        CSTRING_DBG_LOG("STR: %s | INDEX: %ld | LEN: %ld\n", cs->str, pos, len);
        CSTRING_DBG_EXPECTED_ERASE_STR(cs, pos, len);
        CSTRING_DBG_EXPECTED_ERASE_LEN(cs, len);
                CSTRING_DBG_LOG("STR: %s | INDEX: %ld | LEN: %ld\n",
                                cs->str, pos, len);
                CSTRING_DBG_EXPECTED_ERASE_STR(cs, pos, len);
                CSTRING_DBG_EXPECTED_ERASE_LEN(cs, len);
#endif /* CSTRING_DBG */
        size_t newlen = cs->len - len;
        char *tmp;
        CSTRING_MALLOC(tmp, newlen + 1);
        memcpy(tmp, cs->str, pos);
        memcpy(tmp + pos, cs->str + pos + len, cs->len - pos - len);
        CSTRING_FREE(cs); /* Useless check but keep it for consistency */
        cs->len = newlen;
        cs->str = tmp;
        cs->str[cs->len] = '\0';
                newlen = cs->len - len;
                CSTRING_MALLOC(tmp, newlen + 1);
                memcpy(tmp, cs->str, pos);
                memcpy(tmp + pos, cs->str + pos + len, cs->len - pos - len);
                CSTRING_FREE(cs); /* Useless check but keep it for consistency */
                cs->len = newlen;
                cs->str = tmp;
                cs->str[cs->len] = '\0';
#ifdef CSTRING_DBG
        CSTRING_DBG_LOG_CSTR_INFO(cs);
                CSTRING_DBG_LOG_CSTR_INFO(cs);
#endif /* CSTRING_DBG */
    }
        }
}

void
cstring_erase_matching(cstring *cs, const char *s)
{
    if (s != NULL)
        cstring_erase(cs, cstring_find(cs, s), strlen(s));
        if (s != NULL)
                cstring_erase(cs, cstring_find(cs, s), strlen(s));
}

void
cstring_erase_all_matching(cstring *cs, const char *s)
{
    if (s != NULL) {
        size_t len = strlen(s);
        size_t i = cstring_find(cs, s);
        for (; i != CSTRING_NPOS; i = cstring_find(cs, s))
            cstring_erase(cs, i, len);
    }
        size_t len, i;

        if (s != NULL) {
                len = strlen(s);
                i = cstring_find(cs, s);
                for (; i != CSTRING_NPOS; i = cstring_find(cs, s))
                        cstring_erase(cs, i, len);
        }
}

void
cstring_trim(cstring *cs, const char *s)
{
    size_t i = cstring_find_first_of(cs, s);
    for (; i != CSTRING_NPOS; i = cstring_find_first_of(cs, s))
        cstring_erase(cs, i, 1);
        size_t i;
        
        i = cstring_find_first_of(cs, s);
        for (; i != CSTRING_NPOS; i = cstring_find_first_of(cs, s))
                cstring_erase(cs, i, 1);
}

#ifdef CSTRING_DBG


@@ 200,59 210,58 @@ cstring_trim(cstring *cs, const char *s)
void
cstring_push_back(cstring *cs, char c)
{
    if (CSTRING_EXCEEDS_CAPACITY(cs->len, cs->capacity))
        cstring_resize(cs, cs->len << 1);
    cs->str[cs->len] = c;
    cs->str[++cs->len] = '\0';
        if (CSTRING_EXCEEDS_CAPACITY(cs->len, cs->capacity))
                cstring_resize(cs, cs->len << 1);
        cs->str[cs->len] = c;
        cs->str[++cs->len] = '\0';
#ifdef CSTRING_DBG
    CSTRING_DBG_LOG_CSTR_INFO(cs);
        CSTRING_DBG_LOG_CSTR_INFO(cs);
#endif /* CSTRING_DBG */
}

void
cstring_pop_back(cstring *cs)
{
    if (cs->len > 0)
        cs->str[--cs->len] = '\0';
        if (cs->len > 0)
                cs->str[--cs->len] = '\0';
}

void
cstring_replace_char(cstring *cs, size_t i, char c)
{
    if (!CSTRING_OUT_OF_BOUNDS(cs, i))
        cs->str[i] = c;
        if (!CSTRING_OUT_OF_BOUNDS(cs, i))
                cs->str[i] = c;
}

void
cstring_replace_str(cstring *cs, const char *s, size_t pos, size_t olen)
{
    if (!CSTRING_OUT_OF_BOUNDS(cs, pos)
    &&  !CSTRING_OUT_OF_BOUNDS(cs, olen))
    {
        cstring_erase(cs, pos, olen);
        cstring_insert(cs, s, pos);
    }
        if (!CSTRING_OUT_OF_BOUNDS(cs, pos)
        &&  !CSTRING_OUT_OF_BOUNDS(cs, olen)) {
                cstring_erase(cs, pos, olen);
                cstring_insert(cs, s, pos);
        }
}

cstring
cstring_substr(const cstring *cs, size_t pos, size_t len)
{
    if (CSTRING_OUT_OF_BOUNDS(cs, pos)
    ||  CSTRING_OUT_OF_BOUNDS(cs, len))
        return cstring_create("");
    cstring substr = cstring_create(&cs->str[pos]);
    substr.len = len;
    substr.str[len] = '\0';
    cstring_shrink_to_fit(&substr);
    return substr;
        if (CSTRING_OUT_OF_BOUNDS(cs, pos)
        ||  CSTRING_OUT_OF_BOUNDS(cs, len))
                return cstring_create("");
        cstring substr = cstring_create(&cs->str[pos]);
        substr.len = len;
        substr.str[len] = '\0';
        cstring_shrink_to_fit(&substr);
        return substr;
}

void
cstring_swap(cstring *lhs, cstring *rhs)
{
    cstring tmp = *lhs;
    *lhs = *rhs;
    *rhs = tmp;
        cstring tmp = *lhs;
        *lhs = *rhs;
        *rhs = tmp;
}

void


@@ 262,16 271,16 @@ cstring_sort_partial(cstring *cs,
                     enum cstring_sort_flags flags,
                     cstring_sort_callback callback)
{
    /* maybe chanage out of bounds macro */
    if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_REST) ||  pos + len > len)
        len -= pos;

    if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_ASCENDING))
        qsort(cs + pos, len, sizeof(cstring), cstring_cmp_greater);
    else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_DESCENDING))
        qsort(cs + pos, len, sizeof(cstring), cstring_cmp_less);
    else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_CALLBACK))
        qsort(cs + pos, len, sizeof(cstring), callback);
        /* maybe chanage out of bounds macro */
        if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_REST) ||  pos + len > len)
                len -= pos;

        if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_ASCENDING))
                qsort(cs + pos, len, sizeof(cstring), cstring_cmp_greater);
        else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_DESCENDING))
                qsort(cs + pos, len, sizeof(cstring), cstring_cmp_less);
        else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_CALLBACK))
                qsort(cs + pos, len, sizeof(cstring), callback);
}

void


@@ 281,104 290,109 @@ cstring_sort_chars_partial(cstring *cs,
                           enum cstring_sort_flags flags,
                           cstring_sort_callback callback)
{
    if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_REST)
    ||  CSTRING_OUT_OF_BOUNDS(cs, pos + len))
        len = cs->len - pos;

    if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_ASCENDING))
        qsort(cs->str + pos, len, sizeof(char), cstring_cmp_char_greater);
    else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_DESCENDING))
        qsort(cs->str + pos, len, sizeof(char), cstring_cmp_char_less);
    else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_CALLBACK))
        qsort(cs->str + pos, len, sizeof(char), callback);
        if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_REST)
        ||  CSTRING_OUT_OF_BOUNDS(cs, pos + len))
                len = cs->len - pos;

        if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_ASCENDING))
                qsort(cs->str + pos, len, sizeof(char), cstring_cmp_char_greater);
        else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_DESCENDING))
                qsort(cs->str + pos, len, sizeof(char), cstring_cmp_char_less);
        else if (CSTRING_FLAG_CHECK(flags, CSTRING_SORT_CALLBACK))
                qsort(cs->str + pos, len, sizeof(char), callback);
}

void
cstring_clear(cstring *cs)
{
    CSTRING_FREE(cs);
    CSTRING_MALLOC(cs->str, 1);
    cs->str[0] = '\0';
    cs->len = 0;
    cs->capacity = 0;
        CSTRING_FREE(cs);
        CSTRING_MALLOC(cs->str, 1);
        cs->str[0] = '\0';
        cs->len = 0;
        cs->capacity = 0;
}

#define CSTRING_CHECK(cs, s)        \
    if (cstring_empty(cs) || !*(s)) \
        return CSTRING_NPOS
#define CSTRING_CHECK(cs, s)            \
        if (cstring_empty(cs) || !*(s)) \
                return CSTRING_NPOS

size_t
cstring_find(const cstring *cs, const char *s)
{
    CSTRING_CHECK(cs, s);
    CSTRING_FIND_OCCURENCE(cs, s, strstr);
    return CSTRING_NPOS;
        CSTRING_CHECK(cs, s);
        CSTRING_FIND_OCCURENCE(cs, s, strstr);
        return CSTRING_NPOS;
}

/*SIMPLIFY */
size_t
cstring_rfind(const cstring *cs, const char *s)
{
    CSTRING_CHECK(cs, s);
    /*SIMPLIFY */
    int found;
    size_t idx = -1;
    size_t slen = strlen(s);
    size_t  i, j;
    for (i = 0; i <= cs->len - slen; i++) {
        found = 1;
        for (j = 0; j < slen; j++) {
            if (cs->str[i + j] != s[j]) {
                found = 0;
                break;
            }
        size_t idx, slen, i, j;
        int found;

        CSTRING_CHECK(cs, s);
        idx = -1;
        slen = strlen(s);
        for (i = 0; i <= cs->len - slen; i++) {
                found = 1;
                for (j = 0; j < slen; j++) {
                        if (cs->str[i + j] != s[j]) {
                                found = 0;
                                break;
                        }
                }
                if (found)
                        idx = i;
        }
        if (found)
            idx = i;
    }
    return (idx == -1 ? CSTRING_NPOS : idx);
        return (idx == -1 ? CSTRING_NPOS : idx);
}

size_t
cstring_find_first_of(const cstring *cs, const char *s)
{
    CSTRING_CHECK(cs, s);
    for (; *s; s++) {
        CSTRING_FIND_OCCURENCE(cs, *s, strchr);
    }
    return CSTRING_NPOS;
        CSTRING_CHECK(cs, s);
        for (; *s; s++) {
                CSTRING_FIND_OCCURENCE(cs, *s, strchr);
        }
        return CSTRING_NPOS;
}

size_t
cstring_find_first_not_of(const cstring *cs, const char *s)
{
    CSTRING_CHECK(cs, s);
    size_t i = 0;
    for (; i < cs->len; i++)
        if (!cstring_is_one_of(cs->str[i], s))
            return i;
    return CSTRING_NPOS;
        size_t i = 0;

        CSTRING_CHECK(cs, s);
        for (; i < cs->len; i++)
                if (!cstring_is_one_of(cs->str[i], s))
                        return i;
        return CSTRING_NPOS;
}

size_t
cstring_find_last_of(const cstring *cs, const char *s)
{
    CSTRING_CHECK(cs, s);
    size_t i = *(s + strlen(s));
    for (; i >= 0; i--) {
        CSTRING_FIND_OCCURENCE(cs, s[i], strrchr);
    }
    return CSTRING_NPOS;
        size_t i;

        CSTRING_CHECK(cs, s);
        i = *(s + strlen(s));
        for (; i >= 0; i--) {
                CSTRING_FIND_OCCURENCE(cs, s[i], strrchr);
        }
        return CSTRING_NPOS;
}

size_t
cstring_find_last_not_of(const cstring *cs, const char *s)
{
    CSTRING_CHECK(cs, s);
    size_t i = cs->len;
    for (; i >= 0; i--)
        if (!cstring_is_one_of(cs->str[i], s))
            return i;
    return CSTRING_NPOS;
        size_t i = cs->len;

        CSTRING_CHECK(cs, s);
        for (; i >= 0; i--)
                if (!cstring_is_one_of(cs->str[i], s))
                        return i;
        return CSTRING_NPOS;
}

#undef CSTR_CHECK


@@ 386,43 400,47 @@ cstring_find_last_not_of(const cstring *cs, const char *s)
char *
cstring_copy(const char *s)
{
    size_t len = strlen(s);
    char *tmp;
    CSTRING_MALLOC(tmp, len + 1);
    memcpy(tmp, s, len + 1);
    tmp[len] = '\0'; /* Add \0 in case s didn't have it */
    return tmp;
        size_t len;
        char *tmp;

        len = strlen(s);
        CSTRING_MALLOC(tmp, len + 1);
        memcpy(tmp, s, len + 1);
        tmp[len] = '\0'; /* Add \0 in case s didn't have it */
        return tmp;
}

void
cstring_resize(cstring *cs, size_t newcapacity)
{
        char *tmp;

#ifdef CSTRING_DBG
    CSTRING_DBG_LOG("Old capacity: %ld | New capacity: %ld\n",
                    cs->capacity, newcapacity);
        CSTRING_DBG_LOG("Old capacity: %ld | New capacity: %ld\n",
                        cs->capacity, newcapacity);
#endif /* CSTRING_DBG */
    char *tmp;
    CSTRING_MALLOC(tmp, newcapacity + 1); /* no +1? */
    memcpy(tmp, cs->str, cs->len + 1);    /* copy \0 too */
    CSTRING_FREE(cs);
    cs->str = tmp;
    cs->str[cs->len] = '\0';
    cs->capacity = newcapacity;
        CSTRING_MALLOC(tmp, newcapacity + 1); /* no +1? */
        memcpy(tmp, cs->str, cs->len + 1);    /* copy \0 too */
        CSTRING_FREE(cs);
        cs->str = tmp;
        cs->str[cs->len] = '\0';
        cs->capacity = newcapacity;
#ifdef CSTRING_DBG
    CSTRING_DBG_LOG_CSTR_INFO(cs);
        CSTRING_DBG_LOG_CSTR_INFO(cs);
#endif /* CSTRING_DBG */
}

cstring *
cstring_getline(FILE *fd, cstring *cs, char delim)
{
    char c;
    cstring_clear(cs);
    while ((c = fgetc(fd)) != EOF && c != '\n') {
        if (c == delim)
            break;
        else
            cstring_push_back(cs, c);
    }
    return (c == EOF) ? NULL : cs;
        char c;

        cstring_clear(cs);
        while ((c = fgetc(fd)) != EOF && c != '\n') {
                if (c == delim)
                        break;
                else
                        cstring_push_back(cs, c);
        }
        return (c == EOF) ? NULL : cs;
}

M cstring.h => cstring.h +42 -40
@@ 1,3 1,5 @@
/* See LICENSE file for copyright and license details. */

#ifndef CSTRING_H
#define CSTRING_H



@@ 15,40 17,40 @@ extern "C" {
#define CSTRING_ARR_LEN(arr)           ((size_t)sizeof((arr)) / sizeof((arr)[0]))
#define CSTRING_FLAG_CHECK(flag, bit)  (((flag) & (int)(bit)) == (int)(bit))

#define CSTRING_MALLOC(ptr, size) do {                               \
    ptr = (char *)malloc((size));                                    \
    if (ptr == NULL)                                                 \
        fputs("CSTRING_MALLOC(): cannot allocate memory\n", stderr); \
#define CSTRING_MALLOC(ptr, size) do {                                       \
        ptr = (char *)malloc((size));                                        \
        if (ptr == NULL)                                                     \
                fputs("CSTRING_MALLOC(): cannot allocate memory\n", stderr); \
} while (0)

#ifdef CSTRING_DBG
#define CSTRING_DBG_LOG(fmt, ...)                                    \
    fprintf(stderr, "DEBUG: %s:%d:%s(): " fmt,                       \
            __FILE__, __LINE__, __func__, __VA_ARGS__)
#define CSTRING_DBG_LOG(fmt, ...)                                            \
        fprintf(stderr, "DEBUG: %s:%d:%s(): " fmt,                           \
                __FILE__, __LINE__, __func__, __VA_ARGS__)

#define CSTRING_DBG_LOG_CSTR_INFO(cs)                                \
    CSTRING_DBG_LOG("STR: %s | LEN: %ld | CAP: %ld\n",               \
            cs->str, cs->len, cs->capacity)
#define CSTRING_DBG_LOG_CSTR_INFO(cs)                                        \
        CSTRING_DBG_LOG("STR: %s | LEN: %ld | CAP: %ld\n",                   \
                cs->str, cs->len, cs->capacity)

#define CSTRING_DBG_LOG_CSTR_INFO_NPTR(cs)                           \
    CSTRING_DBG_LOG("STR: %s | LEN: %ld | CAP: %ld\n",               \
            cs.str, cs.len, cs.capacity)
#define CSTRING_DBG_LOG_CSTR_INFO_NPTR(cs)                                   \
        CSTRING_DBG_LOG("STR: %s | LEN: %ld | CAP: %ld\n",                   \
                cs.str, cs.len, cs.capacity)

#define CSTRING_DBG_LOG_STR_INFO(s, len)                             \
    CSTRING_DBG_LOG("S: %s | LEN: %ld\n", (s), (len))
#define CSTRING_DBG_LOG_STR_INFO(s, len)                                     \
        CSTRING_DBG_LOG("S: %s | LEN: %ld\n", (s), (len))
#endif /* CSTRING_DBG */

struct cstring {
    char     *str;
    size_t    len;
    size_t    capacity;
        char     *str;
        size_t    len;
        size_t    capacity;
};

enum cstring_sort_flags {
    CSTRING_SORT_ASCENDING  = 1 << 0,
    CSTRING_SORT_DESCENDING = 1 << 1,
    CSTRING_SORT_CALLBACK   = 1 << 2,
    CSTRING_SORT_REST       = 1 << 3
        CSTRING_SORT_ASCENDING  = 1 << 0,
        CSTRING_SORT_DESCENDING = 1 << 1,
        CSTRING_SORT_CALLBACK   = 1 << 2,
        CSTRING_SORT_REST       = 1 << 3
};

typedef struct cstring cstring;


@@ 111,13 113,13 @@ static inline int     cstring_less_or_equal(const cstring *, const cstring *);
static inline void
cstring_prepend(cstring *cs, const char *s)
{
    cstring_insert(cs, s, 0);
        cstring_insert(cs, s, 0);
}

static inline void
cstring_append(cstring *cs, const char *s)
{
    cstring_insert(cs, s, cs->len);
        cstring_insert(cs, s, cs->len);
}

static inline void


@@ 126,7 128,7 @@ cstring_sort(cstring *cs,
             enum cstring_sort_flags flags,
             cstring_sort_callback callback)
{
    cstring_sort_partial(cs, 0, len, flags, callback);
        cstring_sort_partial(cs, 0, len, flags, callback);
}

static inline void


@@ 134,91 136,91 @@ cstring_sort_chars(cstring *cs,
                   enum cstring_sort_flags flags,
                   cstring_sort_callback callback)
{
    cstring_sort_chars_partial(cs, 0, cs->len, flags, callback);
        cstring_sort_chars_partial(cs, 0, cs->len, flags, callback);
}

static inline void
cstring_shrink_to_fit(cstring *cs)
{
    cstring_resize(cs, cs->len);
        cstring_resize(cs, cs->len);
}

static inline int
cstring_empty(const cstring *cs)
{
    return (cs->str == NULL && cs->len == 0);
        return (cs->str == NULL && cs->len == 0);
}

static inline char
cstring_front(const cstring *cs)
{
    return cs->str[0];
        return cs->str[0];
}

static inline char
cstring_back(const cstring *cs)
{
    return (!cstring_empty(cs) ? cs->str[cs->len - 1] : cs->str[0]);
        return (!cstring_empty(cs) ? cs->str[cs->len - 1] : cs->str[0]);
}

static inline int
cstring_starts_with_str(const cstring *cs, const char *s)
{
    return (cstring_find(cs, s) == 0);
        return (cstring_find(cs, s) == 0);
}

static inline int
cstring_ends_with_str(const cstring *cs, const char *s)
{
    return (cstring_find(cs, s) == cs->len - strlen(s));
        return (cstring_find(cs, s) == cs->len - strlen(s));
}

static inline int
cstring_starts_with_char(const cstring *cs, char c)
{
    return (cs->str[0] == c);
        return (cs->str[0] == c);
}

static inline int
cstring_ends_with_char(const cstring *cs, char c)
{
    return (cs->str[cs->len] = c);
        return (cs->str[cs->len] = c);
}

static inline void *
cstring_data(const cstring *cs)
{
    return (void *)cs->str;
        return (void *)cs->str;
}

static inline int
cstring_equal(const cstring *lhs, const cstring *rhs)
{
    return (strcmp(lhs->str, rhs->str) == 0);
        return (strcmp(lhs->str, rhs->str) == 0);
}

static inline int
cstring_greater(const cstring *lhs, const cstring *rhs)
{
    return (strcmp(lhs->str, rhs->str) > 0);
        return (strcmp(lhs->str, rhs->str) > 0);
}

static inline int
cstring_greater_or_equal(const cstring *lhs, const cstring *rhs)
{
    return (strcmp(lhs->str, rhs->str) >= 0);
        return (strcmp(lhs->str, rhs->str) >= 0);
}

static inline int
cstring_less(const cstring *lhs, const cstring *rhs)
{
    return (strcmp(lhs->str, rhs->str) < 0);
        return (strcmp(lhs->str, rhs->str) < 0);
}

static inline int
cstring_less_or_equal(const cstring *lhs, const cstring *rhs)
{
    return (strcmp(lhs->str, rhs->str) <= 0);
        return (strcmp(lhs->str, rhs->str) <= 0);
}

#ifdef __cplusplus

M tests/Makefile => tests/Makefile +20 -20
@@ 6,33 6,33 @@ LDFLAGS = -Llib -lcstring
all: options ${BINS}

options:
	@echo "build options:"
	@echo "CFLAGS   = ${CFLAGS}"
	@echo "LDFLAGS  = ${LDFLAGS}"
	@echo "CC       = ${CC}"
		@echo "build options:"
		@echo "CFLAGS   = ${CFLAGS}"
		@echo "LDFLAGS  = ${LDFLAGS}"
		@echo "CC       = ${CC}"

run:
	@echo "---------------------------"
	@echo "---------------------------"
	@echo "RUNNING: test_basic"
	@echo "---------------------------"
	@echo "---------------------------"
	./test_basic
	
	@echo "---------------------------"
	@echo "---------------------------"
	@echo "RUNNING: test_insert"
	@echo "---------------------------"
	@echo "---------------------------"
	./test_insert
		@echo "---------------------------"
		@echo "---------------------------"
		@echo "RUNNING: test_basic"
		@echo "---------------------------"
		@echo "---------------------------"
		./test_basic
		
		@echo "---------------------------"
		@echo "---------------------------"
		@echo "RUNNING: test_insert"
		@echo "---------------------------"
		@echo "---------------------------"
		./test_insert

clean:
	rm -f ${BINS} *.o
		rm -f ${BINS} *.o

test_basic: test_basic.c
	${CC} test_basic.c ${CFLAGS} -o test_basic ${LDFLAGS}
		${CC} test_basic.c ${CFLAGS} -o test_basic ${LDFLAGS}

test_insert: test_insert.c
	${CC} test_insert.c ${CFLAGS} -o test_insert ${LDFLAGS}
		${CC} test_insert.c ${CFLAGS} -o test_insert ${LDFLAGS}

.PHONY: all options run clean

M tests/test_basic.c => tests/test_basic.c +39 -39
@@ 1,61 1,61 @@
#include "cstring.h"
#include <cstring.h>

// Compilation: gcc test_basic.c -lcstring

int
main(int argc, char **argv)
{
    cstring s = cstring_create("Hello world");
    printf("cstring_create: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring s = cstring_create("Hello world");
        printf("cstring_create: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_sort_chars(&s, CSTRING_SORT_ASCENDING, NULL);
    printf("cstring_sort_chars: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_sort_chars(&s, CSTRING_SORT_ASCENDING, NULL);
        printf("cstring_sort_chars: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_assign(&s, "New string");
    printf("cstring_assign: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_assign(&s, "New string");
        printf("cstring_assign: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_append(&s, "Appended text");
    printf("cstring_append: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_append(&s, "Appended text");
        printf("cstring_append: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_prepend(&s, "OK");
    printf("cstring_prepend: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_prepend(&s, "OK");
        printf("cstring_prepend: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_push_back(&s, 'c');
    printf("cstring_push_back: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_push_back(&s, 'c');
        printf("cstring_push_back: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_insert(&s, "Inserted text", 4);
    printf("cstring_insert: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
    
    cstring_pop_back(&s);
    printf("cstring_pop_back: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_insert(&s, "Inserted text", 4);
        printf("cstring_insert: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        
        cstring_pop_back(&s);
        printf("cstring_pop_back: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_clear(&s);
    printf("cstring_clear: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_clear(&s);
        printf("cstring_clear: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_assign(&s, "CSTRING");
    printf("cstring_assign: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
    printf("cstring_front: %c\n", cstring_front(&s));
    printf("cstring_back: %c\n", cstring_back(&s));
        cstring_assign(&s, "CSTRING");
        printf("cstring_assign: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        printf("cstring_front: %c\n", cstring_front(&s));
        printf("cstring_back: %c\n", cstring_back(&s));

    cstring_replace_char(&s, 3, 'x');
    printf("cstring_replace_char: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_replace_char(&s, 3, 'x');
        printf("cstring_replace_char: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_replace_str(&s, "hell", 0, strlen("hell"));
    printf("cstring_replace_str: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_replace_str(&s, "hell", 0, strlen("hell"));
        printf("cstring_replace_str: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_erase(&s, 1, 4);
    printf("cstring_erase: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_erase(&s, 1, 4);
        printf("cstring_erase: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_assign(&s, "hello aaaa hello abbb helo hello");
    cstring_erase_all_matching(&s, "hello");
    printf("cstring_erase_all_matching: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_assign(&s, "hello aaaa hello abbb helo hello");
        cstring_erase_all_matching(&s, "hello");
        printf("cstring_erase_all_matching: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_trim(&s, " ");
    printf("cstring_trim: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);
        cstring_trim(&s, " ");
        printf("cstring_trim: %s (Len: %ld, Capacity: %ld)\n", s.str, s.len, s.capacity);

    cstring_delete(&s);
    if (cstring_empty(&s))
        puts("cstring_delete: Deleted string.");
        cstring_delete(&s);
        if (cstring_empty(&s))
                puts("cstring_delete: Deleted string.");

    return 0;
        return 0;
}

M tests/test_insert.c => tests/test_insert.c +6 -5
@@ 1,11 1,12 @@
#include <stdio.h>
#include <cstring.h>

// Compilation: gcc test_insert.c -lcstring

int
main(int argc, char **argv)
{
    cstring s = cstring_create("HHHHHHEEEEEEEEEEEEEEEEEEEEEYYYYYYYYYYYYYY");
    printf("%s\n", s.str);
    cstring_delete(&s);
    return 0;
        cstring s = cstring_create("HHHHHHEEEEEEEEEEEEEEEEEEEEEYYYYYYYYYYYYYY");
        printf("%s\n", s.str);
        cstring_delete(&s);
        return 0;
}