~crm/cstring

fb54a79b85158d77c21d18220c33226aaa78f48d — Christos Margiolis 3 years ago b54a148
changes in README and minor stylistic changes
11 files changed, 515 insertions(+), 532 deletions(-)

M LICENSE
M Makefile
A README
D README.md
M config.mk
M cstring.3
M cstring.c
M cstring.h
M tests/Makefile
M tests/test_basic.c
M tests/test_insert.c
M LICENSE => LICENSE +16 -25
@@ 1,29 1,20 @@
BSD 3-Clause License
MIT License

Copyright (c) 2020-present, Christos Margiolis.
All rights reserved.
(c) 2020-Present Christos Margiolis <christos@christosmarg.xyz>

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so, 
subject to the following conditions:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of images.weserv.nl nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

M Makefile => Makefile +7 -8
@@ 8,24 8,23 @@ LIB = cstring
DIST = ${LIB}-${VERSION}
MAN3 = ${LIB}.3

EXT = c
SRC = cstring.c
OBJ = ${SRC:.${EXT}=.o}
OBJ = ${SRC:.c=.o}

all: options ${LIB}

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

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

.${EXT}.o:
.c.o:
	${CC} -c ${CFLAGS} $<

dist: clean

A README => README +134 -0
@@ 0,0 1,134 @@
cstring
=======
A simple and lightweight string library for C inspired by C++'s STL 
`std::string` (don't laugh) class, but with a few additions/improvements.

Building
--------
`cstring` is a static library. The header file is installed in
`/usr/local/include` and the library file in `/usr/local/lib`: 

	# make install clean

You can also run a few tests after you've installed `cstring`:

	$ cd cstring/tests
	$ make && make run clean

In order to make a distribution do:

	$ make dist

If you want to uninstall the library do:

	# sudo make uninstall

A file using `cstring` has to be linked using the `-lcstring` linker flag.  
In case you want to run your project in debug mode, compile the library using
the `-DCSTRING_DBG` option.

Usage
-----
When using this library, you must always call the `cstring_create` and
`cstring_delete` 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)

Read the `man` page for more detailed info.

Functions
---------
- `cstring_create`: Initiliaze string.
- `cstring_delete`: Deallocate string.
- `cstring_assign`: Assign new string.
- `cstring_insert`: Insert at a specific index.
- `cstring_append`: Append to end of string.
- `cstring_prepend`: Prepend to beginning of string.
- `cstring_erase`: Erase portion of string.
- `cstring_erase_matching`: Erase first match.
- `cstring_erase_all_matching`: Erase all matches.
- `cstring_trim`: Trim character.
- `cstring_push_back`: Add character to end of string.
- `cstring_pop_back`: Remove the last character from string.
- `cstring_replace_char`: Replace character at a specific index.
- `cstring_replace_str`: Replace portion of string.
- `cstring_substr`: Extract a substring.
- `cstring_swap`: Swap contents of two strings.
- `cstring_sort`: Sort an array of `cstring` objects.
- `cstring_sort_partial`: Like `cstring_sort` but for part of the array.
- `cstring_sort_chars`: Sort `cstring`'s contents.
- `cstring_sort_chars_partial`: Like `cstring_sort_chars` but for part of the string.
- `cstring_shrink_to_fit`: Shrink string's capacity to fit its size.
- `cstring_clear`: Clear contents of string
- `cstring_find`: Find first occurence of a pattern in string.
- `cstring_rfind`: Fnid last occurence of a pattern in string.
- `cstring_find_first_of`: Find first occurence of specified characters in string.
- `cstring_find_first_not_of`: Find the first character that does not match any of the specified characters.
- `cstring_find_last_of`: Find last occurence of specified characters in string.
- `cstring_find_last_not_of`: Find the last character that does not match any of the specified characters.
- `cstring_front`: Get first character in string.
- `cstring_back`: Get last character in string.
- `cstring_empty`: Check if string is empty.
- `cstring_starts_with_str`: Check if string starts with specified `char *`.
- `cstring_ends_with_str`: Check if string ends with specified `char *`.
- `cstring_starts_with_char`: Check if string starts with specified `char`.
- `cstring_ends_with_char`: Check if string ends with specified `char`.
- `cstring_data`: Get string's content in raw bytes.
- `cstring_copy`: Copy contents of a `char *`.
- `cstring_resize`: Resize string.
- `cstring_getline`: Read a line from a `FILE` stream.
- `cstring_equal`: Check if `lhs == rhs`.
- `cstring_greater`: Check if `lhs > rhs`.
- `cstring_greater_or_equal`: Check if `lhs >= rhs`.
- `cstring_less`: Check if `lhs < rhs`.
- `cstring_less_or_equal`: Check if `lhs <= rhs`.

