Logo Search packages:      
Sourcecode: libnih version File versions  Download package

symbol.c

/* nih-dbus-tool
 *
 * symbol.c - C symbol generation and validation
 *
 * Copyright © 2009 Scott James Remnant <scott@netsplit.com>.
 * Copyright © 2009 Canonical Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2, as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* HAVE_CONFIG_H */


#include <ctype.h>
#include <stdarg.h>
#include <string.h>

#include <nih/macros.h>
#include <nih/alloc.h>
#include <nih/string.h>
#include <nih/logging.h>
#include <nih/error.h>

#include "symbol.h"


/* Prototypes for static functions */
static char *symbol_strcat_interface (char **str, const void *parent,
                              const char *format, ...)
      __attribute__ ((format (printf, 3, 4), warn_unused_result, malloc));
static char *symbol_strcat_title     (char **str, const void *parent,
                              const char *format, ...)
      __attribute__ ((format (printf, 3, 4), warn_unused_result, malloc));


/**
 * symbol_valid:
 * @symbol: Symbol to verify.
 *
 * Verifies whether @symbol matches the rules for C symbol names.  To make
 * things easier for ourselves, we only support a subset of what C99 can
 * really support. ie. no universal character names.
 *
 * Returns: TRUE if valid, FALSE if not.
 **/
int
symbol_valid (const char *symbol)
{
      nih_assert (symbol != NULL);

      /* We can get away with just using strlen() here even through symbol
       * is in UTF-8 because all the valid characters are ASCII.
       */
      for (size_t i = 0; i < strlen (symbol); i++) {
            /* Symbols may contain digits, but not at the beginning. */
            if ((symbol[i] >= '0') && (symbol[i] <= '9')) {
                  if (i == 0)
                        return FALSE;

                  continue;
            }

            /* Valid characters anywhere are [A-Za-z_] */
            if (   ((symbol[i] < 'A') || (symbol[i] > 'Z'))
                && ((symbol[i] < 'a') || (symbol[i] > 'z'))
                && (symbol[i] != '_'))
                  return FALSE;
      }

      /* Symbol must be at least 1 character */
      if (strlen (symbol) < 1)
            return FALSE;

      return TRUE;
}


/**
 * symbol_from_name:
 * @parent: parent object of new string,
 * @name: name to convert.
 *
 * Converts the D-Bus style name @name to C style; basically the name
 * is lower-cased, and underscores inserted between CamelCase words.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the returned string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if allocation fails.
 **/
char *
symbol_from_name (const void *parent,
              const char *name)
{
      char * symbol;
      char * ptr;
      size_t len = 0;

      nih_assert (name != NULL);

      /* Figure out how long the symbol will need to be by counting
       * how many places we need to add underscores and adding them
       * to the length.
       */
      len = strlen (name);
      for (size_t i = 0; i < strlen (name); i++)
            if ((i > 0) && (name[i] >= 'A') && (name[i] <= 'Z')
                && (name[i-1] != '_')
                && ((name[i-1] < 'A') || (name[i-1] > 'Z')))
                  len++;

      /* Allocate the new string */
      symbol = nih_alloc (parent, len + 1);
      if (! symbol)
            return NULL;

      /* Copy the characters across, lowercasing as we go and inserting
       * underscores before any capital unless it follows an underscore.
       */
      ptr = symbol;
      for (size_t i = 0; i < strlen (name); i++) {
            if ((i > 0) && (name[i] >= 'A') && (name[i] <= 'Z')
                && (name[i-1] != '_')
                && ((name[i-1] < 'A') || (name[i-1] > 'Z')))
                  *(ptr++) = '_';

            *(ptr++) = tolower (name[i]);
      }

      *ptr = '\0';

      return symbol;
}


/**
 * symbol_strcat_interface:
 * @str: pointer to string to modify,
 * @parent: parent object of new string,
 * @format: format to append to @str.
 *
 * Replaces periods in the string expanded from @format with underscores
 * and concatenates it onto @str.  The new string is allocated using
 * nih_alloc() and @str will be updated to point to the new string; use
 * the return value simply to check for success.
 *
 * @parent is ignored; though it is usual to pass a parent of @str for
 * style reasons.
 *
 * Returns: newly allocated string or NULL if allocation fails.
 **/
