/*
 *	Dynamic string library
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *
 *	This file is part of JOE (Joe's Own Editor)
 */

/***
 *
 * This is a dynamic string library which supports strings which automatically
 * resize themselves when needed.  The strings know their own size, so getting
 * the length of a string is a fast operation and storing zeroes in the
 * strings is permissible.
 *
 * The strings are stored in malloc blocks and have the following format:
 *
 *   <bksize><length><string><zero>
 *
 * 'bksize' and 'length' are ints which give the size of the malloc block
 * and the length of the string.  A zero character always follows the string
 * for compatibility with normal C zero-terminated strings.  The zero is not
 * counted in the string length.
 *
 * To further the compatibility with C strings, the address of a dynamic string
 * is at <string> above, not at <bksize> (whose address is the start of the
 * malloc block).  Thus, dynamic strings can be passed as arguments to UNIX
 * operating system functions and C library function, but they can not be freed
 * with free()- a special function is provided in this library for freeing
 * dynamic strings.
 *
 * The primary dynamic string function is:
 *
 * char *vsncpy(char *d, int off, char *s, int len);
 *                              Copy a block of characters at address 's' of
 *				length 'len' onto the dynamic string 'd' at
 *				offset 'off'.  The dynamic string is expanded
 *				to handle any values of 'len' and 'off' which
 *				might be given.  If 'off' is greater than the
 *				length of the string, SPACEs are placed in the
 *				gap.  If 'd' is NULL, a string is created.  If
 *				'len' is 0, no copying or string expansion
 *				occurs.
 *
 * Three important macros are provided for helping with vsncpy():
 *
 * sc("Hello")   Gives -->  "Hello",sizeof("Hello")-1
 * sz(s)         Gives -->  s,strlen(s)
 * sv(d)         Gives -->  d,sLEN(d)
 *
 * These are used to build arguments for vsncpy().  Many functions
 * can be created with combinations of sc/sz/sv with vsncpy:
 *
 *    s=vsncpy(NULL,0,NULL,0);		Create an empty dynamic string
 *
 *    s=vsncpy(NULL,0,sc("Hello"));	Create a dynamic string initialized
 *					with "Hello"
 *
 *    d=vsncpy(NULL,0,sv(s));		Duplicate a dynamic string
 *
 *    d=vsncpy(NULL,0,sz(s));		Convert a C string into a dynamic
 *                                      string.
 *
 *    d=vsncpy(sv(d),sv(s));		Append dynamic string s onto d.
 *
 *    d=vsncpy(sv(d),sc(".c"));		Append a ".c" extension to d.
 *
 *
 * These lesser functions are also provided:
 *
 * void vsrm(char *s);		Free a string.  Do nothing if 's' is NULL.
 *
 * int sLEN(char *s);		Return the length of the string 's'.  If 's'
 *				is NULL, return 0.
 *
 * char *vstrunc(char *d,int len);
 *				Set the length of a string.  Expand the string
 *				with blanks if necessary.
 *
 * char *vsensure(char *d,int len);
 *				Expand the malloc block for the string if
 *				necessary so that a string of 'len' chars can
 *				fit in it.
 *
 * sLen(s)=10;			Set the length indicator of the string to 10.
 *
 * char *vsins(char *d,int off,int len);
 *				Insert a gap into a string.
 *
 * char *vsdel(char *d,int off,int len);
 *				Delete characters from a string.
 *
 * Other function are provided as well.  Look through the rest of the header
 * file.  The header file is kind of weird looking because it is intended to
 * handle dynamic arrays of any type with only a few changes.
 */

/* Functions and global variable you have to define.  Replace these with
 * macros or defines here if they are not to be actual functions 
 */

/* An element with name 'a' */
typedef char sELEMENT;

/* Duplicate an element */
/* sELEMENT sdup(); */
#define sdup(a) (a)

/* Delete an element */
/* sELEMENT sdel(); */
#define sdel(a) do {} while(0)		/* effectively do nothing ;-) */

/* Compare a single element */
/* int scmp(); */
#define scmp(a, b) ((a) > (b) ? 1 : ((a) == (b) ? 0 : -1))

/* Compare a single element- case insensitive */
int sicmp(char a, char b);

/* A blank element */
/* extern sELEMENT sblank; */
#define sblank ' '

/* A termination element */
/* extern sELEMENT sterm; */
#define sterm '\0'

/************************/
/* Creation/Destruction */
/************************/

/* sELEMENT *vsmk(int len);
 * Create a variable length array.  Space for 'len' elements is preallocated.
 */
