/*
* macros.c - a simple macro expander
*
* Copyright (c) 2010-2020 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vppinfra/macros.h>
static inline int
macro_isalnum (i8 c)
{
if ((c >= 'A' && c <= 'Z')
|| (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '_'))
return 1;
return 0;
}
static i8 *
builtin_eval (clib_macro_main_t * mm, i8 * varname, i32 complain)
{
uword *p;
i8 *(*fp) (clib_macro_main_t *, i32);
p = hash_get_mem (mm->the_builtin_eval_hash, varname);
if (p == 0)
return 0;
fp = (void *) (p[0]);
return (*fp) (mm, complain);
}
__clib_export int
clib_macro_unset (clib_macro_main_t * mm, char *name)
{
hash_pair_t *p;
u8 *key, *value;
p = hash_get_pair (mm->the_value_table_hash, name);
if (p == 0)
return 1;
key = (u8 *) (p->key);
value = (u8 *) (p->value[0]);
hash_unset_mem (mm->the_value_table_hash, name);
vec_free (value);
vec_free (key);
return 0;
}
__clib_export int
clib_macro_set_value (clib_macro_main_t * mm, char *name, char *value)
{
u8 *key_copy, *value_copy;
int rv;
rv = clib_macro_unset (mm, name);
key_copy = format (0, "%s%c", name, 0);
value_copy = format (0, "%s%c", value, 0);
hash_set_mem (mm->the_value_table_hash, key_copy, value_copy);
return rv;
}
i8 *
clib_macro_get_value (clib_macro_main_t * mm, char *name)
{
uword *p;
p = hash_get_mem (mm->the_value_table_hash, name);
if (p)
return (i8 *) (p[0]);
else
return 0;
}
/*
* eval: takes a string, returns a vector.
* looks up $foobar in the variable table.
*/
__clib_export i8 *
clib_macro_eval (clib_macro_main_t * mm, i8 * s, i32 complain, u16 level,
u16 max_level)
{
i8 *rv = 0;
i8 *varname, *varvalue;
i8 *ts;
if (level >= max_level)
{
if (complain)
clib_warning ("circular definition, level %d", level);
return (i8 *) format (0, " CIRCULAR ");
}
while (*s)
{
switch (*s)
{
case '\\':
s++;
/* fallthrough */
default:
vec_add1 (rv, *s);
s++;
break;
case '$':
s++;
varname = 0;
/*
* Make vector with variable name in it.
*/
while (*s && (macro_isalnum (*s) || (*s == '_') || (*s == '(')))
{
/* handle $(foo) */
if (*s == '(')
{
s++; /* skip '(' */
while (*s && *s != ')')
{
vec_add1 (varname, *s);
s++;
}
if (*s)
s++; /* skip ')' */
break;
}
vec_add1 (varname, *s);
s++;
}
/* null terminate */
vec_add1 (varname, 0);
/* Look for a builtin, e.g. $my_hostname */
if (!(varvalue = builtin_eval (mm, varname, complain)))
{
/* Look in value table */
if (!varvalue)
{
i8 *tmp = clib_macro_get_value (mm, (char *) varname);
if (tmp)
varvalue = (i8 *) format (0, "%s%c", tmp, 0);
}
#ifdef CLIB_UNIX
/* Look in environment. */
if (!varvalue)
{
char *tmp = getenv ((char *) varname);
if (tmp)
varvalue = (i8 *) format (0, "%s%c", tmp, 0);
}
#endif /* CLIB_UNIX */
}
if (varvalue)
{
/* recursively evaluate */
ts = clib_macro_eval (mm, varvalue, complain, level + 1,
max_level);
vec_free (varvalue);
/* add results to answer */
vec_append (rv, ts);
/* Remove NULL termination or the results are sad */
_vec_len (rv) = vec_len (rv) - 1;
vec_free (ts);
}
else
{
if (complain)
clib_warning ("Undefined Variable Reference: %s\n", varname);
vec_append (rv, format (0, "UNSET "));
_vec_len (rv) = vec_len (rv) - 1;
}
vec_free (varname);
}
}
vec_add1 (rv, 0);
return (rv);
}
/*
* eval: takes a string, returns a vector.
* looks up $foobar in the variable table.
*/
i8 *
clib_macro_eval_dollar (clib_macro_main_t * mm, i8 * s, i32 complain)
{
i8 *s2;
i8 *rv;
s2 = (i8 *) format (0, "$(%s)%c", s, 0);
rv = clib_macro_eval (mm, s2, complain, 0 /* level */ , 8 /* max_level */ );
vec_free (s2);
return (rv);
}
__clib_export void
clib_macro_add_builtin (clib_macro_main_t * mm, char *name, void *eval_fn)
{
hash_set_mem (mm->the_builtin_eval_hash, name, (uword) eval_fn);
}
#ifdef CLIB_UNIX
static i8 *
eval_hostname (clib_macro_main_t * mm, i32 complain)
{
char tmp[128];
if (gethostname (tmp, sizeof (tmp)))
return ((i8 *) format (0, "gethostname-error%c", 0));
return ((i8 *) format (0, "%s%c", tmp, 0));
}
#endif
__clib_export void
clib_macro_init (clib_macro_main_t * mm)
{
if (mm->the_builtin_eval_hash != 0)
{
clib_warning ("mm %p already initialized", mm);
return;
}
mm->the_builtin_eval_hash = hash_create_string (0, sizeof (uword));
mm->the_value_table_hash = hash_create_string (0, sizeof (uword));
#ifdef CLIB_UNIX
hash_set_mem (mm->the_builtin_eval_hash, "hostname", (uword) eval_hostname);
#endif
}
__clib_export void
clib_macro_free (clib_macro_main_t * mm)
{
hash_pair_t *p;
u8 **strings_to_free = 0;
int i;
hash_free (mm->the_builtin_eval_hash);
/* *INDENT-OFF* */
hash_foreach_pair (p, mm->the_value_table_hash,
({
vec_add1 (strings_to_free, (u8 *) (p->key));
vec_add1 (strings_to_free, (u8 *) (p->value[0]));
}));
/* *INDENT-ON* */
for (i = 0; i < vec_len (strings_to_free); i++)
vec_free (strings_to_free[i]);
vec_free (strings_to_free);
hash_free (mm->the_value_table_hash);
}
typedef struct
{
u8 *name;
u8 *value;
} name_sort_t;
static int
name_compare (void *a1, void *a2)
{
name_sort_t *ns1 = a1;
name_sort_t *ns2 = a2;
return strcmp ((char *) ns1->name, (char *) ns2->name);
}
__clib_export u8 *
format_clib_macro_main (u8 * s, va_list * args)
{
clib_macro_main_t *mm = va_arg (*args, clib_macro_main_t *);
int evaluate = va_arg (*args, int);
hash_pair_t *p;
name_sort_t *nses = 0, *ns;
int i;
/* *INDENT-OFF* */
hash_foreach_pair (p, mm->the_value_table_hash,
({
vec_add2 (nses, ns, 1);
ns->name = (u8 *)(p->key);
ns->value = (u8 *)(p->value[0]);
}));
/* *INDENT-ON* */
if (vec_len (nses) == 0)
return s;
vec_sort_with_function (nses, name_compare);
for (i = 0; i < vec_len (nses); i++)
{
s = format (s, "%-20s", nses[i].name);
if (evaluate == 0)
s = format (s, "%s\n", nses[i].value);
else
{
u8 *rv = (u8 *) clib_macro_eval_dollar (mm, (i8 *) nses[i].name,
0 /* no complain */ );
s = format (s, "%s\n", rv);
vec_free (rv);
}
}
return s;
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/