/*
 * Copyright (c) 2003-2011
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 *
 * $Id: ds.h 2586 2012-03-15 16:21:40Z brachman $
 */

/*****************************************************************************
 * COPYRIGHT AND PERMISSION NOTICE
 * 
 * Copyright (c) 2001-2003 The Queen in Right of Canada
 * 
 * All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, and/or sell
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, provided that the above copyright notice(s) and this
 * permission notice appear in all copies of the Software and that both the
 * above copyright notice(s) and this permission notice appear in supporting
 * documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE 
 * BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
 * SOFTWARE.
 * 
 * Except as contained in this notice, the name of a copyright holder shall not
 * be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization of the
 * copyright holder.
 ***************************************************************************/

#ifndef _DS_H_
#define _DS_H_

/*
 * Package for safely allocating and using dynamically constructed character
 * strings and dynamically constructed vectors.
 */

#include "dacs_config.h"
#include "range.h"

#include <regex.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>

#define HAVE_DS

#ifndef MAYBE_UNUSED
#define MAYBE_UNUSED	__attribute__ ((__unused__))
#endif

#ifndef ALLOC
#define ALLOC(OBJ)				((OBJ *) malloc(sizeof(OBJ)))
#endif

#ifndef ALLOC_N
#define ALLOC_N(OBJ, N)			((OBJ *) malloc(sizeof(OBJ) * (N)))
#endif

/* Multi-source input descriptor */
typedef struct Dsio {
  FILE *fp;                 /* If non-NULL, the input stream */
  char *buf;                /* If no input stream, the input buffer... */
  char *ptr;                /* ... and a pointer into the input buffer */
  unsigned long length;     /* The length of the input, if known, in bytes */
  unsigned long rem;        /* Bytes remaining in input, if known */
  int have_length;          /* Non-zero if length is unavailable */
  int got_peeked_char;      /* Non-zero if there's a peeked character */
  char peeked_char;         /* The peeked character */
} Dsio;

/* A dynamic string descriptor */
typedef struct Ds {
  unsigned int alloc_flag : 1;	/* Set if this struct was malloc-ed */
  unsigned int clear_flag : 1;	/* Set to zero freed memory */
  unsigned int exact_flag : 1;	/* Allocate the exact amount required */
  unsigned int delnl_flag : 1;	/* Delete newlines read from stream */
  unsigned int crnl_flag  : 1;  /* Make \r\n equivalent to \n from stream */
  unsigned int escnl_flag : 1;	/* Allow lines to be continued */
  void *buf;					/* Pointer to a malloc-ed buffer, or NULL */
  size_t nalloc;				/* Allocated size of the buffer */
  size_t len;					/* Bytes put into the buffer, not */
  								/* necessarily the strlen */
  size_t len_limit;				/* Maximum value of len (0 means no limit) */
  void *(*malloc)(size_t size);
  void (*free)(void *ptr);
  Dsio *io;
} Ds;

enum ds_prompt_flag {
  DS_PROMPT_ECHO    = 0,
  DS_PROMPT_NOECHO  = 1
};

/*
 * Return a pointer to the dynamically allocated buffer.
 * Be careful though; if the buffer is reallocated, this pointer could become
 * invalid.
 */
static inline char *
ds_buf(Ds *ds)
{

  return((char *) ds->buf);
}

static inline unsigned char *
ds_ucbuf(Ds *ds)
{

  return((unsigned char *) ds->buf);
}

/*
 * Return the number of bytes in the buffer that are in use (which is less
 * than or equal to the number of bytes allocated for the buffer).
 */
static inline size_t
ds_len(Ds *ds)
{

  return(ds->len);
}

/*
 * Return the number of bytes allocated for the buffer (which is greater than
 * or equal to the number of bytes "in use").
 */
static inline size_t
ds_size(Ds *ds)
{

  return(ds->nalloc);
}

/*
 * Return the number of bytes remaining for use.
 */
static inline size_t
ds_rem(Ds *ds)
{

  return(ds->nalloc - ds->len);
}

/*
 * Return 0 if DS1 and DS2 are not identical strings (possibly binary),
 * otherwise return 1.
 */
static inline int
ds_eq(Ds *ds1, Ds *ds2)
{

  if (ds1 == ds2)
	return(1);
  if (ds1->len != ds2->len)
	return(0);
  return(memcmp((const void *) ds1->buf, (const void *) ds2->buf, ds1->len)
		 == 0);
}

/* XXX Should be integrated with Ds */
extern char *ds_errmsg;