sELEMENT *vsmk(ptrdiff_t len);

/* void vsrm(sELEMENT *vary);
 * Free an array and everything which is in it.  Does nothing if 'vary' is
 * 0.
 */
void vsrm(sELEMENT *vary);

/********************/
/* Space management */
/********************/

/* int sSIZ(sELEMENT *vary);
 * int sSiz(sELEMENT *vary);
 * Access size part of array.  This int indicates the number of elements which
 * can fit in the array before realloc needs to be called.  It does not include
 * the extra space needed for the terminator and the header.
 *
 * sSIZ returns 0 if you pass it 0.  sSiz does not do this checking,
 * but can be used as an lvalue.
 */
#define sSIZ(a) ((a) ? *((ptrdiff_t *)(a) - 2) : 0)
#define sSiz(a) (*((ptrdiff_t *)(a) - 2))

/* int sLEN(sELEMENT *vary);
 * int sLen(sELEMENT *vary);
 * Access length part of array.  This int indicates the number of elements
 * currently in the array (not including the terminator).  This should be
 * used primarily for reading the size of the array.  It can be used for
 * setting the size of the array, but it must be used with care since it
 * does not eliminate elements (if the size decreases) or make sure there's
 * enough room (if the size increases).  See vensure and vtrunc.
 *
 * sLEN return a length of zero if 'vary' is 0.
 * sLen doesn't do this checking, but can be used as an lvalue
 */
#define sLEN(a) ((a) ? *((const ptrdiff_t *)(a) - 1) : 0)
#define sLen(a) (*((ptrdiff_t *)(a) - 1))

/* int slen(sELEMENT *ary);
 * Compute length of char or variable length array by searching for termination
 * element.  Returns 0 if 'vary' is 0.
 */
ptrdiff_t slen(const sELEMENT *ary);

/* sELEMENT *vsensure(sELEMENT *vary, int len);
 * Make sure there's enough space in the array for 'len' elements.  Whenever
 * vsensure reallocs the array, it allocates 25% more than the necessary
 * minimum space in anticipation of future expansion.  If 'vary' is 0,
 * it creates a new array.
 */
sELEMENT *vsensure(sELEMENT *vary, ptrdiff_t len);

/* sELEMENT *vstrunc(sELEMENT *vary, int len));
 * Truncate array to indicated size.  This zaps or expands with blank elements
 * and sets the LEN() of the array.  A new array is created if 'vary' is 0.
 */
sELEMENT *vstrunc(sELEMENT *vary, ptrdiff_t len);

/************************************/
/* Function which write to an array */
/************************************/

/* sELEMENT *vsfill(sELEMENT *vary, int pos, sELEMENT el, int len);
 * Set 'len' element of 'vary' beginning at 'pos' to duplications of 'el'.
 * Ok, if pos/len are past end of array.  If 'vary' is 0, a new array is
 * created.
 *
 * This does not zap previous values.  If you need that to happen, call
 * vszap first.  It does move the terminator around properly though.
 */
sELEMENT *vsfill(sELEMENT *vary, ptrdiff_t pos, sELEMENT el, ptrdiff_t len);

/* sELEMENT *vsncpy(sELEMENT *vary, int pos, sELEMENT *array, int len));
 * Copy 'len' elements from 'array' onto 'vary' beginning at position 'pos'.
 * 'array' can be a normal char array since the length is passed separately.  The
 * elements are copied, not duplicated.  A new array is created if 'vary' is
 * 0.  This does not zap previous elements.
 */
sELEMENT *vsncpy(sELEMENT *vary, ptrdiff_t pos, const sELEMENT *array, ptrdiff_t len);

/* sELEMENT *vsndup(sELEMENT *vary, int pos, sELEMENT *array, int len));
 * Duplicate 'len' elements from 'array' onto 'vary' beginning at position
 * 'pos'.  'array' can be a char array since its length is passed separately.  A
 * new array is created if 'vary' is 0.
 */
sELEMENT *vsndup(sELEMENT *vary, ptrdiff_t pos, sELEMENT *array, ptrdiff_t len);

/* sELEMENT *vsdup(sELEMENT *vary));
 * Duplicate array.  This is just a functionalized version of:
 *
 *   vsndup(NULL, 0, vary, sLEN(vary));
 *
 * but since you need to be able to refer to this particular function by
 * address often it's given here.
 *
 * (actually, there's bazillions of these simple combinations of the above
 * functions and the macros of the next section.  You'll probably want to make
 * functionalized instances of the ones you use most often - especially since
 * the macros aren't safe).
 */
