/* 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 { 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 tag"), CURRENT_FILE_LINE)); return; } sym = lookup_file (name, SYMBOL_LOOKUP); if (sym) { if (debug_level & DEBUG_TRACE_PATH) DEBUG_MESSAGE1 (_("Module `%s' already loaded -- Skipped"), name); return; } real_filename = xmalloc (strlen (name) + 7); sprintf (real_filename, "%s.mp4hp", name); logical_to_physical_paths (&real_filename); fp = path_search (real_filename, &filename); if (fp == NULL) { MP4HERROR ((warning_status, errno, _("Warning:%s:%d: Cannot open %s"), CURRENT_FILE_LINE, name)); xfree ((voidstar) real_filename); return; } lookup_file (name, SYMBOL_INSERT); push_file (fp, filename); xfree ((voidstar) filename); } /*-------------------------------------. | Loading external module at runtime. | `-------------------------------------*/ #ifdef WITH_MODULES static void mp4h_bp_load (MP4H_BUILTIN_ARGS) { const char *module, *library; char *realname = NULL; library = predefined_attribute ("library", &argc, argv, FALSE); module = predefined_attribute ("module", &argc, argv, FALSE); if (!library && !module) { MP4HERROR ((warning_status, 0, _("Error:%s:%d: Required attribute `pathname' is not specified"), CURRENT_FILE_LINE)); return; } if (library) { realname = xstrdup (library); logical_to_physical_paths (&realname); library_load (realname, obs); xfree ((voidstar) realname); } if (module) { realname = xstrdup (module); logical_to_physical_paths (&realname); module_load (realname, obs); xfree ((voidstar) realname); } } #endif /*-----------------------------. | Discards its body contents. | `-----------------------------*/ static void mp4h_bp_comment (MP4H_BUILTIN_ARGS) { } /*----------------------------. | Set EOL comment delimiter. | `----------------------------*/ static void mp4h_bp_set_eol_comment (MP4H_BUILTIN_ARGS) { bad_argc (argv[0], argc, 0, 2); xfree ((voidstar) eolcomm.string); eolcomm.string = xstrdup (ARG (1)); eolcomm.length = strlen (eolcomm.string); } /*--------------------------------. | Set quotes to disable parsing. | `--------------------------------*/ static void mp4h_bp_set_quotes (MP4H_BUILTIN_ARGS) { const char *display; display = predefined_attribute ("display", &argc, argv, TRUE); visible_quotes = (display && strcmp (display, "visible") == 0); if (argc == 1) { xfree ((voidstar) lquote.string); xfree ((voidstar) rquote.string); lquote.string = NULL; lquote.length = 0; rquote.string = NULL; rquote.length = 0; } else if (argc == 3) { if (*ARG (1) != '<' || LAST_CHAR (ARG (2)) != '>') { MP4HERROR ((warning_status, 0, _("Warning:%s:%d: `<%s>' ignored, invalid arguments %s %s"), CURRENT_FILE_LINE, ARG (0), ARG (1), ARG (2))); return; } xfree ((voidstar) lquote.string); xfree ((voidstar) rquote.string); lquote.string = xstrdup (ARG (1)); lquote.length = strlen (lquote.string); rquote.string = xstrdup (ARG (2)); rquote.length = strlen (rquote.string); } else MP4HERROR ((warning_status, 0, _("Warning:%s:%d: `<%s>' must have 0 or 2 arguments, but got %d"), CURRENT_FILE_LINE, ARG (0), argc-1)); } /*------------------------------------------------------------------------. | Delete all subsequent whitespace from input. The function skip_line () | | lives in input.c. | `------------------------------------------------------------------------*/ static void mp4h_bp_dnl (MP4H_BUILTIN_ARGS) { if (bad_argc (argv[0], argc, 1, 1)) return; skip_line (); } static void mp4h_bp_frozen_dump (MP4H_BUILTIN_ARGS) { if (frozen_dump) input_close (); } /* Relational operators : arguments are strings. */ /*----------------------------------------------------------------. | If first argument is an empty string, returns "true" otherwise | | returns nothing. | `----------------------------------------------------------------*/ static void mp4h_bp_not (MP4H_BUILTIN_ARGS) { int i; if (argc < 2) { obstack_grow (obs, "true", 4); return; } for (i=1; i 1) obstack_1grow (obs, ' '); text = xstrdup (ARG (i)); if (upcase) { for (cp = text; *cp != '\0'; cp++) *cp = toupper (*cp); } else { for (cp = text; *cp != '\0'; cp++) *cp = tolower (*cp); } obstack_grow (obs, text, strlen (text)); xfree ((voidstar) text); } } static void mp4h_bp_downcase (MP4H_BUILTIN_ARGS) { updowncase (obs, argc, argv, FALSE); } static void mp4h_bp_upcase (MP4H_BUILTIN_ARGS) { updowncase (obs, argc, argv, TRUE); } static void mp4h_bp_capitalize (MP4H_BUILTIN_ARGS) { char *text; register char *cp; register int i; boolean next; if (argc == 1) return; for (i=1; i 1) obstack_1grow (obs, ' '); next = TRUE; text = xstrdup (ARG (i)); for (cp = text; *cp != '\0'; cp++) { if (next) *cp = toupper (*cp); if (isspace (*cp)) next = TRUE; else next = FALSE; } obstack_grow (obs, text, strlen (text)); xfree ((voidstar) text); } } /*---------------------------------------------------------------. | Extracts some portion of a string. Optional attributes are | | start and end position. Zero is the position of the first | | character. | `---------------------------------------------------------------*/ static void mp4h_bp_substring (MP4H_BUILTIN_ARGS) { char *text, *cp; int start, end; if (bad_argc (argv[0], argc, 3, 4)) return; text = ARG (1); if (argc>3) { if (!numeric_arg (argv[0], ARG (3), TRUE, &end)) return; if (document_encoding == ENCODING_UTF8) { cp = utf8char_skip(text, end); if (!cp) return; *cp = '\0'; } else *(text+end) = '\0'; } if (argc>2) { if (!numeric_arg (argv[0], ARG (2), TRUE, &start)) return; if (document_encoding == ENCODING_UTF8) { text = utf8char_skip(text, start); if (!text) return; } else text += start; } obstack_grow (obs, text, strlen (text)); } /*----------------------------------------------------------------. | Compares strings. When caseless=true is specified, comparison | | is performed without consideration of case. | `----------------------------------------------------------------*/ static void mp4h_bp_string_eq (MP4H_BUILTIN_ARGS) { const char *caseless; caseless = predefined_attribute ("caseless", &argc, argv, TRUE); if (bad_argc (argv[0], argc, 0, 3)) return; if (argc < 3) { if (*(ARG (1)) == '\0') obstack_grow (obs, "true", 4); return; } if (caseless && strcmp (caseless, "true") == 0) { if (strcasecmp (ARG (1), ARG (2)) == 0) obstack_grow (obs, "true", 4); } else { if (strcmp (ARG (1), ARG (2)) == 0) obstack_grow (obs, "true", 4); } } static void mp4h_bp_string_neq (MP4H_BUILTIN_ARGS) { const char *caseless; caseless = predefined_attribute ("caseless", &argc, argv, TRUE); if (bad_argc (argv[0], argc, 0, 3)) return; if (argc < 3) { if (*(ARG (1)) != '\0') obstack_grow (obs, "true", 4); return; } if (caseless && strcmp (caseless, "true") == 0) { if (strcasecmp (ARG (1), ARG (2)) != 0) obstack_grow (obs, "true", 4); } else { if (strcmp (ARG (1), ARG (2)) != 0) obstack_grow (obs, "true", 4); } } /*-----------------------------------------------------------------. | This function compares two strings and returns this comparison. | | If ``caseless=true'' is specified, this comparison is performed | | without case. | `-----------------------------------------------------------------*/ static void mp4h_bp_string_compare (MP4H_BUILTIN_ARGS) { const char *caseless; int result; caseless = predefined_attribute ("caseless", &argc, argv, TRUE); if (bad_argc (argv[0], argc, 2, 3)) return; if (caseless && strcmp (caseless, "true") == 0) result = strcasecmp (ARG (1), ARG (2)); else result = strcmp (ARG (1), ARG (2)); if (result < 0) obstack_grow (obs, "less", 4); else if (result > 0) obstack_grow (obs, "greater", 7); else obstack_grow (obs, "equal", 5); } /*----------------------------------------------------------------. | First argument is a string, 2nd is a character. | | This function returns an array of numbers, which are locations | | where character appear in the string. | `----------------------------------------------------------------*/ static void mp4h_bp_char_offsets (MP4H_BUILTIN_ARGS) { const char *caseless; char *cp; char c; boolean first = TRUE; caseless = predefined_attribute ("caseless", &argc, argv, TRUE); if (bad_argc (argv[0], argc, 2, 3)) return; if (strlen (ARG (2)) > 1) { MP4HERROR ((warning_status, 0, _("Warning:%s:%d: Second argument of is not a char"), CURRENT_FILE_LINE)); return; } c = ARG (2)[0]; if (caseless && strcmp (caseless, "true") == 0) { for (cp = ARG (1); *cp != '\0'; cp++) { if (tolower(*cp) == tolower(c)) { if (!first) obstack_1grow (obs, '\n'); else first = FALSE; shipout_int (obs, (int) (cp-ARG (1))); } } } else { for (cp = ARG (1); *cp != '\0'; cp++) { if (*cp == c) { if (!first) obstack_1grow (obs, '\n'); else first = FALSE; shipout_int (obs, (int) (cp-ARG (1))); } } } } /* Regular expression support is provided by the PCRE library package, which is open source software, copyright by the University of Cambridge. Latest sources are available from ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ */ static char * utf8char_skip (char *utf, int offset) { if (!utf) return(NULL); while (offset > 0) { offset--; if (utf[0] & 0x80) { if ((utf[1] & 0xc0) != 0x80) return(NULL); if ((utf[0] & 0xe0) == 0xe0) { if ((utf[2] & 0xc0) != 0x80) return(NULL); if ((utf[0] & 0xf0) == 0xf0) { if ((utf[0] & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80) return(NULL); utf += 4; } else { utf += 3; } } else { utf += 2; } } else { utf++; } } return(utf); } static int utf8char_strlen(char *utf) { int ret = 0; if (utf == NULL) return(-1); while (*utf != 0) { utf = utf8char_skip(utf, 1); if (!utf) return(-1); ret++; } return(ret); } static int encoding_strlen(char *str) { if (document_encoding == ENCODING_UTF8) return(utf8char_strlen (str)); else return(strlen (str)); } /*-------------------------------------------------------------------------. | Function to perform substitution by regular expressions. Used by the | | builtins regexp and patsubst. The changed text is placed on the | | obstack. The substitution is REPL, with \& substituted by this part of | | VICTIM matched by the last whole regular expression, taken from REGS[0], | | and \N substituted by the text matched by the Nth parenthesized | | sub-expression, taken from REGS[N]. | `-------------------------------------------------------------------------*/ static int substitute_warned = 0; static void substitute (struct obstack *obs, const char *victim, const char *repl, int *regs) { register unsigned int ch; for (;;) { while ((ch = *repl++) != '\\') { if (ch == '\0') return; obstack_1grow (obs, ch); } switch ((ch = *repl++)) { case '0': if (!substitute_warned) { MP4HERROR ((warning_status, 0, _("\ WARNING:%s:%d: \\0 will disappear, use \\& instead in replacements"), CURRENT_FILE_LINE)); substitute_warned = 1; } /* Fall through. */ case '&': obstack_grow (obs, victim + regs[0], regs[1] - regs[0]); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': ch -= '0'; if (regs[ch*2+1] > 0) obstack_grow (obs, victim + regs[ch*2], regs[ch*2+1] - regs[ch*2]); break; default: obstack_1grow (obs, ch); break; } } } /*--------------------------------------------------------------------------. | Regular expression version of index. Given two arguments, expand to the | | index of the first match of the second argument (a regexp) in the first. | | Expand to -1 if here is no match. | `--------------------------------------------------------------------------*/ static void string_regexp (struct obstack *obs, int argc, token_data **argv, int options, const char *action) { char *victim; /* first argument */ char *regexp; /* regular expression */ int length; /* length of first argument */ int startpos = -1; /* start position of match */ int match_length = 0; /* length of pattern match */ pcre *re; int *match_ptr = NULL; int max_subexps = 0; int rc; victim = ARG (1); remove_special_chars (victim, FALSE); length = strlen (victim); regexp = ARG (2); remove_special_chars (regexp, FALSE); re = xre_compile (regexp, options); if (re == NULL) return; /* Accept any number of subexpressions */ do { max_subexps += 10; match_ptr = (int *) xrealloc ((voidstar) match_ptr, sizeof (int) * max_subexps * 3); rc = pcre_exec (re, NULL, victim, length, 0, 0, match_ptr, max_subexps * 3); } while (rc == PCRE_ERROR_NOMEMORY); if (rc > 0) { match_length = match_ptr[1] - match_ptr[0]; startpos = match_ptr[0]; } if (strcmp(action, "startpos") == 0) { if (startpos >= 0) shipout_int (obs, startpos); } else if (strcmp(action, "endpos") == 0) { if (startpos >= 0) shipout_int (obs, startpos + match_length); } else if (strcmp(action, "length") == 0) { if (startpos >= 0) shipout_int (obs, match_length); } else if (strcmp(action, "delete") == 0) { if (startpos >= 0) { obstack_grow (obs, ARG (1), startpos); obstack_grow (obs, ARG (1) + startpos + match_length, strlen (ARG (1)) - startpos - match_length); } else obstack_grow (obs, ARG (1), strlen (ARG (1))); } else if (strcmp(action, "extract") == 0) { if (startpos >= 0) obstack_grow (obs, ARG (1) + startpos, match_length); } else { if (startpos >= 0) obstack_grow (obs, "true", 4); } pcre_free (re); xfree ((voidstar) match_ptr); } /*----------------------------------------------. | Substitutes regular expressions in a string. | | A string is returned. | `----------------------------------------------*/ static void subst_in_string (struct obstack *obs, int argc, token_data **argv, int extra_re_flags) { char *victim; /* first argument */ char *regexp; /* regular expression */ pcre *re; pcre_extra *re_extra = NULL; const char *errptr; int *match_ptr = NULL; int max_subexps = 0; int options = 0; int rc; int matchpos; /* start position of match */ int offset; /* current match offset */ int length; /* length of first argument */ if (bad_argc (argv[0], argc, 2, 4)) return; victim = ARG (1); remove_special_chars (victim, FALSE); length = strlen (victim); regexp = ARG (2); remove_special_chars (regexp, FALSE); re = xre_compile (regexp, extra_re_flags); if (re == NULL) return; 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, regexp, errptr)); return; } offset = 0; matchpos = 0; while (offset < length) { /* Accept any number of subexpressions */ do { max_subexps += 10; match_ptr = (int *) xrealloc ((voidstar) match_ptr, sizeof (int) * max_subexps * 3); rc = pcre_exec (re, re_extra, victim+offset, length-offset, 0, options, match_ptr, max_subexps * 3); } while (rc == PCRE_ERROR_NOMEMORY); max_subexps -= 10; /* Subsequent calls to regexec do not match beginning o line */ options |= PCRE_NOTBOL; if (rc < 0) { /* Match failed -- either error or there is no match in the rest of the string, in which case the rest of the string is copied verbatim. */ if (rc != PCRE_ERROR_NOMATCH) MP4HERROR ((warning_status, 0, _("Warning:%s:%d: Error matching regular expression `%s'"), CURRENT_FILE_LINE, regexp)); else if (offset < length) obstack_grow (obs, victim + offset, length - offset); break; } /* Copy the part of the string that was skipped by regex (). */ matchpos = match_ptr[0]; if (matchpos > 0) obstack_grow (obs, victim + offset, matchpos); /* Handle the part of the string that was covered by the match. */ substitute (obs, victim+offset, ARG (3), match_ptr); /* Update the offset to the end of the match. If the regexp matched a null string, advance offset one more, to avoid infinite loops. */ offset += match_ptr[1]; if (match_ptr[0] == match_ptr[1]) obstack_1grow (obs, victim[offset++]); } pcre_free (re); pcre_free (re_extra); xfree ((voidstar) match_ptr); } /*------------------------------------------------. | Routine parsing attributes to set regex flags. | `------------------------------------------------*/ static int regex_attributes (int *ptr_argc, token_data **argv) { const char *singleline, *caseless, *reflags; const char *cp; int re_flags = 0; singleline = predefined_attribute ("singleline", ptr_argc, argv, TRUE); if (singleline) { if (strcmp (singleline, "true") == 0) re_flags |= PCRE_DOTALL; else re_flags |= PCRE_MULTILINE; } caseless = predefined_attribute ("caseless", ptr_argc, argv, TRUE); if (caseless && strcmp (caseless, "true") == 0) re_flags |= PCRE_CASELESS; /* This one overrides previous options */ reflags = predefined_attribute ("reflags", ptr_argc, argv, TRUE); if (reflags) { re_flags = 0; for (cp = reflags; *cp != '\0'; cp++) { switch (*cp) { case '-': break; case 'i': re_flags |= PCRE_CASELESS; break; case 'm': re_flags |= PCRE_MULTILINE; break; case 's': re_flags |= PCRE_DOTALL; break; case 'x': re_flags |= PCRE_EXTENDED; break; default: MP4HERROR ((warning_status, 0, _("\ Warning:%s:%d: unknown modifier `%c' in reflags"), CURRENT_FILE_LINE, *cp)); break; } } } return re_flags; } static void mp4h_bp_subst_in_string (MP4H_BUILTIN_ARGS) { int extra_re_flags; extra_re_flags = regex_attributes (&argc, argv); subst_in_string (obs, argc, argv, extra_re_flags); } /*------------------------------------------------. | Substitutes regular expressions in a variable. | `------------------------------------------------*/ static void mp4h_bp_subst_in_var (MP4H_BUILTIN_ARGS) { symbol *var; token_data td; int extra_re_flags; char *text; struct obstack temp_obs; extra_re_flags = regex_attributes (&argc, argv); if (bad_argc (argv[0], argc, 3, 4)) return; var = lookup_variable (ARG (1), SYMBOL_LOOKUP); if (var == NULL) return; TOKEN_DATA_TYPE (&td) = TOKEN_TEXT; TOKEN_DATA_TEXT (&td) = SYMBOL_TEXT (var); obstack_init (&temp_obs); argv[1] = &td; subst_in_string (&temp_obs, argc, argv, extra_re_flags); obstack_1grow (&temp_obs, '\0'); text = obstack_finish (&temp_obs); xfree ((voidstar) SYMBOL_TEXT (var)); SYMBOL_TEXT (var) = xstrdup (text); obstack_free (&temp_obs, NULL); } /*-------------------------------------------------------------------. | This function compares a string and a regular expression. | | Attribute ``action'' can be | | report : returns "true" if string match the regular expression. | | extract : returns the portion of the string matched by the | | regular expression. | | delete : deletes the portion of the string matched by the | | regular expression and prints resulting string. | | startpos : returns index of the first character matching the | | regular expression, -1 if no match. | | endpos : returns index of the last character matching the | | regular expression, -1 if no match. | | length : returns length of the matched string. | `-------------------------------------------------------------------*/ static void mp4h_bp_match (MP4H_BUILTIN_ARGS) { const char *action; int extra_re_flags; extra_re_flags = regex_attributes (&argc, argv); action = predefined_attribute ("action", &argc, argv, TRUE); if (!action) action = "report"; if (bad_argc (argv[0], argc, 3, 4)) return; string_regexp (obs, argc, argv, extra_re_flags, action); } /* Operation on variables: define, undefine, search, insert,... Variables are either strings or array of strings. */ /*--------------------------------------------------------------. | The function generic_variable_lookup is the generic function | | used by mp4h_bp_get_var and mp4h_bp_get_var_once. | `--------------------------------------------------------------*/ static void generic_variable_lookup (MP4H_BUILTIN_ARGS, boolean verbatim) { char *cp, *ptr_index; symbol *var, *index_var; int i; int length; int array_index; if (argc < 2) return; for (i = 1; i < argc; i++) { array_index = -1; ptr_index = strchr (ARG (i), ']'); if (ptr_index != NULL) { if (*(ptr_index-1) == '[') { *(ptr_index-1) = '\0'; ptr_index = NULL; } else { *ptr_index = '\0'; ptr_index = strchr (ARG (i), '['); if (!ptr_index) { MP4HERROR ((warning_status, 0, _("\ Warning:%s:%d: wrong index declaration in <%s>"), CURRENT_FILE_LINE, ARG (0))); return; } *ptr_index = '\0'; ptr_index++; if (!numeric_arg (argv[0], ptr_index, FALSE, &array_index)) { /* Maybe there is an implicit index like in . */ index_var = lookup_variable (ptr_index, SYMBOL_LOOKUP); if (index_var) { if (!numeric_arg (argv[0], SYMBOL_TEXT (index_var), FALSE, &array_index)) index_var = NULL; } if (!index_var) { MP4HERROR ((warning_status, 0, _("\ Warning:%s:%d: wrong index declaration in <%s>"), CURRENT_FILE_LINE, ARG (0))); return; } } if (array_index < 0) continue; } } var = lookup_variable (ARG (i), SYMBOL_LOOKUP); if (var == NULL) return; if (array_index < 0) { cp = SYMBOL_TEXT (var); length = strlen (cp); } else cp = array_value (var, array_index, &length); if (cp) { if (verbatim) obstack_1grow (obs, CHAR_LQUOTE); obstack_grow (obs, cp, length); if (verbatim) obstack_1grow (obs, CHAR_RQUOTE); } } } /*-------------------------------------------------------. | Define a variable. Value is found in the body. | | Intentionally, variables cannot be indexed with this | | tag, because an entire array can be assigned using | | this. | `-------------------------------------------------------*/ static void mp4h_bp_set_var_x (MP4H_BUILTIN_ARGS) { const char *name; symbol *var; name = predefined_attribute ("name", &argc, argv, FALSE); var = lookup_variable (name, SYMBOL_INSERT); if (SYMBOL_TYPE (var) == TOKEN_TEXT) xfree ((voidstar) SYMBOL_TEXT (var)); SYMBOL_TEXT (var) = xstrdup (ARGBODY); SYMBOL_TYPE (var) = TOKEN_TEXT; } /*----------------------------------------------------------------------. | Define a variable. Argument is evaluated or not depending on whether | | the 'expand attributes' flag is on in the builtin_tab above. | `----------------------------------------------------------------------*/ static void mp4h_bp_set_var (MP4H_BUILTIN_ARGS) { char *value, *cp, *ptr_index, *old_value, *new_value; symbol *var, *index_var; register int i; register int j; int length, istep, size; int array_index; if (argc < 2) return; for (i = 1; i < argc; i++) { array_index = -1; istep = 0; value = strchr (ARG (i), '='); if (value == NULL) { /* Look for spaces like in */ if (i+1 < argc && *(ARG (i+1)) == '=') { istep++; if (i+2 < argc && *(ARG (i+1) +1) == '\0') { istep++; value = ARG (i+2); } else value = ARG (i+1) + 1; } else value = ARG (i) + strlen (ARG (i)); } else { *value = '\0'; value++; } /* Remove special quote characters. */ remove_special_chars (value, FALSE); ptr_index = strchr (ARG (i), ']'); if (ptr_index != NULL) { if (*(ptr_index-1) == '[') { *(ptr_index-1) = '\0'; ptr_index = NULL; } else { *ptr_index = '\0'; ptr_index = strchr (ARG (i), '['); if (!ptr_index) { MP4HERROR ((warning_status, 0, _("\ Warning:%s:%d: wrong index declaration in <%s>"), CURRENT_FILE_LINE, ARG (0))); return; } *ptr_index = '\0'; ptr_index++; if (!numeric_arg (argv[0], ptr_index, FALSE, &array_index)) { /* Maybe there is an implicit index like in . */ index_var = lookup_variable (ptr_index, SYMBOL_LOOKUP); if (index_var) { if (!numeric_arg (argv[0], SYMBOL_TEXT (index_var), FALSE, &array_index)) index_var = NULL; } if (!index_var) { MP4HERROR ((warning_status, 0, _("\ Warning:%s:%d: wrong index declaration in <%s>"), CURRENT_FILE_LINE, ARG (0))); return; } } } } var = lookup_variable (ARG (i), SYMBOL_INSERT); if (ptr_index == NULL) { /* single value. */ if (SYMBOL_TYPE (var) == TOKEN_TEXT) xfree ((voidstar) SYMBOL_TEXT (var)); SYMBOL_TEXT (var) = xstrdup (value); } else if (array_index >= 0) { /* an index has been specified. */ if (SYMBOL_TYPE (var) == TOKEN_TEXT) old_value = SYMBOL_TEXT (var); else old_value = ""; length = strlen (old_value) + strlen (value) + array_index + 1; new_value = (char *) xmalloc (length + 1); *new_value = '\0'; if (array_index == 0) { strcat (new_value, value); cp = strchr (old_value, '\n'); if (cp) strcat (new_value, cp); } else { size = array_size (var); if (size == 0) size = 1; if (size <= array_index) { strcat (new_value, old_value); for (j=size; j<=array_index; j++) strcat (new_value, "\n"); strcat (new_value, value); } else { cp = array_value (var, array_index, 0); strncat (new_value, old_value, cp - SYMBOL_TEXT (var)); strcat (new_value, value); cp = strchr (cp, '\n'); if (cp) strcat (new_value, cp); } } if (SYMBOL_TYPE (var) == TOKEN_TEXT) xfree ((voidstar) SYMBOL_TEXT (var)); SYMBOL_TEXT (var) = new_value; } SYMBOL_TYPE (var) = TOKEN_TEXT; i += istep; } } /*------------------------------. | Get the value of a variable. | `------------------------------*/ static void mp4h_bp_get_var (MP4H_BUILTIN_ARGS) { generic_variable_lookup (MP4H_BUILTIN_RECUR, FALSE); } /*--------------------------------------------------------------. | Get the value of a variable without expanding its attribute. | `--------------------------------------------------------------*/ static void mp4h_bp_get_var_once (MP4H_BUILTIN_ARGS) { generic_variable_lookup (MP4H_BUILTIN_RECUR, TRUE); } /*----------------------. | Undefine a variable. | `----------------------*/ static void mp4h_bp_unset_var (MP4H_BUILTIN_ARGS) { int i; for (i=1; i0; i--) { next = (var_stack *) xmalloc (sizeof (var_stack)); var = lookup_variable (ARG (i), SYMBOL_LOOKUP); if (var) { next->text = xstrdup (SYMBOL_TEXT (var)); *(SYMBOL_TEXT (var)) = '\0'; } else next->text = xstrdup (""); next->prev = vs; vs = next; } } /*-----------------------------------------. | Pop a variable from the variable stack. | `-----------------------------------------*/ static void mp4h_bp_restore (MP4H_BUILTIN_ARGS) { symbol *var; var_stack *prev; int i; if (bad_argc (argv[0], argc, 2, 0)) return; for (i=1; i already gobbled all data"), CURRENT_FILE_LINE, argv[0])); return; } if (SYMBOL_TYPE (var) == TOKEN_TEXT) xfree ((voidstar) SYMBOL_TEXT (var)); SYMBOL_TEXT (var) = xstrdup (vs->text); SYMBOL_TYPE (var) = TOKEN_TEXT; prev = vs->prev; xfree ((voidstar) vs->text); xfree ((voidstar) vs); vs = prev; } } static void varstack_check (void) { if (vs) { MP4HERROR ((warning_status, 0, _("\ WARNING:%s:%d: variable stack not empty, it means pushed more items than popped"), CURRENT_FILE_LINE)); } } /*----------------------------. | Test if a variable exists. | `----------------------------*/ static void mp4h_bp_var_exists (MP4H_BUILTIN_ARGS) { symbol *var; var = lookup_variable (ARG (1), SYMBOL_LOOKUP); if (var != NULL) shipout_text (obs, "true"); } /*-----------------------. | Increment a variable. | `-----------------------*/ static void mp4h_bp_increment (MP4H_BUILTIN_ARGS) { int value, incr; symbol *var; char buf[128]; const char *by; by = predefined_attribute ("by", &argc, argv, FALSE); if (bad_argc (argv[0], argc, 2, 2)) return; if (!by) by = "1"; if (bad_argc (argv[0], argc, 2, 2)) return; var = lookup_variable (ARG (1), SYMBOL_LOOKUP); if (var == NULL) return; if (!numeric_arg (argv[0], SYMBOL_TEXT (var), TRUE, &value)) return; if (!numeric_arg (argv[0], by, TRUE, &incr)) return; if (SYMBOL_TYPE (var) == TOKEN_TEXT) xfree ((voidstar) SYMBOL_TEXT (var)); sprintf (buf, "%d", value+incr); SYMBOL_TEXT (var) = xstrdup(buf); } static void mp4h_bp_decrement (MP4H_BUILTIN_ARGS) { int value, incr; symbol *var; char buf[128]; const char *by; by = predefined_attribute ("by", &argc, argv, FALSE); if (bad_argc (argv[0], argc, 2, 2)) return; if (!by) by = "1"; if (bad_argc (argv[0], argc, 2, 2)) return; var = lookup_variable (ARG (1), SYMBOL_LOOKUP); if (var == NULL) return; if (!numeric_arg (argv[0], SYMBOL_TEXT (var), TRUE, &value)) return; if (!numeric_arg (argv[0], by, TRUE, &incr)) return; if (SYMBOL_TYPE (var) == TOKEN_TEXT) xfree ((voidstar) SYMBOL_TEXT (var)); sprintf (buf, "%d", value-incr); SYMBOL_TEXT (var) = xstrdup(buf); } /*--------------------------------. | Dumps informations of symbols. | `--------------------------------*/ static void mp4h_bp_symbol_info (MP4H_BUILTIN_ARGS) { symbol *var; int size; if (bad_argc (argv[0], argc, 0, 2)) return; /* First look if this variable is defined. */ var = lookup_variable (ARG (1), SYMBOL_LOOKUP); if (var != NULL) { size = array_size (var); if (size == 0) size = 1; obstack_grow (obs, "STRING\n", 7); shipout_int (obs, size); return; } var = lookup_symbol (ARG (1), SYMBOL_LOOKUP); if (var != NULL) { if (SYMBOL_TYPE (var) == TOKEN_FUNC) { if (SYMBOL_CONTAINER (var)) obstack_grow (obs, "PRIM COMPLEX", 12); else obstack_grow (obs, "PRIM TAG", 8); } else if (SYMBOL_TYPE (var) == TOKEN_TEXT) { if (SYMBOL_CONTAINER (var)) obstack_grow (obs, "USER COMPLEX", 12); else obstack_grow (obs, "USER TAG", 8); } } } /*--------------------------------. | Copy a variable to another one. | `--------------------------------*/ static void mp4h_bp_copy_var (MP4H_BUILTIN_ARGS) { symbol *var1, *var2; if (bad_argc (argv[0], argc, 3, 3)) return; var1 = lookup_variable (ARG (1), SYMBOL_LOOKUP); if (var1 == NULL) { MP4HERROR ((warning_status, 0, _("Warning:%s:%d: variable `%s' not defined in "), CURRENT_FILE_LINE, ARG (1))); return; } var2 = lookup_variable (ARG (2), SYMBOL_INSERT); if (SYMBOL_TYPE (var2) == TOKEN_TEXT) xfree ((voidstar) SYMBOL_TEXT (var2)); SYMBOL_TEXT (var2) = xstrdup(SYMBOL_TEXT (var1)); SYMBOL_TYPE (var2) = TOKEN_TEXT; } /*--------------------------------. | Defines a variable only if it | | is not set yet. | `--------------------------------*/ static void mp4h_bp_defvar (MP4H_BUILTIN_ARGS) { symbol *var; if (bad_argc (argv[0], argc, 0, 3)) return; var = lookup_variable (ARG (1), SYMBOL_LOOKUP); if (var == NULL) { var = lookup_variable (ARG (1), SYMBOL_INSERT); SYMBOL_TYPE (var) = TOKEN_TEXT; SYMBOL_TEXT (var) = xstrdup(ARG (2)); } else if (SYMBOL_TYPE (var) == TOKEN_TEXT && *(SYMBOL_TEXT (var)) == '\0') { xfree ((voidstar) SYMBOL_TEXT (var)); SYMBOL_TEXT (var) = xstrdup(ARG (2)); } } /* Array functions: */ /*------------------------------------------------------------. | An array is a representation of variables, it is a newline | | separated list of strings. | `------------------------------------------------------------*/ /*-----------------------------------------. | Returns number of elements of an array. | `-----------------------------------------*/ static int array_size (symbol *var) { char *cp; int result = 0; if (var != NULL && SYMBOL_TYPE (var) == TOKEN_TEXT && SYMBOL_TEXT (var) != NULL && *(SYMBOL_TEXT (var)) != '\0') { result++; for (cp=SYMBOL_TEXT (var); *cp != '\0'; cp++) if (*cp == '\n') result++; } return result; } /*------------------------------------. | Returns the nth value of an array. | | Returns NULL if array is too small. | `------------------------------------*/ static char * array_value (symbol *var, int offset, int *length) { char *cp, *value; int i; value = NULL; if (offset == 0) value = SYMBOL_TEXT (var); else if (offset > 0) { value = SYMBOL_TEXT (var); for (i=0; i"), CURRENT_FILE_LINE, ARG (1), ARG (0))); } if (!numeric_arg (argv[0], ARG (2), TRUE, &offset)) return; if (SYMBOL_TYPE (var) != TOKEN_TEXT) { SYMBOL_TYPE (var) = TOKEN_TEXT; SYMBOL_TEXT (var) = xstrdup (""); } ind_start = 0; start = predefined_attribute ("start", &argc, argv, FALSE); if (start) { if (!numeric_arg (argv[0], start, TRUE, &ind_start)) return; } if (offset == 0) return; value = (char *) xmalloc (strlen (SYMBOL_TEXT (var)) + offset + 1); if (ind_start > 0) { old_value = array_value (var, ind_start, 0); *(old_value-1) = '\0'; strcpy ((char *) value, SYMBOL_TEXT (var)); cp = value + strlen (SYMBOL_TEXT (var)) + 1; *(cp-1) = '\n'; *(old_value-1) = '\n'; } else { old_value = SYMBOL_TEXT (var); cp = value; } *cp = '\0'; if (offset > 0) { for (i=0; i val2 ) result = 1; else result = -1; } else { if (sort_caseless) result = strcasecmp (string1, string2); else result = strcmp (string1, string2); } if (sort_sortorder) result = - result; return result; } static void mp4h_bp_sort (MP4H_BUILTIN_ARGS) { symbol *var; const char *caseless, *numeric, *sortorder; char *cp, *value; char **array; int length, size, i; caseless = predefined_attribute ("caseless", &argc, argv, TRUE); sortorder = predefined_attribute ("sortorder", &argc, argv, TRUE); numeric = predefined_attribute ("numeric", &argc, argv, TRUE); if (bad_argc (argv[0], argc, 2, 2)) return; var = lookup_variable (ARG (1), SYMBOL_LOOKUP); if (var == NULL) return; sort_caseless = (caseless != NULL); sort_numeric = (numeric != NULL); if (sortorder && strcmp (sortorder, "reverse") == 0) sort_sortorder = TRUE; else sort_sortorder = FALSE; length = strlen (SYMBOL_TEXT (var)); size = array_size (var); /* Build a pointer to array values. All newlines are replaced by NULL chars to use standard string comparison functions. */ array = (char **) xmalloc ((size+1) * sizeof (char *)); i = 0; value = xstrdup (SYMBOL_TEXT (var)); array[i] = value; for (cp=value; *cp != '\0'; cp++) { if (*cp == '\n') { *cp = '\0'; i++; array[i] = cp + 1; } } qsort ((void *)array, size, sizeof (char *), sort_function); strcpy (SYMBOL_TEXT (var), array[0]); for (i=1; i 1) obstack_grow (obs, sep, strlen (sep)); obstack_1grow (obs, CHAR_LQUOTE); obstack_grow (obs, ARG (i), strlen (ARG (i))); obstack_1grow (obs, CHAR_RQUOTE); } } else dump_args (obs, argc, argv, sep); } else if (strncmp (text, "name", 4) == 0) { obstack_grow (obs, SYMBOL_NAME (sym), strlen (SYMBOL_NAME (sym))); text += 4; } else { obstack_1grow (obs, '%'); text = save; } } }