/*
 * Copyright (c) 2000-2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 *	$Id: str.h,v 1.76 2007/11/14 06:03:08 ca Exp $
 */

#ifndef SM_STR_H
#define SM_STR_H 1

#include "sm/generic.h"
#include "sm/types.h"
#include "sm/error.h"
#include "sm/rpool.h"
#include "sm/limits.h"
#include "sm/rdstr.h"

typedef struct sm_str_S sm_str_T, *sm_str_P;

#ifndef SM_STR_CHECK
# define SM_STR_CHECK	1
#endif

/*
**  sm_str_T -- Stores buf data and length information.
**
**	Members:
**		sm_str_base -- uchar * to data (doesn't need to end with '\0').
**		sm_str_size -- Total bytes allocated.
**		sm_str_len -- Total number of characters in buf.
**		sm_str_max -- Maximum number of characters in buf.
**		sm_str_rpool -- rpool to allocate from.
**
**	Invariants:
**		sm_str_len <= sm_str_size <= sm_str_max
**
**	Notices:
**		- the structure could be extended to a general buffer,
**		like iobuf (VSTRING) to put/get characters out of it.
**		This would make loops through the data more abstract,
**		e.g., instead of
**		for (i = 0; i < len(str); i++)
**			do something with elem(str, i)
**		it could be
**		for (ch = first(str); !EOS(str); ch = get(str))
**			do something with ch
**		however, that requires read/write pointers and hence
**		there can't be independent operations on the string.
**
**		- we could use reference counting to avoid free()ing
**		a buf twice if it is used as:
**		d = sm_str_cat(s1, s2)
**		because now d and s1 are identical (unless the
**		operation fails).
**
**		- a flag field could note whether this is statically
**		allocated and can't be changed,
**		whether the current size is fixed (could be achieve
**		wiht sm_str_max)
**
**		- reference counting could also be used for the data
**		part, which may allow sharing (use copy-on-write).
**
**		- this structure is only "public" due to macros accessing
**		components, e.g., sm_str_len(), otherwise the
**		data structure could be in buf.c or an internal
**		include file.
**
**		- sm_str_max should be always set and it should be
**		less than INT_MAX. Then we can get rid of some
**		overflow tests (sm_str_size wrap-around). Moreover,
**		this would be better for some other code which
**		assumes int is good enough as index.
**		XXX should we enforce this?
**		SM_ASSERT(sm_str_max != 0 && sm_str_max < INT_MAX)
*/

struct sm_str_S
{
	sm_magic_T	 sm_magic;
	uchar		*sm_str_base;
	uint		 sm_str_len;
	uint		 sm_str_size;
	uint		 sm_str_max;
#if SM_STR_READ
	uint		 sm_str_rd;	/* read index; not used anywhere */
	/* matches sm_rcb_rw for sm_rcb_T */
#endif
	sm_rpool_P	 sm_str_rpool;
};

sm_str_P sm_str_new(sm_rpool_P _rpool, uint _len, uint _maxlen);
sm_str_P sm_str_crt(sm_rpool_P _rpool, uchar *_str, uint _len, uint _maxlen);

/* assign necessary elements to an existing str_buf */
#define sm_str_assign(str, rpool, s, len, maxlen) \
	do					\
	{					\
		(str).sm_str_base = (s);	\
		(str).sm_str_size = (len);	\
		(str).sm_str_len = (len);	\
		(str).sm_str_max = (maxlen);	\
		(str).sm_str_rpool = (rpool);	\
		(str).sm_magic = SM_STR_MAGIC;	\
	} while (0)

/* save an existing str */
#define SM_STR_SAVE(str, save)				\
	do						\
	{						\
		(save).sm_str_base = (str).sm_str_base;	\
		(save).sm_str_size = (str).sm_str_size;	\
		(save).sm_str_len = (str).sm_str_len;	\
		(save).sm_str_max = (str).sm_str_max;	\
		(save).sm_str_rpool = (str).sm_str_rpool; \
		(save).sm_magic = SM_STR_MAGIC;		\
	} while (0)