sELEMENT *vsdup(sELEMENT *vary);

/* sELEMENT *vsset(sELEMENT *vary, int pos, sELEMENT element);
 * Set an element in an array.  Any value of 'pos' is valid.  A new array
 * is created if 'vary' is 0.  The previous contents of the position is
 * deleted.    This does not duplicate 'element'.  If you need 'element'
 * duplicated, call: vsset(vary,pos,sdup(element));
 */
sELEMENT *_vsset(sELEMENT *vary, ptrdiff_t pos, sELEMENT el);

#define vsset(v, p, el)  \
 (!(v) || (p) > sLen(v) || (p) >= sSiz(v) ?  \
  _vsset((v), (p), (el)) \
 : \
  ((p) == sLen(v) ? \
   ((v)[(p) + 1] = 0, sLen(v) = (p) + 1, (v)[p] = (el), (v)) \
  : \
   ((v)[p] = (el), (v)) \
  ) \
 )

/* sELEMENT *vsadd(sELEMENT *vary, sELEMENT element);
 * Concatenate a single element to the end of 'vary'.  A new array is created
 * if 'vary' is 0.  This does not duplicate element: call
 * vsadd(vary,sdup(element));  If you need it duplicated.
 */
#define vsadd(v, el) \
 (!(v) || sLen(v) == sSiz(v) ? \
  _vsset((v), sLEN(v), (el)) \
 : \
  ((v)[sLen(v) + 1] = 0, (v)[sLen(v)] = (el), sLen(v) = sLen(v) + 1, (v)) \
 )

/**************************************/
/* Functions which read from an array */
/**************************************/

/* These macros are used to generate the address/size pairs which get
 * passed to the functions of the previous section.
 */

/* { sELEMENT *, int } sv(sELEMENT *array);
 * Return array, size pair.  Uses sLEN to get size.
 */
#define sv(a) (a), sLEN(a)

/* { sELEMENT *, int } sz(sELEMENT *array);
 * Return array, size pair.  Uses slen to get size.
 */
#define sz(a) (a), slen(a)

/* { sELEMENT *, int } sc(sELEMENT *array);
 * Return array, size pair.  Uses 'sizeof' to get size.
 */
#define sc(a) (a), (sizeof(a) / sizeof(sELEMENT) - 1)

/* { sELEMENT *, int } srest(sELEMENT *vary, int pos);
 * Return array, size pair of rest of array beginning at pos.  If
 * pos is past end of array, gives size of 0.
 */
#define srest(a, p) ((a) + (p)), (((p) > sLEN(a)) ? 0 : sLen(a) - (p))

/* { sELEMENT *, int } spart(sELEMENT *vary, int pos, int len);
 * Return array,size pair of 'len' elements of array beginning with pos.  If
 * pos is past end of array, gives size of 0.  If pos+len is past end of array,
 * returns number of elements to end of array.
 */
#define spart(a, p, l) \
 ((a) + (p)), ((p) >= sLEN(a) ? 0 : ((p) + (l) > sLen(a) ? sLen(a) - (p) : (l)))

/* sELEMENT vsget(sELEMENT *vary, int pos);
 * Get an element from an array.  Any value of pos is valid; if it's past the
 * end of the array or if 'vary' is 0, the terminator is returned.  This
 * does not make a duplicate of the returned element.  If you want that, pass
 * the return value of this to sdup.
 */
#define vsget(a, p) ((p) >= sLEN(a) ? sterm : (a)[p])

/**********************/
/* Insertion/Deletion */
/**********************/
#ifdef junk
/* sELEMENT *vsins(sELEMENT *vary, int pos, int n));
 * Insert n empty slots into the array.  If 'pos' >= the length of the array,
 * the array is simply extended.  The new slots are not set to anything.
 * This does not set the elements in the created hole to any particular
 * value: use vsfill if you need that to occur.
 */
sELEMENT *vsins(sELEMENT *vary, ptrdiff_t pos, ptrdiff_t n);

/* sELEMENT *vsdel(sELEMENT *vary, int pos, int n));
 * Delete n slots from the array.  This does not zap the elements first; call
 * vszap first if you need this to happen.
 */
sELEMENT *vsdel(SELEMENT *vary, ptrdiff_t pos, ptrdiff_t n);

/*************************/
/* Searching and Sorting */
/*************************/

/* sELEMENT *vssort(sELEMENT *ary, int len))
 * Sort the elements of an array (char or variable length) using qsort().
 */
