/*
 *	Variable length arrays of strings
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *
 *	This file is part of JOE (Joe's Own Editor)
 */
#ifndef _JOE_VA_H
#define _JOE_VA_H 1

#include "config.h"

#include "vs.h"

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

typedef char *aELEMENT;

/* aELEMENT adup(); */
#define adup(s) vsdup(s)
/* aELEMENT adel(); */
#define adel(s) vsrm(s)
/* int acmp(); */
#define acmp(a,b) vscmp((a),(b))
/* extern aELEMENT ablank; */
#define ablank NULL
/* extern aELEMENT aterm; */
#define aterm NULL

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

/* aELEMENT *vamk(int len);
 * Create a variable length array.  Space for 'len' elements is preallocated.
 */
aELEMENT *vamk PARAMS((int len));

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

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

/* int aSIZ(aELEMENT *vary);
 * int aSiz(aELEMENT *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.
 *
 * aSIZ returns 0 if you pass it 0.  aSiz does not do this checking,
 * but can be used as an lvalue.
 */
#define aSIZ(a) ((a) ? *((int *)(a) - 2) : 0)
#define aSiz(a) (*((int *)(a) - 2))

/* int aLEN(aELEMENT *vary);
 * int aLen(aELEMENT *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.
 *
 * aLEN return a length of zero if 'vary' is 0.
 * aLen doesn't do this checking, but can be used as an lvalue
 */
#define aLEN(a) ((a) ? *((int *)(a) - 1) : 0)
#define aLen(a) (*((int *)(a) - 1))

/* int alen(aELEMENT *ary);
 * Compute length of char or variable length array by searching for termination
 * element.  Returns 0 if 'vary' is 0.
 */
int alen PARAMS((aELEMENT *ary));

/* aELEMENT *vaensure(aELEMENT *vary, int len);
 * Make sure there's enough space in the array for 'len' elements.  Whenever
 * vaensure 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.
 */
aELEMENT *vaensure PARAMS((aELEMENT *vary, int len));

/* aELEMENT *vazap(aELEMENT *vary, int pos, int n);
 * Destroy n elements from an array beginning at pos.  Is ok if pos/n go
 * past end of array.  This does not change the aLEN() value of the array.
 * This does nothing and returns 0 if 'vary' is 0.  Note that this
 * function does not actually write to the array.  This does not stop if
 * a aterm is encountered.
 */
aELEMENT *vazap PARAMS((aELEMENT *vary, int pos, int n));

/* aELEMENT *vatrunc(aELEMENT *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.
 */
aELEMENT *vatrunc PARAMS((aELEMENT *vary, int len));

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

/* aELEMENT *vafill(aELEMENT *vary, int pos, aELEMENT 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
 * vazap first.  It does move the terminator around properly though.
 */
aELEMENT *vafill PARAMS((aELEMENT *vary, int pos, aELEMENT el, int len));

#ifdef junk
/* aELEMENT *vancpy(aELEMENT *vary, int pos, aELEMENT *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 seperately.  The
 * elements are copied, not duplicated.  A new array is created if 'vary' is
 * 0.  This does not zap previous elements.
 */
aELEMENT *vancpy PARAMS((aELEMENT *vary, int pos, aELEMENT *array, int len));
#endif

/* aELEMENT *vandup(aELEMENT *vary, int pos, aELEMENT *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 seperately.  A
 * new array is created if 'vary' is 0.
 */
aELEMENT *vandup PARAMS((aELEMENT *vary, int pos, aELEMENT *array, int len));

/* aELEMENT *vadup(aELEMENT *vary);
 * Duplicate array.  This is just a functionalized version of:
 *
 *   vandup(NULL,0,vary,aLEN(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).
 */
aELEMENT *vadup PARAMS((aELEMENT *vary));

/* aELEMENT *vaset(aELEMENT *vary, int pos, aELEMENT 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: vaset(vary,pos,adup(element));
 */
aELEMENT *_vaset PARAMS((aELEMENT *vary, int pos, aELEMENT el));

#define vaset(v,p,el)  \
 (!(v) || (p) > aLen(v) || ((p) == aLen(v) && (p) == aSiz(v)) ?  \
  _vaset((v),(p),(el)) \
 : \
  ((p) == aLen(v) ? \
   ((v)[(p)+1] = (v)[p], (v)[p] = (el), aLen(v) = (p)+1, (v)) \
  : \
   (adel((v)[p]), (v)[p] = (el), (v)) \
  ) \
 )

/* aELEMENT *vaadd(aELEMENT *vary, aELEMENT 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
 * vaadd(vary,adup(element));  If you need it duplicated.
 */
#define vaadd(v,el) \
 (!(v) || aLen(v) == aSiz(v) ? \
  _vaset((v), aLEN(v), (el)) \
 : \
  ((v)[aLen(v) + 1] = (v)[aLen(v)], (v)[aLen(v)] = (el), \
   aLen(v) = aLen(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.
 */

/* { aELEMENT *, int } av(aELEMENT *array);
 * Return array,size pair.  Uses aLEN to get size.
 */
#define av(a) (a), aLEN(a)

/* { aELEMENT *, int } az(aELEMENT *array);
 * Return array,size pair.  Uses alen to get size.
 */
#define az(a) (a), alen(a)

/* { aELEMENT *, int } ac(aELEMENT *array);
 * Return array,size pair.  Uses 'sizeof' to get size.
 */
#define ac(a) (a), (sizeof(a) / sizeof(aELEMENT))

/* { aELEMENT *, int } arest(aELEMENT *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 arest(a, p) ((a) + (p)), (((p) > aLEN(a)) ? 0 : aLen(a) - (p))

/* { aELEMENT *, int } apart(aELEMENT *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 apart(a, p, l) \
 ((a) + (p)), ((p) >= aLEN(a) ? 0 : ((p) + (l) > aLen(a) ? aLen(a) - (p) : (l)))

/* aELEMENT vaget(aELEMENT *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 adup.
 */
#define vaget(a, p) ((p) >= aLEN(a) ? aterm : (a)[p])

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

/* aELEMENT *vasort(aELEMENT *ary, int len)
 * Sort the elements of an array (char or variable length) using qsort().
 */
aELEMENT *vasort PARAMS((aELEMENT *ary, int len));

/* aELEMENT *vawords(aELEMENT *a, char *s, int len, char *sep, int seplen);
 * Generate list of strings out of words in 's' seperated with the characters
 * in 'sep'.  The characters in 'sep' must be sorted.
 */
aELEMENT *vawords PARAMS((aELEMENT *a, char *s, int len, char *sep, int seplen));

#endif