/* restore str from "save" */
#define SM_STR_RESTORE(str, save) SM_STR_SAVE((save), (str))

sm_ret_T	sm_str_resize_data(sm_str_P _str, uint _len);

#if SM_STR_CHECK
uint		 sm_str_getlen(sm_str_P _str);
uint		 sm_str_getsize(sm_str_P _str);
int		 sm_str_setmax(sm_str_P _str, uint _max);
uint		 sm_str_getmax(sm_str_P _str);
sm_ret_T	 sm_str_space(sm_str_P _str, uint _new_len);
sm_ret_T	 sm_str_wr_elem(sm_str_P _str, uint _i, uchar _c);
uchar		 sm_str_rd_elem(sm_str_P _str, uint _i);
#else /* SM_STR_CHECK */
# define sm_str_getlen(str)	((str)->sm_str_len)
# define sm_str_getsize(str)	((str)->sm_str_size)
# define sm_str_setmax(str, m)	(str)->sm_str_max = (m)
# define sm_str_getmax(str)	(str)->sm_str_max
# define sm_str_space(str, new_len)			\
	(((new_len) <= (str)->sm_str_size) ? SM_SUCCESS	\
		: sm_str_resize_data((str), (new_len)))
# define sm_str_wr_elem(str, i, c)	(str)->sm_str_base[i] = (c)
# define sm_str_rd_elem(str, i)	((str)->sm_str_base[i])
#endif /* SM_STR_CHECK */

# define SM_STR_PUT(str, c)	(((str)->sm_str_len == (str)->sm_str_size) \
	? sm_str_put((str), (c))					\
	: ((str)->sm_str_base[(str)->sm_str_len] = (c),			\
	   (str)->sm_str_len++, SM_SUCCESS))

#define SM_STR_SETLEN(str, len)	(SM_ASSERT((len) <= (str)->sm_str_size), ((str)->sm_str_len) = (len))

#define sm_str_data(str)	((str)->sm_str_base)

void		 sm_str_free(sm_str_P _str);
#define SM_STR_FREE(ptr) do			\
	{					\
		if ((ptr) != NULL)		\
		{				\
			sm_str_free(ptr);	\
			(ptr) = NULL;		\
		}				\
	} while (0)

#if SM_STR_CHECK
void		 sm_str_clr(sm_str_P _str);
#else
# define sm_str_clr(str)	(str)->sm_str_len = 0
#endif

#define SM_STR_CLR(ptr) do {	\
		if ((ptr) != NULL)		\
			sm_str_clr(ptr);	\
	} while (0)

sm_ret_T	 sm_str_shorten(sm_str_P _str, int _l);

/* last character if string is non-empty otherwise -1 (XXX is -1 ok?) */
# define SM_STR_LAST(str) ((str)->sm_str_len > 0 ? ((str)->sm_str_base[(str)->sm_str_len - 1]) : (-1))

uchar		*sm_str_getdata(sm_str_P _str);
uchar		*sm_str_copydata(sm_rpool_P _rpool, sm_str_P _str);

sm_str_P	 sm_str_dup(sm_rpool_P _rpool, const sm_str_P _src);
sm_ret_T	 sm_str_cpy(sm_str_P _dst, const sm_str_P _src);
sm_ret_T	 sm_str_dc(sm_rpool_P _rpool, sm_str_P *_pdst, const sm_str_P _src);
sm_ret_T	 sm_str_cat(sm_str_P _dst, const sm_str_P _src);
sm_ret_T	 sm_str_catpart(sm_str_P _dst, const sm_rdstr_P _src, uint _first, uint _last);
sm_ret_T	 sm_str_catv(sm_str_P _dst, int _n, ...);
sm_ret_T	 sm_str_catmv(sm_str_P _dst, int _n, ...);

