blob: 1a763d5bd6b0cbb9897fb3106e9bc4d94cb424df [file] [log] [blame]
/* 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;
}