Ejemplo n.º 1
0
/** dropt_strnicmp
  *
  *     Compares the first n characters of two strings, ignoring case
  *       differences.  Not recommended for non-ASCII strings.
  *
  * PARAMETERS:
  *     IN s, t : The strings to compare.
  *     IN n    : The maximum number of dropt_char-s to compare.
  *
  * RETURNS:
  *     0 if the strings are equivalent,
  *     < 0 if s is lexically less than t,
  *     > 0 if s is lexically greater than t.
  */
int
dropt_strnicmp(const dropt_char* s, const dropt_char* t, size_t n)
{
    assert(s != NULL);
    assert(t != NULL);

    if (s == t) { return 0; }

    while (n--)
    {
        if (*s == DROPT_TEXT_LITERAL('\0') && *t == DROPT_TEXT_LITERAL('\0'))
        {
            break;
        }
        else if (*s == *t || dropt_tolower(*s) == dropt_tolower(*t))
        {
            s++;
            t++;
        }
        else
        {
            return (dropt_tolower(*s) < dropt_tolower(*t))
                   ? -1
                   : +1;
        }
    }

    return 0;
}
Ejemplo n.º 2
0
/** dropt_vsnprintf
  *
  *     vsnprintf wrapper to provide ISO C99-compliant behavior.
  *
  * PARAMETERS:
  *     OUT s     : The destination buffer.  May be NULL if n is 0.
  *                 If non-NULL, always NUL-terminated.
  *     IN n      : The size of the destination buffer, measured in
  *                   dropt_char-s.
  *     IN format : printf-style format specifier.  Must not be NULL.
  *     IN args   : Arguments to insert into the formatted string.
  *
  * RETURNS:
  *     The number of characters that would be written to the destination
  *       buffer if it's sufficiently large, excluding the NUL-terminator.
  *     Returns -1 on error.
  */
int
dropt_vsnprintf(dropt_char* s, size_t n, const dropt_char* format, va_list args)
{
#if __STDC_VERSION__ >= 199901L || __GNUC__
    /* ISO C99-compliant.
     *
     * As far as I can tell, gcc's implementation of vsnprintf has always
     * matched the behavior required by the C99 standard (which is to
     * return the necessary buffer size).
     *
     * Note that this won't work with wchar_t because there is no true,
     * standard wchar_t equivalent of snprintf.  swprintf comes close but
     * doesn't return the necessary buffer size (and the standard does not
     * provide a guaranteed way to test if truncation occurred), and its
     * format string can't be used interchangeably with snprintf.
     *
     * It's simpler not to support wchar_t on non-Windows platforms.
     */
    assert(format != NULL);
    return vsnprintf(s, n, format, args);
#elif defined __BORLANDC__
    /* Borland's compiler neglects to NUL-terminate. */
    int ret;
    assert(format != NULL);
    ret = vsnprintf(s, n, format, args);
    if (n != 0) { s[n - 1] = DROPT_TEXT_LITERAL('\0'); }
    return ret;
#elif defined _MSC_VER
    /* _vsntprintf and _vsnprintf_s on Windows don't have C99 semantics;
     * they return -1 if truncation occurs.
     */
    va_list argsCopy;
    int ret;

    assert(format != NULL);

    va_copy(argsCopy, args);
    ret = _vsctprintf(format, argsCopy);
    va_end(argsCopy);

    if (n != 0)
    {
        assert(s != NULL);

    #if _MSC_VER >= 1400
        (void) _vsntprintf_s(s, n, _TRUNCATE, format, args);
    #else
        /* This version doesn't necessarily NUL-terminate.  Sigh. */
        (void) _vsnprintf(s, n, format, args);
        s[n - 1] = DROPT_TEXT_LITERAL('\0');
    #endif
    }

    return ret;

#else
    #error Unsupported platform.  dropt_vsnprintf unimplemented.
    return -1;
#endif
}
Ejemplo n.º 3
0
/** dropt_get_error_message
  *
  * PARAMETERS:
  *     IN context : The dropt context.
  *                  Must not be NULL.
  *
  * RETURNS:
  *     The current error message waiting in the dropt context or the empty
  *       string if there are no errors.  Note that calling any dropt
  *       function other than dropt_get_error, dropt_get_error_details, and
  *       dropt_get_error_message may invalidate a previously-returned
  *       string.
  */