sm_ret_T	 sm_str_rm_trail(sm_str_P _str, const char *_rmchars);
sm_ret_T	 sm_str_rm_trail_sp(sm_str_P _str);

bool		 sm_str_eq(const sm_str_P _s1, const sm_str_P _s2);
int		 sm_str_cmp(const sm_str_P _s1, const sm_str_P _s2);
int		 sm_str_casecmp(const sm_str_P _s1, const sm_str_P _s2);

sm_ret_T	 sm_str_scopyn(sm_str_P _str, const char *_src, uint _len);
sm_ret_T	 sm_str_scopy(sm_str_P _str, const char *_src);
sm_str_P	 sm_str_scpy(sm_rpool_P _rpool, const char *_str, uint _maxlen);
sm_str_P	 sm_str_scpy0(sm_rpool_P _rpool, const char *_src, uint _maxlen);
sm_str_P	 sm_str_scpyn(sm_rpool_P _rpool, const char *_src, uint _n,
				uint _maxlen);
sm_ret_T	 sm_str_scat(sm_str_P _str, const char *_append);
sm_ret_T	 sm_str_scatn(sm_str_P _str, const char *_append, uint _len);
sm_ret_T	 sm_str_scatv(sm_str_P _dst, int n, ...);

sm_str_P	 sm_str_scpyn0(sm_rpool_P _rpool, const char *_src, uint _n, uint _maxlen);
sm_ret_T	 sm_str_scatn0(sm_str_P _str, const char *_append, uint _len);
sm_ret_T	 sm_str_scat0(sm_str_P _str, const char *_append);

sm_ret_T	 sm_str_put(sm_str_P _str, uchar _c);
sm_ret_T	 sm_str_term(sm_str_P _str);
sm_ret_T	 sm_str_putuint32(sm_str_P _str, uint32_t _n);
uint		 sm_str_sanitize(sm_str_P _str);
sm_ret_T	 sm_str_split(sm_str_P _str, uchar _delim, bool _excl, sm_str_P _left, sm_str_P _right);
sm_ret_T	 sm_str_splitidx(sm_str_P _str, uint _i, bool _excl, sm_str_P _left, sm_str_P _right);
sm_ret_T	 sm_str2argv(sm_str_P _str, uint _offset, uint _maxargs, uint *_argv);
sm_ret_T	 sm_str2args(sm_str_P _str, uint _offset, uint _maxargs, uint *_argnames, uint *_argvalues);

#if SIZEOF_OFF_T == 4
# define sm_str_put3off_t(str, n1, n2, n3) sm_str_put3uint32(str, n1, n2, n3)
# define sm_str_putoff_t(str, n) sm_str_putuint32(str, n)
#elif SIZEOF_OFF_T == 8
# define sm_str_put3off_t(str, n1, n2, n3) sm_str_put3uint64(str, n1, n2, n3)
# define sm_str_putoff_t(str, n) sm_str_putuint64(rcb, n)
#else
  ERROR _SIZEOF_OFF_T is neither 4 not 8: SIZEOF_OFF_T
#endif


bool		 sm_str2lower(sm_str_P _str);
sm_ret_T	 sm_str_shift(sm_str_P _str, uint _off);
sm_ret_T	 sm_str_unquote(sm_str_P _str);

sm_ret_T	 sm_str_fchrquoted(sm_str_P _str, const uchar *_delim, uint *_pidx);
sm_ret_T	 sm_str_fchr(sm_str_P _str, const uchar *_delim, uint *_pidx);
sm_ret_T	 sm_str_lchr(sm_str_P _str, const uchar *_delim, uint *_pidx);

sm_ret_T	 xtextify(char *_t, char *_taboo, sm_str_P _str);

#endif /* SM_STR_H */


syntax highlighted by Code2HTML, v. 0.9.1