/* mp4h -- A macro processor for HTML documents
Copyright 2000-2001, Denis Barbier
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is a work based on GNU m4 version 1.4n. Below is the
original copyright.
*/
/* GNU m4 -- A simple macro processor
Copyright (C) 1989, 90, 91, 92, 93, 94, 98 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Code for all builtin macros, initialisation of symbol table, and
expansion of user defined macros. */
#define MP4H_MODULE
#include "mp4h.h"
#undef MP4H_MODULE
#include "builtin.h"
#define CHECK_SAFETY_LEVEL(sl) \
if (safety_level > sl) \
{ \
MP4HERROR ((warning_status, 0, \
_("Warning:%s:%d: `<%s>' ignored due to -S flag"), \
CURRENT_FILE_LINE, ARG (0))); \
return; \
}
/* Initialisation of builtin and predefined macros. The table
"builtin_tab" is both used for initialisation, and by the "builtin"
builtin. */
DECLARE (mp4h_bp___file__);
DECLARE (mp4h_bp___line__);
DECLARE (mp4h_bp___version__);
DECLARE (mp4h_bp_lb);
DECLARE (mp4h_bp_rb);
DECLARE (mp4h_bp_dq);
DECLARE (mp4h_bp_bs);
DECLARE (mp4h_bp_timer);
#ifdef HAVE_LOCALE_H
DECLARE (mp4h_bp_mp4h_l10n);
#endif
DECLARE (mp4h_bp_mp4h_output_radix);
#if !defined(HAVE_FILE_FUNCS) || !defined (HAVE_LOCALE_H) || !defined(WITH_MODULES)
DECLARE (mp4h_bp_unsupported);
#endif
/* debug functions */
DECLARE (mp4h_bp_debugmode);
DECLARE (mp4h_bp_debugfile);
DECLARE (mp4h_bp_function_def);
DECLARE (mp4h_bp_debugging_off);
DECLARE (mp4h_bp_debugging_on);
/* file functions */
#ifdef HAVE_FILE_FUNCS
DECLARE (mp4h_bp_get_file_properties);
DECLARE (mp4h_bp_directory_contents);
DECLARE (mp4h_bp_file_exists);
DECLARE (mp4h_bp_real_path);
#endif /* HAVE_FILE_FUNCS */
DECLARE (mp4h_bp_date);
/* flow functions */
DECLARE (mp4h_bp_group);
DECLARE (mp4h_bp_compound);
DECLARE (mp4h_bp_disjoin);
DECLARE (mp4h_bp_noexpand);
DECLARE (mp4h_bp_expand);
DECLARE (mp4h_bp_if);
DECLARE (mp4h_bp_ifeq);
DECLARE (mp4h_bp_ifneq);
DECLARE (mp4h_bp_when);
DECLARE (mp4h_bp_while);
DECLARE (mp4h_bp_foreach);
DECLARE (mp4h_bp_var_case);
DECLARE (mp4h_bp_break);
DECLARE (mp4h_bp_return);
DECLARE (mp4h_bp_warning);
DECLARE (mp4h_bp_exit);
DECLARE (mp4h_bp_at_end_of_file);
/* macro functions */
DECLARE (mp4h_bp_define_tag);
DECLARE (mp4h_bp_provide_tag);
DECLARE (mp4h_bp_let);
DECLARE (mp4h_bp_undef);
DECLARE (mp4h_bp_set_hook);
DECLARE (mp4h_bp_get_hook);
DECLARE (mp4h_bp_attributes_quote);
DECLARE (mp4h_bp_attributes_remove);
DECLARE (mp4h_bp_attributes_extract);
DECLARE (mp4h_bp_define_entity);
/* math functions */
DECLARE (mp4h_bp_add);
DECLARE (mp4h_bp_substract);
DECLARE (mp4h_bp_multiply);
DECLARE (mp4h_bp_divide);
DECLARE (mp4h_bp_modulo);
DECLARE (mp4h_bp_min);
DECLARE (mp4h_bp_max);
DECLARE (mp4h_bp_gt);
DECLARE (mp4h_bp_lt);
DECLARE (mp4h_bp_eq);
DECLARE (mp4h_bp_neq);
/* page functions */
DECLARE (mp4h_bp_include);
DECLARE (mp4h_bp___include);
DECLARE (mp4h_bp_use);
#ifdef WITH_MODULES
DECLARE (mp4h_bp_load);
#endif
DECLARE (mp4h_bp_comment);
DECLARE (mp4h_bp_set_eol_comment);
DECLARE (mp4h_bp_set_quotes);
DECLARE (mp4h_bp_dnl);
DECLARE (mp4h_bp_frozen_dump);
/* relational operators */
DECLARE (mp4h_bp_not);
DECLARE (mp4h_bp_and);
DECLARE (mp4h_bp_or);
/* string functions */
DECLARE (mp4h_bp_downcase);
DECLARE (mp4h_bp_upcase);
DECLARE (mp4h_bp_capitalize);
DECLARE (mp4h_bp_string_length);
DECLARE (mp4h_bp_substring);
DECLARE (mp4h_bp_string_eq);
DECLARE (mp4h_bp_string_neq);
DECLARE (mp4h_bp_string_compare);
DECLARE (mp4h_bp_char_offsets);
/* regexp functions */
DECLARE (mp4h_bp_subst_in_string);
DECLARE (mp4h_bp_subst_in_var);
DECLARE (mp4h_bp_match);
/* variable functions */
DECLARE (mp4h_bp_get_var);
DECLARE (mp4h_bp_get_var_once);
DECLARE (mp4h_bp_set_var_x);
DECLARE (mp4h_bp_set_var);
DECLARE (mp4h_bp_unset_var);
DECLARE (mp4h_bp_preserve);
DECLARE (mp4h_bp_restore);
DECLARE (mp4h_bp_var_exists);
DECLARE (mp4h_bp_increment);
DECLARE (mp4h_bp_decrement);
DECLARE (mp4h_bp_symbol_info);
DECLARE (mp4h_bp_copy_var);
DECLARE (mp4h_bp_defvar);
/* array functions */
DECLARE (mp4h_bp_array_size);
DECLARE (mp4h_bp_array_add_unique);
DECLARE (mp4h_bp_array_member);
DECLARE (mp4h_bp_array_push);
DECLARE (mp4h_bp_array_pop);
DECLARE (mp4h_bp_array_topvalue);
DECLARE (mp4h_bp_array_shift);
DECLARE (mp4h_bp_array_concat);
DECLARE (mp4h_bp_sort);
/* diversion functions */
DECLARE (mp4h_bp_divert);
DECLARE (mp4h_bp_divnum);
DECLARE (mp4h_bp_undivert);
#undef DECLARE
static builtin
builtin_tab[] =
{
/* name container expand function
attributes */
{ "__file__", FALSE, TRUE, mp4h_bp___file__ },
{ "__line__", FALSE, TRUE, mp4h_bp___line__ },
{ "__version__", FALSE, TRUE, mp4h_bp___version__ },
{ "lb", FALSE, TRUE, mp4h_bp_lb },
{ "rb", FALSE, TRUE, mp4h_bp_rb },
{ "dq", FALSE, TRUE, mp4h_bp_dq },
{ "bs", FALSE, TRUE, mp4h_bp_bs },
{ "timer", FALSE, TRUE, mp4h_bp_timer },
#ifdef HAVE_LOCALE_H
{ "mp4h-l10n", FALSE, TRUE, mp4h_bp_mp4h_l10n },
#else
{ "mp4h-l10n", FALSE, TRUE, mp4h_bp_unsupported },
#endif
{ "mp4h-output-radix",FALSE, TRUE, mp4h_bp_mp4h_output_radix },
{ "date", FALSE, TRUE, mp4h_bp_date },
/* debug functions */
{ "debugmode", FALSE, TRUE, mp4h_bp_debugmode },
{ "debugfile", FALSE, TRUE, mp4h_bp_debugfile },
{ "function-def", FALSE, TRUE, mp4h_bp_function_def },
{ "debugging-off", FALSE, TRUE, mp4h_bp_debugging_off },
{ "debugging-on", FALSE, TRUE, mp4h_bp_debugging_on },
/* file functions */
#ifdef HAVE_FILE_FUNCS
{ "get-file-properties", FALSE, TRUE, mp4h_bp_get_file_properties },
{ "directory-contents", FALSE, TRUE, mp4h_bp_directory_contents },
{ "file-exists", FALSE, TRUE, mp4h_bp_file_exists },
{ "real-path", FALSE, TRUE, mp4h_bp_real_path },
#else
{ "get-file-properties", FALSE, TRUE, mp4h_bp_unsupported },
{ "directory-contents", FALSE, TRUE, mp4h_bp_unsupported },
{ "file-exists", FALSE, TRUE, mp4h_bp_unsupported },
{ "real-path", FALSE, TRUE, mp4h_bp_unsupported },
#endif /* HAVE_FILE_FUNCS */
/* flow functions */
{ "group", FALSE, TRUE, mp4h_bp_group },
{ "compound", TRUE, TRUE, mp4h_bp_compound },
{ "disjoin", FALSE, TRUE, mp4h_bp_disjoin },
{ "noexpand", FALSE, FALSE, mp4h_bp_noexpand },
{ "expand", FALSE, TRUE, mp4h_bp_expand },
{ "if", FALSE, FALSE, mp4h_bp_if },
{ "ifeq", FALSE, FALSE, mp4h_bp_ifeq },
{ "ifneq", FALSE, FALSE, mp4h_bp_ifneq },
{ "when", TRUE, TRUE, mp4h_bp_when },
{ "while", TRUE, FALSE, mp4h_bp_while },
{ "foreach", TRUE, TRUE, mp4h_bp_foreach },
{ "var-case", FALSE, FALSE, mp4h_bp_var_case },
{ "break", FALSE, TRUE, mp4h_bp_break },
{ "return", FALSE, TRUE, mp4h_bp_return },
{ "warning", FALSE, TRUE, mp4h_bp_warning },
{ "exit", FALSE, TRUE, mp4h_bp_exit },
{ "at-end-of-file", TRUE, TRUE, mp4h_bp_at_end_of_file },
/* macro functions */
{ "define-tag", TRUE, TRUE, mp4h_bp_define_tag },
{ "provide-tag", TRUE, TRUE, mp4h_bp_provide_tag },
{ "let", FALSE, TRUE, mp4h_bp_let },
{ "undef", FALSE, TRUE, mp4h_bp_undef },
{ "set-hook", TRUE, TRUE, mp4h_bp_set_hook },
{ "get-hook", FALSE, TRUE, mp4h_bp_get_hook },
{ "attributes-quote", FALSE, TRUE, mp4h_bp_attributes_quote },
{ "attributes-remove", FALSE, TRUE, mp4h_bp_attributes_remove },
{ "attributes-extract", FALSE, TRUE, mp4h_bp_attributes_extract },
{ "define-entity", TRUE, TRUE, mp4h_bp_define_entity },
/* numerical relational operators */
{ "gt", FALSE, TRUE, mp4h_bp_gt },
{ "lt", FALSE, TRUE, mp4h_bp_lt },
{ "eq", FALSE, TRUE, mp4h_bp_eq },
{ "neq", FALSE, TRUE, mp4h_bp_neq },
/* math functions */
{ "add", FALSE, TRUE, mp4h_bp_add },
{ "substract", FALSE, TRUE, mp4h_bp_substract },
{ "multiply", FALSE, TRUE, mp4h_bp_multiply },
{ "divide", FALSE, TRUE, mp4h_bp_divide },
{ "modulo", FALSE, TRUE, mp4h_bp_modulo },
{ "min", FALSE, TRUE, mp4h_bp_min },
{ "max", FALSE, TRUE, mp4h_bp_max },
/* page functions */
{ "include", FALSE, FALSE, mp4h_bp_include },
{ "__include", TRUE, TRUE, mp4h_bp___include },
{ "use", FALSE, TRUE, mp4h_bp_use },
#ifdef WITH_MODULES
{ "load", FALSE, TRUE, mp4h_bp_load },
#else
{ "load", FALSE, TRUE, mp4h_bp_unsupported },
#endif
{ "comment", TRUE, TRUE, mp4h_bp_comment },
{ "set-eol-comment", FALSE, TRUE, mp4h_bp_set_eol_comment },
{ "set-quotes", FALSE, TRUE, mp4h_bp_set_quotes },
{ "dnl", FALSE, TRUE, mp4h_bp_dnl },
{ "frozen-dump", FALSE, TRUE, mp4h_bp_frozen_dump },
/* relational operators */
{ "not", FALSE, TRUE, mp4h_bp_not },
{ "and", FALSE, TRUE, mp4h_bp_and },
{ "or", FALSE, TRUE, mp4h_bp_or },
/* string functions */
{ "downcase", FALSE, TRUE, mp4h_bp_downcase },
{ "upcase", FALSE, TRUE, mp4h_bp_upcase },
{ "capitalize", FALSE, TRUE, mp4h_bp_capitalize },
{ "string-length", FALSE, TRUE, mp4h_bp_string_length },
{ "substring", FALSE, TRUE, mp4h_bp_substring },
{ "string-eq", FALSE, TRUE, mp4h_bp_string_eq },
{ "string-neq", FALSE, TRUE, mp4h_bp_string_neq },
{ "string-compare", FALSE, TRUE, mp4h_bp_string_compare },
{ "char-offsets", FALSE, TRUE, mp4h_bp_char_offsets },
/* regexp functions */
{ "subst-in-string", FALSE, TRUE, mp4h_bp_subst_in_string },
{ "subst-in-var", FALSE, TRUE, mp4h_bp_subst_in_var },
{ "match", FALSE, TRUE, mp4h_bp_match },
/* variable functions */
{ "get-var", FALSE, TRUE, mp4h_bp_get_var },
{ "get-var-once", FALSE, TRUE, mp4h_bp_get_var_once },
{ "set-var-x", TRUE, TRUE, mp4h_bp_set_var_x },
{ "set-var", FALSE, TRUE, mp4h_bp_set_var },
{ "set-var-verbatim", FALSE, FALSE, mp4h_bp_set_var },
{ "unset-var", FALSE, TRUE, mp4h_bp_unset_var },
{ "preserve", FALSE, TRUE, mp4h_bp_preserve },
{ "restore", FALSE, TRUE, mp4h_bp_restore },
{ "var-exists", FALSE, TRUE, mp4h_bp_var_exists },
{ "increment", FALSE, TRUE, mp4h_bp_increment },
{ "decrement", FALSE, TRUE, mp4h_bp_decrement },
{ "symbol-info", FALSE, TRUE, mp4h_bp_symbol_info },
{ "copy-var", FALSE, TRUE, mp4h_bp_copy_var },
{ "defvar", FALSE, TRUE, mp4h_bp_defvar },
/* array functions */
{ "array-size", FALSE, TRUE, mp4h_bp_array_size },
{ "array-add-unique", FALSE, TRUE, mp4h_bp_array_add_unique },
{ "array-member", FALSE, TRUE, mp4h_bp_array_member },
{ "array-push", FALSE, TRUE, mp4h_bp_array_push },
{ "array-pop", FALSE, TRUE, mp4h_bp_array_pop },
{ "array-topvalue", FALSE, TRUE, mp4h_bp_array_topvalue },
{ "array-shift", FALSE, TRUE, mp4h_bp_array_shift },
{ "array-concat", FALSE, TRUE, mp4h_bp_array_concat },
{ "sort", FALSE, TRUE, mp4h_bp_sort },
/* diversion functions */
{ "divert", FALSE, TRUE, mp4h_bp_divert },
{ "divnum", FALSE, TRUE, mp4h_bp_divnum },
{ "undivert", FALSE, TRUE, mp4h_bp_undivert },
{ 0, FALSE, FALSE, 0 },
};
/* Local functions */
static void push_builtin_table __P ((builtin *));
static void set_trace __P ((symbol *, const char *));
static void generic_set_hook __P ((MP4H_BUILTIN_PROTO, boolean, int));
static void math_relation __P ((MP4H_BUILTIN_PROTO, mathrel_type));
static void mathop_functions __P ((MP4H_BUILTIN_PROTO, mathop_type));
static void updowncase __P ((struct obstack *, int, token_data **, boolean));
static void varstack_check __P ((void));
static boolean safe_strtod __P ((const char *, const char *, double *));
static boolean safe_strtol __P ((const char *, const char *, long int *));
static void quote_name_value __P ((struct obstack *, char *));
static void matching_attributes __P ((struct obstack *, int, token_data **, boolean, char *));
static char * utf8char_skip __P ((char *, int));
static int utf8char_strlen __P ((char *));
static int encoding_strlen __P ((char *));
static void substitute __P ((struct obstack *, const char *, const char *, int *));
static void string_regexp __P ((struct obstack *, int, token_data **, int, const char *));
static void subst_in_string __P ((struct obstack *, int, token_data **, int));
static void generic_variable_lookup __P ((MP4H_BUILTIN_PROTO, boolean));
static int array_size __P ((symbol *));
static char *array_value __P ((symbol *, int, int *));
static int array_member __P ((const char *, symbol *, boolean));
static int sort_function __P ((const void *, const void *));
static void logical_to_physical_paths __P ((char **));
/* This symbol controls breakings of flow statements. */
static symbol varbreak;
/* Stack preserve/restore variables. */
static var_stack *vs = NULL;
/* Global variables needed by sort algorithms. */
static boolean sort_caseless;
static boolean sort_sortorder;
static boolean sort_numeric;
/* Localization */
struct lconv *my_locale;
/* Table of characters used by PCRE with non-C locales */
static const unsigned char *re_tableptr = NULL;
/* Timer */
static struct tms elapsed_time;
/* Pointer to a string containig the decimal point used
with locales. */
#ifdef HAVE_LOCALE_H
static const char *decimal_point;
#else
#define decimal_point "."
#endif
/* Output radix */
static int output_radix = 6;
/*------------------------------------------------------------------.
| If dynamic modules are enabled, more builtin tables can be active |
| at a time. This implements a list of tables of builtins. |
`------------------------------------------------------------------*/
struct builtin_table
{
struct builtin_table *next;
builtin *table;
};
typedef struct builtin_table builtin_table;
static builtin_table *builtin_tables = NULL;
static void
push_builtin_table (builtin *table)
{
builtin_table *bt;
bt = (builtin_table *) xmalloc (sizeof (struct builtin_table));
bt->next = builtin_tables;
bt->table = table;
builtin_tables = bt;
}
/*----------------------------------------.
| Find the builtin, which lives on ADDR. |
`----------------------------------------*/
const builtin *
find_builtin_by_addr (builtin_func *func)
{
const builtin_table *bt;
const builtin *bp;
for (bt = builtin_tables; bt != NULL; bt = bt->next)
for (bp = bt->table; bp->name != NULL; bp++)
if (bp->func == func)
return bp;
return NULL;
}
/*-----------------------------------.
| Find the builtin, which has NAME. |
`-----------------------------------*/
const builtin *
find_builtin_by_name (const char *name)
{
const builtin_table *bt;
const builtin *bp;
/* This is necessary to load frozen files */
if (builtin_tables == NULL)
push_builtin_table (builtin_tab);
for (bt = builtin_tables; bt != NULL; bt = bt->next)
for (bp = bt->table; bp->name != NULL; bp++)
if (strcasecmp (bp->name, name) == 0)
return bp;
return NULL;
}
/*-----------------------.
| Initialize a symbol. |
`-----------------------*/
void
initialize_builtin (symbol *sym)
{
SYMBOL_TYPE (sym) = TOKEN_VOID;
SYMBOL_TRACED (sym) = FALSE;
SYMBOL_CONTAINER (sym) = FALSE;
SYMBOL_EXPAND_ARGS (sym) = FALSE;
SYMBOL_HOOK_BEGIN (sym) = NULL;
SYMBOL_HOOK_END (sym) = NULL;
}
/*-------------------------------------------------------------------------.
| Install a builtin macro with name NAME, bound to the C function given in |
| BP. TRACED defines whether NAME is to be traced. |
`-------------------------------------------------------------------------*/
void
define_builtin (const char *name, const builtin *bp, boolean traced)
{
symbol *sym;
sym = lookup_symbol (name, SYMBOL_INSERT);
if (SYMBOL_TYPE (sym) == TOKEN_TEXT)
xfree ((voidstar) SYMBOL_TEXT (sym));
SYMBOL_TYPE (sym) = TOKEN_FUNC;
SYMBOL_FUNC (sym) = bp->func;
SYMBOL_TRACED (sym) = traced;
SYMBOL_CONTAINER (sym) = bp->container;
SYMBOL_EXPAND_ARGS (sym) = bp->expand_args;
SYMBOL_HOOK_BEGIN (sym) = NULL;
SYMBOL_HOOK_END (sym) = NULL;
}
/*------------------------------.
| Install a new builtin_table. |
`------------------------------*/
void
install_builtin_table (builtin *table)
{
const builtin *bp;
push_builtin_table (table);
for (bp = table; bp->name != NULL; bp++)
define_builtin (bp->name, bp, FALSE);
}
void
break_init (void) {
initialize_builtin (&varbreak);
SYMBOL_TYPE (&varbreak) = TOKEN_TEXT;
SYMBOL_TEXT (&varbreak) = xstrdup ("");
}
void
break_deallocate (void) {
xfree ((voidstar) SYMBOL_TEXT (&varbreak));
SYMBOL_TYPE (&varbreak) = TOKEN_VOID;
}
/*-------------------------------------------------------------------------.
| Define a predefined or user-defined macro, with name NAME, and expansion |
| TEXT. MODE destinguishes between the "define" and the "pushdef" case. |
| It is also used from main (). |
`-------------------------------------------------------------------------*/
void
define_user_macro (const char *name, char *text, symbol_lookup mode,
boolean container, boolean expand_args, boolean space_delete)
{
symbol *s;
char *begin, *cp;
int offset, bracket_level = 0;
s = lookup_symbol (name, mode);
xfree ((voidstar) SYMBOL_HOOK_BEGIN (s));
xfree ((voidstar) SYMBOL_HOOK_END (s));
if (SYMBOL_TYPE (s) == TOKEN_TEXT)
xfree ((voidstar) SYMBOL_TEXT (s));
initialize_builtin (s);
SYMBOL_TYPE (s) = TOKEN_TEXT;
if (space_delete)
{
for (begin=text; *begin != '\0' && IS_SPACE(*begin); begin++)
;
SYMBOL_TEXT (s) = xstrdup (begin);
offset = 0;
for (cp=SYMBOL_TEXT (s); *cp != '\0'; cp++)
{
switch (*cp)
{
case '<':
bracket_level++;
*(cp-offset) = *cp;
break;
case '>':
bracket_level--;
*(cp-offset) = *cp;
break;
case '\\':
*(cp-offset) = *cp;
if (*(cp+1) != '\0')
{
cp++;
*(cp-offset) = *cp;
}
break;
case '\n':
if (bracket_level>0)
*(cp-offset) = *cp;
else
{
offset++;
while (IS_SPACE(*(cp+1)))
{
cp++;
offset++;
}
}
break;
default:
*(cp-offset) = *cp;
break;
}
}
*(cp-offset) = '\0';
}
else
SYMBOL_TEXT (s) = xstrdup (text);
SYMBOL_CONTAINER (s) = container;
SYMBOL_EXPAND_ARGS (s) = expand_args;
#ifdef DEBUG_INPUT
fprintf (stderr, "Define: %s\nText: %s\nContainer: %d\n",
SYMBOL_NAME (s), SYMBOL_TEXT (s), SYMBOL_CONTAINER (s));
#endif
}
/*--------------------------------------------------------------------------.
| Define a predefined or user-defined entity, with name NAME, and expansion |
| TEXT. MODE destinguishes between the "define" and the "pushdef" case. |
| It is also used from main (). |
`--------------------------------------------------------------------------*/
static void
define_user_entity (const char *name, char *text, symbol_lookup mode)
{
symbol *s;
s = lookup_entity (name, mode);
if (SYMBOL_TYPE (s) == TOKEN_TEXT)
{
xfree ((voidstar) SYMBOL_HOOK_BEGIN (s));
xfree ((voidstar) SYMBOL_HOOK_END (s));
xfree ((voidstar) SYMBOL_TEXT (s));
}
initialize_builtin (s);
SYMBOL_TYPE (s) = TOKEN_TEXT;
SYMBOL_TEXT (s) = xstrdup (text);
#ifdef DEBUG_INPUT
fprintf (stderr, "Define: %s\nText: %s\n",
SYMBOL_NAME (s), SYMBOL_TEXT (s));
#endif
}
/*-----------------------------------------------.
| Initialise all builtin and predefined macros. |
`-----------------------------------------------*/
void
builtin_init (void)
{
install_builtin_table (builtin_tab);
pcre_malloc = xmalloc;
pcre_free = xfree;
}
/*-----------------------------------------------.
| Deallocate all builtin and predefined macros. |
`-----------------------------------------------*/
void
builtin_deallocate (void)
{
builtin_table *bt, *bt_next;
for (bt = builtin_tables; bt != NULL; )
{
bt_next = bt->next;
xfree ((voidstar) bt);
bt = bt_next;
}
varstack_check ();
}
static pcre *
xre_compile (const char *pattern, int cflags)
{
pcre *patcomp;
const char *errbuf;
int erroffset;
if (document_encoding == ENCODING_UTF8)
cflags |= PCRE_UTF8;
patcomp = pcre_compile (pattern, cflags, &errbuf, &erroffset, re_tableptr);
if (patcomp == 0)
{
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Bad regular expression `%s' at position %d: %s"),
CURRENT_FILE_LINE, pattern, erroffset, errbuf));
}
return patcomp;
}
static boolean
safe_strtod (const char *name, const char *nptr, double *value)
{
char *endp;
double result;
result = strtod (nptr, &endp);
if (nptr == NULL || *endp != '\0')
{
if (nptr == NULL)
nptr = endp;
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Argument `%s' non-numeric in built-in `%s'"),
CURRENT_FILE_LINE, nptr, name));
return FALSE;
}
*value = result;
return TRUE;
}
static boolean
safe_strtol (const char *name, const char *nptr, long int *value)
{
char *endp;
long int result;
result = strtol (nptr, &endp, 10);
if (nptr == NULL || *endp != '\0')
{
if (nptr == NULL)
nptr = endp;
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Argument `%s' non-numeric in built-in `%s'"),
CURRENT_FILE_LINE, nptr, name));
return FALSE;
}
*value = result;
return TRUE;
}
/*----------------------------------------------------------------.
| Quote name=value pair. This function must be called only when |
| text is sent to output. |
`----------------------------------------------------------------*/
static void
quote_name_value (struct obstack *obs, char *pair)
{
char *equal_ptr, *cp;
int special_chars;
boolean valid;
equal_ptr = strchr (pair, '=');
if (equal_ptr == NULL)
{
obstack_grow (obs, pair, strlen (pair));
return;
}
/* Skip special characters */
special_chars = 0;
cp = pair;
while (IS_GROUP (*cp))
{
cp++;
special_chars++;
}
valid = TRUE;
while (cp < equal_ptr)
{
if (IS_SPACE (*cp) || IS_CLOSE (*cp))
{
valid = FALSE;
break;
}
cp++;
}
if (!valid)
{
obstack_grow (obs, pair, strlen (pair));
return;
}
if (special_chars)
obstack_grow (obs, pair, special_chars);
*equal_ptr = '\0';
obstack_grow (obs, pair+special_chars, strlen (pair)-special_chars);
*equal_ptr = '=';
obstack_1grow (obs, '=');
if (*(equal_ptr+1) != '"' && *(equal_ptr+1) != CHAR_QUOTE)
obstack_1grow (obs, CHAR_QUOTE);
obstack_grow (obs, equal_ptr+1, strlen (equal_ptr+1)-special_chars);
if (*(equal_ptr+1) != '"' && *(equal_ptr+1) != CHAR_QUOTE)
obstack_1grow (obs, CHAR_QUOTE);
if (special_chars)
obstack_grow (obs, equal_ptr+1+strlen (equal_ptr+1)-special_chars,
special_chars);
}
/*---------------------------------------------------------------.
| Extract from ARGV attributes from a list of attributes names. |
| When MATCH is true, matching attributes are printed, otherwise |
| non matching attributes are printed. |
| First argument is a comma-separated list of attributes names, |
| which may contain regular expressions. |
`---------------------------------------------------------------*/
static void
matching_attributes (struct obstack *obs, int argc, token_data **argv,
boolean match, char *list)
{
register char *cp;
char *name;
int i, j, special_chars;
pcre *re;
pcre_extra *re_extra = NULL;
const char *errptr = NULL;
int rc, max_subexps;
int *match_ptr = NULL;
boolean first = TRUE;
/* Replace comma-separated list by a regular expression */
for (cp=list; *cp != '\0'; cp++)
{
if (*cp == ',')
*cp = '|';
}
name = (char *) xmalloc (strlen (list) + 3);
sprintf (name, "^%s$", list);
/* Compile this expression once */
re = xre_compile (name, PCRE_CASELESS);
xfree ((voidstar) name);
if (re == NULL)
return;
if (argc>2)
{
re_extra = pcre_study (re, 0, &errptr);
if (errptr != NULL)
{
MP4HERROR ((warning_status, 0,
_("Error:%s:%d: Bad regular expression `%s': %s"),
CURRENT_FILE_LINE, list, errptr));
return;
}
}
for (i=1; i 0 && match)
{
if (!first)
obstack_1grow (obs, ' ');
first = FALSE;
obstack_1grow (obs, CHAR_BGROUP);
if (special_chars)
obstack_grow (obs, ARG (i), special_chars);
if (rc == 1)
{
obstack_grow (obs, ARG (i)+special_chars,
strlen (ARG (i)+special_chars));
}
else
{
for (j=1; j
o Last argument is removed by collect_arguments () if it is empty.
For this reason, it does not make sense to define a mimimal
number of arguments.
*/
/* Miscellaneous builtins -- "__file__" and "__line__". */
static void
mp4h_bp___file__ (MP4H_BUILTIN_ARGS)
{
if (argc == 1)
shipout_string (obs, current_file, 0);
else
{
xfree ((voidstar) current_file);
current_file = xstrdup (ARG (1));
}
}
static void
mp4h_bp___line__ (MP4H_BUILTIN_ARGS)
{
if (argc == 1)
shipout_int (obs, current_line);
else
numeric_arg (argv[0], ARG (1), FALSE, ¤t_line);
}
/*-----------------------------.
| Initialize character table. |
`-----------------------------*/
void
pcre_init (void)
{
re_tableptr = pcre_maketables ();
}
void
pcre_deallocate (void)
{
pcre_free ((voidstar) re_tableptr);
}
/*---------------------.
| Initialize locales. |
`---------------------*/
#ifdef HAVE_LOCALE_H
void
locale_init (int category, char *value)
{
setlocale (category, value);
my_locale = localeconv ();
decimal_point = my_locale->decimal_point;
}
/*---------------------------.
| Change locale attributes. |
`---------------------------*/
static void
mp4h_bp_mp4h_l10n (MP4H_BUILTIN_ARGS)
{
char *cp;
int i;
int category;
for (i=1; i tag is not implemented on your OS\n"),
CURRENT_FILE_LINE, ARG (0)));
}
#endif
static void
mp4h_bp___version__ (MP4H_BUILTIN_ARGS)
{
obstack_grow (obs, mp4h_version.v_short, strlen (mp4h_version.v_short));
}
static void
add_1char_protected (struct obstack *obs, char ch)
{
obstack_1grow (obs, CHAR_LQUOTE);
obstack_1grow (obs, ch);
obstack_1grow (obs, CHAR_RQUOTE);
}
static void
mp4h_bp_lb (MP4H_BUILTIN_ARGS)
{
add_1char_protected (obs, '<');
}
static void
mp4h_bp_rb (MP4H_BUILTIN_ARGS)
{
add_1char_protected (obs, '>');
}
static void
mp4h_bp_dq (MP4H_BUILTIN_ARGS)
{
obstack_1grow (obs, CHAR_QUOTE);
}
static void
mp4h_bp_bs (MP4H_BUILTIN_ARGS)
{
add_1char_protected (obs, '\\');
}
/* Enable tracing of all specified macros, or all, if none is specified.
Tracing is disabled by default, when a macro is defined. This can be
overridden by the "t" debug flag. */
/*-----------------------------------------------------------------------.
| Set_trace () is used by "debugging-on" and "debugging-off" to enable |
| and disable tracing of a macro. It disables tracing if DATA is NULL, |
| otherwise it enable tracing. |
`-----------------------------------------------------------------------*/
static void
set_trace (symbol *sym, const char *data)
{
SYMBOL_TRACED (sym) = (boolean) (data != NULL);
}
static void
mp4h_bp_debugging_on (MP4H_BUILTIN_ARGS)
{
symbol *s;
int i;
if (argc == 1)
hack_all_symbols (set_trace, (char *) obs);
else
for (i = 1; i < argc; i++)
{
s = lookup_symbol (ARG (i), SYMBOL_LOOKUP);
if (s != NULL)
set_trace (s, (char *) obs);
else
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Undefined name %s"),
CURRENT_FILE_LINE, ARG (i)));
}
}
/*------------------------------------------------------------------------.
| Disable tracing of all specified macros, or all, if none is specified. |
`------------------------------------------------------------------------*/
static void
mp4h_bp_debugging_off (MP4H_BUILTIN_ARGS)
{
symbol *s;
int i;
if (argc == 1)
hack_all_symbols (set_trace, NULL);
else
for (i = 1; i < argc; i++)
{
s = lookup_symbol (ARG (i), SYMBOL_LOOKUP);
if (s != NULL)
set_trace (s, NULL);
else
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Undefined name %s"),
CURRENT_FILE_LINE, ARG (i)));
}
}
/*----------------------------------------------------------------------.
| On-the-fly control of the format of the tracing output. It takes one |
| argument, which is a character string like given to the -d option, or |
| none in which case the debug_level is zeroed. |
`----------------------------------------------------------------------*/
static void
mp4h_bp_debugmode (MP4H_BUILTIN_ARGS)
{
int new_debug_level;
int change_flag;
if (bad_argc (argv[0], argc, 0, 2))
return;
if (argc == 1)
debug_level = 0;
else
{
if (ARG (1)[0] == '+' || ARG (1)[0] == '-')
{
change_flag = ARG (1)[0];
new_debug_level = debug_decode (ARG (1) + 1);
}
else
{
change_flag = 0;
new_debug_level = debug_decode (ARG (1));
}
if (new_debug_level < 0)
MP4HERROR ((warning_status, 0,
_("Debugmode:%s:%d: bad debug flags: `%s'"),
CURRENT_FILE_LINE, ARG (1)));
else
{
switch (change_flag)
{
case 0:
debug_level = new_debug_level;
break;
case '+':
debug_level |= new_debug_level;
break;
case '-':
debug_level &= ~new_debug_level;
break;
}
}
}
}
/*-------------------------------------------------------------------------.
| Specify the destination of the debugging output. With one argument, the |
| argument is taken as a file name, with no arguments, revert to stderr. |
`-------------------------------------------------------------------------*/
static void
mp4h_bp_debugfile (MP4H_BUILTIN_ARGS)
{
if (bad_argc (argv[0], argc, 1, 2))
return;
if (argc == 1)
debug_set_output (NULL);
else if (!debug_set_output (ARG (1)))
MP4HERROR ((warning_status, errno,
_("Warning:%s:%d: Cannot set error file: %s"),
CURRENT_FILE_LINE, ARG (1)));
}
/*---------------------------------------------------------.
| Print the replacement text for a builtin or user macro. |
`---------------------------------------------------------*/
static void
mp4h_bp_function_def (MP4H_BUILTIN_ARGS)
{
symbol *s;
if (bad_argc (argv[0], argc, 2, 2))
return;
s = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (s == NULL)
return;
switch (SYMBOL_TYPE (s))
{
case TOKEN_TEXT:
obstack_1grow (obs, CHAR_LQUOTE);
shipout_string (obs, SYMBOL_TEXT (s), strlen (SYMBOL_TEXT (s)));
obstack_1grow (obs, CHAR_RQUOTE);
break;
case TOKEN_FUNC:
push_macro (SYMBOL_FUNC (s), SYMBOL_TRACED (s));
break;
case TOKEN_VOID:
break;
default:
MP4HERROR ((warning_status, 0,
_("INTERNAL ERROR: Bad symbol type in mp4h_bp_function_def ()")));
abort ();
}
}
/* file functions */
#ifdef HAVE_FILE_FUNCS
/*-----------------------------------------------------------------.
| Informations on a file. A newline separated string is printed: |
| Line 1: file size |
| Line 2: file type |
| Line 3: time of last change |
| Line 4: time of last modification |
| Line 5: time of last modification |
| Line 6: time of last access |
| Line 7: name of the owner of this file |
| Line 8: name of the group of this file |
`-----------------------------------------------------------------*/
static void
mp4h_bp_get_file_properties (MP4H_BUILTIN_ARGS)
{
struct stat buf;
struct passwd *user;
struct group *group;
CHECK_SAFETY_LEVEL(1);
if (bad_argc (argv[0], argc, 2, 2))
return;
if (stat (ARG (1), &buf))
{
MP4HERROR ((warning_status, errno,
_("Warning:%s:%d: Could not stat `%s'"),
CURRENT_FILE_LINE, ARG (1)));
return;
}
shipout_long (obs, (long) buf.st_size);
obstack_1grow (obs, '\n');
if (S_ISLNK(buf.st_mode))
obstack_grow (obs, "LINK", 4);
else if (S_ISREG(buf.st_mode))
obstack_grow (obs, "FILE", 4);
else if (S_ISDIR(buf.st_mode))
obstack_grow (obs, "DIR", 3);
else
obstack_grow (obs, "UNKNOWN", 7);
obstack_1grow (obs, '\n');
shipout_long (obs, (long) buf.st_ctime);
obstack_1grow (obs, '\n');
shipout_long (obs, (long) buf.st_mtime);
obstack_1grow (obs, '\n');
shipout_long (obs, (long) buf.st_atime);
obstack_1grow (obs, '\n');
user = getpwuid (buf.st_uid);
if (user)
obstack_grow (obs, user->pw_name, strlen (user->pw_name));
else
obstack_grow (obs, "(UNKNOWN)", 9);
obstack_1grow (obs, '\n');
group = getgrgid (buf.st_gid);
if (group)
obstack_grow (obs, group->gr_name, strlen (group->gr_name));
else
obstack_grow (obs, "(UNKNOWN)", 9);
obstack_1grow (obs, '\n');
}
/*-----------------------------------------------------------------.
| Prints a directory contents. A pattern can be specified with |
| the ``matching'' attribute. This pattern is a regexp one, |
| not a shell expansion. |
`-----------------------------------------------------------------*/
static void
mp4h_bp_directory_contents (MP4H_BUILTIN_ARGS)
{
DIR *dir;
struct dirent *entry;
int length;
const char *matching;
pcre *re = NULL;
pcre_extra *re_extra = NULL;
const char *errptr = NULL;
int *match_ptr = NULL;
CHECK_SAFETY_LEVEL(1);
matching = predefined_attribute ("matching", &argc, argv, FALSE);
if (bad_argc (argv[0], argc, 2, 2))
return;
/* First, opens directory. */
dir = opendir (ARG (1));
if (dir == NULL)
return;
if (matching)
{
re = xre_compile (matching, 0);
if (re == NULL)
return;
match_ptr = (int *)
xrealloc ((voidstar) match_ptr, sizeof (int) * 3);
re_extra = pcre_study (re, 0, &errptr);
if (errptr != NULL)
{
MP4HERROR ((warning_status, 0,
_("Error:%s:%d: Bad regular expression `%s': %s"),
CURRENT_FILE_LINE, matching, errptr));
return;
}
}
while ((entry = readdir (dir)) != (struct dirent *)NULL)
{
length = strlen (entry->d_name);
if (!matching ||
(pcre_exec (re, re_extra, entry->d_name, length, 0,
0, match_ptr, 3) > 0
&& match_ptr[1] - match_ptr[0] == length))
{
obstack_grow (obs, entry->d_name, length);
obstack_1grow (obs, '\n');
}
}
if (matching)
{
pcre_free (re);
pcre_free (re_extra);
xfree ((voidstar) match_ptr);
}
closedir (dir);
}
/*--------------------------------.
| Returns "true" if file exists. |
`--------------------------------*/
static void
mp4h_bp_file_exists (MP4H_BUILTIN_ARGS)
{
struct stat file;
CHECK_SAFETY_LEVEL(1);
if (bad_argc (argv[0], argc, 2, 2))
return;
if ((stat (ARG (1), &file)) != -1)
obstack_grow (obs, "true", 4);
}
/*------------------------------------.
| Returns a real (absolute) path name |
`------------------------------------*/
static void
mp4h_bp_real_path (MP4H_BUILTIN_ARGS)
{
const char *pathname;
char resolvedname[MAXPATHLEN];
CHECK_SAFETY_LEVEL(1);
pathname = predefined_attribute ("pathname", &argc, argv, FALSE);
if (!pathname)
MP4HERROR ((warning_status, 0,
_("Error:%s:%d: Required attribute `pathname' is not specified"),
CURRENT_FILE_LINE));
else
if (!realpath(pathname, resolvedname))
MP4HERROR ((warning_status, 0,
_("Error:%s:%d: Cannot form real path for `%s'"),
CURRENT_FILE_LINE, pathname));
else
obstack_grow(obs, resolvedname, strlen(resolvedname));
}
#endif /* HAVE_FILE_FUNCS */
/*------------------------------------------------------------.
| Converts an epoch to a readable string. |
| If no argument is given, current date and time is printed. |
`------------------------------------------------------------*/
static void
mp4h_bp_date (MP4H_BUILTIN_ARGS)
{
time_t epoch_time = (time_t)0;
char *ascii_time;
const char *format, *timespec;
struct tm *timeptr;
char *endp = NULL;
format = predefined_attribute ("format", &argc, argv, FALSE);
timespec = predefined_attribute ("time", &argc, argv, FALSE);
if (!format && !timespec)
/* backwards compatible... */
if (argc > 1)
timespec = ARG (1);
if (timespec)
{
epoch_time = strtol (timespec, &endp, 10);
if (!endp)
{
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Invalid value in date: %s"),
CURRENT_FILE_LINE, ARG (1)));
return;
}
}
else
epoch_time = time ((time_t *)NULL);
timeptr = (struct tm *) localtime (&epoch_time);
if (format)
{
ascii_time = (char *)xmalloc(1000);
strftime(ascii_time, 1000, format, timeptr);
obstack_grow (obs, ascii_time, strlen (ascii_time));
xfree((voidstar) ascii_time);
}
else
{
ascii_time = (char *) asctime (timeptr);
/* Remove newline. */
LAST_CHAR (ascii_time) = '\0';
obstack_grow (obs, ascii_time, strlen (ascii_time));
}
}
/* Flow functions: these functions allow complex structures
There are 2 different kinds of conditions : numerical and
string tests. A numerical test is false if result is null
and right if non null. String-based tests are false if
followed by a null string, and right otherwise. */
/*----------------------------------------------------------.
| Group multiple tags into one. This is useful when |
| combined with tests like , , .... |
`----------------------------------------------------------*/
static void
mp4h_bp_group (MP4H_BUILTIN_ARGS)
{
const char *separator;
separator = predefined_attribute ("separator", &argc, argv, FALSE);
obstack_1grow (obs, CHAR_BGROUP);
/* separator = NULL is handled by dump_args */
dump_args (obs, argc, argv, separator);
obstack_1grow (obs, CHAR_EGROUP);
}
/*------------------------------------------------.
| Like , but this one is a container tag. |
`------------------------------------------------*/
static void
mp4h_bp_compound (MP4H_BUILTIN_ARGS)
{
const char *separator;
separator = predefined_attribute ("separator", &argc, argv, FALSE);
obstack_1grow (obs, CHAR_BGROUP);
/* separator = NULL is handled by dump_args */
dump_args (obs, argc, argv, separator);
obstack_grow (obs, ARGBODY, strlen (ARGBODY));
obstack_1grow (obs, CHAR_EGROUP);
}
/*------------------------------------------------.
| Allow breaking an object into several pieces. |
`------------------------------------------------*/
static void
mp4h_bp_disjoin (MP4H_BUILTIN_ARGS)
{
obstack_1grow (obs, CHAR_EGROUP);
obstack_grow (obs, ARG(1), strlen (ARG(1)));
obstack_1grow (obs, CHAR_BGROUP);
}
/*-------------------------------------------.
| Prevent further expansion of attributes. |
`-------------------------------------------*/
static void
mp4h_bp_noexpand (MP4H_BUILTIN_ARGS)
{
obstack_1grow (obs, CHAR_LQUOTE);
dump_args (obs, argc, argv, NULL);
obstack_1grow (obs, CHAR_RQUOTE);
}
/*----------------------------------------------------------.
| Remove special characters which forbid expansion. |
`----------------------------------------------------------*/
static void
mp4h_bp_expand (MP4H_BUILTIN_ARGS)
{
int i, offset;
register char *cp;
for (i=1; i is with
obstack_grow (obs, "", 2);
obstack_grow (obs, ARG (2), strlen (ARG (2)));
obstack_grow (obs, "", 7);
and a similar else-clause.
But this is broken, because ARG(2) may change the condition, and
then else-clause could be run too.
For this reason, the following implementation is preferred.
*/
var = lookup_variable ("__cond_cnt", SYMBOL_LOOKUP);
if (var == NULL)
{
var = lookup_variable ("__cond_cnt", SYMBOL_INSERT);
SYMBOL_TEXT (var) = (char *) xstrdup ("0");
SYMBOL_TYPE (var) = TOKEN_TEXT;
}
obstack_grow (obs, "", 24);
obstack_grow (obs, "=", 42);
obstack_1grow (obs, CHAR_BGROUP);
obstack_grow (obs, ARG (1), strlen (ARG (1)));
obstack_1grow (obs, CHAR_EGROUP);
obstack_grow (obs, " />", 3);
obstack_grow (obs, " /> >", 52);
obstack_grow (obs, ARG (2), strlen (ARG (2)));
obstack_grow (obs, "", 7);
if (argc>3)
{
obstack_grow (obs, " /> /> >", 60);
obstack_grow (obs, ARG (3), strlen (ARG (3)));
obstack_grow (obs, "", 7);
}
obstack_grow (obs, "", 24);
}
/*------------------------------------------------------.
| String comparisons. |
| If 2nd and 3rd arguments are equal, 4th argument is |
| expanded, otherwise 5th argument is expanded. |
`------------------------------------------------------*/
static void
mp4h_bp_ifeq (MP4H_BUILTIN_ARGS)
{
symbol *var;
if (bad_argc (argv[0], argc, 0, 5))
return;
var = lookup_variable ("__cond_cnt", SYMBOL_LOOKUP);
if (var == NULL)
{
var = lookup_variable ("__cond_cnt", SYMBOL_INSERT);
SYMBOL_TEXT (var) = (char *) xstrdup ("0");
SYMBOL_TYPE (var) = TOKEN_TEXT;
}
obstack_grow (obs, "", 24);
obstack_grow (obs, "= />", 6);
obstack_grow (obs, " /> >", 52);
obstack_grow (obs, ARG (3), strlen (ARG (3)));
obstack_grow (obs, "", 7);
if (argc>4)
{
obstack_grow (obs, " /> /> >", 60);
obstack_grow (obs, ARG (4), strlen (ARG (4)));
obstack_grow (obs, "", 7);
}
obstack_grow (obs, "", 24);
}
/*----------------------------------------------------------.
| String comparisons. |
| If 2nd and 3rd arguments are not equal, 4th argument is |
| expanded, otherwise 5th argument is expanded. |
`----------------------------------------------------------*/
static void
mp4h_bp_ifneq (MP4H_BUILTIN_ARGS)
{
symbol *var;
if (bad_argc (argv[0], argc, 0, 5))
return;
var = lookup_variable ("__cond_cnt", SYMBOL_LOOKUP);
if (var == NULL)
{
var = lookup_variable ("__cond_cnt", SYMBOL_INSERT);
SYMBOL_TEXT (var) = (char *) xstrdup ("0");
SYMBOL_TYPE (var) = TOKEN_TEXT;
}
obstack_grow (obs, "", 24);
obstack_grow (obs, "= />", 6);
obstack_grow (obs, " /> >", 52);
obstack_grow (obs, ARG (3), strlen (ARG (3)));
obstack_grow (obs, "", 7);
if (argc>4)
{
obstack_grow (obs, " /> /> >", 60);
obstack_grow (obs, ARG (4), strlen (ARG (4)));
obstack_grow (obs, "", 7);
}
obstack_grow (obs, "", 24);
}
/*----------------------------------------------------------------.
| This one is a complex tag ; its body is evalled only if first |
| argument is empty. |
`----------------------------------------------------------------*/
static void
mp4h_bp_when (MP4H_BUILTIN_ARGS)
{
/*
This test succeeds if one of these 2 clauses is true
a) there are at least 2 arguments
b) first argument is non empty.
*/
if (argc > 2 || strlen (ARG (1)) > 0)
obstack_grow (obs, ARGBODY, strlen (ARGBODY));
}
/*--------------------------------.
| Loops while condition is true. |
`--------------------------------*/
static void
mp4h_bp_while (MP4H_BUILTIN_ARGS)
{
if (bad_argc (argv[0], argc, 0, 2))
return;
if (argc == 1)
return;
/* ``expr'' is replaced by
``exprexpr''. */
if (strlen (ARG (1)) > 0)
{
/* The ``varbreak '' variable is global and modified by . */
if (strcmp (SYMBOL_TEXT (&varbreak), "true") == 0)
{
xfree ((voidstar) SYMBOL_TEXT (&varbreak));
SYMBOL_TEXT (&varbreak) = xstrdup ("");
return;
}
obstack_grow (obs, "", 2);
obstack_grow (obs, ARGBODY, strlen (ARGBODY));
obstack_grow (obs, "", 2);
obstack_grow (obs, ARGBODY, strlen (ARGBODY));
obstack_grow (obs, "", 8);
obstack_grow (obs, "", 7);
}
}
/*---------------------------------------------------------------------.
| This is a container tag. First argument is the name of a variable, |
| second is the name of an array. Body function is evaluated for each |
| element of this array, this element being put into the variable. |
`---------------------------------------------------------------------*/
static void
mp4h_bp_foreach (MP4H_BUILTIN_ARGS)
{
symbol *var;
char *value;
const char *start, *end, *step;
int ind_start, ind_end, ind_step;
int length;
int i;
start = predefined_attribute ("start", &argc, argv, FALSE);
end = predefined_attribute ("end", &argc, argv, FALSE);
step = predefined_attribute ("step", &argc, argv, FALSE);
if (bad_argc (argv[0], argc, 3, 3))
return;
var = lookup_variable (ARG (2), SYMBOL_LOOKUP);
if (var == NULL)
return;
if (start)
numeric_arg (argv[0], start, TRUE, &ind_start);
else
ind_start = 0;
if (end)
numeric_arg (argv[0], end, TRUE, &ind_end);
else
ind_end = array_size (var);
if (step)
numeric_arg (argv[0], step, TRUE, &ind_step);
else
ind_step = 1;
if (ind_step > 0)
{
for (i=ind_start; i", 2);
obstack_grow (obs, ARGBODY, strlen (ARGBODY));
}
}
else if (ind_step < 0)
{
for (i=ind_end-1; i>=ind_start; i+=ind_step)
{
value = array_value (var, i, &length);
obstack_grow (obs, "", 2);
obstack_grow (obs, ARGBODY, strlen (ARGBODY));
}
}
else
{
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Null step is ignored in loop"),
CURRENT_FILE_LINE));
return;
}
}
/*---------------------------------------------------------------.
| This function implements a ``case'' statement. Its syntax is |
| |
`---------------------------------------------------------------*/
static void
mp4h_bp_var_case (MP4H_BUILTIN_ARGS)
{
char *cp;
int i;
for (i=1; i ", 3);
obstack_1grow (obs, '"');
obstack_grow (obs, cp, strlen (cp));
obstack_1grow (obs, '"');
obstack_grow (obs, "/>>", 3);
obstack_grow (obs, ARG (i+1), strlen (ARG (i+1)));
obstack_grow (obs, "", 7);
}
else
{
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Syntax error in "),
CURRENT_FILE_LINE));
}
}
}
/*------------------------------------.
| Immediately exits from inner loop. |
`------------------------------------*/
static void
mp4h_bp_break (MP4H_BUILTIN_ARGS)
{
xfree ((voidstar) SYMBOL_TEXT (&varbreak));
SYMBOL_TEXT (&varbreak) = xstrdup ("true");
}
/*------------------------------------.
| Immediately exits from inner macro. |
`------------------------------------*/
static void
mp4h_bp_return (MP4H_BUILTIN_ARGS)
{
int i, levels=1;
const char *up;
struct obstack *st;
up = predefined_attribute ("up", &argc, argv, FALSE);
if (up)
numeric_arg (argv[0], up, TRUE, &levels);
if (levels < 0)
levels = expansion_level + 1;
else if (levels == 0)
levels = expansion_level;
else if (levels > expansion_level)
levels = expansion_level;
for (i=0; i1)
{
st = push_string_init ();
obstack_grow (st, ARG (1), strlen (ARG (1)));
push_string_finish (0);
}
}
/*-----------------------------.
| Writes a message to stderr. |
`-----------------------------*/
static void
mp4h_bp_warning (MP4H_BUILTIN_ARGS)
{
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: %s"),
CURRENT_FILE_LINE, ARG (1)));
}
/*--------------------.
| Immediately exits. |
`--------------------*/
static void
mp4h_bp_exit (MP4H_BUILTIN_ARGS)
{
const char *status, *message;
int rc = -1;
status = predefined_attribute ("status", &argc, argv, FALSE);
if (status)
numeric_arg (argv[0], status, FALSE, &rc);
message = predefined_attribute ("message", &argc, argv, FALSE);
if (message)
MP4HERROR ((warning_status, 0,
_("%s"), message));
exit (rc);
}
/*-------------------------------------------------------------------------.
| Save the argument text until EOF has been seen, allowing for user |
| specified cleanup action. GNU version saves all arguments, the standard |
| version only the first. |
`-------------------------------------------------------------------------*/
static void
mp4h_bp_at_end_of_file (MP4H_BUILTIN_ARGS)
{
dump_args (obs, 2, argv, " ");
obstack_1grow (obs, '\0');
push_wrapup (obstack_finish (obs));
}
/* Macro functions: defining, undefining, examining or changing
user defined macros. */
/*-------------------------------------.
| Define new tags, simple or complex. |
`-------------------------------------*/
static void
mp4h_bp_define_tag (MP4H_BUILTIN_ARGS)
{
const builtin *bp;
const char *attributes, *endtag, *whitespace;
symbol *sym;
boolean expand_args = TRUE;
boolean container = FALSE;
boolean space_delete = FALSE;
attributes = predefined_attribute ("attributes", &argc, argv, TRUE);
if (attributes && strcmp (attributes, "verbatim") == 0)
expand_args = FALSE;
endtag = predefined_attribute ("endtag", &argc, argv, TRUE);
if (endtag && strcmp (endtag, "required") == 0)
container = TRUE;
whitespace = predefined_attribute ("whitespace", &argc, argv, TRUE);
if (whitespace && strcmp (whitespace, "delete") == 0)
space_delete = TRUE;
if (bad_argc (argv[0], argc, 2, 3))
return;
if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
return;
if (argc == 1)
{
define_user_macro (ARG (1), "", SYMBOL_INSERT, container,
expand_args, space_delete);
return;
}
switch (TOKEN_DATA_TYPE (argv[argc]))
{
case TOKEN_TEXT:
define_user_macro (ARG (1), ARGBODY, SYMBOL_INSERT, container,
expand_args, space_delete);
break;
case TOKEN_FUNC:
bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[argc]));
if (bp == NULL)
return;
else
define_builtin (ARG (1), bp, TOKEN_DATA_FUNC_TRACED (argv[argc]));
break;
default:
MP4HERROR ((warning_status, 0,
_("INTERNAL ERROR: Bad token data type in mp4h_bp_define_tag ()")));
abort ();
}
/* Clear hooks */
sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (!sym)
return;
xfree ((voidstar) SYMBOL_HOOK_BEGIN (sym));
xfree ((voidstar) SYMBOL_HOOK_END (sym));
}
/*-------------------------------------.
| Define tags if not already defined |
`-------------------------------------*/
static void
mp4h_bp_provide_tag (MP4H_BUILTIN_ARGS)
{
symbol *sym;
sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (sym == NULL)
mp4h_bp_define_tag (MP4H_BUILTIN_RECUR);
}
/*-------------------.
| Define a synonym. |
`-------------------*/
static void
mp4h_bp_let (MP4H_BUILTIN_ARGS)
{
const builtin *bp;
symbol *s;
char *cp;
int i;
for (i = 1; i < argc; i++)
{
cp = strchr (ARG (i), '=');
if (cp)
{
*cp = '\0';
cp++;
s = lookup_symbol (cp, SYMBOL_LOOKUP);
if (s == NULL)
{
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Macro `%s' not defined in "),
CURRENT_FILE_LINE, cp));
continue;
}
switch (SYMBOL_TYPE (s))
{
case TOKEN_FUNC:
bp = find_builtin_by_addr (SYMBOL_FUNC (s));
if (bp)
define_builtin (ARG (i), bp, SYMBOL_TRACED (s));
break;
case TOKEN_TEXT:
define_user_macro (ARG (i), SYMBOL_TEXT (s), SYMBOL_INSERT,
SYMBOL_CONTAINER (s), SYMBOL_EXPAND_ARGS (s), FALSE);
break;
default:
MP4HERROR ((warning_status, 0,
_("INTERNAL ERROR: Bad token data type in mp4h_bp_let ()")));
abort ();
}
}
else
{
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: unknown syntax `%s' in "),
CURRENT_FILE_LINE, ARG (i)));
}
}
}
/*-------------------.
| Undefine symbols. |
`-------------------*/
static void
mp4h_bp_undef (MP4H_BUILTIN_ARGS)
{
register int i;
if (bad_argc (argv[0], argc, 2, 0))
return;
for (i = 1; i < argc; i++)
{
lookup_symbol (ARG (i), SYMBOL_DELETE);
}
}
static void
generic_set_hook (MP4H_BUILTIN_ARGS, boolean before, int action)
{
symbol *sym;
char *text;
char *hook;
sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (sym == NULL)
return;
if (before)
hook = SYMBOL_HOOK_BEGIN (sym);
else
hook = SYMBOL_HOOK_END (sym);
if (!hook)
action = 0;
switch (action)
{
/* Replace current hooks */
case 0:
text = xstrdup (ARGBODY);
break;
/* Insert before the current hooks */
case 1:
text = (char *) xmalloc (strlen (hook) + strlen (ARGBODY) + 1);
strcpy (text, ARGBODY);
strcat (text, hook);
break;
/* Append after the current hooks */
case 2:
text = (char *) xmalloc (strlen (hook) + strlen (ARGBODY) + 1);
strcpy (text, hook);
strcat (text, ARGBODY);
break;
default:
MP4HERROR ((warning_status, 0,
_("INTERNAL ERROR: Illegal value in generic_set_hook ()")));
abort ();
}
xfree ((voidstar) hook);
if (before)
SYMBOL_HOOK_BEGIN (sym) = text;
else
SYMBOL_HOOK_END (sym) = text;
}
static void
mp4h_bp_set_hook (MP4H_BUILTIN_ARGS)
{
const char *action, *position;
symbol *sym;
boolean before = TRUE;
int iaction = 0;
action = predefined_attribute ("action", &argc, argv, TRUE);
position = predefined_attribute ("position", &argc, argv, TRUE);
if (bad_argc (argv[0], argc, 2, 2))
return;
sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (sym == NULL)
return;
before = !(position && strcmp (position, "after") == 0);
if (action)
{
if (strcmp (action, "insert") == 0)
iaction = 1;
else if (strcmp (action, "append") == 0)
iaction = 2;
else
iaction = 0;
}
else
iaction = 0;
generic_set_hook (MP4H_BUILTIN_RECUR, before, iaction);
}
/*-----------------.
| Retrieve hooks. |
`-----------------*/
static void
mp4h_bp_get_hook (MP4H_BUILTIN_ARGS)
{
symbol *sym;
const char *position;
char *hook;
position = predefined_attribute ("position", &argc, argv, TRUE);
if (bad_argc (argv[0], argc, 2, 2))
return;
sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
if (sym == NULL)
return;
if (position && strcmp (position, "after") == 0)
hook = SYMBOL_HOOK_END (sym);
else
hook = SYMBOL_HOOK_BEGIN (sym);
if (hook)
obstack_grow (obs, hook, strlen (hook));
}
/*----------------------------------------------------------.
| These functions allow any manipulations of attributes. |
| There is no need to define similar routines for |
| %Aattributes and %Uattributes, because they can be |
| emulated via mp4h builtins. |
`----------------------------------------------------------*/
static void
mp4h_bp_attributes_quote (MP4H_BUILTIN_ARGS)
{
int i;
if (argc < 2)
return;
for (i = 1; i < argc; i++)
{
if (*(ARG (i)) != '\0')
{
obstack_1grow (obs, ' ');
quote_name_value (obs, ARG (i));
}
}
}
static void
mp4h_bp_attributes_remove (MP4H_BUILTIN_ARGS)
{
if (bad_argc (argv[0], argc, 2, 0))
return;
/* Dirty hack to prevent aggregation of attributes into
a single argument. When a group is begun in expand_macro (),
we finish it here. */
if (expansion_level > 1 && (
expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
|| expansion == READ_ATTR_QUOT))
obstack_1grow (obs, CHAR_EGROUP);
matching_attributes (obs, argc-1, argv+1, FALSE, ARG (1));
if (expansion_level > 1 && (
expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
|| expansion == READ_ATTR_QUOT))
obstack_1grow (obs, CHAR_BGROUP);
}
static void
mp4h_bp_attributes_extract (MP4H_BUILTIN_ARGS)
{
if (bad_argc (argv[0], argc, 2, 0))
return;
/* Dirty hack to prevent aggregation of attributes into
a single argument. When a group is begun in expand_macro (),
we finish it here. */
if (expansion_level > 1 && (
expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
|| expansion == READ_ATTR_QUOT))
obstack_1grow (obs, CHAR_EGROUP);
matching_attributes (obs, argc-1, argv+1, TRUE, ARG (1));
if (expansion_level > 1 && (
expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
|| expansion == READ_ATTR_QUOT))
obstack_1grow (obs, CHAR_BGROUP);
}
/*----------------------.
| Define new entities. |
`----------------------*/
static void
mp4h_bp_define_entity (MP4H_BUILTIN_ARGS)
{
symbol *sym;
if (bad_argc (argv[0], argc, 2, 2))
return;
if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
return;
if (argc == 1)
define_user_entity (ARG (1), "", SYMBOL_INSERT);
else
define_user_entity (ARG (1), ARGBODY, SYMBOL_INSERT);
/* Clear hooks */
sym = lookup_entity (ARG (1), SYMBOL_LOOKUP);
if (!sym)
return;
xfree ((voidstar) SYMBOL_HOOK_BEGIN (sym));
xfree ((voidstar) SYMBOL_HOOK_END (sym));
}
/* Math functions */
/*--------------------------------------------------.
| This function is called by relational operators. |
| These operators are binary operators, operands |
| are either numbers or variable names. |
`--------------------------------------------------*/
static void
math_relation (MP4H_BUILTIN_ARGS, mathrel_type mathrel)
{
boolean result;
symbol *var;
double val1, val2;
if (bad_argc (argv[0], argc, 2, 3))
return;
if (isdigit ((int) ARG (1)[0]) || *(ARG (1)) == '-' || *(ARG (1)) == '+' ||
strncmp (ARG (1), decimal_point, strlen (decimal_point)))
{
if (!safe_strtod (ARG (0), ARG (1), &val1))
return;
}
else
{
var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
if (var == NULL)
{
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Argument `%s' non-numeric in built-in `%s'"),
CURRENT_FILE_LINE, ARG (1), ARG (0)));
return;
}
if (!safe_strtod (ARG (0), SYMBOL_TEXT (var), &val1))
return;
}
if (argc == 2)
val2 = 0.;
else if (isdigit ((int) ARG (2)[0]) ||
*(ARG (2)) == '-' || *(ARG (2)) == '+' ||
strncmp (ARG (2), decimal_point, strlen (decimal_point)))
{
if (!safe_strtod (ARG (0), ARG (2), &val2))
return;
}
else
{
var = lookup_variable (ARG (2), SYMBOL_LOOKUP);
if (var == NULL)
{
MP4HERROR ((warning_status, 0,
_("Warning:%s:%d: Argument `%s' non-numeric in built-in `%s'"),
CURRENT_FILE_LINE, ARG (2), ARG (0)));
return;
}
if (!safe_strtod (ARG (0), SYMBOL_TEXT (var), &val2))
return;
}
switch (mathrel)
{
case MATHREL_EQ:
result = (val1 == val2 ? TRUE : FALSE);
break;
case MATHREL_NEQ:
result = (val1 != val2 ? TRUE : FALSE);
break;
case MATHREL_GT:
result = (val1 > val2 ? TRUE : FALSE);
break;
case MATHREL_LT:
result = (val1 < val2 ? TRUE : FALSE);
break;
default:
MP4HERROR ((warning_status, 0,
_("INTERNAL ERROR: Illegal operator in math_relation ()")));
abort ();
}
if (result)
obstack_grow (obs, "true", 4);
}
static void
mp4h_bp_gt (MP4H_BUILTIN_ARGS)
{
math_relation (MP4H_BUILTIN_RECUR, MATHREL_GT);
}
static void
mp4h_bp_lt (MP4H_BUILTIN_ARGS)
{
math_relation (MP4H_BUILTIN_RECUR, MATHREL_LT);
}
static void
mp4h_bp_eq (MP4H_BUILTIN_ARGS)
{
math_relation (MP4H_BUILTIN_RECUR, MATHREL_EQ);
}
static void
mp4h_bp_neq (MP4H_BUILTIN_ARGS)
{
math_relation (MP4H_BUILTIN_RECUR, MATHREL_NEQ);
}
/*------------------------------------------------------.
| This definition is used to simplify the writings of |
| arithmetic operators, an operation is performed on |
| every arguments. |
| If all arguments are integer values, round-offs must |
| be performed at each step. |
`------------------------------------------------------*/
#define MATH_ARG_LOOP(ops) for (i=2; iresult) result=val)
break;
default:
MP4HERROR ((warning_status, 0,
_("INTERNAL ERROR: Illegal mathop in mp4h_bp_mathop ()")));
}
sprintf (sformat, "%%.%df", output_radix);
sprintf (svalue, sformat, result);
if (result_int)
{
pos_radix = strstr (svalue, decimal_point);
if (pos_radix)
*pos_radix = '\0';
}
shipout_text (obs, svalue);
}
#undef MATH_ARG_LOOP
static void
mp4h_bp_add (MP4H_BUILTIN_ARGS)
{
mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_ADD);
}
static void
mp4h_bp_substract (MP4H_BUILTIN_ARGS)
{
mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_SUB);
}
static void
mp4h_bp_multiply (MP4H_BUILTIN_ARGS)
{
mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_MUL);
}
static void
mp4h_bp_divide (MP4H_BUILTIN_ARGS)
{
mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_DIV);
}
static void
mp4h_bp_modulo (MP4H_BUILTIN_ARGS)
{
mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_MOD);
}
static void
mp4h_bp_min (MP4H_BUILTIN_ARGS)
{
mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_MIN);
}
static void
mp4h_bp_max (MP4H_BUILTIN_ARGS)
{
mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_MAX);
}
/* Page functions */
/*------------------------------------------------------.
| Read and parse a file. This file is searched in the |
| directories specified by the -I option. |
| Because the `alt' attribute must be read unexpanded, |
| this tag calls <__include> whose arguments can be |
| expanded since `alt' has been passed into this tag |
| body. |
`------------------------------------------------------*/
static void
mp4h_bp_include (MP4H_BUILTIN_ARGS)
{
const char *alt, *verbatim;
CHECK_SAFETY_LEVEL(1);
alt = predefined_attribute ("alt", &argc, argv, FALSE);
verbatim = predefined_attribute ("verbatim", &argc, argv, TRUE);
obstack_grow (obs, "<__include ", 11);
dump_args (obs, argc, argv, " ");
if (verbatim && strcmp (verbatim, "true") == 0)
obstack_grow (obs, " verbatim=true", 14);
obstack_grow (obs, " >", 2);
if (alt)
obstack_grow (obs, alt, strlen (alt));
obstack_grow (obs, "", 12);
}
static void
mp4h_bp___include (MP4H_BUILTIN_ARGS)
{
const char *verbatim, *command, *file;
FILE *fp;
char *filename = NULL;
CHECK_SAFETY_LEVEL(1);
verbatim = predefined_attribute ("verbatim", &argc, argv, TRUE);
file = predefined_attribute ("file", &argc, argv, FALSE);
command = predefined_attribute ("command", &argc, argv, FALSE);
if (file && command)
{
command = NULL;
MP4HERROR ((warning_status, 0, _("\
WARNING:%s:%d: `file' and `command' attributes in tag are mutually exclusive, using `file'"),
CURRENT_FILE_LINE));
}
if (command)
{
/* Reads input from command output */
CHECK_SAFETY_LEVEL(0);
filename = xstrdup(command);
remove_special_chars (filename, TRUE);
fp = popen(filename, "r");
if (fp == NULL)
{
MP4HERROR ((warning_status, errno,
_("Warning:%s:%d: Cannot execute %s"),
CURRENT_FILE_LINE, filename));
return;
}
}
else
{
if (file)
{
/* New syntax */
if (bad_argc (argv[0], argc, 1, 1))
return;
}
else
{
/* Old syntax */
if (bad_argc (argv[0], argc, 2, 2))
return;
file = ARG(1);
}
if (!file)
{
MP4HERROR ((warning_status, 0, _("\
Error:%s:%d: must be invoked with one of the `file' and `command' attributes; skipped"),
CURRENT_FILE_LINE));
return;
}
fp = path_search (file, &filename);
if (fp == NULL)
{
if (*(ARGBODY) != '\0')
shipout_string (obs, ARGBODY, 0);
else
{
MP4HERROR ((warning_status, errno,
_("Warning:%s:%d: Cannot open %s"),
CURRENT_FILE_LINE, file));
}
return;
}
}
push_file (fp, filename);
if (verbatim && strcmp (verbatim, "true") == 0)
read_file_verbatim (obs);
xfree ((voidstar) filename);
}
/*-------------------------------------------------.
| Transform a module or library name foo:bar into |
| physical path `foo/bar'. |
`-------------------------------------------------*/
static void
logical_to_physical_paths (char **name)
{
register char *cp;
for (cp=*name; *cp != '\0'; cp++)
{
if (*cp == ':')
*cp = '/';
}
}
/*-------------------------------------------------.
| Read and parse a package if not already loaded. |
`-------------------------------------------------*/
static void
mp4h_bp_use (MP4H_BUILTIN_ARGS)
{
FILE *fp;
const char *name = NULL;
char *filename = NULL, *real_filename = NULL;
symbol *sym;
if (bad_argc (argv[0], argc, 2, 2))
return;
name = predefined_attribute ("name", &argc, argv, FALSE);
if (name == NULL)
{
MP4HERROR ((warning_status, errno,
_("Warning:%s:%d: `name' attribute must be set in