`lhs`: Left hand side  
`rhs`: Right hand side

Macros
------
- `CSTRING_OUT_OF_BOUNDS`: Check if value is out of bounds.
- `CSTRING_ARR_LEN`: Determine an array's length. The macro must be called in the same function the array is declared.
- `CSTRING_MALLOC`: Allocate memory with error checking.  

The following macros can only be used in debug mode:

- `CSTIRNG_DBG_LOG`: Prints a message in the format of `DEBUG: file:line:func(): msg`.
- `CSTRING_DBG_LOG_CSTR_INFO`: Prints all the contents of a `cstring` struct. The argument *has* to be a pointer.
- `CSTRING_DBG_LOG_CSTR_INFO_NPTR`: Like `CSTRING_DBG_LOG_CSTR_INFO` but the argument has to be a non-pointer.
- `CSTRING_DBG_LOG_STR_INFO`: Print contents of a normal string.

Constants
---------
- `CSTRING_NPOS`: Signifies that a pattern hasn't been found inside the string. Its value is -1.
- `CSTRING_INIT_EMPTY`: Used with `cstring_create` in case the string is to be initiliazed as empty.

Flags
-----
- `CSTRING_SORT_ASCENDING`: Sort in ascending order.
- `CSTRING_SORT_DESCENDING`: Sort in descending order.
- `CSTRING_SORT_CALLBACK`: Sort using a callback function.
- `CSTRING_SORT_REST`: Sort the rest of the array.

Example
-------
See the test programs in `tests` for more.

	#include <cstring.h>

	/* outputs "Foobar" to the screen */
	int
	main(int argc, char **argv)
	{
	    cstring s = cstring_create("Foo");
	    cstring_append(&s, "bar");
	    printf("%s\n", s.str);
	    cstring_delete(&s);

	    return 0;
	}

D README.md => README.md +0 -139
@@ 1,139 0,0 @@
# cstring

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

## Building

`cstring` is a static library. The header file is installed in `/usr/local/include` and
the library file in `/usr/local/lib`. 

In order to install it do the following
```shell
$ cd /path/to/cstring
$ sudo make install clean
```

You can also run a few tests
```shell
$ cd /path/to/cstring/tests
$ make && make run clean
```

In order to make a distribution do
```shell
$ cd /path/to/cstring
$ make dist
```

If you want to uninstall the library do the following
```shell
$ cd /path/to/cstring
$ sudo make uninstall
```

A file using `cstring` has to be linked using the `-lcstring` linker flag.  
In case you want to run your project in debug mode, compile the library using the `-DCSTRING_DBG` option.

## Usage