static char *
symbol_strcat_interface (char **     str,
                   const void *parent,
                   const char *format,
                   ...)
{
      nih_local char *name = NULL;
      va_list         args;
      size_t          len;
      char *          ret;

      nih_assert (str != NULL);
      nih_assert (*str != NULL);
      nih_assert (format != NULL);

      va_start (args, format);
      name = nih_vsprintf (NULL, format, args);
      if (! name)
            return NULL;
      va_end (args);

      len = strlen (*str);

      ret = nih_realloc (*str, parent, len + strlen (name) + 1);
      if (! ret)
            return NULL;

      *str = ret;

      /* Copy the characters across, replacing the periods between
       * interface components with underscores.
       */
      for (char *s = name; *s; s++)
            (*str)[len++] = (*s == '.' ? '_' : *s);

      (*str)[len] = '\0';

      return *str;
}

/**
 * symbol_strcat_title:
 * @str: pointer to string to modify,
 * @parent: parent object of new string,
 * @format: format to append to @str.
 *
 * Expands the @format string and modifies it so that each underscore-
 * separated word has the underscores removed and the initial character
 * uppercased.  The new string is allocated using nih_alloc() and @str
 * will be updated to point to the new string; use the return value
 * simply to check for success.
 *
 * If the string pointed to by @str is NULL, a new string will be
 * allocated and if @parent is not NULL, it should be a pointer to another
 * object which will be used as a parent for the returned string.  When all
 * parents of the returned string are freed, the returned string will also
 * be freed.
 *
 * When the string pointed to by @str is not NULL, @parent is ignored;
 * though it is usual to pass a parent of @str for style reasons.
 *
 * Returns: newly allocated string or NULL if allocation fails.
 **/
static char *
symbol_strcat_title (char **     str,
                 const void *parent,
                 const char *format,
                 ...)
{
      va_list         args;
      nih_local char *symbol = NULL;
      size_t          str_len;
      size_t          len;
      char *          ret;
      int             first = TRUE;

      nih_assert (str != NULL);
      nih_assert (format != NULL);

      va_start (args, format);
      symbol = nih_vsprintf (NULL, format, args);
      if (! symbol)
            return NULL;
      va_end (args);

      str_len = *str ? strlen (*str) : 0;
      len = str_len;

      /* Figure out how long the new string will be by counting how many
       * characters that aren't underscores we'll end up adding.
       */
      for (char *s = symbol; *s; s++)
            if (*s != '_')
                  len++;

      ret = nih_realloc (*str, parent, len + 1);
      if (! ret)
            return NULL;

      *str = ret;

      len = str_len;

      /* Copy the characters across, uppercasing the first character of
       * each word and stripping underscores.
       */
      for (char *s = symbol; *s; s++) {
            if (*s == '_') {
                  first = TRUE;
            } else {
                  (*str)[len++] = (first ? toupper (*s) : *s);
                  first = FALSE;
            }
      }

      (*str)[len] = '\0';

      return *str;
}

/**
 * symbol_impl:
 * @parent: parent object of new string,
 * @prefix: prefix for returned symbol,
 * @interface_name: name of D-Bus interface,
 * @name: name of interface member,
 * @postfix: postfix for returned symbol.
 *
 * Generates a C symbol for an implementation function, one that is hidden
 * from the API and thus uniqueness and verboseness is more desirable than
 * readability.  The @prefix is prepended to the @interface_name and
 * member @name, with the @postfix appended.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the returned string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if allocation fails.
 **/
char *
symbol_impl (const void *parent,
           const char *prefix,
           const char *interface_name,
           const char *name,
           const char *postfix)
{
      char *str;

      nih_assert (prefix != NULL);
      nih_assert (interface_name != NULL);
      nih_assert ((postfix != NULL) || (name == NULL));

      str = nih_sprintf (parent, "%s_", prefix);
      if (! str)
            return NULL;

      if (! symbol_strcat_interface (&str, parent, (name ? "%s_" : "%s"),
                               interface_name)) {
            nih_free (str);
            return NULL;
      }

      if (name) {
            if (! nih_strcat (&str, parent, name)) {
                  nih_free (str);
                  return NULL;
            }
      }

      if (postfix) {
            if (! nih_strcat_sprintf (&str, parent, "_%s", postfix)) {
                  nih_free (str);
                  return NULL;
            }
      }

      return str;
}