const dropt_char *
dropt_get_error_message(dropt_context * context)
{
  if (context == NULL) {
    DROPT_MISUSE("No dropt context specified.");
    return DROPT_TEXT_LITERAL("");
  }

  if (context->errorDetails.err == dropt_error_none) {
    return DROPT_TEXT_LITERAL("");
  }

  if (context->errorDetails.message == NULL) {
    if (context->errorHandler != NULL) {
      context->errorDetails.message
        = context->errorHandler(context->errorDetails.err,
                                context->errorDetails.optionName,
                                context->errorDetails.optionArgument,
                                context->errorHandlerData);
    } else {
#ifndef DROPT_NO_STRING_BUFFERS
      context->errorDetails.message
        = dropt_default_error_handler(context->errorDetails.err,
                                      context->errorDetails.optionName,
                                      context->errorDetails.optionArgument);
#endif
    }
  }

  return (context->errorDetails.message == NULL)
         ? DROPT_TEXT_LITERAL("Unknown error")
         : context->errorDetails.message;
}
Ejemplo n.º 4
0
/** dropt_strndup
  *
  *     Duplicates the first n characters of a string.
  *
  * PARAMETERS:
  *     IN s : The string to duplicate.
  *     IN n : The maximum number of dropt_char-s to copy, excluding the
  *              NUL-terminator.
  *
  * RETURNS:
  *     The duplicated string, which is always NUL-terminated.  The caller
  *       is responsible for calling free() on it when no longer needed.
  *     Returns NULL on error.
  */
dropt_char*
dropt_strndup(const dropt_char* s, size_t n)
{
    dropt_char* copy;
    size_t len = 0;

    assert(s != NULL);

    while (len < n && s[len] != DROPT_TEXT_LITERAL('\0'))
    {
        len++;
    }

    if (len + 1 < len)
    {
        /* This overflow check shouldn't be strictly necessary.  len can be
         * at most SIZE_MAX, so SIZE_MAX + 1 can wrap around to 0, but
         * dropt_safe_malloc will return NULL for a 0-sized allocation.
         * However, favor defensive paranoia.
         */
        return NULL;
    }

    copy = dropt_safe_malloc(len + 1 /* NUL */, sizeof *copy);
    if (copy != NULL)
    {
        memcpy(copy, s, len * sizeof *copy);
        copy[len] = DROPT_TEXT_LITERAL('\0');
    }

    return copy;
}
Ejemplo n.º 5
0
/** dropt_default_error_handler
  *
  *     Default error handler.
  *
  * PARAMETERS:
  *     IN error          : The error code.
  *     IN optionName     : The name of the option we failed on.
  *     IN optionArgument : The value of the option we failed on.
  *                         Pass NULL if unwanted.
  *
  * RETURNS:
  *     An allocated string for the given error.  The caller is responsible
  *       for calling free() on it when no longer needed.
  *     May return NULL.
  */
dropt_char *
dropt_default_error_handler(dropt_error error,
                            const dropt_char * optionName,
                            const dropt_char * optionArgument)
{
  dropt_char * s = NULL;

  const dropt_char * separator = DROPT_TEXT_LITERAL(": ");

  if (optionArgument == NULL) {
    separator = optionArgument = DROPT_TEXT_LITERAL("");
  }

  switch (error) {
    case dropt_error_none:
      /* This shouldn't happen (unless client code invokes this
       * directly with dropt_error_none), but it's here for
       * completeness.
       */
      break;

    case dropt_error_bad_configuration:
      s = dropt_strdup(DROPT_TEXT_LITERAL("Invalid option configuration"));
      break;

    case dropt_error_invalid_option:
      s = dropt_asprintf(DROPT_TEXT_LITERAL("Invalid option: %s"),
                         optionName);
      break;
    case dropt_error_insufficient_arguments:
      s = dropt_asprintf(DROPT_TEXT_LITERAL("Value required after option %s"),
                         optionName);
      break;
    case dropt_error_mismatch:
      s = dropt_asprintf(DROPT_TEXT_LITERAL("Invalid value for option %s%s%s"),
                         optionName, separator, optionArgument);
      break;
    case dropt_error_overflow:
      s = dropt_asprintf(DROPT_TEXT_LITERAL("Value too large for option %s%s%s"),
                         optionName, separator, optionArgument);
      break;
    case dropt_error_underflow:
      s = dropt_asprintf(DROPT_TEXT_LITERAL("Value too small for option %s%s%s"),
                         optionName, separator, optionArgument);
      break;
    case dropt_error_insufficient_memory:
      s = dropt_strdup(DROPT_TEXT_LITERAL("Insufficient memory"));
      break;
    case dropt_error_unknown:
    default:
      s = dropt_asprintf(DROPT_TEXT_LITERAL("Unknown error handling option %s"),
                         optionName);
      break;
  }

  return s;
}
Ejemplo n.º 6
0
/** set_short_option_error_details
  *
  *     Generates error details in the dropt context.
  *
  * PARAMETERS:
  *     IN/OUT context    : The dropt context.
  *     IN err            : The error code.
  *     IN shortName      : the "short" name of the option we failed on.
  *     IN optionArgument : The value of the option we failed on.
  *                         Pass NULL if unwanted.
  */
