/*
* libslack - http://libslack.org/
*
* Copyright (C) 1999-2004 raf <raf@raf.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
* or visit http://www.gnu.org/copyleft/gpl.html
*
* 20040102 raf <raf@raf.org>
*/

/*
* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $
* $OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $
* Modified by raf <raf2@raf.org>
*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*

=head1 NAME

I<libslack(str)> - string module

=head1 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);

=head1 DESCRIPTION

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

=over 4

=cut

*/

#include "config.h"
#include "std.h"

#include <netinet/in.h>

#include "err.h"
#include "str.h"
#include "mem.h"
#include "fio.h"

#ifndef HAVE_SNPRINTF
#include "snprintf.h"
#endif

struct String
{
	size_t size;    /* number of bytes allocated */
	size_t length;  /* number of bytes used (including nul) */
	char *str;      /* vector of characters */
	Locker *locker; /* locking strategy for this string */
};

#define CHARSET 256

struct StringTR
{
	int squash;           /* whether or not to squash duplicate characters */
	short table[CHARSET]; /* the translation table */
	Locker *locker;       /* locking strategy for this structure */
};

typedef enum
{
	TRCODE_NOMAP = -1,
	TRCODE_DELETE = -2
}
TRCode;

#define is_alpha(c)  isalpha((int)(unsigned char)(c))
#define is_alnum(c)  isalnum((int)(unsigned char)(c))
#define is_print(c)  isprint((int)(unsigned char)(c))
#define is_space(c)  isspace((int)(unsigned char)(c))
#define is_digit(c)  isdigit((int)(unsigned char)(c))
#define is_xdigit(c) isxdigit((int)(unsigned char)(c))
#define to_lower(c)  tolower((int)(unsigned char)(c))
#define to_upper(c)  toupper((int)(unsigned char)(c))

#ifndef TEST

/* Minimum string length: must be a power of 2 */

static const size_t MIN_STRING_SIZE = 32;

/* Maximum bytes for an empty string: must be a power of 2 greater than MIN_STRING_SIZE */

static const size_t MIN_EMPTY_STRING_SIZE = 1024;

void (flockfile)(FILE *stream); /* Missing from old glibc headers */
void (funlockfile)(FILE *stream);

#ifndef HAVE_FLOCKFILE
#define flockfile(stream)
#define funlockfile(stream)
#define getc_unlocked(stream) getc(stream)
#endif

/*

C<int grow(String *str, size_t bytes)>

Allocates enough memory to add C<bytes> extra bytes to C<str> if necessary.
On success, returns C<0>. On error, returns C<-1>.

*/

static int grow(String *str, size_t bytes)
{
	int grown = 0;

	while (str->length + bytes > str->size)
	{
		if (str->size)
			str->size <<= 1;
		else
			str->size = MIN_STRING_SIZE;

		grown = 1;
	}

	if (grown)
		return mem_resize(&str->str, str->size) ? 0 : -1;

	return 0;
}

/*

C<int shrink(String *str, size_t bytes)>

Allocates less memory for removing C<bytes> bytes from C<str> if necessary.
On success, returns C<0>. On error, returns C<-1>.

*/

static int shrink(String *str, size_t bytes)
{
	int shrunk = 0;

	while (str->length - bytes < str->size >> 1)
	{
		if (str->size <= MIN_EMPTY_STRING_SIZE)
			break;

		str->size >>= 1;
		shrunk = 1;
	}

	if (shrunk)
		return mem_resize(&str->str, str->size) ? 0 : -1;

	return 0;
}

/*

C<int expand(String *str, ssize_t index, size_t range)>

Slides C<str>'s bytes, starting at C<index>, C<range> positions to the right
to make room for more. On success, returns C<0>. On error, returns C<-1>.

*/

static int expand(String *str, ssize_t index, size_t range)
{
	if (grow(str, range) == -1)
		return -1;

	memmove(str->str + index + range, str->str + index, (str->length - index) * sizeof(*str->str));
	str->length += range;

	return 0;
}

/*

C<int contract(String *str, ssize_t index, size_t range)>

Slides C<str>'s bytes, starting at C<index> + C<range>, C<range> positions
to the left to close a gap starting at C<index>. On success, returns C<0>.
On error, returns C<-1>.

*/

static int contract(String *str, ssize_t index, size_t range)
{
	memmove(str->str + index, str->str + index + range, (str->length - index - range) * sizeof(*str->str));

	if (shrink(str, range) == -1)
		return -1;

	str->length -= range;

	return 0;
}

/*

C<int adjust(String *str, ssize_t index, size_t range, size_t length)>

Expands or contracts C<str> as required so that C<str[index + range ..]>
occupies C<str[index + length ..]>. On success, returns C<0>. On error,
returns C<-1>.

*/

static int adjust(String *str, ssize_t index, size_t range, size_t length)
{
	if (range < length)
		return expand(str, index + range, length - range);

	if (range > length)
		return contract(str, index + length, range - length);

	return 0;
}

/*

=item C<String *str_create(const char *format, ...)>

Creates a I<String> specified by C<format> and the following arguments as in
I<sprintf(3)>. On success, returns the new string. It is the caller's
responsibility to deallocate the new string with I<str_release(3)> or
I<str_destroy(3)>. On error, returns C<null> with C<errno> set
appropriately.

B<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 holes 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

=cut

*/

String *str_create(const char *format, ...)
{
	String *str;
	va_list args;
	va_start(args, format);
	str = str_vcreate_with_locker_sized(NULL, MIN_STRING_SIZE, format, args);
	va_end(args);
	return str;
}

/*

=item C<String *str_create_with_locker(Locker *locker, const char *format, ...)>

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

=cut

*/

String *str_create_with_locker(Locker *locker, const char *format, ...)
{
	String *str;
	va_list args;
	va_start(args, format);
	str = str_vcreate_with_locker_sized(locker, MIN_STRING_SIZE, format, args);
	va_end(args);
	return str;
}

/*

=item C<String *str_vcreate(const char *format, va_list args)>

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

=cut

*/

String *str_vcreate(const char *format, va_list args)
{
	return str_vcreate_with_locker_sized(NULL, MIN_STRING_SIZE, format, args);
}

/*

=item C<String *str_vcreate_with_locker(Locker *locker, const char *format, va_list args)>

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

=cut

*/

String *str_vcreate_with_locker(Locker *locker, const char *format, va_list args)
{
	return str_vcreate_with_locker_sized(locker, MIN_STRING_SIZE, format, args);
}

/*

=item C<String *str_create_sized(size_t size, const char *format, ...)>

Creates a I<String> specified by C<format> and the following arguments as in
I<sprintf(3)>. The initial allocation for the string data is at least
C<size> bytes. On success, returns the new string. It is the caller's
responsibility to deallocate the new string with I<str_release(3)> or
I<str_destroy(3)>. On error, returns C<null> with C<errno> set
appropriately.

=cut

*/

String *str_create_sized(size_t size, const char *format, ...)
{
	String *str;
	va_list args;
	va_start(args, format);
	str = str_vcreate_with_locker_sized(NULL, size, format, args);
	va_end(args);
	return str;
}

/*

=item C<String *str_create_with_locker_sized(Locker *locker, size_t size, const char *format, ...)>

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

=cut

*/

String *str_create_with_locker_sized(Locker *locker, size_t size, const char *format, ...)
{
	String *str;
	va_list args;
	va_start(args, format);
	str = str_vcreate_with_locker_sized(locker, size, format, args);
	va_end(args);
	return str;
}

/*

=item C<String *str_vcreate_sized(size_t size, const char *format, va_list args)>

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

=cut

*/

String *str_vcreate_sized(size_t size, const char *format, va_list args)
{
	return str_vcreate_with_locker_sized(NULL, size, format, args);
}

/*

=item C<String *str_vcreate_with_locker_sized(Locker *locker, size_t size, const char *format, va_list args)>

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

=cut

*/

String *str_vcreate_with_locker_sized(Locker *locker, size_t size, const char *format, va_list args)
{
	String *str;
	char *buf = NULL;
	ssize_t length;
	unsigned int bit;

	for (bit = 1; bit; bit <<= 1)
	{
		if (bit >= size)
		{
			size = bit;
			break;
		}
	}

	if (!bit)
		return set_errnull(EINVAL);

	if (!format)
		format = "";

	for (;; size <<= 1)
	{
		if (!mem_resize(&buf, size))
		{
			mem_release(buf);
			return NULL;
		}

		length = vsnprintf(buf, size, format, args);
		if (length != -1 && length < size)
			break;
	}

	if (!(str = mem_new(String))) /* XXX decouple */
	{
		mem_release(buf);
		return NULL;
	}

	str->size = size;
	str->length = length + 1;
	str->str = buf;
	str->locker = locker;

	return str;
}

/*

=item C<String *str_copy(const String *str)>

Creates a copy of C<str>. On success, returns the copy. It is the caller's
responsibility to deallocate the new string with I<str_release(3)> or
I<str_destroy(3)>. On error, returns C<null> with C<errno> set
appropriately.

=cut

*/

String *str_copy(const String *str)
{
	return str_copy_with_locker(NULL, str);
}

/*

=item C<String *str_copy_unlocked(const String *str)>

Equivalent to I<str_copy(3)> except that C<str> is not read locked.

=cut

*/

String *str_copy_unlocked(const String *str)
{
	return str_copy_with_locker_unlocked(NULL, str);
}

/*

=item C<String *str_copy_with_locker(Locker *locker, const String *str)>

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

=cut

*/

String *str_copy_with_locker(Locker *locker, const String *str)
{
	return str_substr_with_locker(locker, str, 0, -1);
}

/*

=item C<String *str_copy_with_locker_unlocked(Locker *locker, const String *str)>

Equivalent to I<str_copy_with_locker(3)> except that C<str> is not read
locked.

=cut

*/

String *str_copy_with_locker_unlocked(Locker *locker, const String *str)
{
	return str_substr_with_locker_unlocked(locker, str, 0, -1);
}

/*

=item C<String *str_fgetline(FILE *stream)>

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

=cut

*/

String *str_fgetline(FILE *stream)
{
	return str_fgetline_with_locker(NULL, stream);
}

/*

=item C<String *str_fgetline_with_locker(Locker *locker, FILE *stream)>

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

=cut

*/

String *str_fgetline_with_locker(Locker *locker, FILE *stream)
{
	String *ret = NULL;
	char buf[BUFSIZ];

	flockfile(stream);

	while (fgetline_unlocked(buf, BUFSIZ, stream))
	{
		if (!ret)
		{
			if (!(ret = str_create_with_locker(locker, "%s", buf)))
				break;
		}
		else if (!str_append(ret, "%s", buf))
		{
			str_destroy(&ret);
			break;
		}

		if (cstr(ret)[ret->length - 2] == '\n')
			break;
	}

	funlockfile(stream);

	return ret;
}

/*

=item C<void str_release(String *str)>

Releases (deallocates) C<str>.

=cut

*/

void str_release(String *str)
{
	Locker *locker;

	if (!str)
		return;

	if (str_wrlock(str))
		return;

	locker = str->locker;
	mem_release(str->str);
	mem_release(str);
	locker_unlock(locker);
}

/*

=item C<void *str_destroy(String **str)>

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

=cut

*/

void *str_destroy(String **str)
{
	if (str && *str)
	{
		str_release(*str);
		*str = NULL;
	}

	return NULL;
}

/*

=item C<int str_rdlock(const String *str)>

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

=cut

*/

#define str_rdlock(str) ((str) ? locker_rdlock((str)->locker) : EINVAL)
#define str_wrlock(str) ((str) ? locker_wrlock((str)->locker) : EINVAL)
#define str_unlock(str) ((str) ? locker_unlock((str)->locker) : EINVAL)

int (str_rdlock)(const String *str)
{
	return str_rdlock(str);
}

/*

=item C<int str_wrlock(const String *str)>

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

=cut

*/

int (str_wrlock)(const String *str)
{
	return str_wrlock(str);
}

/*

=item C<int str_unlock(const String *str)>

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

=cut

*/

