# NAME

libslack(str) - string module

# SYNOPSIS

        #include <slack/std.h>
        #include <slack/str.h>

        typedef struct String String;
        typedef struct StringTR StringTR;

        enum StringAlignment
        {
            ALIGN_LEFT       = '<',
            ALIGN_RIGHT      = '>',
            ALIGN_CENTRE     = '|',
            ALIGN_CENTER     = '|',
            ALIGN_FULL       = '='
        };

        enum StringTROption
        {
            TR_COMPLEMENT = 1,
            TR_DELETE     = 2,
            TR_SQUASH     = 4
        };

        typedef enum StringAlignment StringAlignment;
        typedef enum StringTROption StringTROption;

        String *str_create(const char *format, ...);
        String *str_create_with_locker(Locker *locker, const char *format, ...);
        String *str_vcreate(const char *format, va_list args);
        String *str_vcreate_with_locker(Locker *locker, const char *format, va_list args);
        String *str_create_sized(size_t size, const char *format, ...);
        String *str_create_with_locker_sized(Locker *locker, size_t size, const char *format, ...);
        String *str_vcreate_sized(size_t size, const char *format, va_list args);
        String *str_vcreate_with_locker_sized(Locker *locker, size_t size, const char *format, va_list args);
        String *str_copy(const String *str);
        String *str_copy_unlocked(const String *str);
        String *str_copy_with_locker(Locker *locker, const String *str);
        String *str_copy_with_locker_unlocked(Locker *locker, const String *str);
        String *str_fgetline(FILE *stream);
        String *str_fgetline_with_locker(Locker *locker, FILE *stream);
        void str_release(String *str);
        void *str_destroy(String **str);
        int str_rdlock(const String *str);
        int str_wrlock(const String *str);
        int str_unlock(const String *str);
        int str_empty(const String *str);
        int str_empty_unlocked(const String *str);
        ssize_t str_length(const String *str);
        ssize_t str_length_unlocked(const String *str);
        char *cstr(const String *str);
        ssize_t str_set_length(String *str, size_t length);
        ssize_t str_set_length_unlocked(String *str, size_t length);
        ssize_t str_recalc_length(String *str);
        ssize_t str_recalc_length_unlocked(String *str);
        String *str_clear(String *str);
        String *str_clear_unlocked(String *str);
        String *str_remove(String *str, ssize_t index);
        String *str_remove_unlocked(String *str, ssize_t index);
        String *str_remove_range(String *str, ssize_t index, ssize_t range);
        String *str_remove_range_unlocked(String *str, ssize_t index, ssize_t range);
        String *str_insert(String *str, ssize_t index, const char *format, ...);
        String *str_insert_unlocked(String *str, ssize_t index, const char *format, ...);
        String *str_vinsert(String *str, ssize_t index, const char *format, va_list args);
        String *str_vinsert_unlocked(String *str, ssize_t index, const char *format, va_list args);
        String *str_insert_str(String *str, ssize_t index, const String *src);
        String *str_insert_str_unlocked(String *str, ssize_t index, const String *src);
        String *str_append(String *str, const char *format, ...);
        String *str_append_unlocked(String *str, const char *format, ...);
        String *str_vappend(String *str, const char *format, va_list args);
        String *str_vappend_unlocked(String *str, const char *format, va_list args);
        String *str_append_str(String *str, const String *src);
        String *str_append_str_unlocked(String *str, const String *src);
        String *str_prepend(String *str, const char *format, ...);
        String *str_prepend_unlocked(String *str, const char *format, ...);
        String *str_vprepend(String *str, const char *format, va_list args);
        String *str_vprepend_unlocked(String *str, const char *format, va_list args);
        String *str_prepend_str(String *str, const String *src);
        String *str_prepend_str_unlocked(String *str, const String *src);
        String *str_replace(String *str, ssize_t index, ssize_t range, const char *format, ...);
        String *str_replace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, ...);
        String *str_vreplace(String *str, ssize_t index, ssize_t range, const char *format, va_list args);
        String *str_vreplace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, va_list args);
        String *str_replace_str(String *str, ssize_t index, ssize_t range, const String *src);
        String *str_replace_str_unlocked(String *str, ssize_t index, ssize_t range, const String *src);
        String *str_substr(const String *str, ssize_t index, ssize_t range);
        String *str_substr_unlocked(const String *str, ssize_t index, ssize_t range);
        String *str_substr_with_locker(Locker *locker, const String *str, ssize_t index, ssize_t range);
        String *str_substr_with_locker_unlocked(Locker *locker, const String *str, ssize_t index, ssize_t range);
        String *substr(const char *str, ssize_t index, ssize_t range);
        String *substr_with_locker(Locker *locker, const char *str, ssize_t index, ssize_t range);
        String *str_splice(String *str, ssize_t index, ssize_t range);
        String *str_splice_unlocked(String *str, ssize_t index, ssize_t range);
        String *str_splice_with_locker(Locker *locker, String *str, ssize_t index, ssize_t range);
        String *str_splice_with_locker_unlocked(Locker *locker, String *str, ssize_t index, ssize_t range);
        String *str_repeat(size_t count, const char *format, ...);
        String *str_repeat_with_locker(Locker *locker, size_t count, const char *format, ...);
        String *str_vrepeat(size_t count, const char *format, va_list args);
        String *str_vrepeat_with_locker(Locker *locker, size_t count, const char *format, va_list args);
        int str_tr(String *str, const char *from, const char *to, int option);
        int str_tr_unlocked(String *str, const char *from, const char *to, int option);
        int str_tr_str(String *str, const String *from, const String *to, int option);
        int str_tr_str_unlocked(String *str, const String *from, const String *to, int option);
        int tr(char *str, const char *from, const char *to, int option);
        StringTR *tr_compile(const char *from, const char *to, int option);
        StringTR *tr_compile_with_locker(Locker *locker, const char *from, const char *to, int option);
        StringTR *str_tr_compile(const String *from, const String *to, int option);
        StringTR *str_tr_compile_unlocked(const String *from, const String *to, int option);
        StringTR *str_tr_compile_with_locker(Locker *locker, const String *from, const String *to, int option);
        StringTR *str_tr_compile_with_locker_unlocked(Locker *locker, const String *from, const String *to, int option);
        void tr_release(StringTR *table);
        void *tr_destroy(StringTR **table);
        int str_tr_compiled(String *str, StringTR *table);
        int str_tr_compiled_unlocked(String *str, StringTR *table);
        int tr_compiled(char *str, StringTR *table);
        List *str_regexpr(const char *pattern, const String *text, int cflags, int eflags);
        List *str_regexpr_unlocked(const char *pattern, const String *text, int cflags, int eflags);
        List *str_regexpr_with_locker(Locker *locker, const char *pattern, const String *text, int cflags, int eflags);
        List *str_regexpr_with_locker_unlocked(Locker *locker, const char *pattern, const String *text, int cflags, int eflags);
        List *regexpr(const char *pattern, const char *text, int cflags, int eflags);
        List *regexpr_with_locker(Locker *locker, const char *pattern, const char *text, int cflags, int eflags);
        int regexpr_compile(regex_t *compiled, const char *pattern, int cflags);
        void regexpr_release(regex_t *compiled);
        List *str_regexpr_compiled(const regex_t *compiled, const String *text, int eflags);
        List *str_regexpr_compiled_unlocked(const regex_t *compiled, const String *text, int eflags);
        List *str_regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const String *text, int eflags);
        List *str_regexpr_compiled_with_locker_unlocked(Locker *locker, const regex_t *compiled, const String *text, int eflags);
        List *regexpr_compiled(const regex_t *compiled, const char *text, int eflags);
        List *regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const char *text, int eflags);
        String *str_regsub(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all);
        String *str_regsub_unlocked(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all);
        String *str_regsub_compiled(const regex_t *compiled, const char *replacement, String *text, int eflags, int all);
        String *str_regsub_compiled_unlocked(const regex_t *compiled, const char *replacement, String *text, int eflags, int all);
        List *str_fmt(const String *str, size_t line_width, StringAlignment alignment);
        List *str_fmt_unlocked(const String *str, size_t line_width, StringAlignment alignment);
        List *str_fmt_with_locker(Locker *locker, const String *str, size_t line_width, StringAlignment alignment);
        List *str_fmt_with_locker_unlocked(Locker *locker, const String *str, size_t line_width, StringAlignment alignment);
        List *fmt(const char *str, size_t line_width, StringAlignment alignment);
        List *fmt_with_locker(Locker *locker, const char *str, size_t line_width, StringAlignment alignment);
        List *str_split(const String *str, const char *delim);
        List *str_split_unlocked(const String *str, const char *delim);
        List *str_split_with_locker(Locker *locker, const String *str, const char *delim);
        List *str_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim);
        List *split(const char *str, const char *delim);
        List *split_with_locker(Locker *locker, const char *str, const char *delim);
        List *str_regexpr_split(const String *str, const char *delim, int cflags, int eflags);
        List *str_regexpr_split_unlocked(const String *str, const char *delim, int cflags, int eflags);
        List *str_regexpr_split_with_locker(Locker *locker, const String *str, const char *delim, int cflags, int eflags);
        List *str_regexpr_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim, int cflags, int eflags);
        List *regexpr_split(const char *str, const char *delim, int cflags, int eflags);
        List *regexpr_split_with_locker(Locker *locker, const char *str, const char *delim, int cflags, int eflags);
        String *str_join(const List *list, const char *delim);
        String *str_join_unlocked(const List *list, const char *delim);
        String *str_join_with_locker(Locker *locker, const List *list, const char *delim);
        String *str_join_with_locker_unlocked(Locker *locker, const List *list, const char *delim);
        String *join(const List *list, const char *delim);
        String *join_with_locker(Locker *locker, const List *list, const char *delim);
        int str_soundex(const String *str);
        int str_soundex_unlocked(const String *str);
        int soundex(const char *str);
        String *str_trim(String *str);
        String *str_trim_unlocked(String *str);
        char *trim(char *str);
        String *str_trim_left(String *str);
        String *str_trim_left_unlocked(String *str);
        char *trim_left(char *str);
        String *str_trim_right(String *str);
        String *str_trim_right_unlocked(String *str);
        char *trim_right(char *str);
        String *str_squeeze(String *str);
        String *str_squeeze_unlocked(String *str);
        char *squeeze(char *str);
        String *str_quote(const String *str, const char *quotable, char quote_char);
        String *str_quote_unlocked(const String *str, const char *quotable, char quote_char);
        String *str_quote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char);
        String *str_quote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char);
        String *quote(const char *str, const char *quotable, char quote_char);
        String *quote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char);
        String *str_unquote(const String *str, const char *quotable, char quote_char);
        String *str_unquote_unlocked(const String *str, const char *quotable, char quote_char);
        String *str_unquote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char);
        String *str_unquote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char);
        String *unquote(const char *str, const char *quotable, char quote_char);
        String *unquote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char);
        String *str_encode(const String *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *str_encode_unlocked(const String *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *str_encode_with_locker(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *str_encode_with_locker_unlocked(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *str_decode(const String *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *str_decode_unlocked(const String *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *str_decode_with_locker(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *str_decode_with_locker_unlocked(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *encode(const char *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *encode_with_locker(Locker *locker, const char *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *decode(const char *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *decode_with_locker(Locker *locker, const char *str, const char *uncoded, const char *coded, char quote_char, int printable);
        String *str_lc(String *str);
        String *str_lc_unlocked(String *str);
        char *lc(char *str);
        String *str_lcfirst(String *str);
        String *str_lcfirst_unlocked(String *str);
        char *lcfirst(char *str);
        String *str_uc(String *str);
        String *str_uc_unlocked(String *str);
        char *uc(char *str);
        String *str_ucfirst(String *str);
        String *str_ucfirst_unlocked(String *str);
        char *ucfirst(char *str);
        int str_chop(String *str);
        int str_chop_unlocked(String *str);
        int chop(char *str);
        int str_chomp(String *str);
        int str_chomp_unlocked(String *str);
        int chomp(char *str);
        int str_bin(const String *str);
        int str_bin_unlocked(const String *str);
        int bin(const char *str);
        int str_hex(const String *str);
        int str_hex_unlocked(const String *str);
        int hex(const char *str);
        int str_oct(const String *str);
        int str_oct_unlocked(const String *str);
        int oct(const char *str);
        int strcasecmp(const char *s1, const char *s2);
        int strncasecmp(const char *s1, const char *s2, size_t n);
        size_t strlcpy(char *dst, const char *src, size_t size);
        size_t strlcat(char *dst, const char *src, size_t size);
        char *cstrcpy(char *dst, const char *src);
        char *cstrcat(char *dst, const char *src);
        char *cstrchr(const char *str, int c);
        char *cstrpbrk(const char *str, const char *brk);
        char *cstrrchr(const char *str, int c);
        char *cstrstr(const char *str, const char *srch);
        int asprintf(char **str, const char *format, ...);
        int vasprintf(char **str, const char *format, va_list args);

# DESCRIPTION

This module provides text strings that grow and shrink automatically, and functions for manipulating them. Some of the functions were modelled on the list(3) module. Others were modelled on the string functions and operators in perlfunc(1) and perlop(1). Others came from OpenBSD.

`String *str_create(const char *format, ...)`

  Creates a String specified by `format` and the following arguments as in sprintf(3). On success, returns the new string. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). It is strongly recommended to use str_destroy(3), because it also sets the pointer variable to `null`. On error, returns `null` with `errno` set appropriately.

  Warning: Do not under any circumstances ever pass a non-literal string as the format argument unless you know exactly how many conversions will take place. Being careless with this is a very good way to build potential security vulnerabilities into your programs. The same is true for all functions that take a printf()-like format string as an argument.

          String *str = str_create(buf);       // EVIL
          String *str = str_create("%s", buf); // GOOD

`String *str_create_with_locker(Locker *locker, const char *format, ...)`

  Equivalent to str_create(3) except that multiple threads accessing the new string will be synchronized by `locker`.

`String *str_vcreate(const char *format, va_list args)`

  Equivalent to str_create(3) with the variable argument list specified directly as for vprintf(3).

`String *str_vcreate_with_locker(Locker *locker, const char *format, va_list args)`

  Equivalent to str_vcreate(3) except that multiple threads accessing the new string will be synchronized by `locker`.

`String *str_create_sized(size_t size, const char *format, ...)`

  Creates a String specified by `format` and the following arguments as in sprintf(3). The initial allocation for the string data is at least `size` bytes. On success, returns the new string. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). It is strongly recommended to use str_destroy(3), because it also sets the pointer variable to `null`. On error, returns `null` with `errno` set appropriately.

`String *str_create_with_locker_sized(Locker *locker, size_t size, const char *format, ...)`

  Equivalent to str_create_sized(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_vcreate_sized(size_t size, const char *format, va_list args)`

  Equivalent to str_create_sized(3) with the variable argument list specified directly as for vprintf(3).

`String *str_vcreate_with_locker_sized(Locker *locker, size_t size, const char *format, va_list args)`

  Equivalent to str_vcreate_sized(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_copy(const String *str)`

  Creates a copy of `str`. On success, returns the copy. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). It is strongly recommended to use str_destroy(3), because it also sets the pointer variable to `null`. On error, returns `null` with `errno` set appropriately.

`String *str_copy_unlocked(const String *str)`

  Equivalent to str_copy(3) except that `str` is not read-locked.

`String *str_copy_with_locker(Locker *locker, const String *str)`

  Equivalent to str_copy(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_copy_with_locker_unlocked(Locker *locker, const String *str)`

  Equivalent to str_copy_with_locker(3) except that `str` is not read-locked.

`String *str_fgetline(FILE *stream)`

  Similar to fgets(3) except that it recognises UNIX (`"\n"`), DOS/Windows (`"\r\n"`) and old Macintosh (`"\r"`) line endings (even different line endings in the same file), and it can read a line of any size into the String that it returns. Reading stops after the `EOF`, or after the end of the line is reached. Line endings are always stored as a single `"\n"` character. A `nul` is placed after the last character in the buffer. On success, returns a new String. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). It is strongly recommended to use str_destroy(3), because it also sets the pointer variable to `null`. On error, or when the end of file occurs while no characters have been read, returns `null`. Calls to this function can be mixed with calls to other input functions from the stdio library on the same input stream.

`String *str_fgetline_with_locker(Locker *locker, FILE *stream)`

  Equivalent to str_fgetline(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`void str_release(String *str)`

  Releases (deallocates) `str`.

`void *str_destroy(String **str)`

  Destroys (deallocates and sets to `null`) `*str`. Returns `null`. Note: strings shared by multiple threads must not be destroyed until after all threads have finished with it.

`int str_rdlock(const String *str)`

  Claims a read lock on `str` (if `str` was created with a Locker). Clients must call this before calling cstr(3) (for the purpose of reading the raw string data) on a string that was created with a Locker. It is the client's responsibility to call str_unlock(3) when finished with the raw string data. It is also needed when multiple read-only str(3) module functions need to be called atomically. It is the caller's responsibility to call str_unlock(3) after the atomic operation. The only functions that may be called on `str` between calls to str_rdlock(3) and str_unlock(3) are cstr(3) and any read-only str(3) module functions whose name ends with `_unlocked`. On success, returns `0`. On error, returns an error code.

`int str_wrlock(const String *str)`

  Claims a write lock on `str` (if `str` was created with a Locker). Clients need to call this before calling cstr(3) (for the purpose of modifying the raw string data) on a string that was created with a Locker. It is the client's responsibility to call str_unlock(3) when finished with the raw string data. It is also needed when multiple read/write str(3) module functions need to be called atomically. It is the caller's responsibility to call str_unlock(3) after the atomic operation. The only functions that may be called on `str` between calls to str_wrlock(3) and str_unlock(3) are cstr(3) and any str(3) module functions whose name ends with `_unlocked`. On success, returns `0`. On error, returns an error code.

`int str_unlock(const String *str)`

  Unlocks a read lock or a write lock on `str` obtained with str_rdlock(3) or str_wrlock(3) (if `str` was created with a Locker). On success, returns `0`. On error, returns an error code.

`int str_empty(const String *str)`

  Returns whether or not `str` is the empty string. On error, returns `-1` with `errno` set appropriately.

`int str_empty_unlocked(const String *str)`

  Equivalent to str_empty(3) except that `str` is not read-locked.

`ssize_t str_length(const String *str)`

  Returns the length of `str`. On error, returns `-1` with `errno` set appropriately.

`ssize_t str_length_unlocked(const String *str)`

  Equivalent to str_length(3) except that `str` is not read-locked.

`char *cstr(const String *str)`

  Returns the raw C string in `str`. Do not use this pointer to extend the length of the string. It's OK to use it to reduce the length of the string, provided that you call str_set_length_unlocked(3) or str_recalc_length_unlocked(3) immediately afterwards. When used on a string that is shared by multiple threads, cstr(3) must appear between calls to str_rdlock(3) or str_wrlock(3) and str_unlock(3).

`ssize_t str_set_length(String *str, size_t length)`

  Sets the length of `str` to `length`. Only needed after the raw C string returned by cstr(3) has been used to shorten a string. On success, returns the length of `str`. On error, returns `-1` with `errno` set appropriately.

`ssize_t str_set_length_unlocked(String *str, size_t length)`

  Equivalent to str_set_length(3) except that `str` is not write-locked.

`ssize_t str_recalc_length(String *str)`

  Calculates and stores the length of `str`. Only needed after the raw C string returned by cstr(3) has been used to shorten a string. Note: Treats `str` as a `nul`-terminated string and should be avoided. Use str_set_length(3) instead. On success, returns the length of `str`. On error, returns `-1` with `errno` set appropriately.

`ssize_t str_recalc_length_unlocked(String *str)`

  Equivalent to str_recalc_length(3) except that `str` is not write-locked.

`String *str_clear(String *str)`

  Makes `str` the empty string. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_clear_unlocked(String *str)`

  Equivalent to str_clear(3) except that `str` is not write-locked.

`String *str_remove(String *str, ssize_t index)`

  Removes the `index`'th character from `str`. If `index` is negative, it refers to a character position relative to the end of the string (`-1` is the position after the last character, `-2` is the position of the last character, and so on). On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_remove_unlocked(String *str, ssize_t index)`

  Equivalent to str_remove(3) except that `str` is not write-locked.

`String *str_remove_range(String *str, ssize_t index, ssize_t range)`

  Removes `range` characters from `str` starting at `index`. If `index` or `range` are negative, they refer to character positions relative to the end of the string (`-1` is the position after the last character, `-2` is the position of the last character, and so on). On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_remove_range_unlocked(String *str, ssize_t index, ssize_t range)`

  Equivalent to str_remove_range(3) except that `str` is not write-locked.

`String *str_insert(String *str, ssize_t index, const char *format, ...)`

  Adds the string specified by `format` to `str` at position `index`. If `index` is negative, it refers to a character position relative to the end of the string (`-1` is the position after the last character, `-2` is the position of the last character, and so on). On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_insert_unlocked(String *str, ssize_t index, const char *format, ...)`

  Equivalent to str_insert(3) except that `str` is not write-locked.

`String *str_vinsert(String *str, ssize_t index, const char *format, va_list args)`

  Equivalent to str_insert(3) with the variable argument list specified directly as for vprintf(3).

`String *str_vinsert_unlocked(String *str, ssize_t index, const char *format, va_list args)`

  Equivalent to str_vinsert(3) except that `str` is not write-locked.

`String *str_insert_str(String *str, ssize_t index, const String *src)`

  Inserts `src` into `str`, starting at position `index`. If `index` is negative, it refers to a character position relative to the end of the string (`-1` is the position after the last character, `-2` is the position of the last character, and so on). On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_insert_str_unlocked(String *str, ssize_t index, const String *src)`

  Equivalent to str_insert_str(3) except that `str` is not write-locked and `src` is not read-locked. Note: If `src` needs to be read-locked, it is the caller's responsibility to lock and unlock it explicitly with str_rdlock(3) and str_unlock(3).

`String *str_append(String *str, const char *format, ...)`

  Appends the string specified by `format` to `str`. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_append_unlocked(String *str, const char *format, ...)`

  Equivalent to str_append(3) except that `str` is not write-locked.

`String *str_vappend(String *str, const char *format, va_list args)`

  Equivalent to str_append(3) with the variable argument list specified directly as for vprintf(3).

`String *str_vappend_unlocked(String *str, const char *format, va_list args)`

  Equivalent to str_vappend(3) except that `str` is not write-locked.

`String *str_append_str(String *str, const String *src)`

  Appends `src` to `str`. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_append_str_unlocked(String *str, const String *src)`

  Equivalent to str_append_str(3) except that `str` is not write-locked and `src` is not read-locked. Note: If `src` needs to be read-locked, it is the caller's responsibility to lock and unlock it explicitly with str_rdlock(3) and str_unlock(3).

`String *str_prepend(String *str, const char *format, ...)`

  Prepends the string specified by `format` to `str`. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_prepend_unlocked(String *str, const char *format, ...)`

  Equivalent to str_prepend(3) except that `str` is not write-locked.

`String *str_vprepend(String *str, const char *format, va_list args)`

  Equivalent to str_prepend(3) with the variable argument list specified directly as for vprintf(3).

`String *str_vprepend_unlocked(String *str, const char *format, va_list args)`

  Equivalent to str_vprepend(3) except that `str` is not write-locked.

`String *str_prepend_str(String *str, const String *src)`

  Prepends `src` to `str`. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_prepend_str_unlocked(String *str, const String *src)`

  Equivalent to str_prepend_str(3) except that `str` is not write-locked and `src` is not read-locked. Note: If `src` needs to be read-locked, it is the caller's responsibility to lock and unlock it explicitly with str_rdlock(3) and str_unlock(3).

`String *str_replace(String *str, ssize_t index, ssize_t range, const char *format, ...)`

  Replaces `range` characters in `str`, starting at `index`, with the string specified by `format`. If `index` or `range` are negative, they refer to character positions relative to the end of the string (`-1` is the position after the last character, `-2` is the position of the last character, and so on). On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_replace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, ...)`

  Equivalent to str_replace(3) except that `str` is not write-locked.

`String *str_vreplace(String *str, ssize_t index, ssize_t range, const char *format, va_list args)`

  Equivalent to str_replace(3) with the variable argument list specified directly as for vprintf(3).

`String *str_vreplace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, va_list args)`

  Equivalent to str_vreplace(3) except that `str` is not write-locked.

`String *str_replace_str(String *str, ssize_t index, ssize_t range, const String *src)`

  Replaces `range` characters in `str`, starting at `index`, with `src`. If `index` or `range` are negative, they refer to character positions relative to the end of the string (`-1` is the position after the last character, `-2` is the position of the last character, and so on). On success, return `str`. On error, returns `null` with `errno` set appropriately.

`String *str_replace_str_unlocked(String *str, ssize_t index, ssize_t range, const String *src)`

  Equivalent to str_replace_str(3) except that `str` is not write-locked and `src` is not read-locked. Note: If `src` needs to be read-locked, it is the caller's responsibility to lock and unlock it explicitly with str_rdlock(3) and str_unlock(3).

`String *str_substr(const String *str, ssize_t index, ssize_t range)`

  Creates a new String object consisting of `range` characters from `str`, starting at `index`. If `index` or `range` are negative, they refer to character positions relative to the end of the string (`-1` is the position after the last character, `-2` is the position of the last character, and so on). On success, returns the new string. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). On error, returns `null` with `errno` set appropriately.

`String *str_substr_unlocked(const String *str, ssize_t index, ssize_t range)`

  Equivalent to str_substr(3) except that `str` is not read-locked.

`String *str_substr_with_locker(Locker *locker, const String *str, ssize_t index, ssize_t range)`

  Equivalent to str_substr(3) except that multiple threads accessing the new substring will be synchronised by `locker`.

`String *str_substr_with_locker_unlocked(Locker *locker, const String *str, ssize_t index, ssize_t range)`

  Equivalent to str_substr_with_locker(3) except that `str` is not read-locked.

`String *substr(const char *str, ssize_t index, ssize_t range)`

  Equivalent to str_substr(3) but works on an ordinary C string.

`String *substr_with_locker(Locker *locker, const char *str, ssize_t index, ssize_t range)`

  Equivalent to substr(3) except that multiple threads accessing the new substring will be synchronised by `locker`. Note that no locking is performed on `str` as it is a raw C string.

`String *str_splice(String *str, ssize_t index, ssize_t range)`

  Removes a substring from `str` starting at `index` of length `range` characters. If `index` or `range` are negative, they refer to character positions relative to the end of the string (`-1` is the position after the last character, `-2` is the position of the last character, and so on). On success, returns the substring. It is the caller's responsibility to deallocate the new substring with str_release(3) or str_destroy(3). It is strongly recommended to use str_destroy(3), because it also sets the pointer variable to `null`. On error, returns `null` with `errno` set appropriately.

`String *str_splice_unlocked(String *str, ssize_t index, ssize_t range)`

  Equivalent to str_splice(3) except that `str` is not write-locked.

`String *str_splice_with_locker(Locker *locker, String *str, ssize_t index, ssize_t range)`

  Equivalent to str_splice(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_splice_with_locker_unlocked(Locker *locker, String *str, ssize_t index, ssize_t range)`

  Equivalent to str_splice_with_locker(3) except that `str` is not write-locked.

`String *str_repeat(size_t count, const char *format, ...)`

  Creates a new String containing the string determined by `format` repeated `count` times. On success, return the new string. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). It is strongly recommended to use str_destroy(3), because it also sets the pointer variable to `null`. On error, returns `null` with `errno` set appropriately.

`String *str_repeat_with_locker(Locker *locker, size_t count, const char *format, ...)`

  Equivalent to str_repeat(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_vrepeat(size_t count, const char *format, va_list args)`

  Equivalent to str_repeat(3) with the variable argument list specified directly as for vprintf(3).

`String *str_vrepeat_with_locker(Locker *locker, size_t count, const char *format, va_list args)`

  Equivalent to str_vrepeat(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`int str_tr(String *str, const char *from, const char *to, int option)`

  This is just like the perl(1) tr operator. The following documentation was taken from perlop(1).

  Transliterates all occurrences of the characters in `from` with the corresponding character in `to`. On success, returns the number of characters replaced or deleted. On error, returns `-1` with `errno` set appropriately.

  A character range can be specified with a hyphen, so `str_tr(str, "A-J", "0-9")` does the same replacement as `str_tr(str, "ACEGIBDFHJ", "0246813579")`.

  Note also that the whole range idea is rather unportable between character sets - and even within character sets they might cause results you probably didn't expect. A sound principle is to use only ranges that begin from and end at either alphabets of equal case (a-e, A-E), or digits (0-4). Anything else is unsafe. If in doubt, spell out the character sets in full.

  Options:

          TR_COMPLEMENT Complement from.
          TR_DELETE     Delete found but unreplaced characters.
          TR_SQUASH     Squash duplicate replaced characters.

  If `TR_COMPLEMENT` is specified, `from` is complemented. If `TR_DELETE` is specified, any characters specified by `from` not found in `to` are deleted. (Note that this is slightly more flexible than the behavior of some tr programs, which delete anything they find in `from`.) If `TR_SQUASH` is specified, sequences of characters that were transliterated to the same character are squashed down to a single instance of the character.

  If `TR_DELETE` is used, `to` is always interpreted exactly as specified. Otherwise, if `to` is shorter than `from`, the final character is replicated till it is long enough. If `to` is empty or `null`, `from` is replicated. This latter is useful for counting characters in a class or for squashing character sequences in a class.

  Examples:

          str_tr(s, "A-Z", "a-z", 0);             // canonicalize to lower case
          str_tr(s, "a-z", "A-Z", 0);             // canonicalize to upper case
          str_tr(s, "a-zA-Z", "A-Za-z", 0);       // swap upper and lower case
          str_tr(s, "*", "*", 0);                 // count the stars in str
          str_tr(s, "0-9", "", 0);                // count the digits in $_
          str_tr(s, "a-zA-Z", "", TR_SQUASH);     // bookkeeper -> bokeper
          str_tr(s, "a-zA-Z", " ", TR_COMPLEMENT | TR_SQUASH); // change non-alphas to single space
          str_tr(c, "a-zA-Z", "n-za-mN-ZA-M", 0); // Rot13

          from = str_create("\200-\377");
          to = str_create("%c-\177", '\000');
          str_tr_str(s, from, to, 0);             // clear 8th bit

  If multiple transliterations are given for a character, only the first one is used:

          str_tr(str, "AAA", "XYZ", 0);

  will transliterate any A to X.

`int str_tr_unlocked(String *str, const char *from, const char *to, int option)`

  Equivalent to str_tr(3) except that `str` is not write-locked.

`int str_tr_str(String *str, const String *from, const String *to, int option)`

  Equivalent to str_tr(3) except that `from` and `to` are String objects. This is needed when `from` or `to` need to contain `nul` characters.

`int str_tr_str_unlocked(String *str, const String *from, const String *to, int option)`

  Equivalent to str_tr_str(3) except that `str` is not write-locked and `from` and `to` are not read-locked. Note: If `to` and `from` need to be read-locked, it is the caller's responsibility to lock and unlock them explicitly with str_rdlock(3) and str_unlock(3).

`int tr(char *str, const char *from, const char *to, int option)`

  Equivalent to str_tr(3) but works on an ordinary C string.

`StringTR *tr_compile(const char *from, const char *to, int option)`

  Compiles `from`, `to` and `option` into a translation table to be passed to str_tr_compiled(3) or tr_compiled(3). On success, returns the new translation table. It is the caller's responsibility to deallocate the translation table with tr_release(3) or tr_destroy(3). It is strongly recommended to use tr_destroy(3), because it also sets the pointer variable to `null`. On error, returns `null` with `errno` set appropriately.

`StringTR *tr_compile_with_locker(Locker *locker, const char *from, const char *to, int option)`

  Equivalent to tr_compile(3) except that multiple threads accessing the new translation table will be synchronised by `locker`.

`StringTR *str_tr_compile(const String *from, const String *to, int option)`

  Equivalent to tr_compile(3) except that `from` and `to` are String objects. This is needed when `from` or `to` need to contain `nul` characters.

`StringTR *str_tr_compile_unlocked(const String *from, const String *to, int option)`

  Equivalent to str_tr_compile(3) except that `from` and `to` are not read-locked.

`StringTR *str_tr_compile_with_locker(Locker *locker, const String *from, const String *to, int option)`

  Equivalent to str_tr_compile(3) except that multiple threads accessing the new translation table will be synchronised by `locker`.

`StringTR *str_tr_compile_with_locker_unlocked(Locker *locker, const String *from, const String *to, int option)`

  Equivalent to str_tr_compile_with_locker(3) except that `from` and `to` are not read-locked. Note: If `to` and `from` need to be read-locked, it is the caller's responsibility to lock and unlock them explicitly with str_rdlock(3) and str_unlock(3).

`void tr_release(StringTR *table)`

  Releases (deallocates) `table`.

`void *tr_destroy(StringTR **table)`

  Destroys (deallocates and sets to `null`) `*table`. Returns `null`. Note: translation tables shared by multiple threads must not be destroyed until after all threads have finished with it.

`int str_tr_compiled(String *str, StringTR *table)`

  Performs the character translation specified by `table` (as created by tr_compile(3) or equivalent) on `str`. Use this whenever the same translation will be performed multiple times. On success, returns the number of characters replaced or deleted. On error, returns `-1` with `errno` set appropriately.

`int str_tr_compiled_unlocked(String *str, StringTR *table)`

  Equivalent to str_tr_compiled(3) except that `str` is not write-locked.

`int tr_compiled(char *str, StringTR *table)`

  Equivalent to str_tr_compiled(3) but works on an ordinary C string.

`List *str_regexpr(const char *pattern, const String *text, int cflags, int eflags)`

  str_regexpr(3) is an interface to POSIX 1003.2-compliant regular expression matching. `pattern` is a regular expression. `text` is the string to be searched for matches. `cflags` is passed to regcomp(3) along with `REG_EXTENDED`. `eflags` is passed to regexec(3). On success, returns a List of (at most 33) Strings containing the matching substring followed by the matching substrings of any parenthesised subexpressions. It is the caller's responsibility to deallocate the list with list_release(3) or list_destroy(3). On error (including no match), returns `null` with `errno` set appropriately. Only use this function when the regular expression will be used only once. Otherwise, use regexpr_compile(3) or regcomp(3) and str_regexpr_compiled(3) or regexpr_compiled(3) or regexec(3).

  Note: If you require perl pattern matching, you could use Philip Hazel's PCRE package, `ftp://ftp.cus.cam.ac.uk/pub/software/programs/pcre/` or link against the perl library itself.

`List *str_regexpr_unlocked(const char *pattern, const String *text, int cflags, int eflags)`

  Equivalent to str_regexpr(3) except that `text` is not read-locked.

`List *str_regexpr_with_locker(Locker *locker, const char *pattern, const String *text, int cflags, int eflags)`

  Equivalent to str_regexpr(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`List *str_regexpr_with_locker_unlocked(Locker *locker, const char *pattern, const String *text, int cflags, int eflags)`

  Equivalent to str_regexpr_with_locker(3) except that `text` is not read-locked.

`List *regexpr(const char *pattern, const char *text, int cflags, int eflags)`

  Equivalent to str_regexpr(3) but works on an ordinary C string.

`List *regexpr_with_locker(Locker *locker, const char *pattern, const char *text, int cflags, int eflags)`

  Equivalent to regexpr(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`int regexpr_compile(regex_t *compiled, const char *pattern, int cflags)`

  Compiles a POSIX 1003.2-compliant regular expression. `compiled` is the location in which to compile the expression. `pattern` is the regular expression. `cflags` is passed to regcomp(3) along with `REG_EXTENDED`. Call this, followed by re_compiled(3) when the regular expression will be used multiple times. On success, returns `0`. On error, returns an error code.

`void regexpr_release(regex_t *compiled)`

  Just another name for regfree(3).

`List *str_regexpr_compiled(const regex_t *compiled, const String *text, int eflags)`

  regexpr_compiled(3) is an interface to the POSIX 1003.2 regular expression function, regexec(3). `compiled` is the compiled regular expression prepared by regexpr_compile(3) or regcomp(3). `text` is the string to be searched for a match. `eflags` is passed to regexec(3). On success, returns a List of (at most 33) Strings containing the matching substring followed by the matching substrings of any parenthesised subexpressions. It is the caller's responsibility to deallocate the list with list_release(3) or list_destroy(3). On error (including no match), returns `null` with `errno` set appropriately.

`List *str_regexpr_compiled_unlocked(const regex_t *compiled, const String *text, int eflags)`

  Equivalent to str_regexpr_compiled(3) except that `text` is not write-locked.

`List *str_regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const String *text, int eflags)`

  Equivalent to str_regexpr_compiled(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`List *str_regexpr_compiled_with_locker_unlocked(Locker *locker, const regex_t *compiled, const String *text, int eflags)`

  Equivalent to str_regexpr_compiled_with_locker(3) except that `text` is not read-locked.

`List *regexpr_compiled(const regex_t *compiled, const char *text, int eflags)`

  Equivalent to str_regexpr_compiled(3) but works on an ordinary C string.

`List *regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const char *text, int eflags)`

  Equivalent to regexpr_compiled(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`String *str_regsub(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all)`

  str_regsub(3) is an interface to POSIX 1003.2-compliant regular expression matching and substitution. `pattern` is a regular expression. `text` is the string to be searched for matches. `cflags` is passed to regcomp(3) along with `REG_EXTENDED`. `eflags` is passed to regexec(3). `all` specifies whether to substitute the first match (if zero) or all matches (if non-zero). `replacement` specifies the string that replaces each match. If `replacement` contains `"$#"` or `"${##}"` (where `"#"` is a decimal digit), the substring that matches the corresponding subexpression is interpolated in its place. Up to 32 subexpressions are supported. If `replacement` contains `"$$"`, then `"$"` is interpolated in its place. The following perl(1) quote escape sequences are also understood:

          \l  lowercase next character
          \u  uppercase next character
          \L  lowercase until matching \E
          \U  uppercase until matching \E
          \Q  backslash non-alphanumeric characters until matching \E
          \E  end case/quotemeta modification

  Note that these sequences don't behave exactly like in perl(1). Namely, an `\l` appearing between a `\U` and an `\E` does lowercase the next character and a `\E` sequence without a matching `\L`, `\U` or `\Q` is an error. Also note that only 32 levels of nesting are supported.

  On success, returns `text`. On error (including no match), returns `null` with `errno` set appropriately. Only use this function when the regular expression will be used only once. Otherwise, use regexpr_compile(3) or regcomp(3) and str_regsub_compiled(3).

`String *str_regsub_unlocked(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all)`

  Equivalent to str_regsub(3) except that `text` is not write-locked.

`String *str_regsub_compiled(const regex_t *compiled, const char *replacement, String *text, int eflags, int all)`

  Equivalent to str_regsub(3) but works on an already compiled regex_t, `compiled`.

`String *str_regsub_compiled_unlocked(const regex_t *compiled, const char *replacement, String *text, int eflags, int all)`

  Equivalent to str_regsub_compiled(3) except that `text` is not write-locked.

`List *str_fmt(const String *str, size_t line_width, StringAlignment alignment)`

  Formats `str` into a List of String objects with length no greater than `line_width` (unless there are individual words longer than `line_width`) with the alignment specified by `alignment`:

  `ALIGN_LEFT` (`'<'`)

    The lines will be left justified (with one space between words).

  `ALIGN_RIGHT` ('>')

    The lines will be right justified (with one space between words).

  `ALIGN_CENTRE` or `ALIGN_CENTER` (`'|'`)

    `str` will be split into lines at each newline character (`'\n'`). The lines will then be centred (with one space between words) padded with spaces to the left.

  `ALIGN_FULL` (`'='`)

    The lines will be fully justified (possibly with multiple spaces between words).

  On success, returns a new List of String objects. It is the caller's responsibility to deallocate the list with list_release(3) or list_destroy(3). On error, returns `null` with `errno` set appropriately. Note that `str` is interpreted as a `nul`-terminated string.

  Note: str_fmt(3) provides straightforward formatting completely lacking in any aesthetic sensibilities. If you need awesome paragraph formatting, pipe text through par(1) instead (available from `http://www.cs.berkeley.edu/~amc/Par/`).

`List *str_fmt_unlocked(const String *str, size_t line_width, StringAlignment alignment)`

  Equivalent to str_fmt(3) except that `str` is not read-locked.

`List *str_fmt_with_locker(Locker *locker, const String *str, size_t line_width, StringAlignment alignment)`

  Equivalent to str_fmt(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`List *str_fmt_with_locker_unlocked(Locker *locker, const String *str, size_t line_width, StringAlignment alignment)`

  Equivalent to str_fmt_with_locker(3) except that `str` is not read-locked.

`List *fmt(const char *str, size_t line_width, StringAlignment alignment)`

  Equivalent to str_fmt(3) but works on an ordinary C string.

`List *fmt_with_locker(Locker *locker, const char *str, size_t line_width, StringAlignment alignment)`

  Equivalent to fmt(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`List *str_split(const String *str, const char *delim)`

  Splits `str` into tokens separated by sequences of characters occurring in `delim`. On success, returns a new List of String objects. It is the caller's responsibility to deallocate the list with list_release(3) or list_destroy(3). On error, returns `null` with `errno` set appropriately.

`List *str_split_unlocked(const String *str, const char *delim)`

  Equivalent to str_split(3) except that `str` is not read-locked.

`List *str_split_with_locker(Locker *locker, const String *str, const char *delim)`

  Equivalent to str_split(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`List *str_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim)`

  Equivalent to str_split_with_locker(3) except that `str` is not read-locked.

`List *split(const char *str, const char *delim)`

  Equivalent to str_split(3) but works on an ordinary C string.

`List *split_with_locker(Locker *locker, const char *str, const char *delim)`

  Equivalent to split(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`List *str_regexpr_split(const String *str, const char *delim, int cflags, int eflags)`

  Splits `str` into tokens separated by occurrences of the regular expression, `delim`. `str` is interpreted as a `nul`-terminated C string. `cflags` is passed to regcomp(3) along with `REG_EXTENDED` and `eflags` is passed to regexec(3). On success, returns a new List of String objects. It is the caller's responsibility to deallocate the list with list_release(3) or list_destroy(3). On error, returns `null` with `errno` set appropriately.

`List *str_regexpr_split_unlocked(const String *str, const char *delim, int cflags, int eflags)`

  Equivalent to str_regexpr_split(3) except that `str` is not read-locked.

`List *str_regexpr_split_with_locker(Locker *locker, const String *str, const char *delim, int cflags, int eflags)`

  Equivalent to str_regexpr_split(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`List *str_regexpr_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim, int cflags, int eflags)`

  Equivalent to str_regexpr_split_with_locker(3) except that `str` is not read-locked.

`List *regexpr_split(const char *str, const char *delim, int cflags, int eflags)`

  Equivalent to str_regexpr_split(3) but works on an ordinary C string.

`List *regexpr_split_with_locker(Locker *locker, const char *str, const char *delim, int cflags, int eflags)`

  Equivalent to regexpr_split(3) except that multiple threads accessing the new list will be synchronised by `locker`.

`String *str_join(const List *list, const char *delim)`

  Joins the String objects in `list` with `delim` inserted between each one. On success, returns the resulting String. It is the caller's responsibility to deallocate the string with str_release(3) or str_destroy(3). On error, returns `null` with `errno` set appropriately.

`String *str_join_unlocked(const List *list, const char *delim)`

  Equivalent to str_join(3) except that `list` is not read-locked.

`String *str_join_with_locker(Locker *locker, const List *list, const char *delim)`

  Equivalent to str_join(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_join_with_locker_unlocked(Locker *locker, const List *list, const char *delim)`

  Equivalent to str_join_with_locker(3) except that `list` is not read-locked.

`String *join(const List *list, const char *delim)`

  Equivalent to str_join(3) but works on a list of ordinary C strings.

`String *join_with_locker(Locker *locker, const List *list, const char *delim)`

  Equivalent to join(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`int str_soundex(const String *str)`

  Returns the soundex code of `str` as an integer. On error, returns `-1` with `errno` set appropriately.

`int str_soundex_unlocked(const String *str)`

  Equivalent to str_soundex(3) except that `str` is not read-locked.

`int soundex(const char *str)`

  Equivalent to str_soundex(3) but works on an ordinary C string.

`String *str_trim(String *str)`

  Trims leading and trailing whitespace from `str`. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_trim_unlocked(String *str)`

  Equivalent to str_trim(3) except that `str` is not write-locked.

`char *trim(char *str)`

  Equivalent to str_trim(3) but works on an ordinary C string.

`String *str_trim_left(String *str)`

  Trims leading whitespace from `str`. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_trim_left_unlocked(String *str)`

  Equivalent to str_trim_left(3) except that `str` is not write-locked.

`char *trim_left(char *str)`

  Equivalent to str_trim_left(3) but works on an ordinary C string.

`String *str_trim_right(String *str)`

  Trims trailing whitespace from `str`. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_trim_right_unlocked(String *str)`

  Equivalent to str_trim_right(3) except that `str` is not write-locked.

`char *trim_right(char *str)`

  Equivalent to str_trim_right(3) but works on an ordinary C string.

`String *str_squeeze(String *str)`

  Trims leading and trailing whitespace from `str` and replaces all other sequences of whitespace with a single space. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_squeeze_unlocked(String *str)`

  Equivalent to str_squeeze(3) except that `str` is not write-locked.

`char *squeeze(char *str)`

  Equivalent to str_squeeze(3) but works on an ordinary C string.

`String *str_quote(const String *str, const char *quotable, char quote_char)`

  Creates a new String containing `str` with every occurrence of any character in `quotable` preceded by `quote_char`. On success, returns the new string. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). On error, returns `null` with `errno` set appropriately.

`String *str_quote_unlocked(const String *str, const char *quotable, char quote_char)`

  Equivalent to str_quote(3) except that `str` is not read-locked.

`String *str_quote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char)`

  Equivalent to str_quote(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_quote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char)`

  Equivalent to str_quote_with_locker(3) except that `str` is not read-locked.

`String *quote(const char *str, const char *quotable, char quote_char)`

  Equivalent to str_quote(3) but works on an ordinary C string.

`String *quote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char)`

  Equivalent to quote(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_unquote(const String *str, const char *quotable, char quote_char)`

  Creates a new string containing `str` with every occurrence of `quote_char` that is followed by any character in `quotable` removed. On success, returns the new String. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). On error, returns `null` with `errno` set appropriately.

`String *str_unquote_unlocked(const String *str, const char *quotable, char quote_char)`

  Equivalent to str_unquote(3) except that `str` is not read-locked.

`String *str_unquote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char)`

  Equivalent to str_unquote(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_unquote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char)`

  Equivalent to str_unquote_with_locker(3) except that `str` is not read-locked.

`String *unquote(const char *str, const char *quotable, char quote_char)`

  Equivalent to str_unquote(3) but works on an ordinary C string.

`String *unquote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char)`

  Equivalent to unquote(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_encode(const String *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Returns a copy of `str` with every occurrence in `str` of characters in `uncoded` replaced with `quote_char` followed by the corresponding (by position) character in `coded`. If `printable` is non-zero, other non-printable characters are replaced with their ASCII codes in hexadecimal. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). It is strongly recommended to use str_destroy(3), because it also sets the pointer variable to `null`. On error, returns `null` with `errno` set appropriately.

  Example:

          // Encode a string into a C string literal
          str_encode(str, "\a\b\t\n\v\f\r\\", "abtnvfr\\", '\\', 1);

          // Decode a C string literal
          str_decode(str, "\a\b\t\n\v\f\r\\", "abtnvfr\\", '\\', 1);

`String *str_encode_unlocked(const String *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to str_encode(3) except that `str` is not read-locked.

`String *str_encode_with_locker(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to str_encode(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_encode_with_locker_unlocked(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to str_encode_with_locker(3) except that `str` is not read-locked.

`String *str_decode(const String *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Returns a copy of `str` with every occurrence in `str` of `quote_char` followed by a character in `coded` replaced with the corresponding (by position) character in `uncoded`. If `printable` is non-zero, every occurrence in `str` of an ASCII code in octal or hexadecimal (i.e. "\ooo" or "\xhh") is replaced with the corresponding ASCII character. It is the caller's responsibility to deallocate the new string with str_release(3) or str_destroy(3). It is strongly recommended to use str_destroy(3), because it also sets the pointer variable to `null`. On error, returns `null` with `errno` set appropriately.

`String *str_decode_unlocked(const String *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to str_decode(3) except that `str` is not read-locked.

`String *str_decode_with_locker(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to str_decode(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_decode_with_locker_unlocked(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to str_decode_with_locker(3) except that `str` is not read-locked.

`String *encode(const char *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to str_encode(3) but works on an ordinary C string.

`String *encode_with_locker(Locker *locker, const char *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to encode(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *decode(const char *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to str_decode(3) but works on an ordinary C string.

`String *decode_with_locker(Locker *locker, const char *str, const char *uncoded, const char *coded, char quote_char, int printable)`

  Equivalent to decode(3) except that multiple threads accessing the new string will be synchronised by `locker`.

`String *str_lc(String *str)`

  Converts `str` into lower case. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_lc_unlocked(String *str)`

  Equivalent to str_lc(3) except that `str` is not write-locked.

`char *lc(char *str)`

  Converts `str` into lower case. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_lcfirst(String *str)`

  Converts the first character in `str` into lower case. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_lcfirst_unlocked(String *str)`

  Equivalent to str_lcfirst(3) except that `str` is not write-locked.

`char *lcfirst(char *str)`

  Converts the first character in `str` into lower case. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_uc(String *str)`

  Converts `str` into upper case. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_uc_unlocked(String *str)`

  Equivalent to str_uc(3) except that `str` is not write-locked.

`char *uc(char *str)`

  Converts `str` into upper case. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_ucfirst(String *str)`

  Converts the first character in `str` into upper case. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`String *str_ucfirst_unlocked(String *str)`

  Equivalent to str_ucfirst(3) except that `str` is not write-locked.

`char *ucfirst(char *str)`

  Converts the first character in `str` into upper case. On success, returns `str`. On error, returns `null` with `errno` set appropriately.

`int str_chop(String *str)`

  Removes a character from the end of `str`. On success, returns the character that was removed. On error, returns `-1` with `errno` set appropriately.

`int str_chop_unlocked(String *str)`

  Equivalent to str_chop(3) except that `str` is not write-locked.

`int chop(char *str)`

  Removes a character from the end of `str`. On success, returns the character that was removed. On error, returns `-1` with `errno` set appropriately.

`int str_chomp(String *str)`

  Removes line ending characters (i.e. `'\n'` and `'\r'`) from the end of `str`. On success, returns the number of characters that were removed. On error, returns `-1` with `errno` set appropriately.

`int str_chomp_unlocked(String *str)`

  Equivalent to str_chomp(3) except that `str` is not write-locked.

`int chomp(char *str)`

  Removes line ending characters (i.e. `'\n'` and `'\r'`) from the end of `str`. On success, returns the number of characters that were removed. On error, returns `-1` with `errno` set appropriately.

`int str_bin(const String *str)`

  Returns the integer specified by the binary string, `str`. `str` can either be a string of `[0-1]`, or `"0b"` followed by a string of `[0-1]`. On error, returns `-1` with `errno` set appropriately.

`int str_bin_unlocked(const String *str)`

  Equivalent to str_bin(3) except that `str` is not read-locked.

`int bin(const char *str)`

  Returns the integer specified by the binary string, `str`. `str` can either be a string of `[0-1]`, or `"0b"` followed by a string of `[0-1]`. On error, returns `-1` with `errno` set appropriately.

`int str_hex(const String *str)`

  Returns the integer specified by the hexadecimal string, `str`. `str` can either be a string of `[0-9a-fA-F]`, or `"0x"` followed by a string of `[0-9a-fA-f]`. On error, returns `-1` with `errno` set appropriately.

`int str_hex_unlocked(const String *str)`

  Equivalent to str_hex(3) except that `str` is not read-locked.

`int hex(const char *str)`

  Returns the integer specified by the hexadecimal string, `str`. `str` can either be a string of `[0-9a-fA-F]`, or `"0x"` followed by a string of `[0-9a-fA-f]`. On error, returns `-1` with `errno` set appropriately.

`int str_oct(const String *str)`

  Returns the integer specified by the binary, octal or hexadecimal string, `str`. `str` can either be `"0x"` followed by a string of `[0-9a-fA-F]` (hexadecimal), `"0b"` followed by a string of `[0-1]` (binary), or `"0"` followed by a string of `[0-7]` (octal). On error, returns `-1` with `errno` set appropriately.

`int str_oct_unlocked(const String *str)`

  Equivalent to str_oct(3) except that `str` is not read-locked.

`int oct(const char *str)`

  Returns the integer specified by the binary, octal or hexadecimal string, `str`. `str` can either be `"0x"` followed by a string of `[0-9a-fA-F]` (hexadecimal), `"0b"` followed by a string of `[0-1]` (binary), or `"0"` followed by a string of `[0-7]` (octal). On error, returns `-1` with `errno` set appropriately.

int strcasecmp(const char *s1, const char *s2)

  Compares two strings, `s1` and `s2`, ignoring the case of the characters. It returns an integer less than, equal to, or greater than zero if `s1` is found to be less than, equal to, or greater than `s2`, respectively.

int strncasecmp(const char *s1, const char *s2, size_t n)

  Equivalent to strcasecmp(3) except that it only compares the first `n` characters.

size_t strlcpy(char *dst, const char *src, size_t size)

  Copies `src` into `dst` (which is `size` bytes long). The result, `dst`, will be no longer than `size - 1` bytes, and will be `nul`-terminated (unless `size` is zero). This is similar to strncpy(3) except that it always terminates the string with a `nul` byte (so it's safer), and it doesn't fill the remainder of the buffer with `nul` bytes (so it's faster). Returns the length of `src` (If this is >= `size`, truncation occurred). Use this rather than strcpy(3) or strncpy(3).

size_t strlcat(char *dst, const char *src, size_t size)

  Appends `src` to `dst` (which is `size` bytes long). The result, `dst`, will be no longer than `size - 1` bytes, and will be `nul`-terminated (unless `size` is zero). This is similar to strncat(3) except that the last argument is the size of the buffer, not the amount of space available (so it's more intuitive and hence safer). Returns the sum of the lengths of `src` and `dst` (If this is >= `size`, truncation occurred). Use this rather than strcat(3) or strncat(3).

`char *cstrcpy(char *dst, const char *src)`

  Copies the string pointed to by `src` (including the terminating `nul` character) to the array pointed to by `dst`. The memory areas must not overlap. The array `dst` must be large enough to store the copy. Unless you know that this is the case, use strlcpy(3) instead. This is just like strcpy(3) except that instead of returning `dst` (which you already know), this function returns the address of the terminating `nul` character (`dst + strlen(src)`).

`char *cstrcat(char *dst, const char *src)`

  Appends the string `src` to the string `dst`. The strings must not overlap. The string `dst` must be large enough to store the appended copy of `src`. Unless you know that this is the case, use strlcat(3) instead. This is just like strcat(3) except that, instead of returning `dst` (which you already know), this function returns the address of the terminating `nul` character (`dst + strlen(dst) + strlen(src)`).

`char *cstrchr(const char *str, int c)`

  Scans the string `str` looking for the character `c`. Returns a pointer to the first occurrence of the character `c` in the string `str`. This is just like strchr(3) except that, instead of returning `null` when `c` does not appear in `str`, this function returns the address of the terminating `nul` character (`str + strlen(str)`).

`char *cstrpbrk(const char *str, const char *brk)`

  Scans the string `str` looking for any of the characters in `brk`. Returns a pointer to the first occurrence of any character in `brk` in the string `str`. This is just like strpbrk(3) except that, instead of returning `null` when no match is found in `str`, this function returns the address of the terminating `nul` character (`str + strlen(str)`).

`char *cstrrchr(const char *str, int c)`

  Scans the string `str` looking for the character `c`. Returns a pointer to the last occurrence of the character `c` in the string `str`. This is just like strrchr(3) except that, instead of returning `null` when `c` does not appear in `str`, this function returns the address of the terminating `nul` character (`str + strlen(str)`).

`char *cstrstr(const char *str, const char *srch)`

  Scans the string `str` looking for the string `srch`. Returns a pointer to the first occurrence of the string `srch` in the string `str`. This is just like strstr(3) except that, instead of returning `null` when `srch` does not appear in `str`, this function returns the address of the terminating `nul` character (`str + strlen(str)`).

int asprintf(char **str, const char *format, ...)

  Equivalent to sprintf(3) except that, instead of formatting `format` and subsequent arguments into a buffer supplied by the caller, they are formatted into a buffer that is internally allocated and stored in `*str`. On success, returns the number of bytes stored in `*str` excluding the terminating `nul` character. On error, returns `-1` and stores `null` in `*str`.

int vasprintf(char **str, const char *format, va_list args)

  Equivalent to asprintf(3) with the variable argument list specified directly as for vprintf(3).

# ERRORS

On error, `errno` is set either by an underlying function, or as follows:

`EINVAL`

  When arguments to any of the functions are invalid.

# MT-Level

MT-Disciplined

By default, Strings are not MT-Safe because most programs are single-threaded and synchronisation doesn't come for free. Even in multi-threaded programs, not all Strings are necessarily shared between multiple threads.

When a String is shared between multiple threads which need to be synchronised, the method of synchronisation must be carefully selected by the client code. There are tradeoffs between concurrency and overhead. The greater the concurrency, the greater the overhead. More locks give greater concurrency, but have greater overhead. Readers/Writer locks can give greater concurrency than Mutex locks, but have greater overhead. One lock for each String might be required, or one lock for all (or a set of) Strings might be more appropriate.

Generally, the best synchronisation strategy for a given application can only be determined by testing/benchmarking the written application. It is important to be able to experiment with the synchronisation strategy at this stage of development without pain.

To facilitate this, Strings can be created with string_create_with_locker(3) which takes a Locker argument. The Locker specifies a lock and a set of functions for manipulating the lock. Each String can have its own lock by creating a separate Locker for each String. Multiple Strings can share the same lock by sharing the same Locker. Only the application developer can determine what is appropriate for each application on a string by string basis.

MT-Disciplined means that the application developer has a mechanism for specifying the synchronisation requirements to be applied to library code.

MT-Safe - str_fgetline(3)

Mac OS X doesn't have flockfile(3), funlockfile(3) or getc_unlocked(3). fgetline(3) is not MT-Safe on such platforms. You must guard all stdio calls with explicit synchronisation variables.

# EXAMPLES

Create and manipulate strings:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *str1 = str_create("%s %d", "string", 1);
            String *str2 = str_copy(str1);
            String *str3;
            String *str4;
            String *str5;
            String *str6;

            str_remove(str1, 6);
            str_remove_range(str2, 6, 2);
            str_clear(str1);
            str_insert(str1, 0, "%d", 123);
            str_insert_str(str1, 1, str2);
            str_append(str2, "abc");
            str_append_str(str2, str1);
            str_prepend(str1, "abc");
            str_prepend_str(str1, str2);
            str_replace(str1, 1, -2, "abc");
            str_replace_str(str2, 1, 2, str1);

            str3 = str_substr(str1, 1, 3);
            str4 = substr("abc", 1, 1);
            str5 = str_splice(str3, 1, 1);
            str6 = str_repeat(3, "%c", ' ');

            *cstr(str5) = '\0';
            str_set_length(str5, 0);
            str_recalc_length(str5);

            printf("str1 = '%s' %d\n", cstr(str1), str_length(str1));
            printf("str2 = '%s' %d\n", cstr(str2), str_length(str2));
            printf("str3 = '%s' %d\n", cstr(str3), str_length(str3));
            printf("str4 = '%s' %d\n", cstr(str4), str_length(str4));
            printf("str5 = '%s' %d\n", cstr(str5), str_length(str5));
            printf("str6 = '%s' %d\n", cstr(str6), str_length(str6));

            str_destroy(&str1);
            str_destroy(&str2);
            str_destroy(&str3);
            str_destroy(&str4);
            str_destroy(&str5);
            str_destroy(&str6);

            return EXIT_SUCCESS;
        }

Convert a text file from any system into the local text file format:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *line;

            while (line = str_fgetline(stdin))
            {
                printf("%s", cstr(line));
                str_destroy(&line);
            }

            return EXIT_SUCCESS;
        }

Perform character translation with a pre-compiled translation table to rot13 the input:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            StringTR *trtable = tr_compile("a-zA-Z", "n-za-mN-ZA-M", 0);
            String *line;

            while (line = str_fgetline(stdin))
            {
                str_tr_compiled(line, trtable);
                printf("%s", cstr(line));
                str_destroy(&line);
            }

            tr_destroy(&trtable);

            return EXIT_SUCCESS;
        }

The same as above but using ordinary C strings:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            StringTR *trtable = tr_compile("a-zA-Z", "n-za-mN-ZA-M", 0);
            char line[BUFSIZ];

            while (fgets(line, BUFSIZ, stdin))
            {
                int count = tr_compiled(line, trtable);
                printf("%s", line);
            }

            tr_destroy(&trtable);

            return EXIT_SUCCESS;
        }

Perform regular expression matching and substitution:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            // Find matches in a String object

            String *str = str_create("abcabcabc");
            List *list = str_regexpr("a((.*)a(.*))a", str, 0, 0);
            regex_t compiled[1];

            while (list_has_next(list))
                printf("%s\n", cstr(list_next(list)));

            str_destroy(&str);
            list_destroy(&list);

            // Find matches in an ordinary C string

            list = regexpr("a((.*)a(.*))a", "abcabcabc", 0, 0);

            while (list_has_next(list) == 1)
                printf("%s\n", cstr(list_next(list)));

            list_destroy(&list);

            // Use a pre-compiled regular expression on a String object

            str = str_create("abcabcabc");

            if (!regexpr_compile(compiled, "a((.*)a(.*))a", 0))
            {
                list = str_regexpr_compiled(compiled, str, 0);
                regexpr_release(compiled);
            }

            while (list_has_next(list) == 1)
                printf("%s\n", cstr(list_next(list)));

            str_destroy(&str);
            list_destroy(&list);

            // Use a pre-compiled regular expression on an ordinary C string

            if (!regexpr_compile(compiled, "a((.*)a(.*))a", 0))
            {
                list = regexpr_compiled(compiled, "abcabcabc", 0);
                regexpr_release(compiled);
            }

            while (list_has_next(list) == 1)
                printf("%s\n", cstr(list_next(list)));

            list_destroy(&list);

            // Perform regular expression substitution on a String object

            str = str_create("abcabcabc");
            str_regsub("a", "z", str, 0, 0, 1);
            printf("%s\n", cstr(str));

            str_destroy(&str);

            // Perform regular expression substitution with a pre-compiled pattern

            str = str_create("abcabcabc");

            if (!regexpr_compile(compiled, "a", 0))
            {
                str_regsub_compiled(compiled, "z", str, 0, 1);
                printf("%s\n", cstr(str));
                regexpr_release(compiled);
            }

            str_destroy(&str);

            return EXIT_SUCCESS;
        }

Format some text in a String object in several different ways:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *text = str_create("This is a string with\na few little words");
            List *left = str_fmt(text, 20, ALIGN_LEFT);     // or '<'
            List *right = str_fmt(text, 20, ALIGN_RIGHT);   // or '>'
            List *centre = str_fmt(text, 20, ALIGN_CENTRE); // or '|' or ALIGN_CENTER
            List *full = str_fmt(text, 20, ALIGN_FULL);     // or '='

            printf("Left:\n");
            while (list_has_next(left))
                printf("%s\n", cstr(list_next(left)));

            printf("Right:\n");
            while (list_has_next(right))
                printf("%s\n", cstr(list_next(right)));

            printf("Centre:\n");
            while (list_has_next(centre))
                printf("%s\n", cstr(list_next(centre)));

            printf("Full:\n");
            while (list_has_next(full))
                printf("%s\n", cstr(list_next(full)));

            str_destroy(&text);
            list_destroy(&left);
            list_destroy(&right);
            list_destroy(&centre);
            list_destroy(&full);

            return EXIT_SUCCESS;
        }

Perform the same formatting but on an ordinary C string:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *text = "This is a string with\na few little words";
            List *left = fmt(text, 20, ALIGN_LEFT);     // or '<'
            List *right = fmt(text, 20, ALIGN_RIGHT);   // or '>'
            List *centre = fmt(text, 20, ALIGN_CENTRE); // or '|' or ALIGN_CENTER
            List *full = fmt(text, 20, ALIGN_FULL);     // or '='

            printf("Left:\n");
            while (list_has_next(left))
                printf("%s\n", cstr(list_next(left)));

            printf("Right:\n");
            while (list_has_next(right))
                printf("%s\n", cstr(list_next(right)));

            printf("Centre:\n");
            while (list_has_next(centre))
                printf("%s\n", cstr(list_next(centre)));

            printf("Full:\n");
            while (list_has_next(full))
                printf("%s\n", cstr(list_next(full)));

            list_destroy(&left);
            list_destroy(&right);
            list_destroy(&centre);
            list_destroy(&full);

            return EXIT_SUCCESS;
        }

Split and join a String object without using regular expressions:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *text = str_create("line1\nline2\nline3\n");
            List *lines = str_split(text, "\n");
            String *copy;

            while (list_has_next(lines))
                printf("%s\n", cstr(list_next(lines)));

            copy = str_join(lines, "\n");
            printf("%s\n", cstr(copy));

            str_destroy(&text);
            str_destroy(&copy);
            list_destroy(&lines);

            return EXIT_SUCCESS;
        }

Split an ordinary C string without using regular expressions:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *text = "line1\nline2\nline3\n";
            List *lines = split(text, "\n");

            while (list_has_next(lines))
                printf("%s\n", cstr(list_next(lines)));

            list_destroy(&lines);

            return EXIT_SUCCESS;
        }

Split a String object using regular expressions:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *text = str_create("line1\rline2\r\nline3\n");
            List *lines = str_regexpr_split(text, "(\n|\r|\r\n)", 0, 0);

            while (list_has_next(lines))
                printf("%s\n", cstr(list_next(lines)));

            str_destroy(&text);
            list_destroy(&lines);

            return EXIT_SUCCESS;
        }

Split an ordinary C string using regular expressions:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *text = "line1\rline2\r\nline3\n";
            List *lines = regexpr_split(text, "(\n|\r|\r\n)", 0, 0);

            while (list_has_next(lines))
                printf("%s\n", cstr(list_next(lines)));

            list_destroy(&lines);

            return EXIT_SUCCESS;
        }

Trim and squeeze String objects:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *str1 = str_create("   a  b  c   ");
            String *str2 = str_create("   a  b  c   ");
            String *str3 = str_create("   a  b  c   ");
            String *str4 = str_create("   a  b  c   ");

            str_trim(str1);
            str_trim_left(str2);
            str_trim_right(str3);
            str_squeeze(str4);

            printf("'%s'\n", cstr(str1));
            printf("'%s'\n", cstr(str2));
            printf("'%s'\n", cstr(str3));
            printf("'%s'\n", cstr(str4));

            str_destroy(&str1);
            str_destroy(&str2);
            str_destroy(&str3);
            str_destroy(&str4);

            return EXIT_SUCCESS;
        }

Trim and squeeze ordinary C strings:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *str1 = strdup("   a  b  c   ");
            char *str2 = strdup("   a  b  c   ");
            char *str3 = strdup("   a  b  c   ");
            char *str4 = strdup("   a  b  c   ");

            trim(str1);
            trim_left(str2);
            trim_right(str3);
            squeeze(str4);

            printf("'%s'\n", str1);
            printf("'%s'\n", str2);
            printf("'%s'\n", str3);
            printf("'%s'\n", str4);

            free(str1);
            free(str2);
            free(str3);
            free(str4);

            return EXIT_SUCCESS;
        }

Quote whitespace in a String object:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *str = str_create("this is\ta\nstring with  whitespaces");
            String *quoted = str_quote(str, " \t\n", '\\');
            String *unquoted = str_unquote(quoted, " \t\n", '\\');

            printf("'%s'\n", cstr(str));
            printf("'%s'\n", cstr(quoted));
            printf("'%s'\n", cstr(unquoted));

            str_destroy(&str);
            str_destroy(&quoted);
            str_destroy(&unquoted);

            return EXIT_SUCCESS;
        }

Quote whitespace in an ordinary C string:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *str = "this is\ta\nstring with  whitespaces";
            String *quoted = quote(str, " \t\n", '\\');
            String *unquoted = unquote(cstr(quoted), " \t\n", '\\');

            printf("'%s'\n", str);
            printf("'%s'\n", cstr(quoted));
            printf("'%s'\n", cstr(unquoted));

            str_destroy(&quoted);
            str_destroy(&unquoted);

            return EXIT_SUCCESS;
        }

Apply C string literal encoding and decoded to a String object:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *str = str_create("\a\b\t\n\v\f\rabc123\x16\034");
            String *encoded = str_encode(str, "\a\b\t\n\v\f\r", "abtnvfr", '\\', 1);
            String *decoded = str_decode(str, "\a\b\t\n\v\f\r", "abtnvfr", '\\', 1);

            printf("'%s'\n", cstr(str));
            printf("'%s'\n", cstr(encoded));
            printf("'%s'\n", cstr(decoded));

            str_destroy(&str);
            str_destroy(&encoded);
            str_destroy(&decoded);

            return EXIT_SUCCESS;
        }

Apply C string literal encoding and decoded to an ordinary C string:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *str = "\a\b\t\n\v\f\rabc123\x16\034";
            String *encoded = encode(str, "\a\b\t\n\v\f\r", "abtnvfr", '\\', 1);
            String *decoded = decode(str, "\a\b\t\n\v\f\r", "abtnvfr", '\\', 1);

            printf("'%s'\n", str);
            printf("'%s'\n", cstr(encoded));
            printf("'%s'\n", cstr(decoded));

            str_destroy(&encoded);
            str_destroy(&decoded);

            return EXIT_SUCCESS;
        }

Get the soundex code of a String object:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *smith = str_create("Smith");

            printf("%s %d\n", cstr(smith), str_soundex(smith));

            str_destroy(&smith);

            return EXIT_SUCCESS;
        }

Get the soundex code of an ordinary C string:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *smith = "Smith";

            printf("%s %d\n", smith, soundex(smith));

            return EXIT_SUCCESS;
        }

Convert between upper and lower case in a String object:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *str = str_create("smith");

            printf("%s\n", cstr(str));

            str_ucfirst(str);
            printf("%s\n", cstr(str));

            str_uc(str);
            printf("%s\n", cstr(str));

            str_lcfirst(str);
            printf("%s\n", cstr(str));

            str_lc(str);
            printf("%s\n", cstr(str));

            str_destroy(&str);

            return EXIT_SUCCESS;
        }

Convert between upper and lower case in an ordinary C string:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *str = strdup("smith");
            printf("%s\n", str);
            ucfirst(str);
            printf("%s\n", str);
            uc(str);
            printf("%s\n", str);
            lcfirst(str);
            printf("%s\n", str);
            lc(str);
            printf("%s\n", str);

            free(str);

            return EXIT_SUCCESS;
        }

Chomp line ending characters off the end of a String object, and chop a character off the end of a String object:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *str = str_create("aaa\r\n");
            int bytes = str_chomp(str);

            printf("'%s' %d\n", cstr(str), bytes);
            bytes = str_chop(str);
            printf("'%s' '%c'\n", cstr(str), bytes);

            str_destroy(&str);

            return EXIT_SUCCESS;
        }

Chomp line ending characters off the end of an ordinary C string, and chop a character off the end of an ordinary C string:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *str = strdup("aaa\r\n");
            int bytes = chomp(str);

            printf("'%s' %d\n", str, bytes);
            bytes = chop(str);
            printf("'%s' '%c'\n", str, bytes);

            free(str);

            return EXIT_SUCCESS;
        }

Parse binary, octal, and hexadecimal integers in String objects:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            String *b = str_create("0b1010");
            String *h = str_create("0x0a1b2c3d");
            String *o = str_create("0177");

            printf("%d\n", str_bin(b));
            printf("%d\n", str_hex(h));
            printf("%d\n", str_oct(o));
            printf("%d\n", str_oct(h));
            printf("%d\n", str_oct(b));

            str_destroy(&b);
            str_destroy(&h);
            str_destroy(&o);

            return EXIT_SUCCESS;
        }

Parse binary, octal and hexadecimal integers in ordinary C strings:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *b = "0b1010";
            char *h = "0x0a1b2c3d";
            char *o = "0177";

            printf("%d\n", bin(b));
            printf("%d\n", hex(h));
            printf("%d\n", oct(o));
            printf("%d\n", oct(h));
            printf("%d\n", oct(b));

            return EXIT_SUCCESS;
        }

Examples of versions of some standard string functions with more informative interfaces:

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *src = "text";
            char dst[BUFSIZ];
            char *pos;

            char *eos = cstrcpy(dst, src);
            printf("length '%s' = %d\n", dst, eos - dst);

            eos = cstrcat(dst, src);
            printf("length '%s' = %d\n", dst, eos - dst);

            eos = cstrchr(dst, 'z');
            printf("length '%s' = %d\n", dst, eos - dst);

            for (pos = dst; *(pos = cstrpbrk(pos, "xyz")); ++pos)
                printf("x|y|z at pos %d in %s\n", pos - dst, dst);

            if (*(pos = cstrrchr(dst, 'x')))
                printf("last x in %s at pos %d\n", dst, pos - dst);
            else
                printf("there is no x in %s\n", dst);

            for (pos = dst; *(pos = cstrstr(pos, "text")); ++pos)
                printf("text at pos %d in %s\n", pos - dst, dst);

            return EXIT_SUCCESS;
        }

Examples of string functions that are supplied if they are not already present on the local system.

        #include <slack/std.h>
        #include <slack/str.h>

        int main()
        {
            char *str1 = "smith, john";
            char *str2 = "Smith, Mary";
            char *str3 = NULL;
            char buf[16];
            size_t len;

            printf("%d\n", strcasecmp(str1, str2));
            printf("%d\n", strncasecmp(str1, str2, 5));
            printf("%d\n", strncasecmp(str1, str2, 8));

            if (strlcpy(buf, str1, 16) >= 16)
                printf("truncation occurred\n");
            printf("%s\n", buf);

            if (strlcat(buf, str2, 16) >= 16)
                printf("truncation occurred\n");
            printf("%s\n", buf);

            if (strlcpy(buf, str1, 1) >= 1)
                printf("truncation occurred\n");
            printf("%s\n", buf);

            len = strlcpy(NULL, str1, 0);
            printf("%d\n", len);

            len = asprintf(&str3, "test");
            printf("%s %d\n", str3, len);
            free(str3);

            return EXIT_SUCCESS;
        }

# CAVEAT

The `delim` parameter to the split(3) and join(3) functions is an ordinary C string, so it can't contain `nul` characters.

The `quotable` parameter to the quote(3) and unquote(3) functions is an ordinary C string, so it can't contain `nul` characters.

The `uncoded` and `coded` parameters to the str_encode(3) and str_decode(3) functions are ordinary C strings, so they can't contain `nul` characters.

# BUGS

Doesn't support multi-byte/widechar strings, UTF8, UNICODE or ISO 10646 but support can probably be layered over the top of String.

Uses malloc(3) directly. The type of memory used and the allocation strategy should be decoupled from this code.

# SEE ALSO

libslack(3), locker(3), string(3), regcomp(3), regexec(3), regerror(3), regfree(3), perlfunc(1), perlop(1)

# AUTHOR

20210220 raf <raf@raf.org>