text: /*
   * Vector handling (counted lists of char *'s).
   *
   * A vector is a table for handling a list of strings with less overhead than
   * linked list.  The intention is for vectors, once allocated, to be reused;
   * this saves on memory allocations once the array of char *'s reaches a
   * stable size.
   *
   * This is based on the from rra-c-util util/vector.c library, but that
   * library uses xmalloc routines to exit the program if memory allocation
   * fails.  This is a modified version of the vector library that instead
   * returns false on failure to allocate memory, allowing the caller to do
   * appropriate recovery.
   *
   * Vectors require list of strings, not arbitrary binary data, and cannot
   * handle data elements containing nul characters.
   *
   * Only the portions of the vector library needed by this module is
   * implemented.
   *
   * Written by Russ Allbery <eagle@eyrie.org>
   * Copyright 2013
   *     The Board of Trustees of the Leland Stanford Junior University
   *
   * See LICENSE for licensing terms.
   */
  
  #include <config.h>
  #include <portable/system.h>
  
  #include <plugin/internal.h>
  
  
  /*
   * Allocate a new, empty vector.  Returns NULL if memory allocation fails.
   */
  struct vector *
  strength_vector_new(void)
  {
      return calloc(1, sizeof(struct vector));
  }
  
  
  /*
   * Resize a vector (using realloc to resize the table).  Return false if
   * memory allocation fails.
   */
  static bool
  strength_vector_resize(struct vector *vector, size_t size)
  {
      size_t i;
      char **strings;
  
      /* If we're shrinking the vector, free the excess strings. */
      if (vector->count > size) {
          for (i = size; i < vector->count; i++)
              free(vector->strings[i]);
          vector->count = size;
      }
  
      /* If resizing to zero, free all storage.  Otherwise, realloc. */
      if (size == 0) {
          free(vector->strings);
          vector->strings = NULL;
      } else {
          strings = realloc(vector->strings, size * sizeof(char *));
          if (strings == NULL)
              return false;
          vector->strings = strings;
      }
      vector->allocated = size;
      return true;
  }
  
  
  /*
   * Add a new string to the vector, resizing the vector as necessary.  The
   * vector is resized an element at a time; if a lot of resizes are expected,
   * vector_resize should be called explicitly with a more suitable size.
   * Return false if memory allocation fails.
   */
  bool
  strength_vector_add(struct vector *vector, const char *string)
  {
      size_t next = vector->count;
  
      if (vector->count == vector->allocated)
          if (!strength_vector_resize(vector, vector->allocated + 1))
              return false;
      vector->strings[next] = strdup(string);
      if (vector->strings[next] == NULL)
          return false;
      vector->count++;
      return true;
  }
  
  
  /*
   * Empty a vector but keep the allocated memory for the pointer table.
   */
  static void
  strength_vector_clear(struct vector *vector)
  {
      size_t i;
  
      for (i = 0; i < vector->count; i++)
          if (vector->strings[i] != NULL)
              free(vector->strings[i]);
      vector->count = 0;
  }
  
  
  /*
   * Free a vector completely.
   */
  void
  strength_vector_free(struct vector *vector)
  {
      if (vector == NULL)
          return;
      strength_vector_clear(vector);
      free(vector->strings);
      free(vector);
  }
  
  
  /*
   * Given a vector that we may be reusing, clear it out.  If the first argument
   * is NULL, allocate a new vector.  Used by vector_split*.  Returns NULL if
   * memory allocation fails.
   */
  static struct vector *
  strength_vector_reuse(struct vector *vector)
  {
      if (vector == NULL)
          return strength_vector_new();
      else {
          strength_vector_clear(vector);
          return vector;
      }
  }
  
  
  /*
   * Given a string and a set of separators expressed as a string, count the
   * number of strings that it will split into when splitting on those
   * separators.
   */
  static size_t
  split_multi_count(const char *string, const char *seps)
  {
      const char *p;
      size_t count;
  
      /* If the string is empty, the count of components is zero. */
      if (*string == '\0')
          return 0;
  
      /* Otherwise, walk the string looking for non-consecutive separators. */
      for (count = 1, p = string + 1; *p != '\0'; p++)
          if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL)
              count++;
  
      /*
       * If the string ends in separators, we've overestimated the number of
       * strings by one.
       */
      if (strchr(seps, p[-1]) != NULL)
          count--;
      return count;
  }
  
  
  /*
   * Given a string, split it at any of the provided separators to form a
   * vector, copying each string segment.  If the third argument isn't NULL,
   * reuse that vector; otherwise, allocate a new one.  Any number of
   * consecutive separators are considered a single separator.  Returns NULL on
   * memory allocation failure, after which the provided vector may only have
   * partial results.
   */
  struct vector *
  strength_vector_split_multi(const char *string, const char *seps,
                          struct vector *vector)
  {
      const char *p, *start;
      size_t i, count;
      bool created = false;
  
      /* Set up the vector we'll use to store the results. */
      if (vector == NULL)
          created = true;
      vector = strength_vector_reuse(vector);
      if (vector == NULL)
          return NULL;
  
      /* Count how big a vector we need and resize accordingly. */
      count = split_multi_count(string, seps);
      if (count == 0)
          return vector;
      if (vector->allocated < count && !strength_vector_resize(vector, count))
          goto fail;
  
      /* Now, walk the string and build the components. */
      vector->count = 0;
      for (start = string, p = string, i = 0; *p != '\0'; p++)
          if (strchr(seps, *p) != NULL) {
              if (start != p) {
                  vector->strings[i] = strndup(start, (size_t) (p - start));
                  if (vector->strings[i] == NULL)
                      goto fail;
                  i++;
                  vector->count++;
              }
              start = p + 1;
          }
  
      /* If there is anything left in the string, we have one more component. */
      if (start != p) {
          vector->strings[i] = strndup(start, (size_t) (p - start));
          if (vector->strings[i] == NULL)
              goto fail;
          vector->count++;
      }
      return vector;
  
  fail:
      if (created)
          strength_vector_free(vector);
      return NULL;
  }
  