int (str_unlock)(const String *str)
{
	return str_unlock(str);
}

/*

=item C<int str_empty(const String *str)>

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

=cut

*/

int str_empty(const String *str)
{
	int empty;
	int err;

	if (!str)
		return set_errno(EINVAL);

	if ((err = str_rdlock(str)))
		return set_errno(err);

	empty = str_empty_unlocked(str);

	if ((err = str_unlock(str)))
		return set_errno(err);

	return empty;
}

/*

=item C<int str_empty_unlocked(const String *str)>

Equivalent to I<str_empty(3)> except that C<str> is not read locked.

=cut

*/

int str_empty_unlocked(const String *str)
{
	if (!str)
		return set_errno(EINVAL);

	return (str->length == 1);
}

/*

=item C<ssize_t str_length(const String *str)>

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

=cut

*/

ssize_t str_length(const String *str)
{
	size_t length;
	int err;

	if (!str)
		return set_errno(EINVAL);

	if ((err = str_rdlock(str)))
		return set_errno(err);

	length = str_length_unlocked(str);

	if ((err = str_unlock(str)))
		return set_errno(err);

	return length;
}
/*

=item C<ssize_t str_length_unlocked(const String *str)>

Equivalent to I<str_length(3)> except that C<str> is not read locked.

=cut

*/

ssize_t str_length_unlocked(const String *str)
{
	if (!str)
		return set_errno(EINVAL);

	return str->length - 1;
}

/*

=item C<char *cstr(const String *str)>

Returns the raw C string in C<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 you call I<str_set_length_unlocked(3)> or
I<str_recalc_length_unlocked(3)> immediately afterwards. When used on a
string that is shared by multiple threads, I<cstr(3)> must appear between
calls to I<str_rdlock(3)> or I<str_wrlock(3)> and I<str_unlock(3)>.

=cut

*/

char *cstr(const String *str)
{
	if (!str)
		return set_errnull(EINVAL);

	return str->str;
}

/*

=item C<ssize_t str_set_length(String *str, size_t length)>

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

=cut

*/

ssize_t str_set_length(String *str, size_t length)
{
	ssize_t len;
	int err;

	if (!str)
		return set_errno(EINVAL);

	if ((err = str_wrlock(str)))
		return set_errno(err);

	len = str_set_length_unlocked(str, length);

	if ((err = str_unlock(str)))
		return set_errno(err);

	return len;
}

/*

=item C<ssize_t str_set_length_unlocked(String *str, size_t length)>

Equivalent to I<str_set_length(3)> except that C<str> is not write locked.

=cut

*/

ssize_t str_set_length_unlocked(String *str, size_t length)
{
	if (!str || length >= str->length)
		return set_errno(EINVAL);

	str->length = length + 1;
	str->str[str->length - 1] = '\0';

	return str->length - 1;
}

/*

=item C<ssize_t str_recalc_length(String *str)>

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

=cut

*/

ssize_t str_recalc_length(String *str)
{
	ssize_t len;
	int err;

	if (!str)
		return set_errno(EINVAL);

	if ((err = str_wrlock(str)))
		return set_errno(err);

	len = str_recalc_length_unlocked(str);

	if ((err = str_unlock(str)))
		return set_errno(err);

	return len;
}

/*

=item C<ssize_t str_recalc_length_unlocked(String *str)>

Equivalent to I<str_recalc_length(3)> except that C<str> is not write
locked.

=cut

*/

ssize_t str_recalc_length_unlocked(String *str)
{
	if (!str)
		return set_errno(EINVAL);

	str->length = strlen(str->str) + 1;

	return str->length - 1;
}

/*

=item C<String *str_clear(String *str)>

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

=cut

*/

String *str_clear(String *str)
{
	return str_remove_range(str, 0, -1);
}

/*

=item C<String *str_clear_unlocked(String *str)>

Equivalent to I<str_clear(3)> except that C<str> is not write locked.

=cut

*/

String *str_clear_unlocked(String *str)
{
	return str_remove_range_unlocked(str, 0, -1);
}

/*

=item C<String *str_remove(String *str, ssize_t index)>

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

=cut

*/

String *str_remove(String *str, ssize_t index)
{
	return str_remove_range(str, index, 1);
}

/*

=item C<String *str_remove_unlocked(String *str, ssize_t index)>

Equivalent to I<str_remove(3)> except that C<str> is not write locked.

=cut

*/

String *str_remove_unlocked(String *str, ssize_t index)
{
	return str_remove_range_unlocked(str, index, 1);
}

/*

=item C<String *str_remove_range(String *str, ssize_t index, ssize_t range)>

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

=cut

*/