When using this library, you must **always** call the `cstring_create` and `cstring_delete` 
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)`.  

Read the `man` page for more detailed info.

## Functions

* `cstring_create`: Initiliaze string.
* `cstring_delete`: Deallocate string.
* `cstring_assign`: Assign new string.
* `cstring_insert`: Insert at a specific index.
* `cstring_append`: Append to end of string.
* `cstring_prepend`: Prepend to beginning of string.
* `cstring_erase`: Erase portion of string.
* `cstring_erase_matching`: Erase first match.
* `cstring_erase_all_matching`: Erase all matches.
* `cstring_trim`: Trim character.
* `cstring_push_back`: Add character to end of string.
* `cstring_pop_back`: Remove the last character from string.
* `cstring_replace_char`: Replace character at a specific index.
* `cstring_replace_str`: Replace portion of string.
* `cstring_substr`: Extract a substring.
* `cstring_swap`: Swap contents of two strings.
* `cstring_sort`: Sort an array of `cstring` objects.
* `cstring_sort_partial`: Like `cstring_sort` but for part of the array.
* `cstring_sort_chars`: Sort `cstring`'s contents.
* `cstring_sort_chars_partial`: Like `cstring_sort_chars` but for part of the string.
* `cstring_shrink_to_fit`: Shrink string's capacity to fit its size.
* `cstring_clear`: Clear contents of string
* `cstring_find`: Find first occurence of a pattern in string.
* `cstring_rfind`: Fnid last occurence of a pattern in string.
* `cstring_find_first_of`: Find first occurence of specified characters in string.
* `cstring_find_first_not_of`: Find the first character that does not match any of the specified characters.
* `cstring_find_last_of`: Find last occurence of specified characters in string.
* `cstring_find_last_not_of`: Find the last character that does not match any of the specified characters.
* `cstring_front`: Get first character in string.
* `cstring_back`: Get last character in string.
* `cstring_empty`: Check if string is empty.
* `cstring_starts_with_str`: Check if string starts with specified `char *`.
* `cstring_ends_with_str`: Check if string ends with specified `char *`.
* `cstring_starts_with_char`: Check if string starts with specified `char`.
* `cstring_ends_with_char`: Check if string ends with specified `char`.
* `cstring_data`: Get string's content in raw bytes.
* `cstring_copy`: Copy contents of a `char *`.
* `cstring_resize`: Resize string.
* `cstring_getline`: Read a line from a `FILE` stream.
* `cstring_equal`: Check if `lhs == rhs`.
* `cstring_greater`: Check if `lhs > rhs`.
* `cstring_greater_or_equal`: Check if `lhs >= rhs`.
* `cstring_less`: Check if `lhs < rhs`.
* `cstring_less_or_equal`: Check if `lhs <= rhs`.

`lhs`: Left hand side  
`rhs`: Right hand side

## Macros

* `CSTRING_OUT_OF_BOUNDS`: Check if value is out of bounds.
* `CSTRING_ARR_LEN`: Determine an array's length. The macro must be called in the same function the array is declared.
* `CSTRING_MALLOC`: Allocate memory with error checking.  
The following macros can only be used in debug mode
* `CSTIRNG_DBG_LOG`: Prints a message in the format of `DEBUG: file:line:func(): msg`.
* `CSTRING_DBG_LOG_CSTR_INFO`: Prints all the contents of a `cstring` struct. The argument *has* to be a pointer.
* `CSTRING_DBG_LOG_CSTR_INFO_NPTR`: Like `CSTRING_DBG_LOG_CSTR_INFO` but the argument has to be a non-pointer.
* `CSTRING_DBG_LOG_STR_INFO`: Print contents of a normal string.

## Constants

* `CSTRING_NPOS`: Signifies that a pattern hasn't been found inside the string. Its value is -1.
* `CSTRING_INIT_EMPTY`: Used with `cstring_create` in case the string is to be initiliazed as empty.

## Flags

* `CSTRING_SORT_ASCENDING`: Sort in ascending order.
* `CSTRING_SORT_DESCENDING`: Sort in descending order.
* `CSTRING_SORT_CALLBACK`: Sort using a callback function.
* `CSTRING_SORT_REST`: Sort the rest of the array.

## Example

See the test programs in `tests` for more.

```c
#include <cstring.h>