/**
 * symbol_extern:
 * @parent: parent object of new string,
 * @prefix: prefix for returned symbol,
 * @interface_symbol: symbol for D-Bus interface,
 * @midfix: midfix for returned symbol,
 * @symbol: symbol of interface member,
 * @postfix: postfix for returned symbol.
 *
 * Generates a C symbol for an external function, one that is either part of
 * the API or intended to be supplied externally, thus where readability
 * is more desirable than uniqueness or verboseness.  The @prefix is
 * prepended to the @interface_symbol (if supplied), @midfix (if supplied),
 * member @symbol, with the @postfix (if supplied) appended.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the returned string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if allocation fails.
 **/
char *
symbol_extern (const void *parent,
             const char *prefix,
             const char *interface_symbol,
             const char *midfix,
             const char *symbol,
             const char *postfix)
{
      char *str;

      nih_assert (prefix != NULL);
      nih_assert (symbol != NULL);

      str = nih_sprintf (parent, "%s_", prefix);
      if (! str)
            return NULL;

      if (interface_symbol) {
            if (! nih_strcat_sprintf (&str, parent, "%s_",
                                interface_symbol)) {
                  nih_free (str);
                  return NULL;
            }
      }

      if (midfix) {
            if (! nih_strcat_sprintf (&str, parent, "%s_", midfix)) {
                  nih_free (str);
                  return NULL;
            }
      }

      if (! nih_strcat (&str, parent, symbol)) {
            nih_free (str);
            return NULL;
      }

      if (postfix) {
            if (! nih_strcat_sprintf (&str, parent, "_%s", postfix)) {
                  nih_free (str);
                  return NULL;
            }
      }

      return str;
}

/**
 * symbol_typedef:
 * @parent: parent object of new string,
 * @prefix: prefix for returned symbol,
 * @interface_symbol: symbol for D-Bus interface,
 * @midfix: midfix for returned symbol,
 * @symbol: symbol of interface member,
 * @postfix: postfix for returned symbol.
 *
 * Generates a C typedef name for a function that is expected to be
 * supplied, this has the same basic form as an external symbol except that
 * underscores are removed and the first letter of each part is uppercased.
 * The @prefix is prepended to the @interface_symbol (if supplied), @midfix
 * (if supplied), member @symbol, with the @postfix appended.
 *
 * If @parent is not NULL, it should be a pointer to another object which
 * will be used as a parent for the returned string.  When all parents
 * of the returned string are freed, the returned string will also be
 * freed.
 *
 * Returns: newly allocated string or NULL if allocation fails.
 **/
char *
symbol_typedef (const void *parent,
            const char *prefix,
            const char *interface_symbol,
            const char *midfix,
            const char *symbol,
            const char *postfix)
{
      char *str;

      nih_assert (prefix != NULL);
      nih_assert (symbol != NULL);

      str = NULL;
      if (! symbol_strcat_title (&str, parent, "%s_", prefix))
            return NULL;

      if (interface_symbol) {
            if (! symbol_strcat_title (&str, parent, "%s_",
                                 interface_symbol)) {
                  nih_free (str);
                  return NULL;
            }
      }

      if (midfix) {
            if (! symbol_strcat_title (&str, parent, "%s_", midfix)) {
                  nih_free (str);
                  return NULL;
            }
      }

      if (! symbol_strcat_title (&str, parent, "%s", symbol)) {
            nih_free (str);
            return NULL;
      }

      if (postfix) {
            if (! symbol_strcat_title (&str, parent, "_%s", postfix)) {
                  nih_free (str);
                  return NULL;
            }
      }

      return str;
}

Generated by  Doxygen 1.6.0   Back to index