String *str_remove_range(String *str, ssize_t index, ssize_t range)
{
	String *ret;
	int err;

	if (!str)
		return set_errnull(EINVAL);

	if ((err = str_wrlock(str)))
		return set_errnull(err);

	ret = str_remove_range_unlocked(str, index, range);

	if ((err = str_unlock(str)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_remove_range_unlocked(String *str, ssize_t index, ssize_t range)>

Equivalent to I<str_remove_range(3)> except that C<str> is not write locked.

=cut

*/

String *str_remove_range_unlocked(String *str, ssize_t index, ssize_t range)
{
	if (!str)
		return set_errnull(EINVAL);

	if (index < 0)
		index = str->length + index;

	if (index < 0)
		return set_errnull(EINVAL);

	if (range < 0)
		range = str->length + range - index;

	if (range < 0)
		return set_errnull(EINVAL);

	if (str->length - 1 < index + range)
		return set_errnull(EINVAL);

	contract(str, index, range);

	return str;
}

/*

=item C<String *str_insert(String *str, ssize_t index, const char *format, ...)>

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

=cut

*/

String *str_insert(String *str, ssize_t index, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vinsert(str, index, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_insert_unlocked(String *str, ssize_t index, const char *format, ...)>

Equivalent to I<str_insert(3)> except that C<str> is not write locked.

=cut

*/

String *str_insert_unlocked(String *str, ssize_t index, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vinsert_unlocked(str, index, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_vinsert(String *str, ssize_t index, const char *format, va_list args)>

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

=cut

*/

String *str_vinsert(String *str, ssize_t index, const char *format, va_list args)
{
	String *ret;
	int err;

	if (!str)
		return set_errnull(EINVAL);

	if ((err = str_wrlock(str)))
		return set_errnull(err);

	ret = str_vinsert_unlocked(str, index, format, args);

	if ((err = str_unlock(str)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_vinsert_unlocked(String *str, ssize_t index, const char *format, va_list args)>

Equivalent to I<str_vinsert(3)> except that C<str> is not write locked.

=cut

*/

String *str_vinsert_unlocked(String *str, ssize_t index, const char *format, va_list args)
{
	String *tmp, *ret;

	if (!str)
		return set_errnull(EINVAL);

	if (index < 0)
		index = str->length + index;

	if (index < 0)
		return set_errnull(EINVAL);

	if (str->length - 1 < index)
		return set_errnull(EINVAL);

	if (!(tmp = str_vcreate(format, args)))
		return NULL;

	ret = str_insert_str_unlocked(str, index, tmp);
	str_release(tmp);

	return ret;
}

/*

=item C<String *str_insert_str(String *str, ssize_t index, const String *src)>

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

=cut

*/

String *str_insert_str(String *str, ssize_t index, const String *src)
{
	String *ret;
	int err;

	if (!str || !src)
		return set_errnull(EINVAL);

	if ((err = str_rdlock(src)))
		return set_errnull(err);

	if ((err = str_wrlock(str)))
	{
		str_unlock(src);
		return set_errnull(err);
	}

	ret = str_insert_str_unlocked(str, index, src);

	if ((err = str_unlock(str)))
	{
		str_unlock(src);
		return set_errnull(err);
	}

	if ((err = str_unlock(src)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_insert_str_unlocked(String *str, ssize_t index, const String *src)>

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

=cut

*/

String *str_insert_str_unlocked(String *str, ssize_t index, const String *src)
{
	size_t length;

	if (!str || !src)
		return set_errnull(EINVAL);

	if (index < 0)
		index = str->length + index;

	if (index < 0)
		return set_errnull(EINVAL);

	if (str->length - 1 < index)
		return set_errnull(EINVAL);

	length = src->length - 1;

	if (expand(str, index, length) == -1)
		return NULL;

	memcpy(str->str + index, src->str, length);

	return str;
}

/*

=item C<String *str_append(String *str, const char *format, ...)>

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

=cut

*/

String *str_append(String *str, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vinsert(str, -1, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_append_unlocked(String *str, const char *format, ...)>

Equivalent to I<str_append(3)> except that C<str> is not write locked.

=cut

*/

String *str_append_unlocked(String *str, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vinsert_unlocked(str, -1, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_vappend(String *str, const char *format, va_list args)>

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

=cut

*/

String *str_vappend(String *str, const char *format, va_list args)
{
	return str_vinsert(str, -1, format, args);
}

/*

=item C<String *str_vappend_unlocked(String *str, const char *format, va_list args)>

Equivalent to I<str_vappend(3)> except that C<str> is not write locked.

=cut

*/

String *str_vappend_unlocked(String *str, const char *format, va_list args)
{
	return str_vinsert_unlocked(str, -1, format, args);
}

/*

=item C<String *str_append_str(String *str, const String *src)>

Appends C<src> to C<str>. On success, returns C<str>. On error, returns
C<null> with C<errno> set appropriately.

=cut

*/

String *str_append_str(String *str, const String *src)
{
	return str_insert_str(str, -1, src);
}

/*

=item C<String *str_append_str_unlocked(String *str, const String *src)>

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

=cut

*/

String *str_append_str_unlocked(String *str, const String *src)
{
	return str_insert_str_unlocked(str, -1, src);
}

/*

=item C<String *str_prepend(String *str, const char *format, ...)>

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

=cut

*/

String *str_prepend(String *str, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vinsert(str, 0, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_prepend_unlocked(String *str, const char *format, ...)>

Equivalent to I<str_prepend(3)> except that C<str> is not write locked.

=cut

*/

String *str_prepend_unlocked(String *str, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vinsert_unlocked(str, 0, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_vprepend(String *str, const char *format, va_list args)>

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

=cut

*/

String *str_vprepend(String *str, const char *format, va_list args)
{
	return str_vinsert(str, 0, format, args);
}

/*

=item C<String *str_vprepend_unlocked(String *str, const char *format, va_list args)>

Equivalent to I<str_vprepend(3)> except that C<str> is not write locked.

=cut

*/

String *str_vprepend_unlocked(String *str, const char *format, va_list args)
{
	return str_vinsert_unlocked(str, 0, format, args);
}

/*

=item C<String *str_prepend_str(String *str, const String *src)>

Prepends C<src> to C<str>. On success, returns C<str>. On error, returns
C<null> with C<errno> set appropriately.

=cut

*/

String *str_prepend_str(String *str, const String *src)
{
	return str_insert_str(str, 0, src);
}

/*

=item C<String *str_prepend_str_unlocked(String *str, const String *src)>

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

=cut

*/

String *str_prepend_str_unlocked(String *str, const String *src)
{
	return str_insert_str_unlocked(str, 0, src);
}

/*

=item C<String *str_replace(String *str, ssize_t index, ssize_t range, const char *format, ...)>

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

=cut

*/

String *str_replace(String *str, ssize_t index, ssize_t range, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vreplace(str, index, range, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_replace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, ...)>

Equivalent to I<str_replace(3)> except that C<str> is not write locked.

=cut

*/

String *str_replace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vreplace_unlocked(str, index, range, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_vreplace(String *str, ssize_t index, ssize_t range, const char *format, va_list args)>

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

=cut

*/

String *str_vreplace(String *str, ssize_t index, ssize_t range, const char *format, va_list args)
{
	String *tmp, *ret;

	if (!str)
		return set_errnull(EINVAL);

	if (!(tmp = str_vcreate(format, args)))
		return NULL;

	ret = str_replace_str(str, index, range, tmp);
	str_release(tmp);

	return ret;
}

/*

=item C<String *str_vreplace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, va_list args)>

Equivalent to I<str_vreplace(3)> except that C<str> is not write locked.

=cut

*/

String *str_vreplace_unlocked(String *str, ssize_t index, ssize_t range, const char *format, va_list args)
{
	String *tmp, *ret;

	if (!str)
		return set_errnull(EINVAL);

	if (!(tmp = str_vcreate(format, args)))
		return NULL;

	ret = str_replace_str_unlocked(str, index, range, tmp);
	str_release(tmp);

	return ret;
}

/*

=item C<String *str_replace_str(String *str, ssize_t index, ssize_t range, const String *src)>

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

=cut

*/

String *str_replace_str(String *str, ssize_t index, ssize_t range, const String *src)
{
	String *ret;
	int err;

	if (!src || !str)
		return set_errnull(EINVAL);

	if ((err = str_rdlock(src)))
		return set_errnull(err);

	if ((err = str_wrlock(str)))
	{
		str_unlock(src);
		return set_errnull(err);
	}

	ret = str_replace_str_unlocked(str, index, range, src);

	if ((err = str_unlock(str)))
	{
		str_unlock(src);
		return set_errnull(err);
	}

	if ((err = str_unlock(src)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_replace_str_unlocked(String *str, ssize_t index, ssize_t range, const String *src)>

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

=cut

*/

String *str_replace_str_unlocked(String *str, ssize_t index, ssize_t range, const String *src)
{
	size_t length;

	if (!src || !str)
		return set_errnull(EINVAL);

	if (index < 0)
		index = str->length + index;

	if (index < 0)
		return set_errnull(EINVAL);

	if (range < 0)
		range = str->length + range - index;

	if (range < 0)
		return set_errnull(EINVAL);

	if (str->length - 1 < index + range)
		return set_errnull(EINVAL);

	length = src->length - 1;

	if (adjust(str, index, range, length) == -1)
		return NULL;

	memcpy(str->str + index, src->str, length);

	return str;
}

/*

=item C<String *str_substr(const String *str, ssize_t index, ssize_t range)>

Creates a new I<String> object consisting of C<range> characters from
C<str>, starting at C<index>. If C<index> or C<range> are negative, they
refer to character positions relative to the end of the string (C<-1> is the
position after the last character, C<-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 I<str_release(3)> or
I<str_destroy(3)>. On error, returns C<null> with C<errno> set
appropriately.

=cut

*/

String *str_substr(const String *str, ssize_t index, ssize_t range)
{
	return str_substr_with_locker(NULL, str, index, range);
}

/*

=item C<String *str_substr_unlocked(const String *str, ssize_t index, ssize_t range)>

Equivalent to I<str_substr(3)> except that C<str> is not read locked.

=cut

*/

String *str_substr_unlocked(const String *str, ssize_t index, ssize_t range)
{
	return str_substr_with_locker_unlocked(NULL, str, index, range);
}

/*

=item C<String *str_substr_with_locker(Locker *locker, const String *str, ssize_t index, ssize_t range)>

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

=cut

*/

String *str_substr_with_locker(Locker *locker, const String *str, ssize_t index, ssize_t range)
{
	String *ret;
	int err;

	if (!str)
		return set_errnull(EINVAL);

	if ((err = str_rdlock(str)))
		return set_errnull(err);

	ret = str_substr_with_locker_unlocked(locker, str, index, range);

	if ((err = str_unlock(str)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_substr_with_locker_unlocked(Locker *locker, const String *str, ssize_t index, ssize_t range)>

Equivalent to I<str_substr_with_locker(3)> except that C<str> is not read
locked.

=cut

*/

String *str_substr_with_locker_unlocked(Locker *locker, const String *str, ssize_t index, ssize_t range)
{
	String *ret;

	if (!str)
		return set_errnull(EINVAL);

	if (index < 0)
		index = str->length + index;

	if (index < 0)
		return set_errnull(EINVAL);

	if (range < 0)
		range = str->length + range - index;

	if (range < 0)
		return set_errnull(EINVAL);

	if (str->length - 1 < index + range)
		return set_errnull(EINVAL);

	if (!(ret = str_create_with_locker_sized(locker, range + 1, NULL)))
		return NULL;

	memcpy(ret->str, str->str + index, range);
	ret->length = range + 1;
	ret->str[ret->length - 1] = '\0';

	return ret;
}

/*

=item C<String *substr(const char *str, ssize_t index, ssize_t range)>

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

=cut

*/

String *substr(const char *str, ssize_t index, ssize_t range)
{
	return substr_with_locker(NULL, str, index, range);
}

/*

=item C<String *substr_with_locker(Locker *locker, const char *str, ssize_t index, ssize_t range)>

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

=cut

*/

String *substr_with_locker(Locker *locker, const char *str, ssize_t index, ssize_t range)
{
	String *ret;
	size_t len = 0;

	if (!str)
		return set_errnull(EINVAL);

	if (index < 0 || range < 0)
		len = strlen(str) + 1;

	if (index < 0)
		index = len + index;

	if (index < 0)
		return set_errnull(EINVAL);

	if (range < 0)
		range = len + range - index;

	if (range < 0)
		return set_errnull(EINVAL);

	if (!(ret = str_create_with_locker_sized(locker, range + 1, NULL)))
		return NULL;

	memcpy(ret->str, str + index, range);
	ret->length = range + 1;
	ret->str[ret->length - 1] = '\0';

	return ret;
}

/*

=item C<String *str_splice(String *str, ssize_t index, ssize_t range)>

Removes a substring from C<str> starting at C<index> of length C<range>
characters. If C<index> or C<range> are negative, they refer to character
positions relative to the end of the string (C<-1> is the position after the
last character, C<-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 I<str_release(3)> or I<str_destroy(3)>. On
error, returns C<null> with C<errno> set appropriately.

=cut

*/

String *str_splice(String *str, ssize_t index, ssize_t range)
{
	return str_splice_with_locker(NULL, str, index, range);
}

/*

=item C<String *str_splice_unlocked(String *str, ssize_t index, ssize_t range)>

Equivalent to I<str_splice(3)> except that C<str> is not write locked.

=cut

*/

String *str_splice_unlocked(String *str, ssize_t index, ssize_t range)
{
	return str_splice_with_locker_unlocked(NULL, str, index, range);
}

/*

=item C<String *str_splice_with_locker(Locker *locker, String *str, ssize_t index, ssize_t range)>

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

=cut

*/

String *str_splice_with_locker(Locker *locker, String *str, ssize_t index, ssize_t range)
{
	String *ret;
	int err;

	if (!str)
		return set_errnull(EINVAL);

	if ((err = str_wrlock(str)))
		return set_errnull(err);

	ret = str_splice_with_locker_unlocked(locker, str, index, range);

	if ((err = str_unlock(str)))
	{
		str_release(ret);
		return set_errnull(err);
	}

	return ret;
}

/*

=item C<String *str_splice_with_locker_unlocked(Locker *locker, String *str, ssize_t index, ssize_t range)>

Equivalent to I<str_splice_with_locker(3)> except that C<str> is not write
locked.

=cut

*/

String *str_splice_with_locker_unlocked(Locker *locker, String *str, ssize_t index, ssize_t range)
{
	String *ret;

	if (!str)
		return set_errnull(EINVAL);

	if (!(ret = str_substr_with_locker_unlocked(locker, str, index, range)))
		return NULL;

	if (!str_remove_range_unlocked(str, index, range))
	{
		str_release(ret);
		return NULL;
	}

	return ret;
}

/*

=item C<String *str_repeat(size_t count, const char *format, ...)>

Creates a new I<String> containing the string determined by C<format>
repeated C<count> times. On success, return the new string. It is the
caller's responsibility to deallocate the new string with I<str_release(3)>
or I<str_destroy(3)>. On error, returns C<null> with C<errno> set
appropriately.

=cut

*/

String *str_repeat(size_t count, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vrepeat_with_locker(NULL, count, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_repeat_with_locker(Locker *locker, size_t count, const char *format, ...)>

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

=cut

*/

String *str_repeat_with_locker(Locker *locker, size_t count, const char *format, ...)
{
	String *ret;
	va_list args;
	va_start(args, format);
	ret = str_vrepeat_with_locker(locker, count, format, args);
	va_end(args);
	return ret;
}

/*

=item C<String *str_vrepeat(size_t count, const char *format, va_list args)>

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

=cut

*/

String *str_vrepeat(size_t count, const char *format, va_list args)
{
	return str_vrepeat_with_locker(NULL, count, format, args);
}

/*

=item C<String *str_vrepeat_with_locker(Locker *locker, size_t count, const char *format, va_list args)>

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

=cut

*/

String *str_vrepeat_with_locker(Locker *locker, size_t count, const char *format, va_list args)
{
	String *tmp, *ret;
	ssize_t length;
	size_t i;

	if (!(tmp = str_vcreate(format, args)))
		return NULL;

	if ((length = str_length(tmp)) == -1)
		return NULL;

	if (!(ret = str_create_with_locker_sized(locker, length * count + 1, NULL)))
	{
		str_release(tmp);
		return NULL;
	}

	for (i = 0; i < count; ++i)
	{
		if (!str_append_str(ret, tmp))
		{
			str_release(tmp);
			str_release(ret);
			return NULL;
		}
	}

	str_release(tmp);

	return ret;
}

/*

=item C<int str_tr(String *str, const char *from, const char *to, int option)>

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

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

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

Note also that the whole range idea is rather unportable between character
sets - and even within character sets they may 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, C<from> is complemented. If TR_DELETE is
specified, any characters specified by C<from> not found in C<to> are
deleted. (Note that this is slightly more flexible than the behavior of some
tr programs, which delete anything they find in C<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, C<to> is always interpreted exactly as specified.
Otherwise, if C<to> is shorter than C<from>, the final character is
replicated till it is long enough. If C<to> is empty or C<null>, C<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.

=cut

*/

static StringTR *tr_compile_table(StringTR *table, const char *from, const char *to, int option);

int str_tr(String *str, const char *from, const char *to, int option)
{
	StringTR table[1];

	if (!str || !from)
		return set_errno(EINVAL);

	table->locker = NULL;

	if (!tr_compile_table(table, from, to, option))
		return -1;

	return str_tr_compiled(str, table);
}

/*

=item C<int str_tr_unlocked(String *str, const char *from, const char *to, int option)>

Equivalent to I<str_tr(3)> except that C<str> is not write locked.

=cut

*/

int str_tr_unlocked(String *str, const char *from, const char *to, int option)
{
	StringTR table[1];

	if (!str || !from)
		return set_errno(EINVAL);

	table->locker = NULL;

	if (!tr_compile_table(table, from, to, option))
		return -1;

	return str_tr_compiled_unlocked(str, table);
}

/*

=item C<int str_tr_str(String *str, const String *from, const String *to, int option)>

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

=cut

*/

static StringTR *str_tr_compile_table(StringTR *table, const String *from, const String *to, int option);

int str_tr_str(String *str, const String *from, const String *to, int option)
{
	StringTR table[1];

	if (!str || !from)
		return set_errno(EINVAL);

	table->locker = NULL;

	if (!str_tr_compile_table(table, from, to, option))
		return -1;

	return str_tr_compiled(str, table);
}

/*

=item C<int str_tr_str_unlocked(String *str, const String *from, const String *to, int option)>

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

=cut

*/

static StringTR *str_tr_compile_table_unlocked(StringTR *table, const String *from, const String *to, int option);

int str_tr_str_unlocked(String *str, const String *from, const String *to, int option)
{
	StringTR table[1];

	if (!str || !from)
		return set_errno(EINVAL);

	table->locker = NULL;

	if (!str_tr_compile_table_unlocked(table, from, to, option))
		return -1;

	return str_tr_compiled_unlocked(str, table);
}

/*

=item C<int tr(char *str, const char *from, const char *to, int option)>

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

=cut

*/

int tr(char *str, const char *from, const char *to, int option)
{
	StringTR table[1];

	if (!str || !from)
		return set_errno(EINVAL);

	table->locker = NULL;

	if (!tr_compile_table(table, from, to, option))
		return -1;

	return tr_compiled(str, table);
}

/*

=item C<StringTR *tr_compile(const char *from, const char *to, int option)>

Compiles C<from>, C<to> and C<option> into a translation table to be passed
to I<str_tr_compiled(3)> or I<tr_compiled(3)>. On success, returns the new
translation table. It is the caller's responsibility to deallocate the
translation table with I<tr_release(3)> or I<tr_destroy(3)>. On error,
returns C<null> with C<errno> set appropriately.

=cut

*/

StringTR *tr_compile(const char *from, const char *to, int option)
{
	return tr_compile_with_locker(NULL, from, to, option);
}

/*

=item C<StringTR *tr_compile_with_locker(Locker *locker, const char *from, const char *to, int option)>

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

=cut

*/

StringTR *tr_compile_with_locker(Locker *locker, const char *from, const char *to, int option)
{
	StringTR *ret;

	if (!(ret = mem_new(StringTR))) /* XXX decouple */
		return NULL;

	ret->locker = locker;

	return tr_compile_table(ret, from, to, option);
}

/*

=item C<StringTR *str_tr_compile(const String *from, const String *to, int option)>

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

=cut

*/

StringTR *str_tr_compile(const String *from, const String *to, int option)
{
	return str_tr_compile_with_locker(NULL, from, to, option);
}

/*

=item C<StringTR *str_tr_compile_unlocked(const String *from, const String *to, int option)>

Equivalent to I<str_tr_compile(3)> except that C<from> and C<to> are not
read locked.

=cut

*/

StringTR *str_tr_compile_unlocked(const String *from, const String *to, int option)
{
	return str_tr_compile_with_locker_unlocked(NULL, from, to, option);
}

/*

=item C<StringTR *str_tr_compile_with_locker(Locker *locker, const String *from, const String *to, int option)>

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

=cut

*/

StringTR *str_tr_compile_with_locker(Locker *locker, const String *from, const String *to, int option)
{
	StringTR *ret;

	if (!(ret = mem_new(StringTR))) /* XXX decouple */
		return NULL;

	ret->locker = locker;

	return str_tr_compile_table(ret, from, to, option);
}

/*

=item C<StringTR *str_tr_compile_with_locker_unlocked(Locker *locker, const String *from, const String *to, int option)>

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

=cut

*/

StringTR *str_tr_compile_with_locker_unlocked(Locker *locker, const String *from, const String *to, int option)
{
	StringTR *ret;

	if (!(ret = mem_new(StringTR))) /* XXX decouple */
		return NULL;

	ret->locker = locker;

	return str_tr_compile_table_unlocked(ret, from, to, option);
}

/*

=item C<void tr_release(StringTR *table)>

Releases (deallocates) C<table>.

=cut

*/

void tr_release(StringTR *table)
{
	Locker *locker;

	if (!table)
		return;

	locker = table->locker;
	if (locker_wrlock(locker))
		return;

	mem_release(table);
	locker_unlock(locker);
}

/*

=item C<void *tr_destroy(StringTR **table)>

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

=cut

*/

void *tr_destroy(StringTR **table)
{
	if (table && *table)
	{
		tr_release(*table);
		*table = NULL;
	}

	return NULL;
}

/*

C<static StringTR *do_tr_compile_table(StringTR *table, const char *from, ssize_t fromlen, const char *to, ssize_t tolen, int option)>

Compiles C<from>, C<to> and C<option> into the translation table, C<table>,
to be passed to I<str_tr_compiled(3)> or I<tr_compiled(3)>. If C<fromlen> is
C<-1>, then C<from> is interpreted as a C<nul>-terminated C string.
Otherwise, C<from> is an arbitrary string of length C<fromlen>. If C<tolen>
is C<-1>, then C<to> is interpreted as a C<nul>-terminated C string.
Otherwise, C<to> is an arbitrary string of length C<tolen>. On success,
returns C<table>. On error, returns C<null> with C<errno> set appropriately.

*/

static StringTR *do_tr_compile_table(StringTR *table, const char *from, ssize_t fromlen, const char *to, ssize_t tolen, int option)
{
	const char *f, *t;
	char *xf, *xt;
	char xfrom[CHARSET], xto[CHARSET];
	short tbl[CHARSET];
	int i, j, k;
	int err;

	if (!table || !from)
		return set_errnull(EINVAL);

	for (i = 0; i < CHARSET; ++i)
		tbl[i] = TRCODE_NOMAP;

	/* Parse the from string */

	for (xf = xfrom, f = from; ((fromlen == -1) ? *f : (f - from < fromlen)) && xf - xfrom < CHARSET; ++f)
	{
		i = j = *f;

		if (f[1] == '-' && f[2])
			j = f[2], f += 2;

		if ((unsigned char)j < (unsigned char)i)
			return set_errnull(EINVAL);

		for (k = (unsigned char)i; k <= (unsigned char)j; ++k)
			*xf++ = tbl[k] = k;
	}

	if (xf - xfrom == CHARSET)
		return set_errnull(EINVAL);

	if (option & TR_COMPLEMENT)
	{
		char tmp[CHARSET];

		for (xf = tmp, k = 0; k < CHARSET; ++k)
			if (tbl[k] == TRCODE_NOMAP)
				*xf++ = k;

		memcpy(xfrom, tmp, xf - tmp);
		xf = xfrom + (xf - tmp);
	}

	/* Parse the to string */

	if (!to || ((tolen == -1) ? *to == '\0' : tolen == 0))
		to = (option & TR_DELETE) ? "" : from;

	for (xt = xto, t = to; ((tolen == -1) ? *t : (t - to < tolen)) && xt - xto < CHARSET; ++t)
	{
		i = j = *t;

		if (t[1] == '-' && t[2])
			j = t[2], t += 2;

		if ((unsigned char)j < (unsigned char)i)
			return set_errnull(EINVAL);

		for (k = (unsigned char)i; k <= (unsigned char)j; ++k)
			*xt++ = k;
	}

	if (xt - xto == CHARSET)
		return set_errnull(EINVAL);

	if (!(option & TR_DELETE))
	{
		size_t flen = xf - xfrom;
		size_t tlen = xt - xto;

		if (tlen < flen)
		{
			memset(xt, xt[-1], flen - tlen);
			xt += flen - tlen;
		}
	}

	/* Build the translation table */

	if ((err = locker_wrlock(table->locker)))
		return set_errnull(err);

	table->squash = option & TR_SQUASH;
	for (i = 0; i < CHARSET; ++i)
		table->table[i] = TRCODE_NOMAP;

	for (i = j = 0; xfrom + i < xf; ++i, ++j)
	{
		unsigned char fc = xfrom[i];
		unsigned char tc = xto[j];
		if (table->table[fc] == TRCODE_NOMAP)
			table->table[fc] = (xto + j < xt) ? tc : TRCODE_DELETE;
	}

	if ((err = locker_unlock(table->locker)))
		return set_errnull(err);

	return table;
}

/*

C<StringTR *tr_compile_table(StringTR *table, const char *from, const char *to, int option)>

Equivalent to I<tr_compile(3)> except that C<from>, C<to> and C<option> are
compiled into the translation table pointed to by C<table>. On success,
returns C<table>. On error, returns C<null> with C<errno> set appropriately.

*/

static StringTR *tr_compile_table(StringTR *table, const char *from, const char *to, int option)
{
	return do_tr_compile_table(table, from, -1, to, -1, option);
}

/*

C<StringTR *str_tr_compile_table(StringTR *table, const String *from, const String *to, int option)>

Equivalent to I<tr_compile_table(3)> except that C<from> and C<to> are
I<String> objects. This is needed when C<from> or C<to> need to contain
C<nul> characters.

*/

static StringTR *str_tr_compile_table(StringTR *table, const String *from, const String *to, int option)
{
	StringTR *ret;
	int err;

	if ((err = str_rdlock(from)))
		return set_errnull(err);

	if ((err = str_rdlock(to)))
	{
		str_unlock(from);
		return set_errnull(err);
	}

	ret = str_tr_compile_table_unlocked(table, from, to, option);

	if ((err = str_unlock(from)))
	{
		str_unlock(to);
		return set_errnull(err);
	}

	if ((err = str_unlock(to)))
		return set_errnull(err);

	return ret;
}

/*

C<StringTR *str_tr_compile_table_unlocked(StringTR *table, const String *from, const String *to, int option)>

Equivalent to I<str_tr_compile_table(3)> except that C<from> and C<to> are not
read locked.

*/

static StringTR *str_tr_compile_table_unlocked(StringTR *table, const String *from, const String *to, int option)
{
	return do_tr_compile_table(table, from->str, str_length_unlocked(from), to->str, str_length_unlocked(to), option);
}

/*

C<static int do_tr_compiled(unsigned char *str, size_t *length, StringTR *table)>

Performs the character translation specified by C<table> (as created by
I<tr_compile(3)> or equivalent) on C<str>. If C<length> is C<null>, C<str> is
interpreted as a C<nul>-terminated C string. Otherwise, C<str> is
interpreted as an arbitrary string of length C<*length>. The integer that
C<length> points to is decremented by the number of bytes deleted by the
translation. On success, returns the number of characters replaced or
deleted. On error, returns C<-1> with C<errno> set appropriately.

=cut

*/

static int do_tr_compiled(unsigned char *str, size_t *length, StringTR *table)
{
	int ret = 0;
	int deleted = 0;
	unsigned char *r, *s;
	short t;
	int err;

	if (!str || !table)
		return set_errno(EINVAL);

	if ((err = locker_rdlock(table->locker)))
		return set_errno(err);

	for (r = s = str; (length) ? s - str < *length - 1 : *s; ++s)
	{
		switch (t = table->table[(int)*s])
		{
			case TRCODE_DELETE:
				++deleted;
				++ret;
				break;

			case TRCODE_NOMAP:
				if (!table->squash || r == str || r[-1] != *s)
					*r++ = *s;
				else
					++deleted;
				break;

			default:
				if (!table->squash || r == str || r[-1] != t)
					*r++ = t;
				else
					++deleted;
				++ret;
				break;
		}
	}

	if ((err = locker_unlock(table->locker)))
		return set_errno(err);

	*r = '\0';
	if (length && deleted)
		*length -= deleted;

	return ret;
}

/*

=item C<int str_tr_compiled(String *str, StringTR *table)>

Performs the character translation specified by C<table> (as created by
I<tr_compile(3)> or equivalent) on C<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 C<-1> with C<errno> set
appropriately.

=cut

*/

int str_tr_compiled(String *str, StringTR *table)
{
	int ret;
	int err;

	if (!str || !table)
		return set_errno(EINVAL);

	if ((err = locker_rdlock(table->locker)))
		return set_errno(err);

	if ((err = str_wrlock(str)))
	{
		locker_unlock(table->locker);
		return set_errno(err);
	}

	ret = str_tr_compiled_unlocked(str, table);

	if ((err = str_unlock(str)))
	{
		locker_unlock(table->locker);
		return set_errno(err);
	}

	if ((err = locker_unlock(table->locker)))
		return set_errno(err);

	return ret;
}

/*

=item C<int str_tr_compiled_unlocked(String *str, StringTR *table)>

Equivalent to I<str_tr_compiled(3)> except that C<str> is not write locked.

=cut

*/

int str_tr_compiled_unlocked(String *str, StringTR *table)
{
	if (!str || !table)
		return set_errno(EINVAL);

	return do_tr_compiled((unsigned char *)str->str, &str->length, table);
}

/*

=item C<int tr_compiled(char *str, StringTR *table)>

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

=cut

*/

int tr_compiled(char *str, StringTR *table)
{
	int ret;
	int err;

	if (!str || !table)
		return set_errno(EINVAL);

	if ((err = locker_rdlock(table->locker)))
		return set_errno(err);

	ret = do_tr_compiled((unsigned char *)str, NULL, table);

	if ((err = locker_unlock(table->locker)))
		return set_errno(err);

	return ret;
}

#ifdef HAVE_REGEX_H

/*

=item C<List *str_regexpr(const char *pattern, const String *text, int cflags, int eflags)>

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

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

=cut

*/

List *str_regexpr(const char *pattern, const String *text, int cflags, int eflags)
{
	return str_regexpr_with_locker(NULL, pattern, text, cflags, eflags);
}

/*

=item C<List *str_regexpr_unlocked(const char *pattern, const String *text, int cflags, int eflags)>

Equivalent to I<str_regexpr(3)> except that C<text> is not read locked.

=cut

*/

List *str_regexpr_unlocked(const char *pattern, const String *text, int cflags, int eflags)
{
	return str_regexpr_with_locker_unlocked(NULL, pattern, text, cflags, eflags);
}

/*

=item C<List *str_regexpr_with_locker(Locker *locker, const char *pattern, const String *text, int cflags, int eflags)>

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

=cut

*/

List *str_regexpr_with_locker(Locker *locker, const char *pattern, const String *text, int cflags, int eflags)
{
	List *ret;
	int err;

	if (!pattern || !text)
		return set_errnull(EINVAL);

	if ((err = str_rdlock(text)))
		return set_errnull(err);

	ret = str_regexpr_with_locker_unlocked(locker, pattern, text, cflags, eflags);

	if ((err = str_unlock(text)))
	{
		list_release(ret);
		return set_errnull(err);
	}

	return ret;
}

/*

=item C<List *str_regexpr_with_locker_unlocked(Locker *locker, const char *pattern, const String *text, int cflags, int eflags)>

Equivalent to I<str_regexpr_with_locker(3)> except that C<text> is not read
locked.

=cut

*/

List *str_regexpr_with_locker_unlocked(Locker *locker, const char *pattern, const String *text, int cflags, int eflags)
{
	if (!pattern || !text)
		return set_errnull(EINVAL);

	return regexpr_with_locker(locker, pattern, text->str, cflags, eflags);
}

/*

=item C<List *regexpr(const char *pattern, const char *text, int cflags, int eflags)>

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

=cut

*/

List *regexpr(const char *pattern, const char *text, int cflags, int eflags)
{
	return regexpr_with_locker(NULL, pattern, text, cflags, eflags);
}

/*

=item C<List *regexpr_with_locker(Locker *locker, const char *pattern, const char *text, int cflags, int eflags)>

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

=cut

*/

List *regexpr_with_locker(Locker *locker, const char *pattern, const char *text, int cflags, int eflags)
{
	regex_t compiled[1];
	List *ret;
	int err;

	if (!pattern || !text)
		return set_errnull(EINVAL);

	if ((err = regexpr_compile(compiled, pattern, cflags)))
		return set_errnull(err);

	ret = regexpr_compiled_with_locker(locker, compiled, text, eflags);
	regfree(compiled);

	return ret;
}

/*

=item C<int regexpr_compile(regex_t *compiled, const char *pattern, int cflags)>

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

=cut

*/

int regexpr_compile(regex_t *compiled, const char *pattern, int cflags)
{
	if (!compiled || !pattern)
		return REG_BADPAT;

	return regcomp(compiled, pattern, cflags | REG_EXTENDED);
}

/*

=item C<void regexpr_release(regex_t *compiled)>

Just another name for I<regfree(3)>.

=cut

*/

void regexpr_release(regex_t *compiled)
{
	if (compiled)
		regfree(compiled);
}

/*

=item C<List *str_regexpr_compiled(const regex_t *compiled, const String *text, int eflags)>

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

=cut

*/

List *str_regexpr_compiled(const regex_t *compiled, const String *text, int eflags)
{
	return str_regexpr_compiled_with_locker(NULL, compiled, text, eflags);
}

/*

=item C<List *str_regexpr_compiled_unlocked(const regex_t *compiled, const String *text, int eflags)>

Equivalent to I<str_regexpr_compiled(3)> except that C<text> is not write
locked.

=cut

*/

List *str_regexpr_compiled_unlocked(const regex_t *compiled, const String *text, int eflags)
{
	return str_regexpr_compiled_with_locker_unlocked(NULL, compiled, text, eflags);
}

/*

=item C<List *str_regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const String *text, int eflags)>

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

=cut

*/

List *str_regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const String *text, int eflags)
{
	List *ret;
	int err;

	if (!compiled || !text)
		return set_errnull(EINVAL);

	if ((err = str_rdlock(text)))
		return set_errnull(err);

	ret = str_regexpr_compiled_with_locker_unlocked(locker, compiled, text, eflags);

	if ((err = str_unlock(text)))
	{
		list_release(ret);
		return set_errnull(err);
	}

	return ret;
}

/*

=item C<List *str_regexpr_compiled_with_locker_unlocked(Locker *locker, const regex_t *compiled, const String *text, int eflags)>

Equivalent to I<str_regexpr_compiled_with_locker(3)> except that C<text> is
not read locked.

=cut

*/

List *str_regexpr_compiled_with_locker_unlocked(Locker *locker, const regex_t *compiled, const String *text, int eflags)
{
	if (!compiled || !text)
		return set_errnull(EINVAL);

	return regexpr_compiled_with_locker(locker, compiled, text->str, eflags);
}

/*

=item C<List *regexpr_compiled(const regex_t *compiled, const char *text, int eflags)>

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

=cut

*/

List *regexpr_compiled(const regex_t *compiled, const char *text, int eflags)
{
	return regexpr_compiled_with_locker(NULL, compiled, text, eflags);
}

/*

=item C<List *regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const char *text, int eflags)>

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

=cut

*/

List *regexpr_compiled_with_locker(Locker *locker, const regex_t *compiled, const char *text, int eflags)
{
	regmatch_t match[33];
	List *ret;
	int i;
	int err;

	if (!compiled || !text)
		return set_errnull(EINVAL);

	if ((err = regexec(compiled, text, 33, match, eflags)))
		return set_errnull(err);

	if (!(ret = list_create_with_locker(locker, (list_release_t *)str_release)))
		return NULL;

	for (i = 0; i < 33 && match[i].rm_so != -1; ++i)
	{
		String *m = substr(text, (ssize_t)match[i].rm_so, (ssize_t)(match[i].rm_eo - match[i].rm_so));

		if (!m)
		{
			list_release(ret);
			return NULL;
		}

		if (!list_append(ret, m))
		{
			str_release(m);
			list_release(ret);
			return NULL;
		}
	}

	return ret;
}

/*

=item C<String *str_regsub(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all)>

I<str_regsub(3)> is an interface to POSIX 1003.2 compliant regular
expression matching and substitution. C<pattern> is a regular expression.
C<text> is the string to be searched for matches. C<cflags> is passed to
I<regcomp(3)> along with C<REG_EXTENDED>. C<eflags> is passed to
I<regexec(3)>. C<all> specifies whether to substitute the first match (if
zero) or all matches (if non-zero). C<replacement> specifies the string that
replaces each match. If C<replacement> contains C<"$#"> or C<"${##}"> (where
C<"#"> is a decimal digit), the substring that matches the corresponding
subexpression is interpolated in its place. Up to 32 subexpressions are
supported. If C<replacement> contains C<"$$">, then C<"$"> is interpolated
in its place. The following I<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 I<perl(1)>. Namely,
an C<\l> appearing between a C<\U> and an C<\E> does lowercase the next
character and a C<\E> sequence without a matching C<\L>, C<\U> or C<\Q> is
an error. Also note that only 32 levels of nesting are supported.

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

=cut

*/

String *str_regsub(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all)
{
	regex_t compiled[1];
	String *ret;
	int err;

	if (!pattern || !replacement || !text)
		return set_errnull(EINVAL);

	if ((err = regexpr_compile(compiled, pattern, cflags)))
		return set_errnull(err);

	ret = str_regsub_compiled(compiled, replacement, text, eflags, all);
	regfree(compiled);

	return ret;
}

/*

=item C<String *str_regsub_unlocked(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all)>

Equivalent to I<str_regsub(3)> except that C<text> is not write locked.

=cut

*/

String *str_regsub_unlocked(const char *pattern, const char *replacement, String *text, int cflags, int eflags, int all)
{
	regex_t compiled[1];
	String *ret;
	int err;

	if (!pattern || !replacement || !text)
		return set_errnull(EINVAL);

	if ((err = regexpr_compile(compiled, pattern, cflags)))
		return set_errnull(err);

	ret = str_regsub_compiled_unlocked(compiled, replacement, text, eflags, all);
	regfree(compiled);

	return ret;
}

/*

=item C<String *str_regsub_compiled(const regex_t *compiled, const char *replacement, String *text, int eflags, int all)>

Equivalent to I<str_regsub(3)> but works on an already compiled I<regex_t>,
C<compiled>.

=cut

*/

String *str_regsub_compiled(const regex_t *compiled, const char *replacement, String *text, int eflags, int all)
{
	String *ret;
	int err;

	if (!compiled || !replacement || !text)
		return set_errnull(EINVAL);

	if ((err = str_wrlock(text)))
		return set_errnull(err);

	ret = str_regsub_compiled_unlocked(compiled, replacement, text, eflags, all);

	if ((err = str_unlock(text)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_regsub_compiled_unlocked(const regex_t *compiled, const char *replacement, String *text, int eflags, int all)>

Equivalent to I<str_regsub_compiled(3)> except that C<text> is not write
locked.

=cut

*/

String *str_regsub_compiled_unlocked(const regex_t *compiled, const char *replacement, String *text, int eflags, int all)
{
#define MAX_MATCHES 33
#define MAX_STATES 33
	enum
	{
		RS_LC = 1,
		RS_UC = 2,
		RS_QM = 4,
		RS_FIRST = 8,
		RS_LCFIRST = RS_LC | RS_FIRST,
		RS_UCFIRST = RS_UC | RS_FIRST
	};

	regmatch_t match[MAX_MATCHES];
	String *rep;
	int matches;
	size_t start;
	int states[MAX_STATES];
	int i, s;

	if (!compiled || !replacement || !text)
		return set_errnull(EINVAL);

	for (start = 0, matches = 0; start <= text->length - 1; ++matches)
	{
		if (regexec(compiled, text->str + start, MAX_MATCHES, match, eflags))
			return (matches) ? text : NULL;

		/*
		** Interpolate any $$, $# and ${##} in replacement
		** with subexpression matching substrings
		*/

		if (!(rep = str_create("%s", replacement)))
			return NULL;

		for (i = 0; i < rep->length - 1; ++i)
		{
			if (rep->str[i] == '$')
			{
				if (rep->str[i + 1] == '$')
				{
					if (!str_remove(rep, i))
					{
						str_release(rep);
						return NULL;
					}
				}
				else
				{
					int ref;
					int j = i + 1;

					if (rep->str[j] == '{')
					{
						for (++j, ref = 0; is_digit(rep->str[j]); ++j)
							ref *= 10, ref += rep->str[j] - '0';

						if (rep->str[j] != '}')
						{
							str_release(rep);
							return set_errnull(EINVAL);
						}
					}
					else if (is_digit(rep->str[i + 1]))
					{
						ref = rep->str[j] - '0';
					}
					else
					{
						str_release(rep);
						return set_errnull(EINVAL);
					}

					if (ref < 0 || ref >= MAX_MATCHES || match[ref].rm_so == -1)
					{
						str_release(rep);
						return set_errnull(EINVAL);
					}

					if (!str_replace(rep, i, j + 1 - i, "%.*s", (int)(match[ref].rm_eo - match[ref].rm_so), text->str + match[ref].rm_so))
					{
						str_release(rep);
						return NULL;
					}

					i += match[ref].rm_eo - match[ref].rm_so - 1;
				}
			}
		}

		/* Perform \l \L \u \U \Q \E transformations on replacement */

#define FAIL { str_release(rep); return set_errnull(EINVAL); }
#define PUSH_STATE(state) { if (s >= MAX_STATES - 1) FAIL states[s + 1] = states[s] | (state); ++s; }
#define POP_STATE { if (s == 0) FAIL --s; }
#define REMOVE_CODE { if (!str_remove_range(rep, i, 2)) FAIL --i; }
#define NEG(t) states[s] &= ~(RS_##t##C);

		for (states[s = 0] = 0, i = 0; i < rep->length - 1; ++i)
		{
			if (rep->str[i] == '\\')
			{
				switch (rep->str[i + 1])
				{
					case 'l': { PUSH_STATE(RS_LCFIRST) NEG(U) REMOVE_CODE break; }
					case 'L': { PUSH_STATE(RS_LC) NEG(U) REMOVE_CODE break; }
					case 'u': { PUSH_STATE(RS_UCFIRST) NEG(L) REMOVE_CODE break; }
					case 'U': { PUSH_STATE(RS_UC) NEG(L) REMOVE_CODE break; }
					case 'Q': { PUSH_STATE(RS_QM) REMOVE_CODE break; }
					case 'E': { POP_STATE REMOVE_CODE break; }
					case '\\': { if (!str_remove(rep, i)) FAIL break; }
				}
			}
			else
			{
				if (states[s] & RS_LC)
					rep->str[i] = to_lower(rep->str[i]);

				if (states[s] & RS_UC)
					rep->str[i] = to_upper(rep->str[i]);

				if (states[s] & RS_QM && !is_alnum(rep->str[i]))
					if (!str_insert(rep, i++, "\\"))
						FAIL

				if (states[s] & RS_FIRST)
					POP_STATE
			}
		}

		/* Replace matching substring in text with rep */

		if (!str_replace_str_unlocked(text, start + match[0].rm_so, (ssize_t)(match[0].rm_eo - match[0].rm_so), rep))
		{
			str_release(rep);
			return NULL;
		}

		/* Zero length match (at every position), move on or get stuck */

		if (match[0].rm_so == 0 && match[0].rm_eo == 0)
		{
			++match[0].rm_so;
			++match[0].rm_eo;
		}

		start += match[0].rm_so + rep->length - 1;
		str_release(rep);

		if (!all)
			break;
	}

	return text;
}

#endif

/*

=item C<List *str_fmt(const String *str, size_t line_width, StringAlignment alignment)>

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

=over 4

=item C<ALIGN_LEFT> (C<'<'>)

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

=item C<ALIGN_RIGHT> ('>')

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

=item C<ALIGN_CENTRE> or C<ALIGN_CENTER> (C<'|'>)

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

=item C<ALIGN_FULL> (C<'='>)

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

=back

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

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

=cut

*/

List *str_fmt(const String *str, size_t line_width, StringAlignment alignment)
{
	return str_fmt_with_locker(NULL, str, line_width, alignment);
}

/*

=item C<List *str_fmt_unlocked(const String *str, size_t line_width, StringAlignment alignment)>

Equivalent to I<str_fmt(3)> except that C<str> is not read locked.

=cut

*/

List *str_fmt_unlocked(const String *str, size_t line_width, StringAlignment alignment)
{
	return str_fmt_with_locker_unlocked(NULL, str, line_width, alignment);
}

/*

=item C<List *str_fmt_with_locker(Locker *locker, const String *str, size_t line_width, StringAlignment alignment)>

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

=cut

*/

List *str_fmt_with_locker(Locker *locker, const String *str, size_t line_width, StringAlignment alignment)
{
	List *ret;
	int err;

	if (!str)
		return set_errnull(EINVAL);

	if ((err = str_rdlock(str)))
		return set_errnull(err);

	ret = str_fmt_with_locker_unlocked(locker, str, line_width, alignment);

	if ((err = str_unlock(str)))
	{
		list_release(ret);
		return set_errnull(err);
	}

	return ret;
}

/*

=item C<List *str_fmt_with_locker_unlocked(Locker *locker, const String *str, size_t line_width, StringAlignment alignment)>

Equivalent to I<str_fmt_with_locker(3)> except that C<str> is not read
locked.

=cut

*/

List *str_fmt_with_locker_unlocked(Locker *locker, const String *str, size_t line_width, StringAlignment alignment)
{
	if (!str)
		return set_errnull(EINVAL);

	return fmt_with_locker(locker, str->str, line_width, alignment);
}

/*

=item C<List *fmt(const char *str, size_t line_width, StringAlignment alignment)>

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

=cut

*/

List *fmt(const char *str, size_t line_width, StringAlignment alignment)
{
	return fmt_with_locker(NULL, str, line_width, alignment);
}

/*

=item C<List *fmt_with_locker(Locker *locker, const char *str, size_t line_width, StringAlignment alignment)>

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

=cut

*/

List *fmt_with_locker(Locker *locker, const char *str, size_t line_width, StringAlignment alignment)
{
	List *para;
	String *line = NULL;
	const char *s, *r;
	ssize_t len;

	if (!str)
		return set_errnull(EINVAL);

	switch (alignment)
	{
		case ALIGN_LEFT:
		case ALIGN_RIGHT:
		case ALIGN_FULL:
		{
			if (!(para = list_create_with_locker(locker, (list_release_t *)str_release)))
				return NULL;

			for (s = str; *s; ++s)
			{
				while (is_space(*s))
					++s;

				for (r = s; *r && !is_space(*r); ++r)
				{}

				if (r > s)
				{
					if ((len = str_length(line)) == -1)
						++len;

					if (len + (len != 0) + (r - s) > line_width)
					{
						if (len && !list_append(para, line))
						{
							str_release(line);
							list_release(para);
							return NULL;
						}

						line = NULL;
					}

					if (!line)
					{
						if (!(line = str_create_sized(line_width, "%.*s", r - s, s)))
						{
							list_release(para);
							return NULL;
						}
					}
					else if (!str_append(line, " %.*s", r - s, s))
					{
						str_release(line);
						list_release(para);
						return NULL;
					}

					s = r;

					if (!*s)
						--s;
				}
			}

			if (str_length(line) > 0 && !list_append(para, line))
			{
				str_release(line);
				list_release(para);
				return NULL;
			}

			if (alignment == ALIGN_RIGHT)
			{
				while (list_has_next(para) == 1)
				{
					line = (String *)list_next(para);
					len = str_length(line);

					if (len >= line_width)
						continue;

					if (!str_prepend(line, "%*s", line_width - len, ""))
					{
						list_release(para);
						return NULL;
					}
				}
			}
			else if (alignment == ALIGN_FULL)
			{
				ssize_t lines;
				int i;

				if ((lines = list_length(para)) == -1)
				{
					list_release(para);
					return NULL;
				}

				for (i = 0; i < lines - 1; ++i)
				{
					size_t extra;
					size_t gaps;

					line = (String *)list_item(para, i);
					len = str_length(line);

					if (len >= line_width)
						continue;

					extra = line_width - len;
					gaps = 0;

					for (s = line->str; *s; ++s)
						if (*s == ' ')
							++gaps;

					for (s = line->str; gaps && *s; ++s)
					{
						if (*s == ' ')
						{
							int gap = extra / gaps;

							if (!str_insert(line, s - line->str, "%*s", gap, ""))
							{
								list_release(para);
								return NULL;
							}

							extra -= gap;
							--gaps;
							s += gap;
						}
					}
				}
			}

			break;
		}

		case ALIGN_CENTRE:
		{
			if (!(para = split_with_locker(locker, str, "\n")))
				return NULL;

			while (list_has_next(para) == 1)
			{
				size_t extra;
				line = (String *)list_next(para);
				str_squeeze(line);
				len = str_length(line);

				if (len >= line_width)
					continue;

				extra = (line_width - len) / 2;

				if (extra && !str_prepend(line, "%*s", extra, ""))
				{
					list_release(para);
					return NULL;
				}
			}

			break;
		}

		default:
		{
			return set_errnull(EINVAL);
		}
	}

	return para;
}

/*

C<List *do_split_with_locker(Locker *locker, const char *str, ssize_t length, const char *delim)>

Splits C<str> into tokens separated by sequences of characters occurring in
C<delim>. If C<length> is C<-1>, C<str> is interpreted as a
C<nul>-terminated C string. Otherwise, C<str> is interpreted as an arbitrary
string of length C<length>. On success, returns a new I<List> of I<String>
objects. It is the caller's responsibility to deallocate the list with
I<list_release(3)> or I<list_destroy(3)>. If C<locker> is non-C<null>,
multiple threads accessing the new list will be synchronised by C<locker>.
On error, returns C<null> with C<errno> set appropriately.

*/

static List *do_split_with_locker(Locker *locker, const char *str, ssize_t length, const char *delim)
{
	List *ret;
	const char *s, *r;

	if (!str || !delim)
		return set_errnull(EINVAL);

	if (!(ret = list_create_with_locker(locker, (list_release_t *)str_release)))
		return NULL;

	for (s = str; (length == -1) ? *s : s - str < length; ++s)
	{
		while ((length == -1) ? (*s && strchr(delim, *s)) : (s - str < length && (*s && strchr(delim, *s))))
			++s;

		if (!*delim)
			r = s + 1;
		else
			for (r = s; (length == -1) ? (*r && !strchr(delim, *r)) : (r - str < length && (!*r || !strchr(delim, *r))); ++r)
			{}

		if (r > s)
		{
			String *token = substr(s, 0, r - s);
			if (!token)
			{
				list_release(ret);
				return NULL;
			}

			if (!list_append(ret, token))
			{
				str_release(token);
				list_release(ret);
				return NULL;
			}

			s = r;
			if (!*delim)
				--s;
		}

		if ((length == -1) ? !*s : (s - str == length))
			break;
	}

	return ret;
}

/*

=item C<List *str_split(const String *str, const char *delim)>

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

=cut

*/

List *str_split(const String *str, const char *delim)
{
	return str_split_with_locker(NULL, str, delim);
}

/*

=item C<List *str_split_unlocked(const String *str, const char *delim)>

Equivalent to I<str_split(3)> except that C<str> is not read locked.

=cut

*/

List *str_split_unlocked(const String *str, const char *delim)
{
	return str_split_with_locker_unlocked(NULL, str, delim);
}

/*

=item C<List *str_split_with_locker(Locker *locker, const String *str, const char *delim)>

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

=cut

*/

List *str_split_with_locker(Locker *locker, const String *str, const char *delim)
{
	List *ret;
	int err;

	if (!str || !delim)
		return set_errnull(EINVAL);

	if ((err = str_rdlock(str)))
		return set_errnull(err);

	ret = str_split_with_locker_unlocked(locker, str, delim);

	if ((err = str_unlock(str)))
	{
		list_release(ret);
		return set_errnull(err);
	}

	return ret;
}

/*

=item C<List *str_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim)>

Equivalent to I<str_split_with_locker(3)> except that C<str> is not read
locked.

=cut

*/

List *str_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim)
{
	if (!str || !delim)
		return set_errnull(EINVAL);

	return do_split_with_locker(locker, str->str, str->length - 1, delim);
}

/*

=item C<List *split(const char *str, const char *delim)>

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

=cut

*/

List *split(const char *str, const char *delim)
{
	return split_with_locker(NULL, str, delim);
}

/*

=item C<List *split_with_locker(Locker *locker, const char *str, const char *delim)>

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

=cut

*/

List *split_with_locker(Locker *locker, const char *str, const char *delim)
{
	if (!str || !delim)
		return set_errnull(EINVAL);

	return do_split_with_locker(locker, str, -1, delim);
}

#ifdef HAVE_REGEX_H

/*

=item C<List *str_regexpr_split(const String *str, const char *delim, int cflags, int eflags)>

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

=cut

*/

List *str_regexpr_split(const String *str, const char *delim, int cflags, int eflags)
{
	return str_regexpr_split_with_locker(NULL, str, delim, cflags, eflags);
}

/*

=item C<List *str_regexpr_split_unlocked(const String *str, const char *delim, int cflags, int eflags)>

Equivalent to I<str_regexpr_split(3)> except that C<str> is not read locked.

=cut

*/

List *str_regexpr_split_unlocked(const String *str, const char *delim, int cflags, int eflags)
{
	return str_regexpr_split_with_locker_unlocked(NULL, str, delim, cflags, eflags);
}

/*

=item C<List *str_regexpr_split_with_locker(Locker *locker, const String *str, const char *delim, int cflags, int eflags)>

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

=cut

*/

List *str_regexpr_split_with_locker(Locker *locker, const String *str, const char *delim, int cflags, int eflags)
{
	List *ret;
	int err;

	if (!str || !delim)
		return set_errnull(EINVAL);

	if ((err = str_rdlock(str)))
		return set_errnull(err);

	ret = str_regexpr_split_with_locker_unlocked(locker, str, delim, cflags, eflags);

	if ((err = str_unlock(str)))
	{
		list_release(ret);
		return set_errnull(err);
	}

	return ret;
}

/*

=item C<List *str_regexpr_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim, int cflags, int eflags)>

Equivalent to I<str_regexpr_split_with_locker(3)> except that C<str> is not
read locked.

=cut

*/

List *str_regexpr_split_with_locker_unlocked(Locker *locker, const String *str, const char *delim, int cflags, int eflags)
{
	if (!str || !delim)
		return set_errnull(EINVAL);

	return regexpr_split_with_locker(locker, str->str, delim, cflags, eflags);
}

/*

=item C<List *regexpr_split(const char *str, const char *delim, int cflags, int eflags)>

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

=cut

*/

List *regexpr_split(const char *str, const char *delim, int cflags, int eflags)
{
	return regexpr_split_with_locker(NULL, str, delim, cflags, eflags);
}

/*

=item C<List *regexpr_split_with_locker(Locker *locker, const char *str, const char *delim, int cflags, int eflags)>

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

=cut

*/

List *regexpr_split_with_locker(Locker *locker, const char *str, const char *delim, int cflags, int eflags)
{
	List *ret;
	String *token;
	regex_t compiled[1];
	regmatch_t match[1];
	int start, matches;
	int err;

	if (!str || !delim)
		return set_errnull(EINVAL);

	if ((err = regexpr_compile(compiled, delim, cflags)))
		return set_errnull(err);

	if (!(ret = list_create_with_locker(locker, (list_release_t *)str_release)))
		return NULL;

	for (start = 0, matches = 0; str[start]; ++matches)
	{
		if (regexec(compiled, str + start, 1, match, eflags))
			break;

		/* Zero length match (at every position), make a token of each character */

		if (match[0].rm_so == 0 && match[0].rm_eo == 0)
		{
			++match[0].rm_so;
			++match[0].rm_eo;
		}

		/* Make a token of any text before the match */

		if (match[0].rm_so)
		{
			if (!(token = substr(str, start, (ssize_t)match[0].rm_so)))
			{
				list_release(ret);
				return NULL;
			}

			if (!list_append(ret, token))
			{
				str_release(token);
				list_release(ret);
				return NULL;
			}
		}

		start += match[0].rm_eo;
	}

	/* Make a token of any text after the last match */

	if (str[start])
	{
		if (!(token = str_create("%s", str + start)))
		{
			list_release(ret);
			return NULL;
		}

		if (!list_append(ret, token))
		{
			str_release(token);
			list_release(ret);
			return NULL;
		}
	}

	return ret;
}

#endif

/*

=item C<String *str_join(const List *list, const char *delim)>

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

=cut

*/

String *str_join(const List *list, const char *delim)
{
	return str_join_with_locker(NULL, list, delim);
}

/*

=item C<String *str_join_unlocked(const List *list, const char *delim)>

Equivalent to I<str_join(3)> except that C<list> is not read locked.

=cut

*/

String *str_join_unlocked(const List *list, const char *delim)
{
	return str_join_with_locker_unlocked(NULL, list, delim);
}

/*

=item C<String *str_join_with_locker(Locker *locker, const List *list, const char *delim)>

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

=cut

*/

String *str_join_with_locker(Locker *locker, const List *list, const char *delim)
{
	String *ret;
	int err;

	if (!list)
		return set_errnull(EINVAL);

	if ((err = list_rdlock(list)))
		return set_errnull(err);

	ret = str_join_with_locker_unlocked(locker, list, delim);

	if ((err = list_unlock(list)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_join_with_locker_unlocked(Locker *locker, const List *list, const char *delim)>

Equivalent to I<str_join_with_locker(3)> except that C<list> is not read
locked.

=cut

*/

String *str_join_with_locker_unlocked(Locker *locker, const List *list, const char *delim)
{
	String *ret;
	String *del;
	Lister *lister;
	int i;

	if (!list)
		return set_errnull(EINVAL);

	if (!(ret = str_create_with_locker(locker, NULL)))
		return NULL;

	if (!(del = str_create(delim ? "%s" : NULL, delim)))
	{
		str_release(ret);
		return NULL;
	}

	if (!(lister = lister_create_unlocked(list)))
	{
		str_release(ret);
		str_release(del);
		return NULL;
	}

	for (i = 0; lister_has_next(lister) == 1; ++i)
	{
		String *s = (String *)lister_next(lister);

		if (i && !str_append_str(ret, del))
		{
			str_release(ret);
			str_release(del);
			lister_release_unlocked(lister);
			return NULL;
		}

		if (s && !str_append_str(ret, s))
		{
			str_release(ret);
			str_release(del);
			lister_release_unlocked(lister);
			return NULL;
		}
	}

	str_release(del);
	lister_release_unlocked(lister);

	return ret;
}

/*

=item C<String *join(const List *list, const char *delim)>

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

=cut

*/

String *join(const List *list, const char *delim)
{
	return join_with_locker(NULL, list, delim);
}

/*

=item C<String *join_with_locker(Locker *locker, const List *list, const char *delim)>

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

=cut

*/

String *join_with_locker(Locker *locker, const List *list, const char *delim)
{
	String *ret;
	String *del;
	Lister *lister;
	int i;

	if (!list)
		return set_errnull(EINVAL);

	if (!(ret = str_create_with_locker(locker, NULL)))
		return NULL;

	if (!(del = str_create(delim ? "%s" : NULL, delim)))
	{
		str_release(ret);
		return NULL;
	}

	if (!(lister = lister_create((List *)list)))
	{
		str_release(ret);
		str_release(del);
		return NULL;
	}

	for (i = 0; lister_has_next(lister) == 1; ++i)
	{
		char *s = (char *)lister_next(lister);

		if (i && !str_append_str(ret, del))
		{
			str_release(ret);
			str_release(del);
			lister_release(lister);
			return NULL;
		}

		if (s && !str_append(ret, "%s", s))
		{
			str_release(ret);
			str_release(del);
			lister_release(lister);
			return NULL;
		}
	}

	str_release(del);
	lister_release(lister);

	return ret;
}

/*

=item C<int str_soundex(const String *str)>

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

=cut

*/

int str_soundex(const String *str)
{
	int ret;
	int err;

	if (!str)
		return set_errno(EINVAL);

	if ((err = str_rdlock(str)))
		return set_errno(err);

	ret = str_soundex_unlocked(str);

	if ((err = str_unlock(str)))
		return set_errno(err);

	return ret;
}

/*

=item C<int str_soundex_unlocked(const String *str)>

Equivalent to I<str_soundex(3)> except that C<str> is not read locked.

=cut

*/

int str_soundex_unlocked(const String *str)
{
	if (!str)
		return set_errno(EINVAL);

	return soundex(str->str);
}

/*

=item C<int soundex(const char *str)>

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

=cut

*/

int soundex(const char *str)
{
	const char * const soundex_table = "\000123\00012\000\00022455\00012623\0001\0002\0002";
	union { char c[4]; int i; } soundex;
	int last, small;

	if (!str)
		return set_errno(EINVAL);

	soundex.i = 0;

	for (last = -1, small = 0; *str && small < 4; ++str)
	{
		if (is_alpha(*str))
		{
			int code = to_upper(*str);

			if (small == 0)
			{
				soundex.c[small++] = code;
				last = soundex_table[code - 'A'];
			}
			else
			{
				if ((code = soundex_table[code - 'A']) != last)
				{
					if (code != 0)
						soundex.c[small++] = code;

					last = code;
				}
			}
		}
	}

	while (small < 4)
		soundex.c[small++] = '0';

	return ntohl(soundex.i);
}

/*

=item C<String *str_trim(String *str)>

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

=cut

*/

String *str_trim(String *str)
{
	String *ret;
	int err;

	if (!str)
		return set_errnull(EINVAL);

	if ((err = str_wrlock(str)))
		return set_errnull(err);

	ret = str_trim_unlocked(str);

	if ((err = str_unlock(str)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_trim_unlocked(String *str)>

Equivalent to I<str_trim(3)> except that C<str> is not write locked.

=cut

*/

String *str_trim_unlocked(String *str)
{
	char *s;

	if (!str)
		return set_errnull(EINVAL);

	for (s = str->str; is_space(*s); ++s)
	{}

	if (s > str->str)
	{
		if (!str_remove_range_unlocked(str, 0, s - str->str))
			return NULL;
	}

	for (s = str->str + str->length - 1; s > str->str && is_space(s[-1]); --s)
	{}

	if (is_space(*s))
	{
		if (!str_remove_range_unlocked(str, s - str->str, str->length - 1 - (s - str->str)))
			return NULL;
	}

	return str;
}

/*

=item C<char *trim(char *str)>

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

=cut

*/

char *trim(char *str)
{
	char *s;
	size_t len;

	if (!str)
		return set_errnull(EINVAL);

	for (s = str; is_space(*s); ++s)
	{}

	len = strlen(s);

	if (s > str)
		memmove(str, s, len + 1);

	for (s = str + len; s > str && is_space(*--s); )
		*s = '\0';

	return str;
}

/*

=item C<String *str_trim_left(String *str)>

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

=cut

*/

String *str_trim_left(String *str)
{
	String *ret;
	int err;

	if (!str)
		return set_errnull(EINVAL);

	if ((err = str_wrlock(str)))
		return set_errnull(err);

	ret = str_trim_left_unlocked(str);

	if ((err = str_unlock(str)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_trim_left_unlocked(String *str)>

Equivalent to I<str_trim_left(3)> except that C<str> is not write locked.

=cut

*/

String *str_trim_left_unlocked(String *str)
{
	char *s;

	if (!str)
		return set_errnull(EINVAL);

	for (s = str->str; is_space(*s); ++s)
	{}

	if (s > str->str)
		if (!str_remove_range_unlocked(str, 0, s - str->str))
			return NULL;

	return str;
}

/*

=item C<char *trim_left(char *str)>

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

=cut

*/

char *trim_left(char *str)
{
	char *s;
	size_t len;

	if (!str)
		return set_errnull(EINVAL);

	for (s = str; is_space(*s); ++s)
	{}

	len = strlen(s);

	if (s > str)
		memmove(str, s, len + 1);

	return str;
}

/*

=item C<String *str_trim_right(String *str)>

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

=cut

*/

String *str_trim_right(String *str)
{
	String *ret;
	int err;

	if (!str)
		return set_errnull(EINVAL);

	if ((err = str_wrlock(str)))
		return set_errnull(err);

	ret = str_trim_right_unlocked(str);

	if ((err = str_unlock(str)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_trim_right_unlocked(String *str)>

Equivalent to I<str_trim_right(3)> except that C<str> is not write locked.

=cut

*/

String *str_trim_right_unlocked(String *str)
{
	char *s;

	if (!str)
		return set_errnull(EINVAL);

	for (s = str->str + str->length - 1; s > str->str && is_space(s[-1]); --s)
	{}

	if (is_space(*s))
		if (!str_remove_range_unlocked(str, s - str->str, str->length - 1 - (s - str->str)))
			return NULL;

	return str;
}

/*

=item C<char *trim_right(char *str)>

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

=cut

*/

char *trim_right(char *str)
{
	char *s;
	size_t len;

	if (!str)
		return set_errnull(EINVAL);

	len = strlen(str);

	for (s = str + len; s > str && is_space(*--s); )
		*s = '\0';

	return str;
}

/*

=item C<String *str_squeeze(String *str)>

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

=cut

*/

String *str_squeeze(String *str)
{
	String *ret;
	int err;

	if (!str)
		return set_errnull(EINVAL);

	if ((err = str_wrlock(str)))
		return set_errnull(err);

	ret = str_squeeze_unlocked(str);

	if ((err = str_unlock(str)))
		return set_errnull(err);

	return ret;
}

/*

=item C<String *str_squeeze_unlocked(String *str)>

Equivalent to I<str_squeeze(3)> except that C<str> is not write locked.

=cut

*/

String *str_squeeze_unlocked(String *str)
{
	char *s, *r;
	int started = 0;
	int was_space = 0;

	if (!str)
		return set_errnull(EINVAL);

	for (r = s = str->str; s - str->str < str->length - 1; ++s)
	{
		if (!is_space(*s))
		{
			if (was_space && started)
				*r++ = ' ';
			*r++ = *s;
			started = 1;
		}

		was_space = is_space(*s);
	}

	if (r - str->str < str->length)
		if (!str_remove_range_unlocked(str, r - str->str, str->length - 1 - (r - str->str)))
			return NULL;

	return str;
}

/*

=item C<char *squeeze(char *str)>

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

=cut

*/

char *squeeze(char *str)
{
	char *s, *r;
	int started = 0;
	int was_space = 0;

	if (!str)
		return set_errnull(EINVAL);

	for (r = s = str; *s; ++s)
	{
		if (!is_space(*s))
		{
			if (was_space && started)
				*r++ = ' ';
			*r++ = *s;
			started = 1;
		}

		was_space = is_space(*s);
	}

	*r = '\0';

	return str;
}

/*

=item C<String *str_quote(const String *str, const char *quotable, char quote_char)>

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

=cut

*/

String *str_quote(const String *str, const char *quotable, char quote_char)
{
	return str_quote_with_locker(NULL, str, quotable, quote_char);
}

/*

=item C<String *str_quote_unlocked(const String *str, const char *quotable, char quote_char)>

Equivalent to I<str_quote(3)> except that C<str> is not read locked.

=cut

*/

String *str_quote_unlocked(const String *str, const char *quotable, char quote_char)
{
	return str_quote_with_locker_unlocked(NULL, str, quotable, quote_char);
}

/*

=item C<String *str_quote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char)>

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

=cut

*/

String *str_quote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char)
{
	String *ret;
	size_t i;

	if (!str || !quotable)
		return set_errnull(EINVAL);

	if (!(ret = str_copy_with_locker(locker, str)))
		return NULL;

	for (i = 0; i < ret->length - 1; ++i)
	{
		if (ret->str[i] && strchr(quotable, ret->str[i]))
		{
			if (!str_insert(ret, i++, "%c", quote_char))
			{
				str_release(ret);
				return NULL;
			}
		}
	}

	return ret;
}

/*

=item C<String *str_quote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char)>

Equivalent to I<str_quote_with_locker(3)> except that C<str> is not read locked.

=cut

*/

String *str_quote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char)
{
	String *ret;
	size_t i;

	if (!str || !quotable)
		return set_errnull(EINVAL);

	if (!(ret = str_copy_with_locker_unlocked(locker, str)))
		return NULL;

	for (i = 0; i < ret->length - 1; ++i)
	{
		if (ret->str[i] && strchr(quotable, ret->str[i]))
		{
			if (!str_insert(ret, i++, "%c", quote_char))
			{
				str_release(ret);
				return NULL;
			}
		}
	}

	return ret;
}

/*

=item C<String *quote(const char *str, const char *quotable, char quote_char)>

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

=cut

*/

String *quote(const char *str, const char *quotable, char quote_char)
{
	return quote_with_locker(NULL, str, quotable, quote_char);
}

/*

=item C<String *quote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char)>

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

=cut

*/

String *quote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char)
{
	String *ret;
	size_t i;

	if (!str || !quotable)
		return set_errnull(EINVAL);

	if (!(ret = str_create_with_locker(locker, "%s", str)))
		return NULL;

	for (i = 0; i < ret->length - 1; ++i)
	{
		if (strchr(quotable, ret->str[i]))
		{
			if (!str_insert(ret, i++, "%c", quote_char))
			{
				str_release(ret);
				return NULL;
			}
		}
	}

	return ret;
}

/*

=item C<String *str_unquote(const String *str, const char *quotable, char quote_char)>

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

=cut

*/

String *str_unquote(const String *str, const char *quotable, char quote_char)
{
	return str_unquote_with_locker(NULL, str, quotable, quote_char);
}

/*

=item C<String *str_unquote_unlocked(const String *str, const char *quotable, char quote_char)>

Equivalent to I<str_unquote(3)> except that C<str> is not read locked.

=cut

*/

String *str_unquote_unlocked(const String *str, const char *quotable, char quote_char)
{
	return str_unquote_with_locker_unlocked(NULL, str, quotable, quote_char);
}

/*

=item C<String *str_unquote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char)>

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

=cut

*/

String *str_unquote_with_locker(Locker *locker, const String *str, const char *quotable, char quote_char)
{
	String *ret;
	int i;

	if (!str || !quotable)
		return set_errnull(EINVAL);

	if (!(ret = str_copy_with_locker(locker, str)))
		return NULL;

	for (i = 0; i < (int)ret->length - 2; ++i)
	{
		if (ret->str[i] == quote_char && ret->str[i + 1] && strchr(quotable, ret->str[i + 1]))
		{
			if (!str_remove(ret, i))
			{
				str_release(ret);
				return NULL;
			}
		}
	}

	return ret;
}

/*

=item C<String *str_unquote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char)>

Equivalent to I<str_unquote_with_locker(3)> except that C<str> is not read
locked.

=cut

*/

String *str_unquote_with_locker_unlocked(Locker *locker, const String *str, const char *quotable, char quote_char)
{
	String *ret;
	int i;

	if (!str || !quotable)
		return set_errnull(EINVAL);

	if (!(ret = str_copy_with_locker_unlocked(locker, str)))
		return NULL;

	for (i = 0; i < (int)ret->length - 2; ++i)
	{
		if (ret->str[i] == quote_char && ret->str[i + 1] && strchr(quotable, ret->str[i + 1]))
		{
			if (!str_remove(ret, i))
			{
				str_release(ret);
				return NULL;
			}
		}
	}

	return ret;
}

/*

=item C<String *unquote(const char *str, const char *quotable, char quote_char)>

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

=cut

*/

String *unquote(const char *str, const char *quotable, char quote_char)
{
	return unquote_with_locker(NULL, str, quotable, quote_char);
}

/*

=item C<String *unquote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char)>

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

=cut

*/

String *unquote_with_locker(Locker *locker, const char *str, const char *quotable, char quote_char)
{
	String *ret;
	int i;

	if (!str || !quotable)
		return set_errnull(EINVAL);

	if (!(ret = str_create_with_locker(locker, "%s", str)))
		return NULL;

	for (i = 0; i < (int)ret->length - 2; ++i)
	{
		if (ret->str[i] == quote_char && strchr(quotable, ret->str[i + 1]))
		{
			if (!str_remove(ret, i))
			{
				str_release(ret);
				return NULL;
			}
		}
	}

	return ret;
}

/*

C<static String *do_encode_with_locker(Locker *locker, const char *str, ssize_t length, const char *uncoded, const char *coded, char quote_char, int printable)>

Performs encoding as described in I<str_encode(3)>.

*/

static String *do_encode_with_locker(Locker *locker, const char *str, size_t length, const char *uncoded, const char *coded, char quote_char, int printable)
{
	static const char hex[] = "0123456789abcdef";
	String *encoded;
	const char *target;
	const char *s;

	if (!str || !uncoded || !coded)
		return set_errnull(EINVAL);

	if (!(encoded = str_create_with_locker_sized(locker, length * 4 + 1, "")))
		return NULL;

	for (s = str; s - str < length; ++s)
	{
		if (*s && (target = strchr(uncoded, (unsigned char)*s)))
		{
			if (!str_append(encoded, "%c%c", (unsigned char)quote_char, coded[target - uncoded]))
			{
				str_release(encoded);
				return NULL;
			}
		}
		else if (printable && !is_print(*s))
		{
			if (!str_append(encoded, "%cx%c%c", (unsigned char)quote_char, hex[(unsigned char)*s >> 4], hex[(unsigned char)*s & 0x0f]))
			{
				str_release(encoded);
				return NULL;
			}
		}
		else
		{
			if (!str_append(encoded, "%c", (unsigned char)*s))
			{
				str_release(encoded);
				return NULL;
			}
		}
	}

	return encoded;
}

/*

C<static String *do_decode_with_locker(Locker *locker, const char *str, ssize_t length, const char *uncoded, const char *coded, char quote_char, int printable)>

Performs decoding as described in I<str_decode(3)>.

*/

static String *do_decode_with_locker(Locker *locker, const char *str, size_t length, const char *uncoded, const char *coded, char quote_char, int printable)
{
	String *decoded;
	const char *start;
	const char *slosh;
	char *target;

	if (!str || !uncoded || !coded)
		return set_errnull(EINVAL);

	if (!(decoded = str_create_with_locker_sized(locker, length + 1, "")))
		return NULL;

	for (start = str; start - str < length; start = slosh + 1)
	{
		for (slosh = start; slosh - str < length; ++slosh)
			if (*slosh == quote_char)
				break;

		if (slosh - str == length)
			break;

		if (printable)
		{
			int digits = 0;
			const char *s = slosh + 1;
			char c = '\0';

			if (is_digit(*s) && *s <= '7')
			{
				--s;

				do
				{
					++digits;
					c <<= 3, c |= *++s - '0';
				}
				while (digits < 3 && is_digit(s[1]) && s[1] <= '7');
			}
			else if (*s == 'x' && is_xdigit(s[1]))
			{
				do
				{
					++digits;
					c <<= 4;

					switch (*++s)
					{
						case '0': case '1': case '2': case '3': case '4':
						case '5': case '6': case '7': case '8': case '9':
							c |= *s - '0';
							break;
						case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
							c |= *s - 'a' + 10;
							break;
						case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
							c |= *s - 'A' + 10;
							break;
					}
				}
				while (digits < 2 && is_xdigit(s[1]));
			}

			if (digits)
			{
				if (!str_append(decoded, "%*.*s%c", slosh - start, slosh - start, start, c))
				{
					str_release(decoded);
					return NULL;
				}

				slosh = s; /* Skip over ASCII code */
				continue;
			}
		}

		if (!slosh[1] || !(target = strchr(coded, slosh[1])))
		{
			if (!str_append(decoded, "%*.*s%c", slosh - start, slosh - start, start, quote_char))
			{
				str_release(decoded);
				return NULL;
			}

			continue;
		}

		if (!str_append(decoded, "%*.*s%c", slosh - start, slosh - start, start, uncoded[target - coded]))
		{
			str_release(decoded);
			return NULL;
		}

		++slosh; /* Skip over quoted char */
	}

	if (!str_append(decoded, "%s", start))
	{
		str_release(decoded);
		return NULL;
	}

	return decoded;
}

/*

=item C<String *str_encode(const String *str, const char *uncoded, const char *coded, char quote_char, int printable)>

Returns a copy of C<str> with every occurrence in C<str> of characters in
C<uncoded> replaced with C<quote_char> followed by the corresponding (by
position) character in C<coded>. If C<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
I<str_release(3)> or I<str_destroy(3)>. On error, returns C<null> with
C<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);

=cut

*/

String *str_encode(const String *str, const char *uncoded, const char *coded, char quote_char, int printable)
{
	return str_encode_with_locker(NULL, str, uncoded, coded, quote_char, printable);
}

/*

=item C<String *str_encode_unlocked(const String *str, const char *uncoded, const char *coded, char quote_char, int printable)>

Equivalent to I<str_encode(3)> except that C<str> is not read locked.

=cut

*/

String *str_encode_unlocked(const String *str, const char *uncoded, const char *coded, char quote_char, int printable)
{
	return str_encode_with_locker_unlocked(NULL, str, uncoded, coded, quote_char, printable);
}

/*

=item C<String *str_encode_with_locker(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable)>

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

=cut

*/

String *str_encode_with_locker(Locker *locker, const String *str, const char *uncoded, const char *coded, char quote_char, int printable)
{
	String *ret;
	int err;

	if (!str || !uncoded || !coded)
		return set_errnull(EINVAL);

	if ((err = str_rdl