#ifdef __cplusplus
extern "C" {
#endif

extern Ds *ds_alloc(void);
extern Ds *ds_alloc_size(size_t nbytes);
extern Ds *ds_init(Ds *ds);
extern Ds *ds_init_size(Ds *ds, size_t nbytes);
extern Ds *ds_dup(Ds *dsx);
extern Ds *ds_reinit(Ds *ds);
extern Ds *ds_reinit_size(Ds *ds, size_t nbytes);
extern Ds *ds_reset(Ds *ds);
extern Ds *ds_reset_buf(Ds *ds);
extern char *ds_fill(Ds *ds, size_t nbytes);
extern void ds_free(Ds *ds);
extern Ds *ds_trim(Ds *ds);
extern Ds *ds_grow(Ds *ds, size_t new_size);
extern int ds_appendc(Ds *ds, int ch);
extern char *ds_append(Ds *ds, char *str);
extern char *ds_appendn(Ds *ds, char *str, size_t len);
extern char *ds_concat(Ds *ds, char *str);
extern char *ds_concatc(Ds *ds, int ch);
extern char *ds_concatn(Ds *ds, char *str, size_t len);
extern char *ds_prepend(Ds *ds, char *str);
extern int ds_prependc(Ds *ds, int ch);
extern Ds *ds_set(Ds *ds, char *str);
extern Ds *ds_setn(Ds *dsx, unsigned char *s, size_t slen);
extern char *ds_copyb(Ds *ds, void *src, size_t len, unsigned int offset);
extern Ds *ds_copyspn(Ds *dsx, unsigned char *s, char *end_chars);
extern char *ds_insertb(Ds *ds, void *src, size_t len, unsigned int offset);
extern Ds *ds_range(Ds *dsx, char *str, char *range_spec, Range_syntax *ors);
extern size_t ds_chop(Ds *ds, size_t chop);
extern int ds_zapnull(Ds *ds);

extern int ds_getc(Ds *ds, FILE *fp, int *chp);
extern char *ds_gets(Ds *ds, FILE *fp);
extern char *ds_agets(Ds *ds, FILE *fp);
extern Ds *ds_load_file(Ds *ds, char *pathname);
extern Ds *ds_getf(FILE *fp);
extern Ds *ds_agetf(Ds *ds, FILE *fp);
extern char *ds_prompt(Ds *ds, char *prompt, unsigned int flag);
extern Ds *ds_subst(Ds *ds, unsigned char ch_src, unsigned char ch_rpl);

extern char *ds_xprintf(const char *fmt, ...);
extern char *ds_vxprintf(const char *fmt, va_list ap);
extern int ds_sprintf(Ds *ds, unsigned int offset, const char *fmt, ...);
extern int ds_asprintf(Ds *ds, const char *fmt, ...);
extern int ds_vasprintf(Ds *ds, const char *fmt, va_list ap);
extern int ds_psprintf(Ds *ds, const char *fmt, ...);

extern FILE *ds_fopen_secure(const char *path, const char *mode, size_t size);
extern int ds_fclose(FILE *fp);
extern void ds_fclean(FILE *fp);

extern Ds *ds_dsappend(Ds *dsx, int no_nul, unsigned int n, ...);
extern Ds *ds_dsvaappend(Ds *dsx, int no_nul, unsigned int n, va_list ap);

extern Dsio *dsio_set(Ds *ds, FILE *fp, char *buf, unsigned long len,
					  int have_length);
extern int dsio_eof(Ds *ds);
extern int dsio_nextc(Ds *ds, int *ch_ptr);
extern int dsio_peekc(Ds *ds, int *ch_ptr);
extern char *dsio_agets(Ds *ds);
extern char *dsio_gets(Ds *ds);
extern char *dsio_agets_buf(Ds *ds, char *buf);
extern int dsio_load(Ds *ds);
extern char *dsio_load_str(Ds *ds);
extern int dsio_free(Ds *ds);

#ifdef HAVE_READLINE
extern char *ds_readline(Ds *ds, char *prompt, char *cprompt);
#endif
#ifdef __cplusplus
}
#endif

extern int ds_default_clear_flag;
extern int ds_default_delnl_flag;
extern size_t ds_default_len_limit;
extern void *(*ds_default_malloc_func)(size_t size);
extern void (*ds_default_free_func)(void *ptr);

/*
 * Although Dsvec is a related but really separate idea, it is currently
 * included in this library.
 * The data structure and supporting functions comprise a means of constructing
 * and dynamically extending a vector of objects (such as pointers or
 * structures).
 */