static void
set_short_option_error_details(dropt_context * context, dropt_error err,
                               dropt_char shortName, const dropt_char * optionArgument)
{
  /* "-?" is just a placeholder. */
  dropt_char shortNameBuf[] = DROPT_TEXT_LITERAL("-?");

  assert(context != NULL);
  assert(shortName != DROPT_TEXT_LITERAL('\0'));

  shortNameBuf[1] = shortName;

  set_error_details(context, err,
                    make_char_array(shortNameBuf, ARRAY_LENGTH(shortNameBuf) - 1),
                    optionArgument);
}
Ejemplo n.º 7
0
/** find_option_short
  *
  *     Finds the option specification for a short option name (i.e., an
  *     option of the form "-o").
  *
  * PARAMETERS:
  *     IN context   : The dropt context.
  *     IN shortName : The short option name to search for.
  *
  * RETURNS:
  *     A pointer to the corresponding option specification or NULL if not
  *       found.
  */
static const dropt_option *
find_option_short(const dropt_context * context, dropt_char shortName)
{
  assert(context != NULL);
  assert(shortName != DROPT_TEXT_LITERAL('\0'));
  assert(context->ncmpstr != NULL);

  if (context->sortedByShort != NULL) {
    option_proxy * found = bsearch(&shortName, context->sortedByShort,
                                   context->numOptions, sizeof * (context->sortedByShort),
                                   cmp_key_option_proxy_short);
    return (found == NULL) ? NULL : found->option;
  }

  /* Fall back to a linear search. */
  {
    const dropt_option * option;
    for (option = context->options; is_valid_option(option); option++) {
      if (context->ncmpstr(&shortName, &option->short_name, 1) == 0) {
        return option;
      }
    }
  }
  return NULL;
}
Ejemplo n.º 8
0
/** dropt_ssclear
  *
  *     Clears and re-initializes a dropt_stringstream.
  *
  * PARAMETERS:
  *     IN/OUT ss : The dropt_stringstream
  */
void
dropt_ssclear(dropt_stringstream* ss)
{
    assert(ss != NULL);

    ss->string[0] = DROPT_TEXT_LITERAL('\0');
    ss->used = 0;

    dropt_ssresize(ss, default_stringstream_buffer_size);
}
Ejemplo n.º 9
0
/** is_valid_option
  *
  * PARAMETERS:
  *     IN option : Specification for an individual option.
  *
  * RETURNS:
  *     true if the specified option is valid, false if it's a sentinel
  *       value.
  */
static bool
is_valid_option(const dropt_option * option)
{
  return    option != NULL
            && !(option->long_name == NULL
                 && option->short_name == DROPT_TEXT_LITERAL('\0')
                 && option->description == NULL
                 && option->arg_description == NULL
                 && option->handler == NULL
                 && option->handler_data == NULL
                 && option->attr == 0);
}
Ejemplo n.º 10
0
/** dropt_new_context
  *
  *     Creates a new dropt context.
  *
  * PARAMETERS:
  *     IN options : The list of option specifications.
  *                  Must not be NULL.
  *
  * RETURNS:
  *     An allocated dropt context.  The caller is responsible for freeing
  *       it with dropt_free_context when no longer needed.
  *     Returns NULL on error.
  */
dropt_context *
dropt_new_context(const dropt_option * options)
{
  dropt_context * context = NULL;
  size_t n;

  if (options == NULL) {
    DROPT_MISUSE("No option list specified.");
    goto exit;
  }

  /* Sanity-check the options. */
  for (n = 0; is_valid_option(&options[n]); n++) {
    if (options[n].short_name == DROPT_TEXT_LITERAL('=')
        || (options[n].long_name != NULL
            && dropt_strchr(options[n].long_name, DROPT_TEXT_LITERAL('=')) != NULL)) {
      DROPT_MISUSE("Invalid option list. '=' may not be used in an option name.");
      goto exit;
    }
  }

  context = malloc(sizeof * context);
  if (context == NULL) {
    goto exit;
  } else {
    dropt_context emptyContext = { 0 };
    *context = emptyContext;

    context->options = options;
    context->numOptions = n;
    dropt_set_strncmp(context, NULL);
  }

exit:
  return context;
}
Ejemplo n.º 11
0
/** dropt_ssopen
  *
  *     Constructs a new dropt_stringstream.
  *
  * RETURNS:
  *     An initialized dropt_stringstream.  The caller is responsible for
  *       calling either dropt_ssclose() or dropt_ssfinalize() on it when
  *       no longer needed.
  *     Returns NULL on error.
  */