sELEMENT *vssort(sELEMENT *ary, ptrdiff_t len);
#endif
/* int vsbsearch(sELEMENT *ary, int len, sELEMENT element);
 * Do a binary search on a sorted variable length or char array.  Returns position
 * of matching element or the position where the element should be if it was
 * not found.  (You should test with scmp to find out which).
 *
 * Hmm... this should really indicate whether or not the element was found.
 */
ptrdiff_t vsbsearch(const sELEMENT *ary, ptrdiff_t len, sELEMENT el);

#ifdef junk
/* int vsfirst(sELEMENT *ary, int len, sELEMENT element);
 * Find offset to first matching element in 'vary' or return ~0 if not found.
 */
ptrdiff_t vsfirst(sELEMENT *ary, ptrdiff_t len, sELEMENT element);

/* int vslast(sELEMENT *ary, int len, sELEMENT element);
 * Find offset to last matching element in 'vary' or return ~0 if none found.
 */
ptrdiff_t vslast(sELEMENT *ary, ptrdiff_t len, sELEMENT element);

/* int vss(sELEMENT *a, int alen, sELEMENT *b, int blen);
 * Do a substring search on 'a'.  Return offset from 'a' to first matching
 * occurrence of 'b' in 'a' or return ~0 if none found.
 */
ptrdiff_t vss(sELEMENT *a, ptrdiff_t alen, sELEMENT *b, ptrdiff_t blen);
#endif
/* int vscmpn(sELEMENT *a, int alen, sELEMENT *b, int blen);
 *
 * Compare two arrays using scmp.  If 'a' > 'b', return 1.  If 'a' == 'b',
 * return 0.  If 'a' < 'b', return -1.  Longer strings are > shorter ones if
 * their beginning match.
 */
int vscmpn(sELEMENT *a, ptrdiff_t alen, sELEMENT *b, ptrdiff_t blen);

/* int vscmp(sELEMENT *a, sELEMENT *b);
 *
 * Functionalized version of: vscmpn(sv(a), sv(b));
 */
int vscmp(sELEMENT *a, sELEMENT *b);

#ifdef junk
/* int vsicmpn(sELEMENT *a, int alen, sELEMENT *b, int blen);
 *
 * Compare two arrays using sicmp.  If 'a' > 'b', return 1.  If 'a' == 'b',
 * return 0.  If 'a' < 'b', return -1.  Longer strings are > shorter ones if
 * their beginning match.
 *
 * This is same as vscmpn except that it is case insensitive.
 */
int vsicmpn(sELEMENT *a, ptrdiff_t alen, sELEMENT *b, ptrdiff_t blen);

/* int vsicmp(sELEMENT *a, sELEMENT *b);
 *
 * Functionalized version of: vsicmpn(sv(a), sv(b));
 */
int vsicmp(sELEMENT *a, sELEMENT *b);
#endif
/* int vsscan(sELEMENT *a, int alen, sELEMENT *b, int blen);
 * Find offset of first matching element in 'a' which matches any
 * of the elements passed in 'b'.  Array 'b' must be sorted.
 *
 * Hmm... this really needs to return what the found element is.
 */
ptrdiff_t vsscan(const sELEMENT *a, ptrdiff_t alen, const sELEMENT *b, ptrdiff_t blen);

/* int vsspan(sELEMENT *a, int alen, sELEMENT *b, int blen);
 * Find offset of first matching element in 'a' which does not match any
 * of the elements passed in 'b'.  Array 'b' must be sorted.
 */
ptrdiff_t vsspan(const sELEMENT *a, ptrdiff_t alen, const sELEMENT *b, ptrdiff_t blen);

/***************/
/* Other stuff */
/***************/
#ifdef junk
/* char *vsread(char *d, int p, int (*getC)(void *ptr), void *ptr);
 * Replace 'd' with next line read from read-character function 'getC'.  If 
 * 'd' is 0, a new string is allocated.  If there is no more input, the string
 * is freed and 0 is returned.  The \n is deleted from the entered line.
 *
 * 'ptr' is passed as the first arg to 'getC'.  'getC' should return -1 if
 * there is no more input.
 */
char *vsread();

/* char *vwords(char *s, char **a, int len, char t);
 *
 * Generate a 't'-separated word list from the words in the zero-terminated
 * array of zero-terminated strings 'a'.  For example a simple 'echo.c':
 *
 * main(argc, argv)
 * char *argv[];
 * {
 * printf("%s\n",vwords(NULL,argv,argc,' ')):
 * }
 *
 */
char *vwords();
#endif