typedef struct Dsvec {
  unsigned int alloc_flag : 1;	/* Set if this struct was malloc-ed */
  unsigned int clear_flag : 1;	/* Set to zero freed memory */
  unsigned int exact_flag : 1;	/* Allocate the exact amount required */
  void **ptr;					/* Pointer to a malloc-ed vector, or NULL */
  size_t el_size;				/* Size of each element, in bytes */
  unsigned int nalloc;			/* Number of elements allocated */
  unsigned int nused;			/* Number of elements used */
  unsigned int nused_limit;		/* Max value of nused (0 means no limit) */
  void *(*malloc)(size_t size);
  void (*free)(void *ptr);
} Dsvec;

/* Macro for accessing pointer at offset IND, which is of type TYPE. */
#define dsvec_ptr(DSV, IND, TYPE)	((TYPE) dsvec_ptr_index(DSV, IND))
/* Macro for accessing object at offset IND, which is of type TYPE. */
#define dsvec_obj(DSV, IND, TYPE)	(&((TYPE) dsvec_base(DSV))[IND])

#define dsvec_new_obj(DSV, TYPE)	(dsvec_add_obj(DSV) == -1 \
		? (TYPE) NULL : dsvec_obj(DSV, DSV->nused - 1, TYPE))

#define dsvec_base_type(DSV, TYPE)	(TYPE) dsvec_base(DSV)

static inline void **
dsvec_base(Dsvec *dsv)
{

  if (dsv == NULL)
	return(NULL);
  return(dsv->ptr);
}

static inline unsigned int
dsvec_len(Dsvec *dsv)
{

  if (dsv == NULL)
	return(0);
  return(dsv->nused);
}

static inline unsigned int
dsvec_size(Dsvec *dsv)
{

  if (dsv == NULL)
	return(0);
  return(dsv->el_size);
}

typedef struct Mkargv {
  int keepq;
  int keepws;
  char *ifs;
  char *startq;
  char *endq;
} Mkargv;

/* For dsvec_select() */
typedef struct Select_arg {
  char *regex;
  regex_t *preg;
  int regex_flags;
  int anchored;
  char *errbuf;
  size_t errbuf_size;
} Select_arg;

/* For dsvec_complete() */
typedef struct Completions {
  int start;		/* If non-negative, the index of the first completion. */
  int end;			/* The index of the last completion. */
#ifdef NOTDEF
  Dsvec *dsv;
#endif
  int prefix_cnt;	/* Number of initial common completion characters. */
  int skip_cnt;		/* Number of completion characters after prefix. */
} Completions;

/* XXX Should be integrated with Dsvec */
extern char *dsvec_errmsg;

extern int dsvec_default_initial_nalloc;
extern int dsvec_default_clear_flag;
extern int dsvec_default_nused_limit;
extern void *(*dsvec_default_malloc_func)(size_t size);
extern void (*dsvec_default_free_func)(void *ptr);