/* outputs "Foobar" to the screen */
int
main(int argc, char **argv)
{
    cstring s = cstring_create("Foo");
    cstring_append(&s, "bar");
    printf("%s\n", s.str);
    cstring_delete(&s);

    return 0;
}
```

M config.mk => config.mk +4 -5
@@ 4,7 4,7 @@ VERSION = 0.1

# paths
PREFIX = /usr/local
MAN_DIR = ${PREFIX}/man/man3
MAN_DIR = ${PREFIX}/share/man/man3
INC_DIR = ${PREFIX}/include
LIB_DIR = ${PREFIX}/lib



@@ 14,9 14,8 @@ LIBS = -Llib

# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L \
	   -DVERSION=\"${VERSION}\"
CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations \
	 -O3 ${INCS} ${CPPFLAGS}
	   -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\"
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = ${LIBS}
ARFLAGS = rs



@@ 31,5 30,5 @@ TAR = tar -cf
GZIP = gzip

# compiler
CC = gcc
CC = cc
AR = ar

M cstring.3 => cstring.3 +7 -7
@@ 26,17 26,17 @@ option.
.Sh STRUCTURES
.Bl -tag -width Ds
typedef struct _cstring {
        size_t  len;            /* string length */
        size_t  capacity;       /* string capacity */
        char   *str;            /* contents of string */
	size_t	len;		/* string length */
	size_t	capacity;	/* string capacity */
	char   *str;		/* contents of string */

CSTRING_SORT_ASCENDING  0x01    /* sort in ascending order */
CSTRING_SORT_ASCENDING	0x01	/* sort in ascending order */
.br
CSTRING_SORT_DESCENDING 0x02    /* sort in descending order */
CSTRING_SORT_DESCENDING 0x02	/* sort in descending order */
.br
CSTRING_SORT_CALLBACK   0x04    /* sort using a callback function */
CSTRING_SORT_CALLBACK	0x04	/* sort using a callback function */
.br
CSTRING_SORT_REST       0x10    /* sort the rest of the array */
CSTRING_SORT_REST	0x10	/* sort the rest of the array */
.br
} cstring;
.Sh TYPEDEFS

M cstring.c => cstring.c +232 -232
@@ 1,24 1,24 @@
/* See LICENSE file for copyright and license details. */
#include "cstring.h"

#define CSTRING_EXCEEDS_CAPACITY(len, cap)  ((len) >= (cap))
#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 */



@@ 32,173 32,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)
{
        size_t slen, newlen;
        char *tmp;

        if (!CSTRING_OUT_OF_BOUNDS(cs->len, 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);
	size_t slen, newlen;
	char *tmp;

	if (!CSTRING_OUT_OF_BOUNDS(cs->len, 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");                                                       \
// FIXME
#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");							    \
} while (0)

#define CSTRING_DBG_EXPECTED_ERASE_LEN(cs, len)                             \
        CSTRING_DBG_LOG("CSTRING_DBG_EXPECTED_ERASE_LEN: %ld\n", cs->len - len)
#define CSTRING_DBG_EXPECTED_ERASE_LEN(cs, 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)
{
        size_t newlen;
        char *tmp;
	size_t newlen;
	char *tmp;

        if (!cstring_empty(cs)
        && (!CSTRING_OUT_OF_BOUNDS(cs->len, pos)
        ||  !CSTRING_OUT_OF_BOUNDS(cs->len, len))) {
	if (!cstring_empty(cs)
	&& (!CSTRING_OUT_OF_BOUNDS(cs->len, pos)
	||  !CSTRING_OUT_OF_BOUNDS(cs->len, 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 */
                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';
		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)
{
        size_t len, i;
	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);
        }
	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;
        
        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


@@ 209,181 209,181 @@ 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->len, i))
                cs->str[i] = c;
	if (!CSTRING_OUT_OF_BOUNDS(cs->len, 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->len, pos)
        &&  !CSTRING_OUT_OF_BOUNDS(cs->len, olen)) {
                cstring_erase(cs, pos, olen);
                cstring_insert(cs, s, pos);
        }
	if (!CSTRING_OUT_OF_BOUNDS(cs->len, pos)
	&&  !CSTRING_OUT_OF_BOUNDS(cs->len, 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->len, pos)
        ||  CSTRING_OUT_OF_BOUNDS(cs->len, 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->len, pos)
	||  CSTRING_OUT_OF_BOUNDS(cs->len, 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
cstring_sort_partial(cstring *cs, size_t pos, size_t len, int flags,
                     cstring_sort_callback cb)
		     cstring_sort_callback cb)
{
        if (flags & CSTRING_SORT_REST ||  CSTRING_OUT_OF_BOUNDS(len, pos + len))
                len -= pos;
	if (flags & CSTRING_SORT_REST ||  CSTRING_OUT_OF_BOUNDS(len, pos + len))
		len -= pos;

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

void
cstring_sort_chars_partial(cstring *cs, size_t pos, size_t len, int flags,
                           cstring_sort_callback cb)
			   cstring_sort_callback cb)
{
        if (flags & CSTRING_SORT_REST ||  CSTRING_OUT_OF_BOUNDS(cs->len, pos + len))
                len = cs->len - pos;
	if (flags & CSTRING_SORT_REST ||  CSTRING_OUT_OF_BOUNDS(cs->len, pos + len))
		len = cs->len - pos;

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

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)
{
        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;
        }
        return (idx == -1 ? CSTRING_NPOS : idx);
	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;
	}
	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)
{
        size_t i = 0;
	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;
	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)
{
        size_t i;
	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;
	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)
{
        size_t i = cs->len;
	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;
	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


@@ 391,62 391,62 @@ cstring_find_last_not_of(const cstring *cs, const char *s)
int
cstring_ends_with_str(const cstring *cs, const char *s)
{
        /* avoid cstring_substr */
        cstring sub;
        size_t slen;
        int found;
	/* avoid cstring_substr */
	cstring sub;
	size_t slen;
	int found;

        slen = strlen(s);
        sub = cstring_substr(cs, cs->len - slen, slen);
        found = !strcmp(sub.str, s);
        cstring_delete(&sub);
        return found;
	slen = strlen(s);
	sub = cstring_substr(cs, cs->len - slen, slen);
	found = !strcmp(sub.str, s);
	cstring_delete(&sub);
	return found;
}

char *
cstring_copy(const char *s)
{
        size_t len;
        char *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;
	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;
	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 */
        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;
	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;
	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 +70 -71
@@ 13,73 13,73 @@ extern "C" {
#define CSTRING_NPOS -1
#define CSTRING_INIT_EMPTY ""
#define CSTRING_OUT_OF_BOUNDS(len, pos) ((pos) > (len))
#define CSTRING_ARR_LEN(arr)           ((size_t)sizeof((arr)) / sizeof((arr)[0]))
#define CSTRING_ARR_LEN(arr) ((size_t)sizeof((arr)) / sizeof((arr)[0]))

#define CSTRING_MALLOC(ptr, size) do {                                       \
        ptr = malloc((size));                                                \
        if (ptr == NULL)                                                     \
                fputs("CSTRING_MALLOC(): cannot allocate memory\n", stderr); \
#define CSTRING_MALLOC(ptr, size) do {					     \
	ptr = 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 */

typedef struct _cstring {
        size_t    len;                  /* strlen of str */
        size_t    capacity;             /* total capacity of the array */
        char     *str;                  /* the string */

#define CSTRING_SORT_ASCENDING  0x01    /* sort in ascending order */
#define CSTRING_SORT_DESCENDING 0x02    /* sort in descending order */
#define CSTRING_SORT_CALLBACK   0x04    /* sort using a callback function */
#define CSTRING_SORT_REST       0x10    /* sort the rest of the array */
	size_t len;			/* strlen of str */
	size_t capacity;		/* total capacity of the array */
	char *str;			/* the string */

#define CSTRING_SORT_ASCENDING 0x01	/* sort in ascending order */
#define CSTRING_SORT_DESCENDING 0x02	/* sort in descending order */
#define CSTRING_SORT_CALLBACK 0x04	/* sort using a callback function */
#define CSTRING_SORT_REST 0x10		/* sort the rest of the array */
} cstring;

typedef int (*cstring_sort_callback)(const void *, const void *);

extern cstring    cstring_create(const char *);
extern void       cstring_delete(cstring *);
extern void       cstring_assign(cstring *, const char *);
extern void       cstring_insert(cstring *, const char *, size_t);
extern void       cstring_erase(cstring *, size_t, size_t);
extern void       cstring_erase_matching(cstring *, const char *);
extern void       cstring_erase_all_matching(cstring *, const char *);
extern void       cstring_trim(cstring *, const char *);
extern void       cstring_push_back(cstring *, char);
extern void       cstring_pop_back(cstring *);
extern void       cstring_replace_char(cstring *, size_t, char);
extern void       cstring_replace_str(cstring *, const char *, size_t, size_t);
extern cstring    cstring_substr(const cstring *, size_t, size_t);
extern void       cstring_swap(cstring *, cstring *);
extern void       cstring_sort_partial(cstring *, size_t, size_t, int,
                                       cstring_sort_callback);
extern void       cstring_sort_chars_partial(cstring *cs, size_t, size_t, int,
                                             cstring_sort_callback);
extern void       cstring_clear(cstring *);
extern size_t     cstring_find(const cstring *, const char *);
extern size_t     cstring_rfind(const cstring *, const char *);
extern size_t     cstring_find_first_of(const cstring *, const char *);
extern size_t     cstring_find_first_not_of(const cstring *,const  char *);
extern size_t     cstring_find_last_of(const cstring *, const char *);
extern size_t     cstring_find_last_not_of(const cstring *, const char *);
extern int        cstring_ends_with_str(const cstring *, const char *);
extern char      *cstring_copy(const char *);
extern void       cstring_resize(cstring *, size_t);
extern cstring   *cstring_getline(FILE *, cstring *, char);
extern cstring	  cstring_create(const char *);
extern void	  cstring_delete(cstring *);
extern void	  cstring_assign(cstring *, const char *);
extern void	  cstring_insert(cstring *, const char *, size_t);
extern void	  cstring_erase(cstring *, size_t, size_t);
extern void	  cstring_erase_matching(cstring *, const char *);
extern void	  cstring_erase_all_matching(cstring *, const char *);
extern void	  cstring_trim(cstring *, const char *);
extern void	  cstring_push_back(cstring *, char);
extern void	  cstring_pop_back(cstring *);
extern void	  cstring_replace_char(cstring *, size_t, char);
extern void	  cstring_replace_str(cstring *, const char *, size_t, size_t);
extern cstring	  cstring_substr(const cstring *, size_t, size_t);
extern void	  cstring_swap(cstring *, cstring *);
extern void	  cstring_sort_partial(cstring *, size_t, size_t, int,
				       cstring_sort_callback);
extern void	  cstring_sort_chars_partial(cstring *cs, size_t, size_t, int,
					     cstring_sort_callback);
extern void	  cstring_clear(cstring *);
extern size_t	  cstring_find(const cstring *, const char *);
extern size_t	  cstring_rfind(const cstring *, const char *);
extern size_t	  cstring_find_first_of(const cstring *, const char *);
extern size_t	  cstring_find_first_not_of(const cstring *,const  char *);
extern size_t	  cstring_find_last_of(const cstring *, const char *);
extern size_t	  cstring_find_last_not_of(const cstring *, const char *);
extern int	  cstring_ends_with_str(const cstring *, const char *);
extern char	 *cstring_copy(const char *);
extern void	  cstring_resize(cstring *, size_t);
extern cstring	 *cstring_getline(FILE *, cstring *, char);

/* static inlines */
static inline void    cstring_prepend(cstring *, const char *);


@@ 103,103 103,102 @@ 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
cstring_sort(cstring *cs, size_t len, int flags, cstring_sort_callback cb)
{
        cstring_sort_partial(cs, 0, len, flags, cb);
	cstring_sort_partial(cs, 0, len, flags, cb);
}

static inline void
cstring_sort_chars(cstring *cs, int flags, cstring_sort_callback cb)
{
        cstring_sort_chars_partial(cs, 0, cs->len, flags, cb);
	cstring_sort_chars_partial(cs, 0, cs->len, flags, cb);
}

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_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 +3 -3
@@ 11,9 11,9 @@ all: options ${BINS}

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

run:
	for bin in ${BINS}; do \

M tests/test_basic.c => tests/test_basic.c +38 -38
@@ 5,57 5,57 @@
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 +4 -4
@@ 5,8 5,8 @@
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;
}