dropt_stringstream *
dropt_ssopen(void)
{
  dropt_stringstream * ss = malloc(sizeof * ss);
  if (ss != NULL) {
    ss->used = 0;
    ss->maxSize = default_stringstream_buffer_size;
    ss->string = dropt_safe_malloc(ss->maxSize, sizeof * ss->string);
    if (ss->string == NULL) {
      free(ss);
      ss = NULL;
    } else {
      ss->string[0] = DROPT_TEXT_LITERAL('\0');
    }
  }
  return ss;
}
Ejemplo n.º 12
0
/** dropt_get_help
  *
  * PARAMETERS:
  *     IN context    : The dropt context.
  *                     Must not be NULL.
  *     IN helpParams : The help parameters.
  *                     Pass NULL to use the default help parameters.
  *
  * RETURNS:
  *     An allocated help string for the available options.  The caller is
  *       responsible for calling free() on it when no longer needed.
  *     Returns NULL on error.
  */
dropt_char *
dropt_get_help(const dropt_context * context, const dropt_help_params * helpParams)
{
  dropt_char * helpText = NULL;
  dropt_stringstream * ss = dropt_ssopen();

  if (context == NULL) {
    DROPT_MISUSE("No dropt context specified.");
  } else if (ss != NULL) {
    const dropt_option * option;
    dropt_help_params hp;

    if (helpParams == NULL) {
      dropt_init_help_params(&hp);
    } else {
      hp = *helpParams;
    }

    for (option = context->options; is_valid_option(option); option++) {
      bool hasLongName =    option->long_name != NULL
                            && option->long_name[0] != DROPT_TEXT_LITERAL('\0');
      bool hasShortName = option->short_name != DROPT_TEXT_LITERAL('\0');

      /* The number of characters printed on the current line so far. */
      int n;

      if (option->description == NULL || (option->attr & dropt_attr_hidden)) {
        /* Undocumented option.  Ignore it and move on. */
        continue;
      } else if (hasLongName && hasShortName) {
        n = dropt_ssprintf(ss, DROPT_TEXT_LITERAL("%*s-%c, --%s"),
                           hp.indent, DROPT_TEXT_LITERAL(""),
                           option->short_name, option->long_name);
      } else if (hasLongName) {
        n = dropt_ssprintf(ss, DROPT_TEXT_LITERAL("%*s--%s"),
                           hp.indent, DROPT_TEXT_LITERAL(""),
                           option->long_name);
      } else if (hasShortName) {
        n = dropt_ssprintf(ss, DROPT_TEXT_LITERAL("%*s-%c"),
                           hp.indent, DROPT_TEXT_LITERAL(""),
                           option->short_name);
      } else {
        /* Comment text.  Don't bother with indentation. */
        assert(option->description != NULL);
        dropt_ssprintf(ss, DROPT_TEXT_LITERAL("%s\n"), option->description);
        goto next;
      }

      if (n < 0) {
        n = 0;
      }

      if (option->arg_description != NULL) {
        int m = dropt_ssprintf(ss,
                               (option->attr & dropt_attr_optional_val)
                               ? DROPT_TEXT_LITERAL("[=%s]")
                               : DROPT_TEXT_LITERAL("=%s"),
                               option->arg_description);
        if (m > 0) {
          n += m;
        }
      }

      /* Check for equality to make sure that there's at least one
       * space between the option name and its description.
       */
      if ((unsigned int) n >= hp.description_start_column) {
        dropt_ssprintf(ss, DROPT_TEXT_LITERAL("\n"));
        n = 0;
      }

      {
        const dropt_char * line = option->description;
        while (line != NULL) {
          int lineLen;
          const dropt_char * nextLine;
          const dropt_char * newline = dropt_strchr(line, DROPT_TEXT_LITERAL('\n'));

          if (newline == NULL) {
            lineLen = dropt_strlen(line);
            nextLine = NULL;
          } else {
            lineLen = newline - line;
            nextLine = newline + 1;
          }

          dropt_ssprintf(ss, DROPT_TEXT_LITERAL("%*s%.*s\n"),
                         hp.description_start_column - n, DROPT_TEXT_LITERAL(""),
                         lineLen, line);
          n = 0;

          line = nextLine;
        }
      }

next:
      if (hp.blank_lines_between_options) {
        dropt_ssprintf(ss, DROPT_TEXT_LITERAL("\n"));
      }
    }
    helpText = dropt_ssfinalize(ss);
  }

  return helpText;
}