#ifdef __cplusplus
extern "C" {
#endif
extern Dsvec *dsvec_init(Dsvec *odsv, size_t size);
extern Dsvec *dsvec_alloc(size_t size);
extern Dsvec *dsvec_init_size(Dsvec *odsv, size_t size, int n);
extern Dsvec *dsvec_alloc_size(size_t size, int n);
extern void dsvec_free(Dsvec *dsv);
extern Dsvec *dsvec_grow(Dsvec *dsv, unsigned int new_nelements);
extern void *dsvec_ptr_index(Dsvec *dsv, unsigned int ind);
extern int dsvec_append(Dsvec *primary, Dsvec *secondary);
extern int dsvec_set(Dsvec *odsv, void *base, size_t size, int n);
extern int dsvec_add_obj(Dsvec *dsv);
extern Dsvec *dsvec_list(Dsvec *odsv, char **list);

extern void dsvec_sort(Dsvec *dsv, int (*compar)(const void *, const void *));
extern int dsvec_compar_string(const void *ap, const void *bp);
extern int dsvec_compar_string_nocase(const void *ap, const void *bp);
extern int dsvec_find(Dsvec *dsv, void *el,
					  int (*compar)(const void *, const void *));
extern Dsvec *dsvec_select(Dsvec *dsv, void *el,
						   void *(*compar_init)(const void *),
						   void *compar_init_arg,
						   int (*compar)(const void *, const void *, void *));
extern void * dsvec_select_init(const void *arg);
extern void *dsvec_select_initx(const void *arg);
extern int dsvec_select_compar(const void *a, const void *b, void *compar_arg);

extern int dsvec_delete_ptr_index(Dsvec *dsv, unsigned int ind);
extern Dsvec *dsvec_load(Ds *ds, Dsvec *dsv);
extern Dsvec *ds_mkargv(Dsvec *dsv, char *str, Mkargv *conf);
extern Dsvec *ds_mkargv_add(Dsvec *dsv, char *el);
extern Dsvec *ds_mkargv_addv(Dsvec *dsv, int argc, char **argv);

extern int dsvec_add_ptr(Dsvec *dsv, void *ptr);
extern Dsvec *dsvec_copy(Dsvec *odsv, Dsvec *old_dsv);
extern Dsvec *dsvec_subset(Dsvec *odsv, Dsvec *old_dsv, unsigned int start,
						   int len);
extern int dsvec_delete(Dsvec *dsv, unsigned int ind, unsigned int nelements);
extern int dsvec_delete_ptr(Dsvec *dsv, void *ptr);
extern int dsvec_insert(Dsvec *primary, unsigned int ind, Dsvec *secondary);
extern int dsvec_insert_ptr(Dsvec *dsv, unsigned int ind, void *ptr);
extern int dsvec_prepend_ptr(Dsvec *dsv, void *ptr);
extern Dsvec *dsvec_range(Dsvec *odsv, Dsvec *primary, char *range_spec,
						  Range_syntax *rs);
extern int dsvec_replace(Dsvec *primary, unsigned int ind,
						 unsigned int nelements, Dsvec *secondary);
extern int dsvec_replace_ptr(Dsvec *dsv, void *ptr, unsigned int ind);
extern int dsvec_rotate(Dsvec *dsv, int npos);
extern int dsvec_shift(Dsvec *dsv, int count);
extern Dsvec *dsvec_slice(Dsvec *odsv, Dsvec *primary, unsigned int ind,
						  unsigned int len);
extern Dsvec *dsvec_lines(Dsvec *dsv, char *buf);
extern Dsvec *dsvec_load_lines(Dsvec *dsv, char *pathname);
extern int dsvec_streq(Dsvec *dsv1, Dsvec *dsv2);
extern int dsvec_strneq(Dsvec *dsv1, Dsvec *dsv2, size_t len);
extern Dsvec *dsvec_strlist(Dsvec *odsv, char *str_spec);

extern int dsvec_is_subset(Dsvec *dsv, Dsvec *dsv_subset,
						   int (*compar)(const void *, const void *));
extern int dsvec_is_equiv(Dsvec *dsv_a, Dsvec *dsv_b,
						  int (*compar)(const void *, const void *));
extern int dsvec_is_identical(Dsvec *dsv_a, Dsvec *dsv_b,
							  int (*compar)(const void *, const void *));

extern int dsvec_binary_search(Dsvec *dsv, void *ww,
							   int (*compar)(void *, void *, void *, size_t *),
							   void *conf);
extern int dsvec_complete(Dsvec *dsv, char *word,
						  int (*compar)(void *, void *, void *, size_t *),
						  void *conf, Completions *completions);

extern int dsvec_selftest(FILE *out);

#ifdef NOTDEF
extern int dsvec_mkargv(Dsvec *dsv, char ***argvp, int *argcp);
#endif

#ifdef __cplusplus
}
#endif

static MAYBE_UNUSED Dsvec *
dsvec_set_init(Dsvec *odsv, void *base, size_t size)
{
  Dsvec *dsv;

  if (base == NULL || size == 0)
	return(NULL);

  if (odsv != NULL) {
	if (odsv->el_size != size)
	  return(NULL);
	dsv = odsv;
  }
  else
	dsv = dsvec_init(odsv, size);

  return(dsv);
}

/*
 * Create or append N elements of type OBJ_TYPE to a vector, starting at the
 * address BASE.  If appending, the elements must be of the same size as the
 * existing one.
 * If N is NULL, then a NULL element marks the end of the copied list; the
 * NULL element is NOT copied.
 * DSV is set to point to the list and on entry must either be NULL or a
 * valid vector.
 */
#define dsvec_set(DSV, BASE, OBJ_TYPE, N) \
  { \
    int i, st; \
    Dsvec *xdsv; \
    OBJ_TYPE *ptr; \
    \
	st = ((xdsv = dsvec_set_init(DSV, BASE, sizeof(OBJ_TYPE))) == NULL) ? -1 : 0; \
    ptr = BASE; \
    for (i = 0; st == 0 && ((N == 0) || (i < N)); i++) { \
      if (N == 0 && ptr[i] == NULL) \
        break; \
      if (dsvec_add_ptr(xdsv, ptr[i]) == -1) \
		 st = -1; \
    } \
    DSV = (st == -1) ? NULL : xdsv; \
  }

#endif
