PageRenderTime 60ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/wml_backend/p2_mp4h/src/builtin.c

https://bitbucket.org/shlomif/website-meta-language
C | 5049 lines | 3787 code | 642 blank | 620 comment | 918 complexity | 5d8c9d6158cf1d6e7a702cb8ec102c2a MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.0
  1. /* mp4h -- A macro processor for HTML documents
  2. Copyright 2000-2002, Denis Barbier
  3. All rights reserved.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. This program is a work based on GNU m4 version 1.4n. Below is the
  9. original copyright.
  10. */
  11. /* GNU m4 -- A simple macro processor
  12. Copyright (C) 1989, 90, 91, 92, 93, 94, 98 Free Software Foundation, Inc.
  13. This program is free software; you can redistribute it and/or modify
  14. it under the terms of the GNU General Public License as published by
  15. the Free Software Foundation; either version 2, or (at your option)
  16. any later version.
  17. This program is distributed in the hope that it will be useful,
  18. but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. GNU General Public License for more details.
  21. You should have received a copy of the GNU General Public License
  22. along with this program; if not, write to the Free Software
  23. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24. */
  25. /* Code for all builtin macros, initialisation of symbol table, and
  26. expansion of user defined macros. */
  27. #define MP4H_MODULE
  28. #include "mp4h.h"
  29. #undef MP4H_MODULE
  30. #include "builtin.h"
  31. #define CHECK_SAFETY_LEVEL(sl) \
  32. if (safety_level > sl) \
  33. { \
  34. MP4HERROR ((warning_status, 0, \
  35. _("Warning:%s:%d: `<%s>' ignored due to -S flag"), \
  36. CURRENT_FILE_LINE, ARG (0))); \
  37. return; \
  38. }
  39. /* Initialisation of builtin and predefined macros. The table
  40. "builtin_tab" is both used for initialisation, and by the "builtin"
  41. builtin. */
  42. DECLARE (mp4h_bp___file__);
  43. DECLARE (mp4h_bp___line__);
  44. DECLARE (mp4h_bp___version__);
  45. DECLARE (mp4h_bp_lb);
  46. DECLARE (mp4h_bp_rb);
  47. DECLARE (mp4h_bp_dq);
  48. DECLARE (mp4h_bp_bs);
  49. DECLARE (mp4h_bp_timer);
  50. #ifdef HAVE_LOCALE_H
  51. DECLARE (mp4h_bp_mp4h_l10n);
  52. #endif
  53. DECLARE (mp4h_bp_mp4h_output_radix);
  54. #if !defined(HAVE_FILE_FUNCS) || !defined (HAVE_LOCALE_H) || !defined(WITH_MODULES)
  55. DECLARE (mp4h_bp_unsupported);
  56. #endif
  57. /* debug functions */
  58. DECLARE (mp4h_bp_debugmode);
  59. DECLARE (mp4h_bp_debugfile);
  60. DECLARE (mp4h_bp_function_def);
  61. DECLARE (mp4h_bp_debugging_off);
  62. DECLARE (mp4h_bp_debugging_on);
  63. /* file functions */
  64. #ifdef HAVE_FILE_FUNCS
  65. DECLARE (mp4h_bp_get_file_properties);
  66. DECLARE (mp4h_bp_directory_contents);
  67. DECLARE (mp4h_bp_file_exists);
  68. DECLARE (mp4h_bp_real_path);
  69. #endif /* HAVE_FILE_FUNCS */
  70. DECLARE (mp4h_bp_date);
  71. /* flow functions */
  72. DECLARE (mp4h_bp_group);
  73. DECLARE (mp4h_bp_compound);
  74. DECLARE (mp4h_bp_disjoin);
  75. DECLARE (mp4h_bp_noexpand);
  76. DECLARE (mp4h_bp_expand);
  77. DECLARE (mp4h_bp_if);
  78. DECLARE (mp4h_bp_ifeq);
  79. DECLARE (mp4h_bp_ifneq);
  80. DECLARE (mp4h_bp_when);
  81. DECLARE (mp4h_bp_while);
  82. DECLARE (mp4h_bp_foreach);
  83. DECLARE (mp4h_bp_var_case);
  84. DECLARE (mp4h_bp_break);
  85. DECLARE (mp4h_bp_return);
  86. DECLARE (mp4h_bp_warning);
  87. DECLARE (mp4h_bp_exit);
  88. DECLARE (mp4h_bp_at_end_of_file);
  89. /* macro functions */
  90. DECLARE (mp4h_bp_define_tag);
  91. DECLARE (mp4h_bp_provide_tag);
  92. DECLARE (mp4h_bp_let);
  93. DECLARE (mp4h_bp_undef);
  94. DECLARE (mp4h_bp_set_hook);
  95. DECLARE (mp4h_bp_get_hook);
  96. DECLARE (mp4h_bp_attributes_quote);
  97. DECLARE (mp4h_bp_attributes_remove);
  98. DECLARE (mp4h_bp_attributes_extract);
  99. DECLARE (mp4h_bp_define_entity);
  100. /* math functions */
  101. DECLARE (mp4h_bp_add);
  102. DECLARE (mp4h_bp_substract);
  103. DECLARE (mp4h_bp_multiply);
  104. DECLARE (mp4h_bp_divide);
  105. DECLARE (mp4h_bp_modulo);
  106. DECLARE (mp4h_bp_min);
  107. DECLARE (mp4h_bp_max);
  108. DECLARE (mp4h_bp_gt);
  109. DECLARE (mp4h_bp_lt);
  110. DECLARE (mp4h_bp_eq);
  111. DECLARE (mp4h_bp_neq);
  112. /* page functions */
  113. DECLARE (mp4h_bp_include);
  114. DECLARE (mp4h_bp___include);
  115. DECLARE (mp4h_bp_use);
  116. #ifdef WITH_MODULES
  117. DECLARE (mp4h_bp_load);
  118. #endif
  119. DECLARE (mp4h_bp_comment);
  120. DECLARE (mp4h_bp_set_eol_comment);
  121. DECLARE (mp4h_bp_set_quotes);
  122. DECLARE (mp4h_bp_dnl);
  123. DECLARE (mp4h_bp_frozen_dump);
  124. /* relational operators */
  125. DECLARE (mp4h_bp_not);
  126. DECLARE (mp4h_bp_and);
  127. DECLARE (mp4h_bp_or);
  128. /* string functions */
  129. DECLARE (mp4h_bp_downcase);
  130. DECLARE (mp4h_bp_upcase);
  131. DECLARE (mp4h_bp_capitalize);
  132. DECLARE (mp4h_bp_string_length);
  133. DECLARE (mp4h_bp_substring);
  134. DECLARE (mp4h_bp_string_eq);
  135. DECLARE (mp4h_bp_string_neq);
  136. DECLARE (mp4h_bp_string_compare);
  137. DECLARE (mp4h_bp_char_offsets);
  138. DECLARE (mp4h_bp_printf);
  139. /* regexp functions */
  140. DECLARE (mp4h_bp_subst_in_string);
  141. DECLARE (mp4h_bp_subst_in_var);
  142. DECLARE (mp4h_bp_match);
  143. /* variable functions */
  144. DECLARE (mp4h_bp_get_var);
  145. DECLARE (mp4h_bp_get_var_once);
  146. DECLARE (mp4h_bp_set_var_x);
  147. DECLARE (mp4h_bp_set_var);
  148. DECLARE (mp4h_bp_unset_var);
  149. DECLARE (mp4h_bp_preserve);
  150. DECLARE (mp4h_bp_restore);
  151. DECLARE (mp4h_bp_var_exists);
  152. DECLARE (mp4h_bp_increment);
  153. DECLARE (mp4h_bp_decrement);
  154. DECLARE (mp4h_bp_symbol_info);
  155. DECLARE (mp4h_bp_copy_var);
  156. DECLARE (mp4h_bp_defvar);
  157. /* array functions */
  158. DECLARE (mp4h_bp_array_size);
  159. DECLARE (mp4h_bp_array_add_unique);
  160. DECLARE (mp4h_bp_array_member);
  161. DECLARE (mp4h_bp_array_push);
  162. DECLARE (mp4h_bp_array_pop);
  163. DECLARE (mp4h_bp_array_topvalue);
  164. DECLARE (mp4h_bp_array_shift);
  165. DECLARE (mp4h_bp_array_concat);
  166. DECLARE (mp4h_bp_sort);
  167. /* diversion functions */
  168. DECLARE (mp4h_bp_divert);
  169. DECLARE (mp4h_bp_divnum);
  170. DECLARE (mp4h_bp_undivert);
  171. #undef DECLARE
  172. static builtin
  173. builtin_tab[] =
  174. {
  175. /* name container expand function
  176. attributes */
  177. { "__file__", FALSE, TRUE, mp4h_bp___file__ },
  178. { "__line__", FALSE, TRUE, mp4h_bp___line__ },
  179. { "__version__", FALSE, TRUE, mp4h_bp___version__ },
  180. { "lb", FALSE, TRUE, mp4h_bp_lb },
  181. { "rb", FALSE, TRUE, mp4h_bp_rb },
  182. { "dq", FALSE, TRUE, mp4h_bp_dq },
  183. { "bs", FALSE, TRUE, mp4h_bp_bs },
  184. { "timer", FALSE, TRUE, mp4h_bp_timer },
  185. #ifdef HAVE_LOCALE_H
  186. { "mp4h-l10n", FALSE, TRUE, mp4h_bp_mp4h_l10n },
  187. #else
  188. { "mp4h-l10n", FALSE, TRUE, mp4h_bp_unsupported },
  189. #endif
  190. { "mp4h-output-radix",FALSE, TRUE, mp4h_bp_mp4h_output_radix },
  191. { "date", FALSE, TRUE, mp4h_bp_date },
  192. /* debug functions */
  193. { "debugmode", FALSE, TRUE, mp4h_bp_debugmode },
  194. { "debugfile", FALSE, TRUE, mp4h_bp_debugfile },
  195. { "function-def", FALSE, TRUE, mp4h_bp_function_def },
  196. { "debugging-off", FALSE, TRUE, mp4h_bp_debugging_off },
  197. { "debugging-on", FALSE, TRUE, mp4h_bp_debugging_on },
  198. /* file functions */
  199. #ifdef HAVE_FILE_FUNCS
  200. { "get-file-properties", FALSE, TRUE, mp4h_bp_get_file_properties },
  201. { "directory-contents", FALSE, TRUE, mp4h_bp_directory_contents },
  202. { "file-exists", FALSE, TRUE, mp4h_bp_file_exists },
  203. { "real-path", FALSE, TRUE, mp4h_bp_real_path },
  204. #else
  205. { "get-file-properties", FALSE, TRUE, mp4h_bp_unsupported },
  206. { "directory-contents", FALSE, TRUE, mp4h_bp_unsupported },
  207. { "file-exists", FALSE, TRUE, mp4h_bp_unsupported },
  208. { "real-path", FALSE, TRUE, mp4h_bp_unsupported },
  209. #endif /* HAVE_FILE_FUNCS */
  210. /* flow functions */
  211. { "group", FALSE, TRUE, mp4h_bp_group },
  212. { "compound", TRUE, TRUE, mp4h_bp_compound },
  213. { "disjoin", FALSE, TRUE, mp4h_bp_disjoin },
  214. { "noexpand", FALSE, FALSE, mp4h_bp_noexpand },
  215. { "expand", FALSE, TRUE, mp4h_bp_expand },
  216. { "if", FALSE, FALSE, mp4h_bp_if },
  217. { "ifeq", FALSE, FALSE, mp4h_bp_ifeq },
  218. { "ifneq", FALSE, FALSE, mp4h_bp_ifneq },
  219. { "when", TRUE, TRUE, mp4h_bp_when },
  220. { "while", TRUE, FALSE, mp4h_bp_while },
  221. { "foreach", TRUE, TRUE, mp4h_bp_foreach },
  222. { "var-case", FALSE, FALSE, mp4h_bp_var_case },
  223. { "break", FALSE, TRUE, mp4h_bp_break },
  224. { "return", FALSE, TRUE, mp4h_bp_return },
  225. { "warning", FALSE, TRUE, mp4h_bp_warning },
  226. { "exit", FALSE, TRUE, mp4h_bp_exit },
  227. { "at-end-of-file", TRUE, TRUE, mp4h_bp_at_end_of_file },
  228. /* macro functions */
  229. { "define-tag", TRUE, TRUE, mp4h_bp_define_tag },
  230. { "provide-tag", TRUE, TRUE, mp4h_bp_provide_tag },
  231. { "let", FALSE, TRUE, mp4h_bp_let },
  232. { "undef", FALSE, TRUE, mp4h_bp_undef },
  233. { "set-hook", TRUE, TRUE, mp4h_bp_set_hook },
  234. { "get-hook", FALSE, TRUE, mp4h_bp_get_hook },
  235. { "attributes-quote", FALSE, TRUE, mp4h_bp_attributes_quote },
  236. { "attributes-remove", FALSE, TRUE, mp4h_bp_attributes_remove },
  237. { "attributes-extract", FALSE, TRUE, mp4h_bp_attributes_extract },
  238. { "define-entity", TRUE, TRUE, mp4h_bp_define_entity },
  239. /* numerical relational operators */
  240. { "gt", FALSE, TRUE, mp4h_bp_gt },
  241. { "lt", FALSE, TRUE, mp4h_bp_lt },
  242. { "eq", FALSE, TRUE, mp4h_bp_eq },
  243. { "neq", FALSE, TRUE, mp4h_bp_neq },
  244. /* math functions */
  245. { "add", FALSE, TRUE, mp4h_bp_add },
  246. { "substract", FALSE, TRUE, mp4h_bp_substract },
  247. { "multiply", FALSE, TRUE, mp4h_bp_multiply },
  248. { "divide", FALSE, TRUE, mp4h_bp_divide },
  249. { "modulo", FALSE, TRUE, mp4h_bp_modulo },
  250. { "min", FALSE, TRUE, mp4h_bp_min },
  251. { "max", FALSE, TRUE, mp4h_bp_max },
  252. /* page functions */
  253. { "include", FALSE, FALSE, mp4h_bp_include },
  254. { "__include", TRUE, TRUE, mp4h_bp___include },
  255. { "use", FALSE, TRUE, mp4h_bp_use },
  256. #ifdef WITH_MODULES
  257. { "load", FALSE, TRUE, mp4h_bp_load },
  258. #else
  259. { "load", FALSE, TRUE, mp4h_bp_unsupported },
  260. #endif
  261. { "comment", TRUE, TRUE, mp4h_bp_comment },
  262. { "set-eol-comment", FALSE, TRUE, mp4h_bp_set_eol_comment },
  263. { "set-quotes", FALSE, TRUE, mp4h_bp_set_quotes },
  264. { "dnl", FALSE, TRUE, mp4h_bp_dnl },
  265. { "frozen-dump", FALSE, TRUE, mp4h_bp_frozen_dump },
  266. /* relational operators */
  267. { "not", FALSE, TRUE, mp4h_bp_not },
  268. { "and", FALSE, TRUE, mp4h_bp_and },
  269. { "or", FALSE, TRUE, mp4h_bp_or },
  270. /* string functions */
  271. { "downcase", FALSE, TRUE, mp4h_bp_downcase },
  272. { "upcase", FALSE, TRUE, mp4h_bp_upcase },
  273. { "capitalize", FALSE, TRUE, mp4h_bp_capitalize },
  274. { "string-length", FALSE, TRUE, mp4h_bp_string_length },
  275. { "substring", FALSE, TRUE, mp4h_bp_substring },
  276. { "string-eq", FALSE, TRUE, mp4h_bp_string_eq },
  277. { "string-neq", FALSE, TRUE, mp4h_bp_string_neq },
  278. { "string-compare", FALSE, TRUE, mp4h_bp_string_compare },
  279. { "char-offsets", FALSE, TRUE, mp4h_bp_char_offsets },
  280. { "printf", FALSE, TRUE, mp4h_bp_printf },
  281. /* regexp functions */
  282. { "subst-in-string", FALSE, TRUE, mp4h_bp_subst_in_string },
  283. { "subst-in-var", FALSE, TRUE, mp4h_bp_subst_in_var },
  284. { "match", FALSE, TRUE, mp4h_bp_match },
  285. /* variable functions */
  286. { "get-var", FALSE, TRUE, mp4h_bp_get_var },
  287. { "get-var-once", FALSE, TRUE, mp4h_bp_get_var_once },
  288. { "set-var-x", TRUE, TRUE, mp4h_bp_set_var_x },
  289. { "set-var", FALSE, TRUE, mp4h_bp_set_var },
  290. { "set-var-verbatim", FALSE, FALSE, mp4h_bp_set_var },
  291. { "unset-var", FALSE, TRUE, mp4h_bp_unset_var },
  292. { "preserve", FALSE, TRUE, mp4h_bp_preserve },
  293. { "restore", FALSE, TRUE, mp4h_bp_restore },
  294. { "var-exists", FALSE, TRUE, mp4h_bp_var_exists },
  295. { "increment", FALSE, TRUE, mp4h_bp_increment },
  296. { "decrement", FALSE, TRUE, mp4h_bp_decrement },
  297. { "symbol-info", FALSE, TRUE, mp4h_bp_symbol_info },
  298. { "copy-var", FALSE, TRUE, mp4h_bp_copy_var },
  299. { "defvar", FALSE, TRUE, mp4h_bp_defvar },
  300. /* array functions */
  301. { "array-size", FALSE, TRUE, mp4h_bp_array_size },
  302. { "array-add-unique", FALSE, TRUE, mp4h_bp_array_add_unique },
  303. { "array-member", FALSE, TRUE, mp4h_bp_array_member },
  304. { "array-push", FALSE, TRUE, mp4h_bp_array_push },
  305. { "array-pop", FALSE, TRUE, mp4h_bp_array_pop },
  306. { "array-topvalue", FALSE, TRUE, mp4h_bp_array_topvalue },
  307. { "array-shift", FALSE, TRUE, mp4h_bp_array_shift },
  308. { "array-concat", FALSE, TRUE, mp4h_bp_array_concat },
  309. { "sort", FALSE, TRUE, mp4h_bp_sort },
  310. /* diversion functions */
  311. { "divert", FALSE, TRUE, mp4h_bp_divert },
  312. { "divnum", FALSE, TRUE, mp4h_bp_divnum },
  313. { "undivert", FALSE, TRUE, mp4h_bp_undivert },
  314. { 0, FALSE, FALSE, 0 },
  315. };
  316. /* Local functions */
  317. static void push_builtin_table __P ((builtin *));
  318. static void set_trace __P ((symbol *, const char *));
  319. static void generic_set_hook __P ((MP4H_BUILTIN_PROTO, boolean, int));
  320. static void math_relation __P ((MP4H_BUILTIN_PROTO, mathrel_type));
  321. static void mathop_functions __P ((MP4H_BUILTIN_PROTO, mathop_type));
  322. static void updowncase __P ((struct obstack *, int, token_data **, boolean));
  323. static void varstack_check __P ((void));
  324. static boolean safe_strtod __P ((const char *, const char *, double *));
  325. static boolean safe_strtol __P ((const char *, const char *, long int *));
  326. static void quote_name_value __P ((struct obstack *, char *));
  327. static void matching_attributes __P ((struct obstack *, int, token_data **, boolean, char *));
  328. static char * utf8char_skip __P ((char *, int));
  329. static int utf8char_strlen __P ((char *));
  330. static int encoding_strlen __P ((char *));
  331. static void substitute __P ((struct obstack *, const char *, const char *, int *));
  332. static void string_regexp __P ((struct obstack *, int, token_data **, int, const char *));
  333. static void subst_in_string __P ((struct obstack *, int, token_data **, int));
  334. static void generic_variable_lookup __P ((MP4H_BUILTIN_PROTO, boolean));
  335. static int array_size __P ((symbol *));
  336. static char *array_value __P ((symbol *, int, int *));
  337. static int array_member __P ((const char *, symbol *, boolean));
  338. static int sort_function __P ((const void *, const void *));
  339. static void logical_to_physical_paths __P ((char **));
  340. /* This symbol controls breakings of flow statements. */
  341. static symbol varbreak;
  342. /* Stack preserve/restore variables. */
  343. static var_stack *vs = NULL;
  344. /* Global variables needed by sort algorithms. */
  345. static boolean sort_caseless;
  346. static boolean sort_sortorder;
  347. static boolean sort_numeric;
  348. /* Localization */
  349. struct lconv *my_locale;
  350. /* Table of characters used by PCRE with non-C locales */
  351. static const unsigned char *re_tableptr = NULL;
  352. /* Timer */
  353. static struct tms elapsed_time;
  354. /* Pointer to a string containig the decimal point used
  355. with locales. */
  356. #ifdef HAVE_LOCALE_H
  357. static const char *decimal_point;
  358. #else
  359. #define decimal_point "."
  360. #endif
  361. /* Output radix */
  362. static int output_radix = 6;
  363. /*------------------------------------------------------------------.
  364. | If dynamic modules are enabled, more builtin tables can be active |
  365. | at a time. This implements a list of tables of builtins. |
  366. `------------------------------------------------------------------*/
  367. struct builtin_table
  368. {
  369. struct builtin_table *next;
  370. builtin *table;
  371. };
  372. typedef struct builtin_table builtin_table;
  373. static builtin_table *builtin_tables = NULL;
  374. static void
  375. push_builtin_table (builtin *table)
  376. {
  377. builtin_table *bt;
  378. bt = (builtin_table *) xmalloc (sizeof (struct builtin_table));
  379. bt->next = builtin_tables;
  380. bt->table = table;
  381. builtin_tables = bt;
  382. }
  383. /*----------------------------------------.
  384. | Find the builtin, which lives on ADDR. |
  385. `----------------------------------------*/
  386. const builtin *
  387. find_builtin_by_addr (builtin_func *func)
  388. {
  389. const builtin_table *bt;
  390. const builtin *bp;
  391. for (bt = builtin_tables; bt != NULL; bt = bt->next)
  392. for (bp = bt->table; bp->name != NULL; bp++)
  393. if (bp->func == func)
  394. return bp;
  395. return NULL;
  396. }
  397. /*-----------------------------------.
  398. | Find the builtin, which has NAME. |
  399. `-----------------------------------*/
  400. const builtin *
  401. find_builtin_by_name (const char *name)
  402. {
  403. const builtin_table *bt;
  404. const builtin *bp;
  405. /* This is necessary to load frozen files */
  406. if (builtin_tables == NULL)
  407. push_builtin_table (builtin_tab);
  408. for (bt = builtin_tables; bt != NULL; bt = bt->next)
  409. for (bp = bt->table; bp->name != NULL; bp++)
  410. if (strcasecmp (bp->name, name) == 0)
  411. return bp;
  412. return NULL;
  413. }
  414. /*-----------------------.
  415. | Initialize a symbol. |
  416. `-----------------------*/
  417. void
  418. initialize_builtin (symbol *sym)
  419. {
  420. SYMBOL_TYPE (sym) = TOKEN_VOID;
  421. SYMBOL_TRACED (sym) = FALSE;
  422. SYMBOL_CONTAINER (sym) = FALSE;
  423. SYMBOL_EXPAND_ARGS (sym) = FALSE;
  424. SYMBOL_HOOK_BEGIN (sym) = NULL;
  425. SYMBOL_HOOK_END (sym) = NULL;
  426. }
  427. /*-------------------------------------------------------------------------.
  428. | Install a builtin macro with name NAME, bound to the C function given in |
  429. | BP. TRACED defines whether NAME is to be traced. |
  430. `-------------------------------------------------------------------------*/
  431. void
  432. define_builtin (const char *name, const builtin *bp, boolean traced)
  433. {
  434. symbol *sym;
  435. sym = lookup_symbol (name, SYMBOL_INSERT);
  436. if (SYMBOL_TYPE (sym) == TOKEN_TEXT)
  437. xfree ((voidstar) SYMBOL_TEXT (sym));
  438. SYMBOL_TYPE (sym) = TOKEN_FUNC;
  439. SYMBOL_FUNC (sym) = bp->func;
  440. SYMBOL_TRACED (sym) = traced;
  441. SYMBOL_CONTAINER (sym) = bp->container;
  442. SYMBOL_EXPAND_ARGS (sym) = bp->expand_args;
  443. SYMBOL_HOOK_BEGIN (sym) = NULL;
  444. SYMBOL_HOOK_END (sym) = NULL;
  445. }
  446. /*------------------------------.
  447. | Install a new builtin_table. |
  448. `------------------------------*/
  449. void
  450. install_builtin_table (builtin *table)
  451. {
  452. const builtin *bp;
  453. push_builtin_table (table);
  454. for (bp = table; bp->name != NULL; bp++)
  455. define_builtin (bp->name, bp, FALSE);
  456. }
  457. void
  458. break_init (void) {
  459. initialize_builtin (&varbreak);
  460. SYMBOL_TYPE (&varbreak) = TOKEN_TEXT;
  461. SYMBOL_TEXT (&varbreak) = xstrdup ("");
  462. }
  463. void
  464. break_deallocate (void) {
  465. xfree ((voidstar) SYMBOL_TEXT (&varbreak));
  466. SYMBOL_TYPE (&varbreak) = TOKEN_VOID;
  467. }
  468. /*-------------------------------------------------------------------------.
  469. | Define a predefined or user-defined macro, with name NAME, and expansion |
  470. | TEXT. MODE destinguishes between the "define" and the "pushdef" case. |
  471. | It is also used from main (). |
  472. `-------------------------------------------------------------------------*/
  473. void
  474. define_user_macro (const char *name, char *text, symbol_lookup mode,
  475. boolean container, boolean expand_args, boolean space_delete)
  476. {
  477. symbol *s;
  478. char *begin, *cp;
  479. int offset, bracket_level = 0;
  480. s = lookup_symbol (name, mode);
  481. xfree ((voidstar) SYMBOL_HOOK_BEGIN (s));
  482. xfree ((voidstar) SYMBOL_HOOK_END (s));
  483. if (SYMBOL_TYPE (s) == TOKEN_TEXT)
  484. xfree ((voidstar) SYMBOL_TEXT (s));
  485. initialize_builtin (s);
  486. SYMBOL_TYPE (s) = TOKEN_TEXT;
  487. if (space_delete)
  488. {
  489. for (begin=text; *begin != '\0' && IS_SPACE(*begin); begin++)
  490. ;
  491. SYMBOL_TEXT (s) = xstrdup (begin);
  492. offset = 0;
  493. for (cp=SYMBOL_TEXT (s); *cp != '\0'; cp++)
  494. {
  495. switch (*cp)
  496. {
  497. case '<':
  498. bracket_level++;
  499. *(cp-offset) = *cp;
  500. break;
  501. case '>':
  502. bracket_level--;
  503. *(cp-offset) = *cp;
  504. break;
  505. case '\\':
  506. *(cp-offset) = *cp;
  507. if (*(cp+1) != '\0')
  508. {
  509. cp++;
  510. *(cp-offset) = *cp;
  511. }
  512. break;
  513. case '\n':
  514. if (bracket_level>0)
  515. *(cp-offset) = *cp;
  516. else
  517. {
  518. offset++;
  519. while (IS_SPACE(*(cp+1)))
  520. {
  521. cp++;
  522. offset++;
  523. }
  524. }
  525. break;
  526. default:
  527. *(cp-offset) = *cp;
  528. break;
  529. }
  530. }
  531. *(cp-offset) = '\0';
  532. }
  533. else
  534. SYMBOL_TEXT (s) = xstrdup (text);
  535. SYMBOL_CONTAINER (s) = container;
  536. SYMBOL_EXPAND_ARGS (s) = expand_args;
  537. #ifdef DEBUG_INPUT
  538. fprintf (stderr, "Define: %s\nText: %s\nContainer: %d\n",
  539. SYMBOL_NAME (s), SYMBOL_TEXT (s), SYMBOL_CONTAINER (s));
  540. #endif
  541. }
  542. /*--------------------------------------------------------------------------.
  543. | Define a predefined or user-defined entity, with name NAME, and expansion |
  544. | TEXT. MODE destinguishes between the "define" and the "pushdef" case. |
  545. | It is also used from main (). |
  546. `--------------------------------------------------------------------------*/
  547. static void
  548. define_user_entity (const char *name, char *text, symbol_lookup mode)
  549. {
  550. symbol *s;
  551. s = lookup_entity (name, mode);
  552. if (SYMBOL_TYPE (s) == TOKEN_TEXT)
  553. {
  554. xfree ((voidstar) SYMBOL_HOOK_BEGIN (s));
  555. xfree ((voidstar) SYMBOL_HOOK_END (s));
  556. xfree ((voidstar) SYMBOL_TEXT (s));
  557. }
  558. initialize_builtin (s);
  559. SYMBOL_TYPE (s) = TOKEN_TEXT;
  560. SYMBOL_TEXT (s) = xstrdup (text);
  561. #ifdef DEBUG_INPUT
  562. fprintf (stderr, "Define: %s\nText: %s\n",
  563. SYMBOL_NAME (s), SYMBOL_TEXT (s));
  564. #endif
  565. }
  566. /*-----------------------------------------------.
  567. | Initialise all builtin and predefined macros. |
  568. `-----------------------------------------------*/
  569. void
  570. builtin_init (void)
  571. {
  572. install_builtin_table (builtin_tab);
  573. pcre_malloc = xmalloc;
  574. pcre_free = xfree;
  575. }
  576. /*-----------------------------------------------.
  577. | Deallocate all builtin and predefined macros. |
  578. `-----------------------------------------------*/
  579. void
  580. builtin_deallocate (void)
  581. {
  582. builtin_table *bt, *bt_next;
  583. for (bt = builtin_tables; bt != NULL; )
  584. {
  585. bt_next = bt->next;
  586. xfree ((voidstar) bt);
  587. bt = bt_next;
  588. }
  589. varstack_check ();
  590. }
  591. static pcre *
  592. xre_compile (const char *pattern, int cflags)
  593. {
  594. pcre *patcomp;
  595. const char *errbuf;
  596. int erroffset;
  597. if (document_encoding == ENCODING_UTF8)
  598. cflags |= PCRE_UTF8;
  599. patcomp = pcre_compile (pattern, cflags, &errbuf, &erroffset, re_tableptr);
  600. if (patcomp == 0)
  601. {
  602. MP4HERROR ((warning_status, 0,
  603. _("Warning:%s:%d: Bad regular expression `%s' at position %d: %s"),
  604. CURRENT_FILE_LINE, pattern, erroffset, errbuf));
  605. }
  606. return patcomp;
  607. }
  608. static boolean
  609. safe_strtod (const char *name, const char *nptr, double *value)
  610. {
  611. char *endp;
  612. double result;
  613. result = strtod (nptr, &endp);
  614. if (nptr == NULL || *endp != '\0')
  615. {
  616. if (nptr == NULL)
  617. nptr = endp;
  618. MP4HERROR ((warning_status, 0,
  619. _("Warning:%s:%d: Argument `%s' non-numeric in <%s>"),
  620. CURRENT_FILE_LINE, nptr, name));
  621. return FALSE;
  622. }
  623. *value = result;
  624. return TRUE;
  625. }
  626. static boolean
  627. safe_strtol (const char *name, const char *nptr, long int *value)
  628. {
  629. char *endp;
  630. long int result;
  631. result = strtol (nptr, &endp, 10);
  632. if (nptr == NULL || *endp != '\0')
  633. {
  634. if (nptr == NULL)
  635. nptr = endp;
  636. MP4HERROR ((warning_status, 0,
  637. _("Warning:%s:%d: Argument `%s' non-numeric in <%s>"),
  638. CURRENT_FILE_LINE, nptr, name));
  639. return FALSE;
  640. }
  641. *value = result;
  642. return TRUE;
  643. }
  644. /*----------------------------------------------------------------.
  645. | Quote name=value pair. This function must be called only when |
  646. | text is sent to output. |
  647. `----------------------------------------------------------------*/
  648. static void
  649. quote_name_value (struct obstack *obs, char *pair)
  650. {
  651. char *equal_ptr, *cp;
  652. int special_chars;
  653. boolean valid;
  654. equal_ptr = strchr (pair, '=');
  655. if (equal_ptr == NULL)
  656. {
  657. obstack_grow (obs, pair, strlen (pair));
  658. return;
  659. }
  660. /* Skip special characters */
  661. special_chars = 0;
  662. cp = pair;
  663. while (IS_GROUP (*cp))
  664. {
  665. cp++;
  666. special_chars++;
  667. }
  668. valid = TRUE;
  669. while (cp < equal_ptr)
  670. {
  671. if (IS_SPACE (*cp) || IS_CLOSE (*cp))
  672. {
  673. valid = FALSE;
  674. break;
  675. }
  676. cp++;
  677. }
  678. if (!valid)
  679. {
  680. obstack_grow (obs, pair, strlen (pair));
  681. return;
  682. }
  683. if (special_chars)
  684. obstack_grow (obs, pair, special_chars);
  685. *equal_ptr = '\0';
  686. obstack_grow (obs, pair+special_chars, strlen (pair)-special_chars);
  687. *equal_ptr = '=';
  688. obstack_1grow (obs, '=');
  689. if (*(equal_ptr+1) != '"' && *(equal_ptr+1) != CHAR_QUOTE)
  690. obstack_1grow (obs, CHAR_QUOTE);
  691. obstack_grow (obs, equal_ptr+1, strlen (equal_ptr+1)-special_chars);
  692. if (*(equal_ptr+1) != '"' && *(equal_ptr+1) != CHAR_QUOTE)
  693. obstack_1grow (obs, CHAR_QUOTE);
  694. if (special_chars)
  695. obstack_grow (obs, equal_ptr+1+strlen (equal_ptr+1)-special_chars,
  696. special_chars);
  697. }
  698. /*---------------------------------------------------------------.
  699. | Extract from ARGV attributes from a list of attributes names. |
  700. | When MATCH is true, matching attributes are printed, otherwise |
  701. | non matching attributes are printed. |
  702. | First argument is a comma-separated list of attributes names, |
  703. | which may contain regular expressions. |
  704. `---------------------------------------------------------------*/
  705. static void
  706. matching_attributes (struct obstack *obs, int argc, token_data **argv,
  707. boolean match, char *list)
  708. {
  709. register char *cp;
  710. char *name;
  711. int i, j, special_chars;
  712. pcre *re;
  713. pcre_extra *re_extra = NULL;
  714. const char *errptr = NULL;
  715. int rc, max_subexps;
  716. int *match_ptr = NULL;
  717. boolean first = TRUE;
  718. /* Replace comma-separated list by a regular expression */
  719. for (cp=list; *cp != '\0'; cp++)
  720. {
  721. if (*cp == ',')
  722. *cp = '|';
  723. }
  724. name = (char *) xmalloc (strlen (list) + 3);
  725. sprintf (name, "^%s$", list);
  726. /* Compile this expression once */
  727. re = xre_compile (name, PCRE_CASELESS);
  728. xfree ((voidstar) name);
  729. if (re == NULL)
  730. return;
  731. if (argc>2)
  732. {
  733. re_extra = pcre_study (re, 0, &errptr);
  734. if (errptr != NULL)
  735. {
  736. MP4HERROR ((warning_status, 0,
  737. _("Error:%s:%d: Bad regular expression `%s': %s"),
  738. CURRENT_FILE_LINE, list, errptr));
  739. return;
  740. }
  741. }
  742. for (i=1; i<argc; i++)
  743. {
  744. special_chars = 0;
  745. cp = ARG (i);
  746. while (IS_GROUP (*cp))
  747. {
  748. cp++;
  749. special_chars++;
  750. }
  751. if (*(ARG (i)+special_chars) != '\0')
  752. {
  753. cp = strchr (ARG (i)+special_chars, '=');
  754. if (cp)
  755. *cp = '\0';
  756. }
  757. /* Ensure that all subexpressions are stored */
  758. max_subexps = 0;
  759. do
  760. {
  761. max_subexps += 10;
  762. match_ptr = (int *)
  763. xrealloc ((voidstar) match_ptr, sizeof (int) * max_subexps * 3);
  764. rc = pcre_exec (re, re_extra, ARG (i)+special_chars,
  765. strlen (ARG (i)+special_chars), 0,
  766. 0, match_ptr, max_subexps * 3);
  767. }
  768. while (rc == PCRE_ERROR_NOMEMORY);
  769. max_subexps -= 10;
  770. if (rc > 0 && match)
  771. {
  772. if (!first)
  773. obstack_1grow (obs, ' ');
  774. first = FALSE;
  775. obstack_1grow (obs, CHAR_BGROUP);
  776. if (special_chars)
  777. obstack_grow (obs, ARG (i), special_chars);
  778. if (rc == 1)
  779. {
  780. obstack_grow (obs, ARG (i)+special_chars,
  781. strlen (ARG (i)+special_chars));
  782. }
  783. else
  784. {
  785. for (j=1; j<rc; j++)
  786. obstack_grow (obs, ARG (i)+special_chars+match_ptr[j*2],
  787. match_ptr[j*2+1] - match_ptr[j*2]);
  788. }
  789. if (cp)
  790. {
  791. obstack_1grow (obs, '=');
  792. obstack_grow (obs, cp+1, strlen (cp+1));
  793. }
  794. obstack_1grow (obs, CHAR_EGROUP);
  795. }
  796. else if (rc == PCRE_ERROR_NOMATCH && !match)
  797. {
  798. if (cp)
  799. *cp = '=';
  800. if (!first)
  801. obstack_1grow (obs, ' ');
  802. first = FALSE;
  803. obstack_1grow (obs, CHAR_BGROUP);
  804. obstack_grow (obs, ARG (i), strlen (ARG (i)));
  805. obstack_1grow (obs, CHAR_EGROUP);
  806. }
  807. }
  808. pcre_free (re);
  809. pcre_free (re_extra);
  810. xfree ((voidstar) match_ptr);
  811. }
  812. /*
  813. The rest of this file is the code for builtins and expansion of user
  814. defined macros. All the functions for builtins have a prototype as:
  815. void mp4h_bp_MACRONAME (
  816. struct obstack *obs,
  817. int argc,
  818. char *argv[],
  819. read_type expansion
  820. );
  821. The function are expected to leave their expansion on the obstack OBS,
  822. as an unfinished object. ARGV is a table of ARGC pointers to the
  823. individual arguments to the macro. Please note that in general
  824. argv[argc] != NULL.
  825. For container tags, argv[argc] contains the body function.
  826. */
  827. /* Notes for hackers :
  828. o Execution must not depend on argv[0]. Indeed, user may define
  829. synonyms with <let myfunc primitive>
  830. o Last argument is removed by collect_arguments () if it is empty.
  831. For this reason, it does not make sense to define a mimimal
  832. number of arguments.
  833. */
  834. /* Miscellaneous builtins -- "__file__" and "__line__". */
  835. static void
  836. mp4h_bp___file__ (MP4H_BUILTIN_ARGS)
  837. {
  838. if (argc == 1)
  839. shipout_string (obs, current_file, 0);
  840. else
  841. {
  842. xfree ((voidstar) current_file);
  843. current_file = xstrdup (ARG (1));
  844. }
  845. }
  846. static void
  847. mp4h_bp___line__ (MP4H_BUILTIN_ARGS)
  848. {
  849. if (argc == 1)
  850. shipout_int (obs, current_line);
  851. else
  852. numeric_arg (argv[0], ARG (1), FALSE, &current_line);
  853. }
  854. /*-----------------------------.
  855. | Initialize character table. |
  856. `-----------------------------*/
  857. void
  858. pcre_init (void)
  859. {
  860. re_tableptr = pcre_maketables ();
  861. }
  862. void
  863. pcre_deallocate (void)
  864. {
  865. pcre_free ((voidstar) re_tableptr);
  866. }
  867. /*---------------------.
  868. | Initialize locales. |
  869. `---------------------*/
  870. #ifdef HAVE_LOCALE_H
  871. void
  872. locale_init (int category, char *value)
  873. {
  874. setlocale (category, value);
  875. my_locale = localeconv ();
  876. decimal_point = my_locale->decimal_point;
  877. }
  878. /*---------------------------.
  879. | Change locale attributes. |
  880. `---------------------------*/
  881. static void
  882. mp4h_bp_mp4h_l10n (MP4H_BUILTIN_ARGS)
  883. {
  884. char *cp;
  885. int i;
  886. int category;
  887. for (i=1; i<argc; i++)
  888. {
  889. category = -1;
  890. cp = strchr (ARG (i), '=');
  891. if (cp)
  892. {
  893. *cp = '\0';
  894. if (strcmp (ARG (i), "LC_ALL") == 0)
  895. category = LC_ALL;
  896. else if (strcmp (ARG (i), "LC_COLLATE") == 0)
  897. category = LC_COLLATE;
  898. else if (strcmp (ARG (i), "LC_CTYPE") == 0)
  899. category = LC_CTYPE;
  900. else if (strcmp (ARG (i), "LC_MESSAGES") == 0)
  901. category = LC_MESSAGES;
  902. else if (strcmp (ARG (i), "LC_MONETARY") == 0)
  903. category = LC_MONETARY;
  904. else if (strcmp (ARG (i), "LC_NUMERIC") == 0)
  905. category = LC_NUMERIC;
  906. else if (strcmp (ARG (i), "LC_TIME") == 0)
  907. category = LC_TIME;
  908. }
  909. if (category == -1)
  910. MP4HERROR ((warning_status, 0,
  911. _("Warning:%s:%d: Unknown locale `%s'"), CURRENT_FILE_LINE, ARG (i)));
  912. else
  913. {
  914. locale_init (category, cp+1);
  915. pcre_deallocate ();
  916. pcre_init ();
  917. }
  918. }
  919. /* Compute syntax table */
  920. syntax_init ();
  921. }
  922. #endif /* HAVE_LOCALE_H */
  923. /*----------------------------------------------.
  924. | Set output radix used when printing numbers. |
  925. `----------------------------------------------*/
  926. static void
  927. mp4h_bp_mp4h_output_radix (MP4H_BUILTIN_ARGS)
  928. {
  929. if (bad_argc (argv[0], argc, 0, 2))
  930. return;
  931. safe_strtol (ARG (0), ARG (1), (long int *) &output_radix);
  932. }
  933. static void
  934. mp4h_bp_timer (MP4H_BUILTIN_ARGS)
  935. {
  936. char buf[128];
  937. times(&elapsed_time);
  938. sprintf (buf, "user %d\nsys %d\n", (int) elapsed_time.tms_utime,
  939. (int) elapsed_time.tms_stime);
  940. obstack_grow (obs, buf, strlen (buf));
  941. }
  942. #if !defined(HAVE_FILE_FUNCS) || !defined (HAVE_LOCALE_H) || !defined(WITH_MODULES)
  943. static void
  944. mp4h_bp_unsupported (MP4H_BUILTIN_ARGS)
  945. {
  946. MP4HERROR ((warning_status, 0,
  947. _("Warning:%s:%d: The <%s> tag is not implemented on your OS\n"),
  948. CURRENT_FILE_LINE, ARG (0)));
  949. }
  950. #endif
  951. static void
  952. mp4h_bp___version__ (MP4H_BUILTIN_ARGS)
  953. {
  954. obstack_grow (obs, PACKAGE_VERSION, strlen (PACKAGE_VERSION));
  955. }
  956. static void
  957. add_1char_protected (struct obstack *obs, char ch)
  958. {
  959. obstack_1grow (obs, CHAR_LQUOTE);
  960. obstack_1grow (obs, ch);
  961. obstack_1grow (obs, CHAR_RQUOTE);
  962. }
  963. static void
  964. mp4h_bp_lb (MP4H_BUILTIN_ARGS)
  965. {
  966. add_1char_protected (obs, '<');
  967. }
  968. static void
  969. mp4h_bp_rb (MP4H_BUILTIN_ARGS)
  970. {
  971. add_1char_protected (obs, '>');
  972. }
  973. static void
  974. mp4h_bp_dq (MP4H_BUILTIN_ARGS)
  975. {
  976. obstack_1grow (obs, CHAR_QUOTE);
  977. }
  978. static void
  979. mp4h_bp_bs (MP4H_BUILTIN_ARGS)
  980. {
  981. add_1char_protected (obs, '\\');
  982. }
  983. /* Enable tracing of all specified macros, or all, if none is specified.
  984. Tracing is disabled by default, when a macro is defined. This can be
  985. overridden by the "t" debug flag. */
  986. /*-----------------------------------------------------------------------.
  987. | Set_trace () is used by "debugging-on" and "debugging-off" to enable |
  988. | and disable tracing of a macro. It disables tracing if DATA is NULL, |
  989. | otherwise it enable tracing. |
  990. `-----------------------------------------------------------------------*/
  991. static void
  992. set_trace (symbol *sym, const char *data)
  993. {
  994. SYMBOL_TRACED (sym) = (boolean) (data != NULL);
  995. }
  996. static void
  997. mp4h_bp_debugging_on (MP4H_BUILTIN_ARGS)
  998. {
  999. symbol *s;
  1000. int i;
  1001. if (argc == 1)
  1002. hack_all_symbols (set_trace, (char *) obs);
  1003. else
  1004. for (i = 1; i < argc; i++)
  1005. {
  1006. s = lookup_symbol (ARG (i), SYMBOL_LOOKUP);
  1007. if (s != NULL)
  1008. set_trace (s, (char *) obs);
  1009. else
  1010. MP4HERROR ((warning_status, 0,
  1011. _("Warning:%s:%d: Undefined name %s"),
  1012. CURRENT_FILE_LINE, ARG (i)));
  1013. }
  1014. }
  1015. /*------------------------------------------------------------------------.
  1016. | Disable tracing of all specified macros, or all, if none is specified. |
  1017. `------------------------------------------------------------------------*/
  1018. static void
  1019. mp4h_bp_debugging_off (MP4H_BUILTIN_ARGS)
  1020. {
  1021. symbol *s;
  1022. int i;
  1023. if (argc == 1)
  1024. hack_all_symbols (set_trace, NULL);
  1025. else
  1026. for (i = 1; i < argc; i++)
  1027. {
  1028. s = lookup_symbol (ARG (i), SYMBOL_LOOKUP);
  1029. if (s != NULL)
  1030. set_trace (s, NULL);
  1031. else
  1032. MP4HERROR ((warning_status, 0,
  1033. _("Warning:%s:%d: Undefined name %s"),
  1034. CURRENT_FILE_LINE, ARG (i)));
  1035. }
  1036. }
  1037. /*----------------------------------------------------------------------.
  1038. | On-the-fly control of the format of the tracing output. It takes one |
  1039. | argument, which is a character string like given to the -d option, or |
  1040. | none in which case the debug_level is zeroed. |
  1041. `----------------------------------------------------------------------*/
  1042. static void
  1043. mp4h_bp_debugmode (MP4H_BUILTIN_ARGS)
  1044. {
  1045. int new_debug_level;
  1046. int change_flag;
  1047. if (bad_argc (argv[0], argc, 0, 2))
  1048. return;
  1049. if (argc == 1)
  1050. debug_level = 0;
  1051. else
  1052. {
  1053. if (ARG (1)[0] == '+' || ARG (1)[0] == '-')
  1054. {
  1055. change_flag = ARG (1)[0];
  1056. new_debug_level = debug_decode (ARG (1) + 1);
  1057. }
  1058. else
  1059. {
  1060. change_flag = 0;
  1061. new_debug_level = debug_decode (ARG (1));
  1062. }
  1063. if (new_debug_level < 0)
  1064. MP4HERROR ((warning_status, 0,
  1065. _("Warning:%s:%d: Bad debug flags: `%s'"),
  1066. CURRENT_FILE_LINE, ARG (1)));
  1067. else
  1068. {
  1069. switch (change_flag)
  1070. {
  1071. case 0:
  1072. debug_level = new_debug_level;
  1073. break;
  1074. case '+':
  1075. debug_level |= new_debug_level;
  1076. break;
  1077. case '-':
  1078. debug_level &= ~new_debug_level;
  1079. break;
  1080. }
  1081. }
  1082. }
  1083. }
  1084. /*-------------------------------------------------------------------------.
  1085. | Specify the destination of the debugging output. With one argument, the |
  1086. | argument is taken as a file name, with no arguments, revert to stderr. |
  1087. `-------------------------------------------------------------------------*/
  1088. static void
  1089. mp4h_bp_debugfile (MP4H_BUILTIN_ARGS)
  1090. {
  1091. if (bad_argc (argv[0], argc, 1, 2))
  1092. return;
  1093. if (argc == 1)
  1094. debug_set_output (NULL);
  1095. else if (!debug_set_output (ARG (1)))
  1096. MP4HERROR ((warning_status, errno,
  1097. _("Warning:%s:%d: Cannot set error file: %s"),
  1098. CURRENT_FILE_LINE, ARG (1)));
  1099. }
  1100. /*---------------------------------------------------------.
  1101. | Print the replacement text for a builtin or user macro. |
  1102. `---------------------------------------------------------*/
  1103. static void
  1104. mp4h_bp_function_def (MP4H_BUILTIN_ARGS)
  1105. {
  1106. symbol *s;
  1107. if (bad_argc (argv[0], argc, 2, 2))
  1108. return;
  1109. s = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
  1110. if (s == NULL)
  1111. return;
  1112. switch (SYMBOL_TYPE (s))
  1113. {
  1114. case TOKEN_TEXT:
  1115. obstack_1grow (obs, CHAR_LQUOTE);
  1116. shipout_string (obs, SYMBOL_TEXT (s), strlen (SYMBOL_TEXT (s)));
  1117. obstack_1grow (obs, CHAR_RQUOTE);
  1118. break;
  1119. case TOKEN_FUNC:
  1120. push_macro (SYMBOL_FUNC (s), SYMBOL_TRACED (s));
  1121. break;
  1122. case TOKEN_VOID:
  1123. break;
  1124. default:
  1125. MP4HERROR ((warning_status, 0,
  1126. "INTERNAL ERROR: Bad symbol type in mp4h_bp_function_def ()"));
  1127. exit (1);
  1128. }
  1129. }
  1130. /* file functions */
  1131. #ifdef HAVE_FILE_FUNCS
  1132. /*-----------------------------------------------------------------.
  1133. | Informations on a file. A newline separated string is printed: |
  1134. | Line 1: file size |
  1135. | Line 2: file type |
  1136. | Line 3: time of last change |
  1137. | Line 4: time of last modification |
  1138. | Line 5: time of last modification |
  1139. | Line 6: time of last access |
  1140. | Line 7: name of the owner of this file |
  1141. | Line 8: name of the group of this file |
  1142. `-----------------------------------------------------------------*/
  1143. static void
  1144. mp4h_bp_get_file_properties (MP4H_BUILTIN_ARGS)
  1145. {
  1146. struct stat buf;
  1147. struct passwd *user;
  1148. struct group *group;
  1149. CHECK_SAFETY_LEVEL(1);
  1150. if (bad_argc (argv[0], argc, 2, 2))
  1151. return;
  1152. if (stat (ARG (1), &buf))
  1153. {
  1154. MP4HERROR ((warning_status, errno,
  1155. _("Warning:%s:%d: Could not stat `%s'"),
  1156. CURRENT_FILE_LINE, ARG (1)));
  1157. return;
  1158. }
  1159. shipout_long (obs, (long) buf.st_size);
  1160. obstack_1grow (obs, '\n');
  1161. if (S_ISLNK(buf.st_mode))
  1162. obstack_grow (obs, "LINK", 4);
  1163. else if (S_ISREG(buf.st_mode))
  1164. obstack_grow (obs, "FILE", 4);
  1165. else if (S_ISDIR(buf.st_mode))
  1166. obstack_grow (obs, "DIR", 3);
  1167. else
  1168. obstack_grow (obs, "UNKNOWN", 7);
  1169. obstack_1grow (obs, '\n');
  1170. shipout_long (obs, (long) buf.st_ctime);
  1171. obstack_1grow (obs, '\n');
  1172. shipout_long (obs, (long) buf.st_mtime);
  1173. obstack_1grow (obs, '\n');
  1174. shipout_long (obs, (long) buf.st_atime);
  1175. obstack_1grow (obs, '\n');
  1176. user = getpwuid (buf.st_uid);
  1177. if (user)
  1178. obstack_grow (obs, user->pw_name, strlen (user->pw_name));
  1179. else
  1180. obstack_grow (obs, "(UNKNOWN)", 9);
  1181. obstack_1grow (obs, '\n');
  1182. group = getgrgid (buf.st_gid);
  1183. if (group)
  1184. obstack_grow (obs, group->gr_name, strlen (group->gr_name));
  1185. else
  1186. obstack_grow (obs, "(UNKNOWN)", 9);
  1187. obstack_1grow (obs, '\n');
  1188. }
  1189. /*-----------------------------------------------------------------.
  1190. | Prints a directory contents. A pattern can be specified with |
  1191. | the ``matching'' attribute. This pattern is a regexp one, |
  1192. | not a shell expansion. |
  1193. `-----------------------------------------------------------------*/
  1194. static void
  1195. mp4h_bp_directory_contents (MP4H_BUILTIN_ARGS)
  1196. {
  1197. DIR *dir;
  1198. struct dirent *entry;
  1199. int length;
  1200. const char *matching;
  1201. pcre *re = NULL;
  1202. pcre_extra *re_extra = NULL;
  1203. const char *errptr = NULL;
  1204. int *match_ptr = NULL;
  1205. boolean first = TRUE;
  1206. CHECK_SAFETY_LEVEL(1);
  1207. matching = predefined_attribute ("matching", &argc, argv, FALSE);
  1208. if (bad_argc (argv[0], argc, 2, 2))
  1209. return;
  1210. /* First, opens directory. */
  1211. dir = opendir (ARG (1));
  1212. if (dir == NULL)
  1213. return;
  1214. if (matching)
  1215. {
  1216. re = xre_compile (matching, 0);
  1217. if (re == NULL)
  1218. return;
  1219. match_ptr = (int *)
  1220. xrealloc ((voidstar) match_ptr, sizeof (int) * 3);
  1221. re_extra = pcre_study (re, 0, &errptr);
  1222. if (errptr != NULL)
  1223. {
  1224. MP4HERROR ((warning_status, 0,
  1225. _("Error:%s:%d: Bad regular expression `%s': %s"),
  1226. CURRENT_FILE_LINE, matching, errptr));
  1227. return;
  1228. }
  1229. }
  1230. while ((entry = readdir (dir)) != (struct dirent *)NULL)
  1231. {
  1232. length = strlen (entry->d_name);
  1233. if (!matching ||
  1234. (pcre_exec (re, re_extra, entry->d_name, length, 0,
  1235. 0, match_ptr, 3) > 0
  1236. && match_ptr[1] - match_ptr[0] == length))
  1237. {
  1238. if (!first)
  1239. obstack_1grow (obs, '\n');
  1240. obstack_grow (obs, entry->d_name, length);
  1241. first = FALSE;
  1242. }
  1243. }
  1244. if (matching)
  1245. {
  1246. pcre_free (re);
  1247. pcre_free (re_extra);
  1248. xfree ((voidstar) match_ptr);
  1249. }
  1250. closedir (dir);
  1251. }
  1252. /*--------------------------------.
  1253. | Returns "true" if file exists. |
  1254. `--------------------------------*/
  1255. static void
  1256. mp4h_bp_file_exists (MP4H_BUILTIN_ARGS)
  1257. {
  1258. struct stat file;
  1259. CHECK_SAFETY_LEVEL(1);
  1260. if (bad_argc (argv[0], argc, 2, 2))
  1261. return;
  1262. if ((stat (ARG (1), &file)) != -1)
  1263. obstack_grow (obs, "true", 4);
  1264. }
  1265. /*------------------------------------.
  1266. | Returns a real (absolute) path name |
  1267. `------------------------------------*/
  1268. static void
  1269. mp4h_bp_real_path (MP4H_BUILTIN_ARGS)
  1270. {
  1271. const char *pathname;
  1272. char resolvedname[MAXPATHLEN];
  1273. CHECK_SAFETY_LEVEL(1);
  1274. pathname = predefined_attribute ("pathname", &argc, argv, FALSE);
  1275. if (!pathname)
  1276. MP4HERROR ((warning_status, 0,
  1277. _("Warning:%s:%d: In <%s>, required attribute `%s' is not specified"),
  1278. CURRENT_FILE_LINE, ARG (0), "pathname"));
  1279. else
  1280. if (!realpath(pathname, resolvedname))
  1281. MP4HERROR ((warning_status, errno,
  1282. _("Error:%s:%d: Cannot form real path for `%s'"),
  1283. CURRENT_FILE_LINE, pathname));
  1284. else
  1285. obstack_grow(obs, resolvedname, strlen(resolvedname));
  1286. }
  1287. #endif /* HAVE_FILE_FUNCS */
  1288. /*------------------------------------------------------------.
  1289. | Converts an epoch to a readable string. |
  1290. | If no argument is given, current date and time is printed. |
  1291. `------------------------------------------------------------*/
  1292. static void
  1293. mp4h_bp_date (MP4H_BUILTIN_ARGS)
  1294. {
  1295. time_t epoch_time = (time_t)0;
  1296. char *ascii_time;
  1297. const char *format, *timespec;
  1298. struct tm *timeptr;
  1299. char *endp = NULL;
  1300. format = predefined_attribute ("format", &argc, argv, FALSE);
  1301. timespec = predefined_attribute ("time", &argc, argv, FALSE);
  1302. if (!format && !timespec)
  1303. /* backwards compatible... */
  1304. if (argc > 1)
  1305. timespec = ARG (1);
  1306. if (timespec)
  1307. {
  1308. epoch_time = strtol (timespec, &endp, 10);
  1309. if (!endp)
  1310. {
  1311. MP4HERROR ((warning_status, 0,
  1312. _("Warning:%s:%d: Invalid value in date: %s"),
  1313. CURRENT_FILE_LINE, ARG (1)));
  1314. return;
  1315. }
  1316. }
  1317. else
  1318. epoch_time = time ((time_t *)NULL);
  1319. timeptr = (struct tm *) localtime (&epoch_time);
  1320. if (format)
  1321. {
  1322. ascii_time = (char *)xmalloc(1000);
  1323. strftime(ascii_time, 1000, format, timeptr);
  1324. obstack_grow (obs, ascii_time, strlen (ascii_time));
  1325. xfree((voidstar) ascii_time);
  1326. }
  1327. else
  1328. {
  1329. ascii_time = (char *) asctime (timeptr);
  1330. /* Remove newline. */
  1331. LAST_CHAR (ascii_time) = '\0';
  1332. obstack_grow (obs, ascii_time, strlen (ascii_time));
  1333. }
  1334. }
  1335. /* Flow functions: these functions allow complex structures
  1336. There are 2 different kinds of conditions : numerical and
  1337. string tests. A numerical test is false if result is null
  1338. and right if non null. String-based tests are false if
  1339. followed by a null string, and right otherwise. */
  1340. /*----------------------------------------------------------.
  1341. | Group multiple tags into one. This is useful when |
  1342. | combined with tests like <if>, <ifeq>, .... |
  1343. `----------------------------------------------------------*/
  1344. static void
  1345. mp4h_bp_group (MP4H_BUILTIN_ARGS)
  1346. {
  1347. const char *separator;
  1348. separator = predefined_attribute ("separator", &argc, argv, FALSE);
  1349. obstack_1grow (obs, CHAR_BGROUP);
  1350. /* separator = NULL is handled by dump_args */
  1351. dump_args (obs, argc, argv, separator);
  1352. obstack_1grow (obs, CHAR_EGROUP);
  1353. }
  1354. /*------------------------------------------------.
  1355. | Like <group>, but this one is a container tag. |
  1356. `------------------------------------------------*/
  1357. static void
  1358. mp4h_bp_compound (MP4H_BUILTIN_ARGS)
  1359. {
  1360. const char *separator;
  1361. separator = predefined_attribute ("separator", &argc, argv, FALSE);
  1362. obstack_1grow (obs, CHAR_BGROUP);
  1363. /* separator = NULL is handled by dump_args */
  1364. dump_args (obs, argc, argv, separator);
  1365. obstack_grow (obs, ARGBODY, strlen (ARGBODY));
  1366. obstack_1grow (obs, CHAR_EGROUP);
  1367. }
  1368. /*------------------------------------------------.
  1369. | Allow breaking an object into several pieces. |
  1370. `------------------------------------------------*/
  1371. static void
  1372. mp4h_bp_disjoin (MP4H_BUILTIN_ARGS)
  1373. {
  1374. obstack_1grow (obs, CHAR_EGROUP);
  1375. obstack_grow (obs, ARG(1), strlen (ARG(1)));
  1376. obstack_1grow (obs, CHAR_BGROUP);
  1377. }
  1378. /*-------------------------------------------.
  1379. | Prevent further expansion of attributes. |
  1380. `-------------------------------------------*/
  1381. static void
  1382. mp4h_bp_noexpand (MP4H_BUILTIN_ARGS)
  1383. {
  1384. obstack_1grow (obs, CHAR_LQUOTE);
  1385. dump_args (obs, argc, argv, NULL);
  1386. obstack_1grow (obs, CHAR_RQUOTE);
  1387. }
  1388. /*----------------------------------------------------------.
  1389. | Remove special characters which forbid expansion. |
  1390. `----------------------------------------------------------*/
  1391. static void
  1392. mp4h_bp_expand (MP4H_BUILTIN_ARGS)
  1393. {
  1394. int i, offset;
  1395. register char *cp;
  1396. for (i=1; i<argc; i++)
  1397. {
  1398. offset = 0;
  1399. for (cp=ARG (i); *cp != '\0'; cp++)
  1400. {
  1401. if (*cp == CHAR_LQUOTE || *cp == CHAR_RQUOTE)
  1402. offset++;
  1403. else
  1404. *(cp-offset) = *cp;
  1405. }
  1406. *(cp-offset) = '\0';
  1407. }
  1408. dump_args (obs, argc, argv, NULL);
  1409. }
  1410. /*--------------------------------------------------------------.
  1411. | If followed by a non-empty string, second argument is |
  1412. | expanded, otherwise 3rd argument is read. |
  1413. `--------------------------------------------------------------*/
  1414. static void
  1415. mp4h_bp_if (MP4H_BUILTIN_ARGS)
  1416. {
  1417. symbol *var;
  1418. if (bad_argc (argv[0], argc, 0, 4))
  1419. return;
  1420. if (argc == 1)
  1421. return;
  1422. /* The ``natural'' way to implement <if/> is with
  1423. obstack_grow (obs, "<when ", 6);
  1424. obstack_grow (obs, ARG (1), strlen (ARG (1)));
  1425. obstack_grow (obs, " >", 2);
  1426. obstack_grow (obs, ARG (2), strlen (ARG (2)));
  1427. obstack_grow (obs, "</when>", 7);
  1428. and a similar else-clause.
  1429. But this is broken, because ARG(2) may change the condition, and
  1430. then else-clause could be run too.
  1431. For this reason, the following implementation is preferred.
  1432. */
  1433. var = lookup_variable ("__cond_cnt", SYMBOL_LOOKUP);
  1434. if (var == NULL)
  1435. {
  1436. var = lookup_variable ("__cond_cnt", SYMBOL_INSERT);
  1437. SYMBOL_TEXT (var) = (char *) xstrdup ("0");
  1438. SYMBOL_TYPE (var) = TOKEN_TEXT;
  1439. }
  1440. obstack_grow (obs, "<increment __cond_cnt />", 24);
  1441. obstack_grow (obs, "<set-var __cond_var<get-var __cond_cnt />=", 42);
  1442. obstack_1grow (obs, CHAR_BGROUP);
  1443. obstack_grow (obs, ARG (1), strlen (ARG (1)));
  1444. obstack_1grow (obs, CHAR_EGROUP);
  1445. obstack_grow (obs, " />", 3);
  1446. obstack_grow (obs, "<when <get-var __cond_var<get-var __cond_cnt /> /> >", 52);
  1447. obstack_grow (obs, ARG (2), strlen (ARG (2)));
  1448. obstack_grow (obs, "</when>", 7);
  1449. if (argc>3)
  1450. {
  1451. obstack_grow (obs, "<when <not <get-var __cond_var<get-var __cond_cnt /> /> /> >", 60);
  1452. obstack_grow (obs, ARG (3), strlen (ARG (3)));
  1453. obstack_grow (obs, "</when>", 7);
  1454. }
  1455. obstack_grow (obs, "<decrement __cond_cnt />", 24);
  1456. }
  1457. /*------------------------------------------------------.
  1458. | String comparisons. |
  1459. | If 2nd and 3rd arguments are equal, 4th argument is |
  1460. | expanded, otherwise 5th argument is expanded. |
  1461. `------------------------------------------------------*/
  1462. static void
  1463. mp4h_bp_ifeq (MP4H_BUILTIN_ARGS)
  1464. {
  1465. symbol *var;
  1466. if (bad_argc (argv[0], argc, 0, 5))
  1467. return;
  1468. var = lookup_variable ("__cond_cnt", SYMBOL_LOOKUP);
  1469. if (var == NULL)
  1470. {
  1471. var = lookup_variable ("__cond_cnt", SYMBOL_INSERT);
  1472. SYMBOL_TEXT (var) = (char *) xstrdup ("0");
  1473. SYMBOL_TYPE (var) = TOKEN_TEXT;
  1474. }
  1475. obstack_grow (obs, "<increment __cond_cnt />", 24);
  1476. obstack_grow (obs, "<set-var __cond_var<get-var __cond_cnt />=<string-eq ", 53);
  1477. obstack_1grow (obs, CHAR_BGROUP);
  1478. obstack_grow (obs, ARG (1), strlen (ARG (1)));
  1479. obstack_1grow (obs, CHAR_EGROUP);
  1480. obstack_1grow (obs, ' ');
  1481. obstack_1grow (obs, CHAR_BGROUP);
  1482. obstack_grow (obs, ARG (2), strlen (ARG (2)));
  1483. obstack_1grow (obs, CHAR_EGROUP);
  1484. obstack_grow (obs, " /> />", 6);
  1485. obstack_grow (obs, "<when <get-var __cond_var<get-var __cond_cnt /> /> >", 52);
  1486. obstack_grow (obs, ARG (3), strlen (ARG (3)));
  1487. obstack_grow (obs, "</when>", 7);
  1488. if (argc>4)
  1489. {
  1490. obstack_grow (obs, "<when <not <get-var __cond_var<get-var __cond_cnt /> /> /> >", 60);
  1491. obstack_grow (obs, ARG (4), strlen (ARG (4)));
  1492. obstack_grow (obs, "</when>", 7);
  1493. }
  1494. obstack_grow (obs, "<decrement __cond_cnt />", 24);
  1495. }
  1496. /*----------------------------------------------------------.
  1497. | String comparisons. |
  1498. | If 2nd and 3rd arguments are not equal, 4th argument is |
  1499. | expanded, otherwise 5th argument is expanded. |
  1500. `----------------------------------------------------------*/
  1501. static void
  1502. mp4h_bp_ifneq (MP4H_BUILTIN_ARGS)
  1503. {
  1504. symbol *var;
  1505. if (bad_argc (argv[0], argc, 0, 5))
  1506. return;
  1507. var = lookup_variable ("__cond_cnt", SYMBOL_LOOKUP);
  1508. if (var == NULL)
  1509. {
  1510. var = lookup_variable ("__cond_cnt", SYMBOL_INSERT);
  1511. SYMBOL_TEXT (var) = (char *) xstrdup ("0");
  1512. SYMBOL_TYPE (var) = TOKEN_TEXT;
  1513. }
  1514. obstack_grow (obs, "<increment __cond_cnt />", 24);
  1515. obstack_grow (obs, "<set-var __cond_var<get-var __cond_cnt />=<string-neq ", 54);
  1516. obstack_1grow (obs, CHAR_BGROUP);
  1517. obstack_grow (obs, ARG (1), strlen (ARG (1)));
  1518. obstack_1grow (obs, CHAR_EGROUP);
  1519. obstack_1grow (obs, ' ');
  1520. obstack_1grow (obs, CHAR_BGROUP);
  1521. obstack_grow (obs, ARG (2), strlen (ARG (2)));
  1522. obstack_1grow (obs, CHAR_EGROUP);
  1523. obstack_grow (obs, " /> />", 6);
  1524. obstack_grow (obs, "<when <get-var __cond_var<get-var __cond_cnt /> /> >", 52);
  1525. obstack_grow (obs, ARG (3), strlen (ARG (3)));
  1526. obstack_grow (obs, "</when>", 7);
  1527. if (argc>4)
  1528. {
  1529. obstack_grow (obs, "<when <not <get-var __cond_var<get-var __cond_cnt /> /> /> >", 60);
  1530. obstack_grow (obs, ARG (4), strlen (ARG (4)));
  1531. obstack_grow (obs, "</when>", 7);
  1532. }
  1533. obstack_grow (obs, "<decrement __cond_cnt />", 24);
  1534. }
  1535. /*----------------------------------------------------------------.
  1536. | This one is a complex tag ; its body is evalled only if first |
  1537. | argument is empty. |
  1538. `----------------------------------------------------------------*/
  1539. static void
  1540. mp4h_bp_when (MP4H_BUILTIN_ARGS)
  1541. {
  1542. /*
  1543. This test succeeds if one of these 2 clauses is true
  1544. a) there are at least 2 arguments
  1545. b) first argument is non empty.
  1546. */
  1547. if (argc > 2 || strlen (ARG (1)) > 0)
  1548. obstack_grow (obs, ARGBODY, strlen (ARGBODY));
  1549. }
  1550. /*--------------------------------.
  1551. | Loops while condition is true. |
  1552. `--------------------------------*/
  1553. static void
  1554. mp4h_bp_while (MP4H_BUILTIN_ARGS)
  1555. {
  1556. if (bad_argc (argv[0], argc, 0, 2))
  1557. return;
  1558. if (argc == 1)
  1559. return;
  1560. /* ``<while test>expr</while>'' is replaced by
  1561. ``<when test>expr<while test>expr</while></when>''. */
  1562. if (strlen (ARG (1)) > 0)
  1563. {
  1564. /* The ``varbreak '' variable is global and modified by <break>. */
  1565. if (strcmp (SYMBOL_TEXT (&varbreak), "true") == 0)
  1566. {
  1567. xfree ((voidstar) SYMBOL_TEXT (&varbreak));
  1568. SYMBOL_TEXT (&varbreak) = xstrdup ("");
  1569. return;
  1570. }
  1571. obstack_grow (obs, "<when ", 6);
  1572. dump_args (obs, argc, argv, " ");
  1573. obstack_grow (obs, " >", 2);
  1574. obstack_grow (obs, ARGBODY, strlen (ARGBODY));
  1575. obstack_grow (obs, "<while ", 7);
  1576. dump_args (obs, argc, argv, " ");
  1577. obstack_grow (obs, " >", 2);
  1578. obstack_grow (obs, ARGBODY, strlen (ARGBODY));
  1579. obstack_grow (obs, "</while>", 8);
  1580. obstack_grow (obs, "</when>", 7);
  1581. }
  1582. }
  1583. /*---------------------------------------------------------------------.
  1584. | This is a container tag. First argument is the name of a variable, |
  1585. | second is the name of an array. Body function is evaluated for each |
  1586. | element of this array, this element being put into the variable. |
  1587. `---------------------------------------------------------------------*/
  1588. static void
  1589. mp4h_bp_foreach (MP4H_BUILTIN_ARGS)
  1590. {
  1591. symbol *var;
  1592. char *value;
  1593. const char *start, *end, *step;
  1594. int ind_start, ind_end, ind_step;
  1595. int length;
  1596. int i;
  1597. start = predefined_attribute ("start", &argc, argv, FALSE);
  1598. end = predefined_attribute ("end", &argc, argv, FALSE);
  1599. step = predefined_attribute ("step", &argc, argv, FALSE);
  1600. if (bad_argc (argv[0], argc, 3, 3))
  1601. return;
  1602. var = lookup_variable (ARG (2), SYMBOL_LOOKUP);
  1603. if (var == NULL)
  1604. return;
  1605. if (start)
  1606. numeric_arg (argv[0], start, TRUE, &ind_start);
  1607. else
  1608. ind_start = 0;
  1609. if (end)
  1610. numeric_arg (argv[0], end, TRUE, &ind_end);
  1611. else
  1612. ind_end = array_size (var);
  1613. if (step)
  1614. numeric_arg (argv[0], step, TRUE, &ind_step);
  1615. else
  1616. ind_step = 1;
  1617. if (ind_step > 0)
  1618. {
  1619. for (i=ind_start; i<ind_end; i+=ind_step)
  1620. {
  1621. value = array_value (var, i, &length);
  1622. obstack_grow (obs, "<set-var ", 9);
  1623. obstack_grow (obs, ARG (1), strlen (ARG (1)));
  1624. obstack_1grow (obs, '=');
  1625. obstack_1grow (obs, CHAR_LQUOTE);
  1626. obstack_grow (obs, value, length);
  1627. obstack_1grow (obs, CHAR_RQUOTE);
  1628. obstack_grow (obs, "/>", 2);
  1629. obstack_grow (obs, ARGBODY, strlen (ARGBODY));
  1630. }
  1631. }
  1632. else if (ind_step < 0)
  1633. {
  1634. for (i=ind_end-1; i>=ind_start; i+=ind_step)
  1635. {
  1636. value = array_value (var, i, &length);
  1637. obstack_grow (obs, "<set-var ", 9);
  1638. obstack_grow (obs, ARG (1), strlen (ARG (1)));
  1639. obstack_1grow (obs, '=');
  1640. obstack_1grow (obs, CHAR_LQUOTE);
  1641. obstack_grow (obs, value, length);
  1642. obstack_1grow (obs, CHAR_RQUOTE);
  1643. obstack_grow (obs, "/>", 2);
  1644. obstack_grow (obs, ARGBODY, strlen (ARGBODY));
  1645. }
  1646. }
  1647. else
  1648. {
  1649. MP4HERROR ((warning_status, 0,
  1650. _("Warning:%s:%d: Null step is ignored in <%s> loop"),
  1651. CURRENT_FILE_LINE, ARG (0)));
  1652. return;
  1653. }
  1654. }
  1655. /*---------------------------------------------------------------.
  1656. | This function implements a ``case'' statement. Its syntax is |
  1657. | <var-case |
  1658. | var=value expression |
  1659. | ............... |
  1660. | var=value expression> |
  1661. `---------------------------------------------------------------*/
  1662. static void
  1663. mp4h_bp_var_case (MP4H_BUILTIN_ARGS)
  1664. {
  1665. char *cp;
  1666. int i;
  1667. for (i=1; i<argc-1; i+=2)
  1668. {
  1669. cp = strchr (ARG (i), '=');
  1670. if (cp)
  1671. {
  1672. *cp = '\0';
  1673. cp++;
  1674. obstack_grow (obs, "<when <string-eq <get-var ", 26);
  1675. obstack_grow (obs, ARG (i), strlen (ARG (i)));
  1676. obstack_grow (obs, "/> ", 3);
  1677. obstack_1grow (obs, '"');
  1678. obstack_grow (obs, cp, strlen (cp));
  1679. obstack_1grow (obs, '"');
  1680. obstack_grow (obs, "/>>", 3);
  1681. obstack_grow (obs, ARG (i+1), strlen (ARG (i+1)));
  1682. obstack_grow (obs, "</when>", 7);
  1683. }
  1684. else
  1685. {
  1686. MP4HERROR ((warning_status, 0,
  1687. _("Warning:%s:%d: Syntax error in <var-case>"),
  1688. CURRENT_FILE_LINE));
  1689. }
  1690. }
  1691. }
  1692. /*------------------------------------.
  1693. | Immediately exits from inner loop. |
  1694. `------------------------------------*/
  1695. static void
  1696. mp4h_bp_break (MP4H_BUILTIN_ARGS)
  1697. {
  1698. xfree ((voidstar) SYMBOL_TEXT (&varbreak));
  1699. SYMBOL_TEXT (&varbreak) = xstrdup ("true");
  1700. }
  1701. /*------------------------------------.
  1702. | Immediately exits from inner macro. |
  1703. `------------------------------------*/
  1704. static void
  1705. mp4h_bp_return (MP4H_BUILTIN_ARGS)
  1706. {
  1707. int i, levels=1;
  1708. const char *up;
  1709. struct obstack *st;
  1710. up = predefined_attribute ("up", &argc, argv, FALSE);
  1711. if (up)
  1712. numeric_arg (argv[0], up, TRUE, &levels);
  1713. if (levels < 0)
  1714. levels = expansion_level + 1;
  1715. else if (levels == 0)
  1716. levels = expansion_level;
  1717. else if (levels > expansion_level)
  1718. levels = expansion_level;
  1719. for (i=0; i<levels; i++)
  1720. skip_buffer ();
  1721. if (argc>1)
  1722. {
  1723. st = push_string_init ();
  1724. obstack_grow (st, ARG (1), strlen (ARG (1)));
  1725. push_string_finish (0);
  1726. }
  1727. }
  1728. /*-----------------------------.
  1729. | Writes a message to stderr. |
  1730. `-----------------------------*/
  1731. static void
  1732. mp4h_bp_warning (MP4H_BUILTIN_ARGS)
  1733. {
  1734. MP4HERROR ((warning_status, 0,
  1735. _("Warning:%s:%d: %s"),
  1736. CURRENT_FILE_LINE, ARG (1)));
  1737. }
  1738. /*--------------------.
  1739. | Immediately exits. |
  1740. `--------------------*/
  1741. static void
  1742. mp4h_bp_exit (MP4H_BUILTIN_ARGS)
  1743. {
  1744. const char *status, *message;
  1745. int rc = -1;
  1746. status = predefined_attribute ("status", &argc, argv, FALSE);
  1747. if (status)
  1748. numeric_arg (argv[0], status, FALSE, &rc);
  1749. message = predefined_attribute ("message", &argc, argv, FALSE);
  1750. if (message)
  1751. MP4HERROR ((warning_status, 0, "%s", message));
  1752. exit (rc);
  1753. }
  1754. /*-------------------------------------------------------------------------.
  1755. | Save the argument text until EOF has been seen, allowing for user |
  1756. | specified cleanup action. GNU version saves all arguments, the standard |
  1757. | version only the first. |
  1758. `-------------------------------------------------------------------------*/
  1759. static void
  1760. mp4h_bp_at_end_of_file (MP4H_BUILTIN_ARGS)
  1761. {
  1762. dump_args (obs, 2, argv, " ");
  1763. obstack_1grow (obs, '\0');
  1764. push_wrapup (obstack_finish (obs));
  1765. }
  1766. /* Macro functions: defining, undefining, examining or changing
  1767. user defined macros. */
  1768. /*-------------------------------------.
  1769. | Define new tags, simple or complex. |
  1770. `-------------------------------------*/
  1771. static void
  1772. mp4h_bp_define_tag (MP4H_BUILTIN_ARGS)
  1773. {
  1774. const builtin *bp;
  1775. const char *attributes, *endtag, *whitespace;
  1776. symbol *sym;
  1777. boolean expand_args = TRUE;
  1778. boolean container = FALSE;
  1779. boolean space_delete = FALSE;
  1780. attributes = predefined_attribute ("attributes", &argc, argv, TRUE);
  1781. if (attributes && strcmp (attributes, "verbatim") == 0)
  1782. expand_args = FALSE;
  1783. endtag = predefined_attribute ("endtag", &argc, argv, TRUE);
  1784. if (endtag && strcmp (endtag, "required") == 0)
  1785. container = TRUE;
  1786. whitespace = predefined_attribute ("whitespace", &argc, argv, TRUE);
  1787. if (whitespace && strcmp (whitespace, "delete") == 0)
  1788. space_delete = TRUE;
  1789. if (bad_argc (argv[0], argc, 2, 3))
  1790. return;
  1791. if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
  1792. return;
  1793. if (argc == 1)
  1794. {
  1795. define_user_macro (ARG (1), "", SYMBOL_INSERT, container,
  1796. expand_args, space_delete);
  1797. return;
  1798. }
  1799. switch (TOKEN_DATA_TYPE (argv[argc]))
  1800. {
  1801. case TOKEN_TEXT:
  1802. define_user_macro (ARG (1), ARGBODY, SYMBOL_INSERT, container,
  1803. expand_args, space_delete);
  1804. break;
  1805. case TOKEN_FUNC:
  1806. bp = find_builtin_by_addr (TOKEN_DATA_FUNC (argv[argc]));
  1807. if (bp == NULL)
  1808. return;
  1809. else
  1810. define_builtin (ARG (1), bp, TOKEN_DATA_FUNC_TRACED (argv[argc]));
  1811. break;
  1812. default:
  1813. MP4HERROR ((warning_status, 0,
  1814. "INTERNAL ERROR: Bad token data type in mp4h_bp_define_tag ()"));
  1815. exit (1);
  1816. }
  1817. /* Clear hooks */
  1818. sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
  1819. if (!sym)
  1820. return;
  1821. xfree ((voidstar) SYMBOL_HOOK_BEGIN (sym));
  1822. xfree ((voidstar) SYMBOL_HOOK_END (sym));
  1823. }
  1824. /*-------------------------------------.
  1825. | Define tags if not already defined |
  1826. `-------------------------------------*/
  1827. static void
  1828. mp4h_bp_provide_tag (MP4H_BUILTIN_ARGS)
  1829. {
  1830. symbol *sym;
  1831. sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
  1832. if (sym == NULL)
  1833. mp4h_bp_define_tag (MP4H_BUILTIN_RECUR);
  1834. }
  1835. /*-------------------.
  1836. | Define a synonym. |
  1837. `-------------------*/
  1838. static void
  1839. mp4h_bp_let (MP4H_BUILTIN_ARGS)
  1840. {
  1841. const builtin *bp;
  1842. symbol *s;
  1843. char *cp;
  1844. int i;
  1845. for (i = 1; i < argc; i++)
  1846. {
  1847. cp = strchr (ARG (i), '=');
  1848. if (cp)
  1849. {
  1850. *cp = '\0';
  1851. cp++;
  1852. s = lookup_symbol (cp, SYMBOL_LOOKUP);
  1853. if (s == NULL)
  1854. {
  1855. MP4HERROR ((warning_status, 0,
  1856. _("Warning:%s:%d: Macro `%s' not defined in <%s>"),
  1857. CURRENT_FILE_LINE, cp, ARG (0)));
  1858. continue;
  1859. }
  1860. switch (SYMBOL_TYPE (s))
  1861. {
  1862. case TOKEN_FUNC:
  1863. bp = find_builtin_by_addr (SYMBOL_FUNC (s));
  1864. if (bp)
  1865. define_builtin (ARG (i), bp, SYMBOL_TRACED (s));
  1866. break;
  1867. case TOKEN_TEXT:
  1868. define_user_macro (ARG (i), SYMBOL_TEXT (s), SYMBOL_INSERT,
  1869. SYMBOL_CONTAINER (s), SYMBOL_EXPAND_ARGS (s), FALSE);
  1870. break;
  1871. default:
  1872. MP4HERROR ((warning_status, 0,
  1873. "INTERNAL ERROR: Bad token data type in mp4h_bp_let ()"));
  1874. exit (1);
  1875. }
  1876. }
  1877. else
  1878. {
  1879. MP4HERROR ((warning_status, 0,
  1880. _("Warning:%s:%d: Unknown syntax `%s' in <%s>"),
  1881. CURRENT_FILE_LINE, ARG (i), ARG (0)));
  1882. }
  1883. }
  1884. }
  1885. /*-------------------.
  1886. | Undefine symbols. |
  1887. `-------------------*/
  1888. static void
  1889. mp4h_bp_undef (MP4H_BUILTIN_ARGS)
  1890. {
  1891. register int i;
  1892. if (bad_argc (argv[0], argc, 2, 0))
  1893. return;
  1894. for (i = 1; i < argc; i++)
  1895. {
  1896. lookup_symbol (ARG (i), SYMBOL_DELETE);
  1897. }
  1898. }
  1899. static void
  1900. generic_set_hook (MP4H_BUILTIN_ARGS, boolean before, int action)
  1901. {
  1902. symbol *sym;
  1903. char *text;
  1904. char *hook;
  1905. sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
  1906. if (sym == NULL)
  1907. return;
  1908. if (before)
  1909. hook = SYMBOL_HOOK_BEGIN (sym);
  1910. else
  1911. hook = SYMBOL_HOOK_END (sym);
  1912. if (!hook)
  1913. action = 0;
  1914. switch (action)
  1915. {
  1916. /* Replace current hooks */
  1917. case 0:
  1918. text = xstrdup (ARGBODY);
  1919. break;
  1920. /* Insert before the current hooks */
  1921. case 1:
  1922. text = (char *) xmalloc (strlen (hook) + strlen (ARGBODY) + 1);
  1923. strcpy (text, ARGBODY);
  1924. strcat (text, hook);
  1925. break;
  1926. /* Append after the current hooks */
  1927. case 2:
  1928. text = (char *) xmalloc (strlen (hook) + strlen (ARGBODY) + 1);
  1929. strcpy (text, hook);
  1930. strcat (text, ARGBODY);
  1931. break;
  1932. default:
  1933. MP4HERROR ((warning_status, 0,
  1934. "INTERNAL ERROR: Illegal value in generic_set_hook ()"));
  1935. exit (1);
  1936. }
  1937. xfree ((voidstar) hook);
  1938. if (before)
  1939. SYMBOL_HOOK_BEGIN (sym) = text;
  1940. else
  1941. SYMBOL_HOOK_END (sym) = text;
  1942. }
  1943. static void
  1944. mp4h_bp_set_hook (MP4H_BUILTIN_ARGS)
  1945. {
  1946. const char *action, *position;
  1947. symbol *sym;
  1948. boolean before = TRUE;
  1949. int iaction = 0;
  1950. action = predefined_attribute ("action", &argc, argv, TRUE);
  1951. position = predefined_attribute ("position", &argc, argv, TRUE);
  1952. if (bad_argc (argv[0], argc, 2, 2))
  1953. return;
  1954. sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
  1955. if (sym == NULL)
  1956. return;
  1957. before = !(position && strcmp (position, "after") == 0);
  1958. if (action)
  1959. {
  1960. if (strcmp (action, "insert") == 0)
  1961. iaction = 1;
  1962. else if (strcmp (action, "append") == 0)
  1963. iaction = 2;
  1964. else
  1965. iaction = 0;
  1966. }
  1967. else
  1968. iaction = 0;
  1969. generic_set_hook (MP4H_BUILTIN_RECUR, before, iaction);
  1970. }
  1971. /*-----------------.
  1972. | Retrieve hooks. |
  1973. `-----------------*/
  1974. static void
  1975. mp4h_bp_get_hook (MP4H_BUILTIN_ARGS)
  1976. {
  1977. symbol *sym;
  1978. const char *position;
  1979. char *hook;
  1980. position = predefined_attribute ("position", &argc, argv, TRUE);
  1981. if (bad_argc (argv[0], argc, 2, 2))
  1982. return;
  1983. sym = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
  1984. if (sym == NULL)
  1985. return;
  1986. if (position && strcmp (position, "after") == 0)
  1987. hook = SYMBOL_HOOK_END (sym);
  1988. else
  1989. hook = SYMBOL_HOOK_BEGIN (sym);
  1990. if (hook)
  1991. obstack_grow (obs, hook, strlen (hook));
  1992. }
  1993. /*----------------------------------------------------------.
  1994. | These functions allow any manipulations of attributes. |
  1995. | There is no need to define similar routines for |
  1996. | %Aattributes and %Uattributes, because they can be |
  1997. | emulated via mp4h builtins. |
  1998. `----------------------------------------------------------*/
  1999. static void
  2000. mp4h_bp_attributes_quote (MP4H_BUILTIN_ARGS)
  2001. {
  2002. int i;
  2003. if (argc < 2)
  2004. return;
  2005. for (i = 1; i < argc; i++)
  2006. {
  2007. if (*(ARG (i)) != '\0')
  2008. {
  2009. obstack_1grow (obs, ' ');
  2010. quote_name_value (obs, ARG (i));
  2011. }
  2012. }
  2013. }
  2014. static void
  2015. mp4h_bp_attributes_remove (MP4H_BUILTIN_ARGS)
  2016. {
  2017. if (bad_argc (argv[0], argc, 2, 0))
  2018. return;
  2019. /* Dirty hack to prevent aggregation of attributes into
  2020. a single argument. When a group is begun in expand_macro (),
  2021. we finish it here. */
  2022. if (expansion_level > 1 && (
  2023. expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
  2024. || expansion == READ_ATTR_QUOT))
  2025. obstack_1grow (obs, CHAR_EGROUP);
  2026. matching_attributes (obs, argc-1, argv+1, FALSE, ARG (1));
  2027. if (expansion_level > 1 && (
  2028. expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
  2029. || expansion == READ_ATTR_QUOT))
  2030. obstack_1grow (obs, CHAR_BGROUP);
  2031. }
  2032. static void
  2033. mp4h_bp_attributes_extract (MP4H_BUILTIN_ARGS)
  2034. {
  2035. if (bad_argc (argv[0], argc, 2, 0))
  2036. return;
  2037. /* Dirty hack to prevent aggregation of attributes into
  2038. a single argument. When a group is begun in expand_macro (),
  2039. we finish it here. */
  2040. if (expansion_level > 1 && (
  2041. expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
  2042. || expansion == READ_ATTR_QUOT))
  2043. obstack_1grow (obs, CHAR_EGROUP);
  2044. matching_attributes (obs, argc-1, argv+1, TRUE, ARG (1));
  2045. if (expansion_level > 1 && (
  2046. expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
  2047. || expansion == READ_ATTR_QUOT))
  2048. obstack_1grow (obs, CHAR_BGROUP);
  2049. }
  2050. /*----------------------.
  2051. | Define new entities. |
  2052. `----------------------*/
  2053. static void
  2054. mp4h_bp_define_entity (MP4H_BUILTIN_ARGS)
  2055. {
  2056. symbol *sym;
  2057. if (bad_argc (argv[0], argc, 2, 2))
  2058. return;
  2059. if (TOKEN_DATA_TYPE (argv[1]) != TOKEN_TEXT)
  2060. return;
  2061. if (argc == 1)
  2062. define_user_entity (ARG (1), "", SYMBOL_INSERT);
  2063. else
  2064. define_user_entity (ARG (1), ARGBODY, SYMBOL_INSERT);
  2065. /* Clear hooks */
  2066. sym = lookup_entity (ARG (1), SYMBOL_LOOKUP);
  2067. if (!sym)
  2068. return;
  2069. xfree ((voidstar) SYMBOL_HOOK_BEGIN (sym));
  2070. xfree ((voidstar) SYMBOL_HOOK_END (sym));
  2071. }
  2072. /* Math functions */
  2073. /*--------------------------------------------------.
  2074. | This function is called by relational operators. |
  2075. | These operators are binary operators, operands |
  2076. | are either numbers or variable names. |
  2077. `--------------------------------------------------*/
  2078. static void
  2079. math_relation (MP4H_BUILTIN_ARGS, mathrel_type mathrel)
  2080. {
  2081. boolean result;
  2082. symbol *var;
  2083. double val1, val2;
  2084. if (bad_argc (argv[0], argc, 2, 3))
  2085. return;
  2086. if (isdigit ((int) ARG (1)[0]) || *(ARG (1)) == '-' || *(ARG (1)) == '+' ||
  2087. strncmp (ARG (1), decimal_point, strlen (decimal_point)))
  2088. {
  2089. if (!safe_strtod (ARG (0), ARG (1), &val1))
  2090. return;
  2091. }
  2092. else
  2093. {
  2094. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  2095. if (var == NULL)
  2096. {
  2097. MP4HERROR ((warning_status, 0,
  2098. _("Warning:%s:%d: Argument `%s' non-numeric in <%s>"),
  2099. CURRENT_FILE_LINE, ARG (1), ARG (0)));
  2100. return;
  2101. }
  2102. if (!safe_strtod (ARG (0), SYMBOL_TEXT (var), &val1))
  2103. return;
  2104. }
  2105. if (argc == 2)
  2106. val2 = 0.;
  2107. else if (isdigit ((int) ARG (2)[0]) ||
  2108. *(ARG (2)) == '-' || *(ARG (2)) == '+' ||
  2109. strncmp (ARG (2), decimal_point, strlen (decimal_point)))
  2110. {
  2111. if (!safe_strtod (ARG (0), ARG (2), &val2))
  2112. return;
  2113. }
  2114. else
  2115. {
  2116. var = lookup_variable (ARG (2), SYMBOL_LOOKUP);
  2117. if (var == NULL)
  2118. {
  2119. MP4HERROR ((warning_status, 0,
  2120. _("Warning:%s:%d: Argument `%s' non-numeric in <%s>"),
  2121. CURRENT_FILE_LINE, ARG (2), ARG (0)));
  2122. return;
  2123. }
  2124. if (!safe_strtod (ARG (0), SYMBOL_TEXT (var), &val2))
  2125. return;
  2126. }
  2127. switch (mathrel)
  2128. {
  2129. case MATHREL_EQ:
  2130. result = (val1 == val2 ? TRUE : FALSE);
  2131. break;
  2132. case MATHREL_NEQ:
  2133. result = (val1 != val2 ? TRUE : FALSE);
  2134. break;
  2135. case MATHREL_GT:
  2136. result = (val1 > val2 ? TRUE : FALSE);
  2137. break;
  2138. case MATHREL_LT:
  2139. result = (val1 < val2 ? TRUE : FALSE);
  2140. break;
  2141. default:
  2142. MP4HERROR ((warning_status, 0,
  2143. "INTERNAL ERROR: Illegal operator in math_relation ()"));
  2144. exit (1);
  2145. }
  2146. if (result)
  2147. obstack_grow (obs, "true", 4);
  2148. }
  2149. static void
  2150. mp4h_bp_gt (MP4H_BUILTIN_ARGS)
  2151. {
  2152. math_relation (MP4H_BUILTIN_RECUR, MATHREL_GT);
  2153. }
  2154. static void
  2155. mp4h_bp_lt (MP4H_BUILTIN_ARGS)
  2156. {
  2157. math_relation (MP4H_BUILTIN_RECUR, MATHREL_LT);
  2158. }
  2159. static void
  2160. mp4h_bp_eq (MP4H_BUILTIN_ARGS)
  2161. {
  2162. math_relation (MP4H_BUILTIN_RECUR, MATHREL_EQ);
  2163. }
  2164. static void
  2165. mp4h_bp_neq (MP4H_BUILTIN_ARGS)
  2166. {
  2167. math_relation (MP4H_BUILTIN_RECUR, MATHREL_NEQ);
  2168. }
  2169. /*------------------------------------------------------.
  2170. | This definition is used to simplify the writings of |
  2171. | arithmetic operators, an operation is performed on |
  2172. | every arguments. |
  2173. | If all arguments are integer values, round-offs must |
  2174. | be performed at each step. |
  2175. `------------------------------------------------------*/
  2176. #define MATH_ARG_LOOP(ops) for (i=2; i<argc; i++) \
  2177. {if (safe_strtod (ARG (0), ARG (i), &val)) ops;\
  2178. if (result_int) result = floor (result);}
  2179. static void
  2180. mathop_functions (MP4H_BUILTIN_ARGS, mathop_type mathop)
  2181. {
  2182. double val, result;
  2183. int i;
  2184. char svalue[128];
  2185. char *pos_radix;
  2186. char sformat[32];
  2187. boolean result_int = TRUE;
  2188. if (bad_argc (argv[0], argc, 3, 0))
  2189. return;
  2190. /* If all operands are integers, an integer must be returned. */
  2191. for (i=1; i<argc; i++)
  2192. if (strstr (ARG (i), decimal_point) != NULL)
  2193. result_int = FALSE;
  2194. if (mathop == MATHOP_MOD)
  2195. {
  2196. /* Modulus is a special case since operands must be integers,
  2197. and having more than 2 operands is illegal. */
  2198. if (bad_argc (argv[0], argc, 3, 3))
  2199. return;
  2200. if (!result_int)
  2201. {
  2202. MP4HERROR ((warning_status, 0,
  2203. _("Warning:%s:%d: Mathop `mod' has non-integer operands"),
  2204. CURRENT_FILE_LINE));
  2205. }
  2206. else
  2207. {
  2208. int val1, val2;
  2209. val1 = strtol (ARG (1), (char **)NULL, 10);
  2210. val2 = strtol (ARG (2), (char **)NULL, 10);
  2211. val1 %= val2;
  2212. shipout_int (obs, val1);
  2213. }
  2214. return;
  2215. }
  2216. /* Initialization. */
  2217. if (!safe_strtod (ARG (0), ARG (1), &result))
  2218. result = 0.;
  2219. /* Loop on operands. */
  2220. switch (mathop)
  2221. {
  2222. case MATHOP_ADD:
  2223. MATH_ARG_LOOP(result += val)
  2224. break;
  2225. case MATHOP_SUB:
  2226. MATH_ARG_LOOP(result -= val)
  2227. break;
  2228. case MATHOP_MUL:
  2229. MATH_ARG_LOOP(result *= val)
  2230. break;
  2231. case MATHOP_DIV:
  2232. MATH_ARG_LOOP(result /= val)
  2233. break;
  2234. case MATHOP_MIN:
  2235. MATH_ARG_LOOP(if (val<result) result=val)
  2236. break;
  2237. case MATHOP_MAX:
  2238. MATH_ARG_LOOP(if (val>result) result=val)
  2239. break;
  2240. default:
  2241. MP4HERROR ((warning_status, 0,
  2242. "INTERNAL ERROR: Illegal mathop in mp4h_bp_mathop ()"));
  2243. }
  2244. sprintf (sformat, "%%.%df", output_radix);
  2245. sprintf (svalue, sformat, result);
  2246. if (result_int)
  2247. {
  2248. pos_radix = strstr (svalue, decimal_point);
  2249. if (pos_radix)
  2250. *pos_radix = '\0';
  2251. }
  2252. shipout_text (obs, svalue);
  2253. }
  2254. #undef MATH_ARG_LOOP
  2255. static void
  2256. mp4h_bp_add (MP4H_BUILTIN_ARGS)
  2257. {
  2258. mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_ADD);
  2259. }
  2260. static void
  2261. mp4h_bp_substract (MP4H_BUILTIN_ARGS)
  2262. {
  2263. mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_SUB);
  2264. }
  2265. static void
  2266. mp4h_bp_multiply (MP4H_BUILTIN_ARGS)
  2267. {
  2268. mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_MUL);
  2269. }
  2270. static void
  2271. mp4h_bp_divide (MP4H_BUILTIN_ARGS)
  2272. {
  2273. mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_DIV);
  2274. }
  2275. static void
  2276. mp4h_bp_modulo (MP4H_BUILTIN_ARGS)
  2277. {
  2278. mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_MOD);
  2279. }
  2280. static void
  2281. mp4h_bp_min (MP4H_BUILTIN_ARGS)
  2282. {
  2283. mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_MIN);
  2284. }
  2285. static void
  2286. mp4h_bp_max (MP4H_BUILTIN_ARGS)
  2287. {
  2288. mathop_functions (MP4H_BUILTIN_RECUR, MATHOP_MAX);
  2289. }
  2290. /* Page functions */
  2291. /*------------------------------------------------------.
  2292. | Read and parse a file. This file is searched in the |
  2293. | directories specified by the -I option. |
  2294. | Because the `alt' attribute must be read unexpanded, |
  2295. | this tag calls <__include> whose arguments can be |
  2296. | expanded since `alt' has been passed into this tag |
  2297. | body. |
  2298. `------------------------------------------------------*/
  2299. static void
  2300. mp4h_bp_include (MP4H_BUILTIN_ARGS)
  2301. {
  2302. const char *alt, *verbatim;
  2303. CHECK_SAFETY_LEVEL(1);
  2304. alt = predefined_attribute ("alt", &argc, argv, FALSE);
  2305. verbatim = predefined_attribute ("verbatim", &argc, argv, TRUE);
  2306. obstack_grow (obs, "<__include ", 11);
  2307. dump_args (obs, argc, argv, " ");
  2308. if (verbatim && strcmp (verbatim, "true") == 0)
  2309. obstack_grow (obs, " verbatim=true", 14);
  2310. obstack_grow (obs, " >", 2);
  2311. if (alt)
  2312. obstack_grow (obs, alt, strlen (alt));
  2313. obstack_grow (obs, "</__include>", 12);
  2314. }
  2315. static void
  2316. mp4h_bp___include (MP4H_BUILTIN_ARGS)
  2317. {
  2318. const char *verbatim, *command, *file;
  2319. FILE *fp;
  2320. char *filename = NULL;
  2321. CHECK_SAFETY_LEVEL(1);
  2322. verbatim = predefined_attribute ("verbatim", &argc, argv, TRUE);
  2323. file = predefined_attribute ("file", &argc, argv, FALSE);
  2324. command = predefined_attribute ("command", &argc, argv, FALSE);
  2325. if (file && command)
  2326. {
  2327. command = NULL;
  2328. MP4HERROR ((warning_status, 0, _("\
  2329. Warning:%s:%d: `file' and `command' attributes in <include> tag are mutually exclusive, using `file'"),
  2330. CURRENT_FILE_LINE));
  2331. }
  2332. if (command)
  2333. {
  2334. /* Reads input from command output */
  2335. CHECK_SAFETY_LEVEL(0);
  2336. filename = xstrdup(command);
  2337. remove_special_chars (filename, TRUE);
  2338. fp = popen(filename, "r");
  2339. if (fp == NULL)
  2340. {
  2341. MP4HERROR ((warning_status, errno,
  2342. _("Warning:%s:%d: Cannot execute %s"),
  2343. CURRENT_FILE_LINE, filename));
  2344. return;
  2345. }
  2346. }
  2347. else
  2348. {
  2349. if (file)
  2350. {
  2351. /* New syntax */
  2352. if (bad_argc (argv[0], argc, 1, 1))
  2353. return;
  2354. }
  2355. else
  2356. {
  2357. /* Old syntax */
  2358. if (bad_argc (argv[0], argc, 2, 2))
  2359. return;
  2360. file = ARG(1);
  2361. }
  2362. if (!file)
  2363. {
  2364. MP4HERROR ((warning_status, 0, _("\
  2365. Error:%s:%d: <include> must be invoked with one of the `file' and `command' attributes; skipped"),
  2366. CURRENT_FILE_LINE));
  2367. return;
  2368. }
  2369. fp = path_search (file, &filename);
  2370. if (fp == NULL)
  2371. {
  2372. if (*(ARGBODY) != '\0')
  2373. shipout_string (obs, ARGBODY, 0);
  2374. else
  2375. {
  2376. MP4HERROR ((warning_status, errno,
  2377. _("Warning:%s:%d: Cannot open %s"),
  2378. CURRENT_FILE_LINE, file));
  2379. }
  2380. return;
  2381. }
  2382. }
  2383. push_file (fp, filename);
  2384. if (verbatim && strcmp (verbatim, "true") == 0)
  2385. read_file_verbatim (obs);
  2386. xfree ((voidstar) filename);
  2387. }
  2388. /*-------------------------------------------------.
  2389. | Transform a module or library name foo:bar into |
  2390. | physical path `foo/bar'. |
  2391. `-------------------------------------------------*/
  2392. static void
  2393. logical_to_physical_paths (char **name)
  2394. {
  2395. register char *cp;
  2396. for (cp=*name; *cp != '\0'; cp++)
  2397. {
  2398. if (*cp == ':')
  2399. *cp = '/';
  2400. }
  2401. }
  2402. /*-------------------------------------------------.
  2403. | Read and parse a package if not already loaded. |
  2404. `-------------------------------------------------*/
  2405. static void
  2406. mp4h_bp_use (MP4H_BUILTIN_ARGS)
  2407. {
  2408. FILE *fp;
  2409. const char *name = NULL;
  2410. char *filename = NULL, *real_filename = NULL;
  2411. symbol *sym;
  2412. if (bad_argc (argv[0], argc, 2, 2))
  2413. return;
  2414. name = predefined_attribute ("name", &argc, argv, FALSE);
  2415. if (name == NULL)
  2416. {
  2417. MP4HERROR ((warning_status, 0,
  2418. _("Error:%s:%d: In <%s>, required attribute `%s' is not specified"),
  2419. CURRENT_FILE_LINE, ARG (0), "name"));
  2420. return;
  2421. }
  2422. sym = lookup_file (name, SYMBOL_LOOKUP);
  2423. if (sym)
  2424. {
  2425. if (debug_level & DEBUG_TRACE_MODULES)
  2426. DEBUG_MESSAGE1("module `%s' handle already seen", name);
  2427. return;
  2428. }
  2429. real_filename = xmalloc (strlen (name) + 7);
  2430. sprintf (real_filename, "%s.mp4hp", name);
  2431. logical_to_physical_paths (&real_filename);
  2432. fp = path_search (real_filename, &filename);
  2433. if (fp == NULL)
  2434. {
  2435. MP4HERROR ((warning_status, errno,
  2436. _("Warning:%s:%d: Cannot open %s"),
  2437. CURRENT_FILE_LINE, name));
  2438. xfree ((voidstar) real_filename);
  2439. return;
  2440. }
  2441. else
  2442. if (debug_level & DEBUG_TRACE_MODULES)
  2443. DEBUG_MESSAGE2("module `%s': loaded ok (=%s)", name, filename);
  2444. lookup_file (name, SYMBOL_INSERT);
  2445. push_file (fp, filename);
  2446. xfree ((voidstar) filename);
  2447. }
  2448. /*-------------------------------------.
  2449. | Loading external module at runtime. |
  2450. `-------------------------------------*/
  2451. #ifdef WITH_MODULES
  2452. static void
  2453. mp4h_bp_load (MP4H_BUILTIN_ARGS)
  2454. {
  2455. const char *module, *library;
  2456. char *realname = NULL;
  2457. library = predefined_attribute ("library", &argc, argv, FALSE);
  2458. module = predefined_attribute ("module", &argc, argv, FALSE);
  2459. if (!library && !module)
  2460. {
  2461. MP4HERROR ((warning_status, 0,
  2462. _("Error:%s:%d: In <%s>, at least one of '%s' and '%s' attributes must be specified"),
  2463. CURRENT_FILE_LINE, ARG (0), "module", "library"));
  2464. return;
  2465. }
  2466. if (library)
  2467. {
  2468. realname = xstrdup (library);
  2469. logical_to_physical_paths (&realname);
  2470. library_load (realname, obs);
  2471. xfree ((voidstar) realname);
  2472. }
  2473. if (module)
  2474. {
  2475. realname = xstrdup (module);
  2476. logical_to_physical_paths (&realname);
  2477. module_load (realname, obs);
  2478. xfree ((voidstar) realname);
  2479. }
  2480. }
  2481. #endif
  2482. /*-----------------------------.
  2483. | Discards its body contents. |
  2484. `-----------------------------*/
  2485. static void
  2486. mp4h_bp_comment (MP4H_BUILTIN_ARGS)
  2487. {
  2488. }
  2489. /*----------------------------.
  2490. | Set EOL comment delimiter. |
  2491. `----------------------------*/
  2492. static void
  2493. mp4h_bp_set_eol_comment (MP4H_BUILTIN_ARGS)
  2494. {
  2495. bad_argc (argv[0], argc, 0, 2);
  2496. xfree ((voidstar) eolcomm.string);
  2497. eolcomm.string = xstrdup (ARG (1));
  2498. eolcomm.length = strlen (eolcomm.string);
  2499. }
  2500. /*--------------------------------.
  2501. | Set quotes to disable parsing. |
  2502. `--------------------------------*/
  2503. static void
  2504. mp4h_bp_set_quotes (MP4H_BUILTIN_ARGS)
  2505. {
  2506. const char *display;
  2507. display = predefined_attribute ("display", &argc, argv, TRUE);
  2508. visible_quotes = (display && strcmp (display, "visible") == 0);
  2509. if (argc == 1)
  2510. {
  2511. xfree ((voidstar) lquote.string);
  2512. xfree ((voidstar) rquote.string);
  2513. lquote.string = NULL;
  2514. lquote.length = 0;
  2515. rquote.string = NULL;
  2516. rquote.length = 0;
  2517. }
  2518. else if (argc == 3)
  2519. {
  2520. if (*ARG (1) != '<' || LAST_CHAR (ARG (2)) != '>')
  2521. {
  2522. MP4HERROR ((warning_status, 0,
  2523. _("Warning:%s:%d: <%s> ignored, invalid arguments: %s %s"),
  2524. CURRENT_FILE_LINE, ARG (0), ARG (1), ARG (2)));
  2525. return;
  2526. }
  2527. xfree ((voidstar) lquote.string);
  2528. xfree ((voidstar) rquote.string);
  2529. lquote.string = xstrdup (ARG (1));
  2530. lquote.length = strlen (lquote.string);
  2531. rquote.string = xstrdup (ARG (2));
  2532. rquote.length = strlen (rquote.string);
  2533. }
  2534. else
  2535. MP4HERROR ((warning_status, 0,
  2536. _("Warning:%s:%d: `<%s>' must have 0 or 2 arguments, but got %d"),
  2537. CURRENT_FILE_LINE, ARG (0), argc-1));
  2538. }
  2539. /*------------------------------------------------------------------------.
  2540. | Delete all subsequent whitespace from input. The function skip_line () |
  2541. | lives in input.c. |
  2542. `------------------------------------------------------------------------*/
  2543. static void
  2544. mp4h_bp_dnl (MP4H_BUILTIN_ARGS)
  2545. {
  2546. if (bad_argc (argv[0], argc, 1, 1))
  2547. return;
  2548. skip_line ();
  2549. }
  2550. static void
  2551. mp4h_bp_frozen_dump (MP4H_BUILTIN_ARGS)
  2552. {
  2553. if (frozen_dump)
  2554. input_close ();
  2555. }
  2556. /* Relational operators : arguments are strings. */
  2557. /*----------------------------------------------------------------.
  2558. | If first argument is an empty string, returns "true" otherwise |
  2559. | returns nothing. |
  2560. `----------------------------------------------------------------*/
  2561. static void
  2562. mp4h_bp_not (MP4H_BUILTIN_ARGS)
  2563. {
  2564. int i;
  2565. if (argc < 2)
  2566. {
  2567. obstack_grow (obs, "true", 4);
  2568. return;
  2569. }
  2570. for (i=1; i<argc; i++)
  2571. {
  2572. remove_special_chars (ARG (i), FALSE);
  2573. if (*(ARG (i)) == '\0')
  2574. {
  2575. obstack_grow (obs, "true", 4);
  2576. return;
  2577. }
  2578. }
  2579. }
  2580. /*-----------------------------------------------------------.
  2581. | When at least one argument is empty, nothing is returned. |
  2582. | Otherwise, the last string is returned. |
  2583. `-----------------------------------------------------------*/
  2584. static void
  2585. mp4h_bp_and (MP4H_BUILTIN_ARGS)
  2586. {
  2587. int i;
  2588. char *cp;
  2589. if (argc == 1)
  2590. return;
  2591. for (i=1; i<argc; i++)
  2592. {
  2593. for (cp=ARG (i); IS_GROUP (*cp); cp++ )
  2594. ;
  2595. if (*cp == '\0')
  2596. break;
  2597. }
  2598. if (i == argc)
  2599. obstack_grow (obs, ARG (argc-1), strlen (ARG (argc-1)));
  2600. }
  2601. /*--------------------------------------------.
  2602. | "or" returns the first non-empty argument. |
  2603. `--------------------------------------------*/
  2604. static void
  2605. mp4h_bp_or (MP4H_BUILTIN_ARGS)
  2606. {
  2607. int i;
  2608. char *cp;
  2609. for (i=1; i<argc; i++)
  2610. {
  2611. for (cp=ARG (i); IS_GROUP (*cp); cp++ )
  2612. ;
  2613. if (*cp != '\0')
  2614. {
  2615. obstack_grow (obs, ARG (i), strlen (ARG (i)));
  2616. return;
  2617. }
  2618. }
  2619. }
  2620. /* String functions */
  2621. /*-----------------------------------.
  2622. | Returns the length of the string. |
  2623. `-----------------------------------*/
  2624. static void
  2625. mp4h_bp_string_length (MP4H_BUILTIN_ARGS)
  2626. {
  2627. if (bad_argc (argv[0], argc, 0, 2))
  2628. return;
  2629. if (argc == 1)
  2630. shipout_int (obs, 0);
  2631. else
  2632. shipout_int (obs, encoding_strlen (ARG (1)));
  2633. }
  2634. /*---------------------------------------------------------------.
  2635. | This routine converts its argument to uppercase or lowercase |
  2636. | letters, depending on the last argument. |
  2637. `---------------------------------------------------------------*/
  2638. static void
  2639. updowncase (struct obstack *obs, int argc, token_data **argv, boolean upcase)
  2640. {
  2641. char *text;
  2642. char *cp;
  2643. int i;
  2644. if (argc == 1)
  2645. return;
  2646. for (i=1; i<argc; i++)
  2647. {
  2648. if (i > 1)
  2649. obstack_1grow (obs, ' ');
  2650. text = xstrdup (ARG (i));
  2651. if (upcase)
  2652. {
  2653. for (cp = text; *cp != '\0'; cp++)
  2654. *cp = toupper (*cp);
  2655. }
  2656. else
  2657. {
  2658. for (cp = text; *cp != '\0'; cp++)
  2659. *cp = tolower (*cp);
  2660. }
  2661. obstack_grow (obs, text, strlen (text));
  2662. xfree ((voidstar) text);
  2663. }
  2664. }
  2665. static void
  2666. mp4h_bp_downcase (MP4H_BUILTIN_ARGS)
  2667. {
  2668. updowncase (obs, argc, argv, FALSE);
  2669. }
  2670. static void
  2671. mp4h_bp_upcase (MP4H_BUILTIN_ARGS)
  2672. {
  2673. updowncase (obs, argc, argv, TRUE);
  2674. }
  2675. static void
  2676. mp4h_bp_capitalize (MP4H_BUILTIN_ARGS)
  2677. {
  2678. char *text;
  2679. register char *cp;
  2680. register int i;
  2681. boolean next;
  2682. if (argc == 1)
  2683. return;
  2684. for (i=1; i<argc; i++)
  2685. {
  2686. if (i > 1)
  2687. obstack_1grow (obs, ' ');
  2688. next = TRUE;
  2689. text = xstrdup (ARG (i));
  2690. for (cp = text; *cp != '\0'; cp++)
  2691. {
  2692. if (next)
  2693. *cp = toupper (*cp);
  2694. if (isspace (*cp))
  2695. next = TRUE;
  2696. else
  2697. next = FALSE;
  2698. }
  2699. obstack_grow (obs, text, strlen (text));
  2700. xfree ((voidstar) text);
  2701. }
  2702. }
  2703. /*---------------------------------------------------------------.
  2704. | Extracts some portion of a string. Optional attributes are |
  2705. | start and end position. Zero is the position of the first |
  2706. | character. |
  2707. `---------------------------------------------------------------*/
  2708. static void
  2709. mp4h_bp_substring (MP4H_BUILTIN_ARGS)
  2710. {
  2711. char *text, *cp;
  2712. int start, end;
  2713. if (bad_argc (argv[0], argc, 3, 4))
  2714. return;
  2715. text = ARG (1);
  2716. if (argc>3)
  2717. {
  2718. if (!numeric_arg (argv[0], ARG (3), TRUE, &end))
  2719. return;
  2720. if (document_encoding == ENCODING_UTF8)
  2721. {
  2722. cp = utf8char_skip(text, end);
  2723. if (!cp)
  2724. return;
  2725. *cp = '\0';
  2726. }
  2727. else
  2728. *(text+end) = '\0';
  2729. }
  2730. if (argc>2)
  2731. {
  2732. if (!numeric_arg (argv[0], ARG (2), TRUE, &start))
  2733. return;
  2734. if (document_encoding == ENCODING_UTF8)
  2735. {
  2736. text = utf8char_skip(text, start);
  2737. if (!text)
  2738. return;
  2739. }
  2740. else
  2741. text += start;
  2742. }
  2743. obstack_grow (obs, text, strlen (text));
  2744. }
  2745. /*----------------------------------------------------------------.
  2746. | Compares strings. When caseless=true is specified, comparison |
  2747. | is performed without consideration of case. |
  2748. `----------------------------------------------------------------*/
  2749. static void
  2750. mp4h_bp_string_eq (MP4H_BUILTIN_ARGS)
  2751. {
  2752. const char *caseless;
  2753. caseless = predefined_attribute ("caseless", &argc, argv, TRUE);
  2754. if (bad_argc (argv[0], argc, 0, 3))
  2755. return;
  2756. if (argc < 3)
  2757. {
  2758. if (*(ARG (1)) == '\0')
  2759. obstack_grow (obs, "true", 4);
  2760. return;
  2761. }
  2762. if (caseless && strcmp (caseless, "true") == 0)
  2763. {
  2764. if (strcasecmp (ARG (1), ARG (2)) == 0)
  2765. obstack_grow (obs, "true", 4);
  2766. }
  2767. else
  2768. {
  2769. if (strcmp (ARG (1), ARG (2)) == 0)
  2770. obstack_grow (obs, "true", 4);
  2771. }
  2772. }
  2773. static void
  2774. mp4h_bp_string_neq (MP4H_BUILTIN_ARGS)
  2775. {
  2776. const char *caseless;
  2777. caseless = predefined_attribute ("caseless", &argc, argv, TRUE);
  2778. if (bad_argc (argv[0], argc, 0, 3))
  2779. return;
  2780. if (argc < 3)
  2781. {
  2782. if (*(ARG (1)) != '\0')
  2783. obstack_grow (obs, "true", 4);
  2784. return;
  2785. }
  2786. if (caseless && strcmp (caseless, "true") == 0)
  2787. {
  2788. if (strcasecmp (ARG (1), ARG (2)) != 0)
  2789. obstack_grow (obs, "true", 4);
  2790. }
  2791. else
  2792. {
  2793. if (strcmp (ARG (1), ARG (2)) != 0)
  2794. obstack_grow (obs, "true", 4);
  2795. }
  2796. }
  2797. /*-----------------------------------------------------------------.
  2798. | This function compares two strings and returns this comparison. |
  2799. | If ``caseless=true'' is specified, this comparison is performed |
  2800. | without case. |
  2801. `-----------------------------------------------------------------*/
  2802. static void
  2803. mp4h_bp_string_compare (MP4H_BUILTIN_ARGS)
  2804. {
  2805. const char *caseless;
  2806. int result;
  2807. caseless = predefined_attribute ("caseless", &argc, argv, TRUE);
  2808. if (bad_argc (argv[0], argc, 2, 3))
  2809. return;
  2810. if (caseless && strcmp (caseless, "true") == 0)
  2811. result = strcasecmp (ARG (1), ARG (2));
  2812. else
  2813. result = strcmp (ARG (1), ARG (2));
  2814. if (result < 0)
  2815. obstack_grow (obs, "less", 4);
  2816. else if (result > 0)
  2817. obstack_grow (obs, "greater", 7);
  2818. else
  2819. obstack_grow (obs, "equal", 5);
  2820. }
  2821. /*----------------------------------------------------------------.
  2822. | First argument is a string, 2nd is a character. |
  2823. | This function returns an array of numbers, which are locations |
  2824. | where character appear in the string. |
  2825. `----------------------------------------------------------------*/
  2826. static void
  2827. mp4h_bp_char_offsets (MP4H_BUILTIN_ARGS)
  2828. {
  2829. const char *caseless;
  2830. char *cp;
  2831. char c;
  2832. boolean first = TRUE;
  2833. caseless = predefined_attribute ("caseless", &argc, argv, TRUE);
  2834. if (bad_argc (argv[0], argc, 2, 3))
  2835. return;
  2836. if (strlen (ARG (2)) > 1)
  2837. {
  2838. MP4HERROR ((warning_status, 0,
  2839. _("Warning:%s:%d: Second argument of <%s> is not a char"),
  2840. CURRENT_FILE_LINE, ARG (0)));
  2841. return;
  2842. }
  2843. c = ARG (2)[0];
  2844. if (caseless && strcmp (caseless, "true") == 0)
  2845. {
  2846. for (cp = ARG (1); *cp != '\0'; cp++)
  2847. {
  2848. if (tolower(*cp) == tolower(c))
  2849. {
  2850. if (!first)
  2851. obstack_1grow (obs, '\n');
  2852. else
  2853. first = FALSE;
  2854. shipout_int (obs, (int) (cp-ARG (1)));
  2855. }
  2856. }
  2857. }
  2858. else
  2859. {
  2860. for (cp = ARG (1); *cp != '\0'; cp++)
  2861. {
  2862. if (*cp == c)
  2863. {
  2864. if (!first)
  2865. obstack_1grow (obs, '\n');
  2866. else
  2867. first = FALSE;
  2868. shipout_int (obs, (int) (cp-ARG (1)));
  2869. }
  2870. }
  2871. }
  2872. }
  2873. /*----------------------------.
  2874. | Implement printf function. |
  2875. `----------------------------*/
  2876. static void
  2877. mp4h_bp_printf (MP4H_BUILTIN_ARGS)
  2878. {
  2879. const char *text, *save;
  2880. int i, len, count_arg;
  2881. if (bad_argc (argv[0], argc, 2, 0))
  2882. return;
  2883. count_arg = 1;
  2884. for (text = ARG(1); *text != '\0';)
  2885. {
  2886. if (*text != '%')
  2887. {
  2888. save = text;
  2889. len = 1;
  2890. for (text++ ; *text != '%' && *text != '\0'; text++)
  2891. len++;
  2892. obstack_grow (obs, save, len);
  2893. if (*text == '\0')
  2894. break;
  2895. }
  2896. text++;
  2897. if (*text == 's')
  2898. {
  2899. count_arg++;
  2900. obstack_grow (obs, ARG(count_arg), strlen(ARG(count_arg)));
  2901. text++;
  2902. }
  2903. else if (*text == '%')
  2904. {
  2905. obstack_1grow (obs, '%');
  2906. text++;
  2907. }
  2908. else if (isdigit ((int) *text))
  2909. {
  2910. char *endp;
  2911. i = (int)strtol (text, &endp, 10) + 1;
  2912. text = endp;
  2913. if (NULL == text || *text != '$')
  2914. {
  2915. obstack_1grow (obs, '%');
  2916. shipout_int (obs, i - 1);
  2917. continue;
  2918. }
  2919. text++;
  2920. if (*text == 's')
  2921. {
  2922. obstack_grow (obs, ARG(i), strlen(ARG(i)));
  2923. text ++;
  2924. }
  2925. else if (*text == '%')
  2926. {
  2927. obstack_1grow (obs, '%');
  2928. text++;
  2929. }
  2930. else
  2931. {
  2932. obstack_1grow (obs, '%');
  2933. shipout_int (obs, i - 1);
  2934. }
  2935. }
  2936. else
  2937. obstack_1grow (obs, '%');
  2938. }
  2939. }
  2940. /*
  2941. Regular expression support is provided by the PCRE library package,
  2942. which is open source software, copyright by the University of
  2943. Cambridge.
  2944. Latest sources are available from
  2945. ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
  2946. */
  2947. static char *
  2948. utf8char_skip (char *utf, int offset)
  2949. {
  2950. if (!utf)
  2951. return(NULL);
  2952. while (offset > 0) {
  2953. offset--;
  2954. if (utf[0] & 0x80) {
  2955. if ((utf[1] & 0xc0) != 0x80)
  2956. return(NULL);
  2957. if ((utf[0] & 0xe0) == 0xe0) {
  2958. if ((utf[2] & 0xc0) != 0x80)
  2959. return(NULL);
  2960. if ((utf[0] & 0xf0) == 0xf0) {
  2961. if ((utf[0] & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
  2962. return(NULL);
  2963. utf += 4;
  2964. } else {
  2965. utf += 3;
  2966. }
  2967. } else {
  2968. utf += 2;
  2969. }
  2970. } else {
  2971. utf++;
  2972. }
  2973. }
  2974. return(utf);
  2975. }
  2976. static int
  2977. utf8char_strlen(char *utf)
  2978. {
  2979. int ret = 0;
  2980. if (utf == NULL)
  2981. return(-1);
  2982. while (*utf != 0) {
  2983. utf = utf8char_skip(utf, 1);
  2984. if (!utf)
  2985. return(-1);
  2986. ret++;
  2987. }
  2988. return(ret);
  2989. }
  2990. static int
  2991. encoding_strlen(char *str)
  2992. {
  2993. if (document_encoding == ENCODING_UTF8)
  2994. return(utf8char_strlen (str));
  2995. else
  2996. return(strlen (str));
  2997. }
  2998. /*-------------------------------------------------------------------------.
  2999. | Function to perform substitution by regular expressions. Used by the |
  3000. | builtins regexp and patsubst. The changed text is placed on the |
  3001. | obstack. The substitution is REPL, with \& substituted by this part of |
  3002. | VICTIM matched by the last whole regular expression, taken from REGS[0], |
  3003. | and \N substituted by the text matched by the Nth parenthesized |
  3004. | sub-expression, taken from REGS[N]. |
  3005. `-------------------------------------------------------------------------*/
  3006. static int substitute_warned = 0;
  3007. static void
  3008. substitute (struct obstack *obs, const char *victim, const char *repl,
  3009. int *regs)
  3010. {
  3011. register unsigned int ch;
  3012. for (;;)
  3013. {
  3014. while ((ch = *repl++) != '\\')
  3015. {
  3016. if (ch == '\0')
  3017. return;
  3018. obstack_1grow (obs, ch);
  3019. }
  3020. switch ((ch = *repl++))
  3021. {
  3022. case '0':
  3023. if (!substitute_warned)
  3024. {
  3025. MP4HERROR ((warning_status, 0, _("\
  3026. Warning:%s:%d: \\0 will disappear, use \\& instead in replacements"),
  3027. CURRENT_FILE_LINE));
  3028. substitute_warned = 1;
  3029. }
  3030. /* Fall through. */
  3031. case '&':
  3032. obstack_grow (obs, victim + regs[0],
  3033. regs[1] - regs[0]);
  3034. break;
  3035. case '1': case '2': case '3': case '4': case '5': case '6':
  3036. case '7': case '8': case '9':
  3037. ch -= '0';
  3038. if (regs[ch*2+1] > 0)
  3039. obstack_grow (obs, victim + regs[ch*2],
  3040. regs[ch*2+1] - regs[ch*2]);
  3041. break;
  3042. default:
  3043. obstack_1grow (obs, ch);
  3044. break;
  3045. }
  3046. }
  3047. }
  3048. /*--------------------------------------------------------------------------.
  3049. | Regular expression version of index. Given two arguments, expand to the |
  3050. | index of the first match of the second argument (a regexp) in the first. |
  3051. | Expand to -1 if here is no match. |
  3052. `--------------------------------------------------------------------------*/
  3053. static void
  3054. string_regexp (struct obstack *obs, int argc, token_data **argv,
  3055. int options, const char *action)
  3056. {
  3057. char *victim; /* first argument */
  3058. char *regexp; /* regular expression */
  3059. int length; /* length of first argument */
  3060. int startpos = -1; /* start position of match */
  3061. int match_length = 0; /* length of pattern match */
  3062. pcre *re;
  3063. int *match_ptr = NULL;
  3064. int max_subexps = 0;
  3065. int rc;
  3066. victim = ARG (1);
  3067. remove_special_chars (victim, FALSE);
  3068. length = strlen (victim);
  3069. regexp = ARG (2);
  3070. remove_special_chars (regexp, FALSE);
  3071. re = xre_compile (regexp, options);
  3072. if (re == NULL)
  3073. return;
  3074. /* Accept any number of subexpressions */
  3075. do
  3076. {
  3077. max_subexps += 10;
  3078. match_ptr = (int *)
  3079. xrealloc ((voidstar) match_ptr, sizeof (int) * max_subexps * 3);
  3080. rc = pcre_exec (re, NULL, victim, length, 0,
  3081. 0, match_ptr, max_subexps * 3);
  3082. }
  3083. while (rc == PCRE_ERROR_NOMEMORY);
  3084. if (rc > 0)
  3085. {
  3086. match_length = match_ptr[1] - match_ptr[0];
  3087. startpos = match_ptr[0];
  3088. }
  3089. if (strcmp(action, "startpos") == 0)
  3090. {
  3091. if (startpos >= 0)
  3092. shipout_int (obs, startpos);
  3093. }
  3094. else if (strcmp(action, "endpos") == 0)
  3095. {
  3096. if (startpos >= 0)
  3097. shipout_int (obs, startpos + match_length);
  3098. }
  3099. else if (strcmp(action, "length") == 0)
  3100. {
  3101. if (startpos >= 0)
  3102. shipout_int (obs, match_length);
  3103. }
  3104. else if (strcmp(action, "delete") == 0)
  3105. {
  3106. if (startpos >= 0)
  3107. {
  3108. obstack_grow (obs, ARG (1), startpos);
  3109. obstack_grow (obs, ARG (1) + startpos + match_length,
  3110. strlen (ARG (1)) - startpos - match_length);
  3111. }
  3112. else
  3113. obstack_grow (obs, ARG (1), strlen (ARG (1)));
  3114. }
  3115. else if (strcmp(action, "extract") == 0)
  3116. {
  3117. if (startpos >= 0)
  3118. obstack_grow (obs, ARG (1) + startpos, match_length);
  3119. }
  3120. else
  3121. {
  3122. if (startpos >= 0)
  3123. obstack_grow (obs, "true", 4);
  3124. }
  3125. pcre_free (re);
  3126. xfree ((voidstar) match_ptr);
  3127. }
  3128. /*----------------------------------------------.
  3129. | Substitutes regular expressions in a string. |
  3130. | A string is returned. |
  3131. `----------------------------------------------*/
  3132. static void
  3133. subst_in_string (struct obstack *obs, int argc, token_data **argv,
  3134. int extra_re_flags)
  3135. {
  3136. char *victim; /* first argument */
  3137. char *regexp; /* regular expression */
  3138. pcre *re;
  3139. pcre_extra *re_extra = NULL;
  3140. const char *errptr;
  3141. int *match_ptr = NULL;
  3142. int max_subexps = 0;
  3143. int options = 0;
  3144. int rc;
  3145. int matchpos; /* start position of match */
  3146. int offset; /* current match offset */
  3147. int length; /* length of first argument */
  3148. if (bad_argc (argv[0], argc, 2, 4))
  3149. return;
  3150. victim = ARG (1);
  3151. remove_special_chars (victim, FALSE);
  3152. length = strlen (victim);
  3153. regexp = ARG (2);
  3154. remove_special_chars (regexp, FALSE);
  3155. re = xre_compile (regexp, extra_re_flags);
  3156. if (re == NULL)
  3157. return;
  3158. re_extra = pcre_study (re, 0, &errptr);
  3159. if (errptr != NULL)
  3160. {
  3161. MP4HERROR ((warning_status, 0,
  3162. _("Error:%s:%d: Bad regular expression `%s': %s"),
  3163. CURRENT_FILE_LINE, regexp, errptr));
  3164. return;
  3165. }
  3166. offset = 0;
  3167. matchpos = 0;
  3168. while (offset < length)
  3169. {
  3170. /* Accept any number of subexpressions */
  3171. do
  3172. {
  3173. max_subexps += 10;
  3174. match_ptr = (int *)
  3175. xrealloc ((voidstar) match_ptr, sizeof (int) * max_subexps * 3);
  3176. rc = pcre_exec (re, re_extra, victim+offset, length-offset, 0,
  3177. options, match_ptr, max_subexps * 3);
  3178. }
  3179. while (rc == PCRE_ERROR_NOMEMORY);
  3180. max_subexps -= 10;
  3181. /* Subsequent calls to regexec do not match beginning o line */
  3182. options |= PCRE_NOTBOL;
  3183. if (rc < 0)
  3184. {
  3185. /* Match failed -- either error or there is no match in the
  3186. rest of the string, in which case the rest of the string is
  3187. copied verbatim. */
  3188. if (rc != PCRE_ERROR_NOMATCH)
  3189. MP4HERROR ((warning_status, 0,
  3190. _("Warning:%s:%d: Error matching regular expression `%s'"),
  3191. CURRENT_FILE_LINE, regexp));
  3192. else if (offset < length)
  3193. obstack_grow (obs, victim + offset, length - offset);
  3194. break;
  3195. }
  3196. /* Copy the part of the string that was skipped by regex (). */
  3197. matchpos = match_ptr[0];
  3198. if (matchpos > 0)
  3199. obstack_grow (obs, victim + offset, matchpos);
  3200. /* Handle the part of the string that was covered by the match. */
  3201. substitute (obs, victim+offset, ARG (3), match_ptr);
  3202. /* Update the offset to the end of the match. If the regexp
  3203. matched a null string, advance offset one more, to avoid
  3204. infinite loops. */
  3205. offset += match_ptr[1];
  3206. if (match_ptr[0] == match_ptr[1])
  3207. obstack_1grow (obs, victim[offset++]);
  3208. }
  3209. pcre_free (re);
  3210. pcre_free (re_extra);
  3211. xfree ((voidstar) match_ptr);
  3212. }
  3213. /*------------------------------------------------.
  3214. | Routine parsing attributes to set regex flags. |
  3215. `------------------------------------------------*/
  3216. static int
  3217. regex_attributes (int *ptr_argc, token_data **argv)
  3218. {
  3219. const char *singleline, *caseless, *reflags;
  3220. const char *cp;
  3221. int re_flags = 0;
  3222. singleline = predefined_attribute ("singleline", ptr_argc, argv, TRUE);
  3223. if (singleline)
  3224. {
  3225. if (strcmp (singleline, "true") == 0)
  3226. re_flags |= PCRE_DOTALL;
  3227. else
  3228. re_flags |= PCRE_MULTILINE;
  3229. }
  3230. caseless = predefined_attribute ("caseless", ptr_argc, argv, TRUE);
  3231. if (caseless && strcmp (caseless, "true") == 0)
  3232. re_flags |= PCRE_CASELESS;
  3233. /* This one overrides previous options */
  3234. reflags = predefined_attribute ("reflags", ptr_argc, argv, TRUE);
  3235. if (reflags)
  3236. {
  3237. re_flags = 0;
  3238. for (cp = reflags; *cp != '\0'; cp++)
  3239. {
  3240. switch (*cp)
  3241. {
  3242. case '-': break;
  3243. case 'i': re_flags |= PCRE_CASELESS; break;
  3244. case 'm': re_flags |= PCRE_MULTILINE; break;
  3245. case 's': re_flags |= PCRE_DOTALL; break;
  3246. case 'x': re_flags |= PCRE_EXTENDED; break;
  3247. default:
  3248. MP4HERROR ((warning_status, 0, _("\
  3249. Warning:%s:%d: Unknown modifier `%c' in reflags"),
  3250. CURRENT_FILE_LINE, *cp));
  3251. break;
  3252. }
  3253. }
  3254. }
  3255. return re_flags;
  3256. }
  3257. static void
  3258. mp4h_bp_subst_in_string (MP4H_BUILTIN_ARGS)
  3259. {
  3260. int extra_re_flags;
  3261. extra_re_flags = regex_attributes (&argc, argv);
  3262. subst_in_string (obs, argc, argv, extra_re_flags);
  3263. }
  3264. /*------------------------------------------------.
  3265. | Substitutes regular expressions in a variable. |
  3266. `------------------------------------------------*/
  3267. static void
  3268. mp4h_bp_subst_in_var (MP4H_BUILTIN_ARGS)
  3269. {
  3270. symbol *var;
  3271. token_data td;
  3272. int extra_re_flags;
  3273. char *text;
  3274. struct obstack temp_obs;
  3275. extra_re_flags = regex_attributes (&argc, argv);
  3276. if (bad_argc (argv[0], argc, 3, 4))
  3277. return;
  3278. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3279. if (var == NULL)
  3280. return;
  3281. TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
  3282. TOKEN_DATA_TEXT (&td) = SYMBOL_TEXT (var);
  3283. obstack_init (&temp_obs);
  3284. argv[1] = &td;
  3285. subst_in_string (&temp_obs, argc, argv, extra_re_flags);
  3286. obstack_1grow (&temp_obs, '\0');
  3287. text = obstack_finish (&temp_obs);
  3288. xfree ((voidstar) SYMBOL_TEXT (var));
  3289. SYMBOL_TEXT (var) = xstrdup (text);
  3290. obstack_free (&temp_obs, NULL);
  3291. }
  3292. /*-------------------------------------------------------------------.
  3293. | This function compares a string and a regular expression. |
  3294. | Attribute ``action'' can be |
  3295. | report : returns "true" if string match the regular expression. |
  3296. | extract : returns the portion of the string matched by the |
  3297. | regular expression. |
  3298. | delete : deletes the portion of the string matched by the |
  3299. | regular expression and prints resulting string. |
  3300. | startpos : returns index of the first character matching the |
  3301. | regular expression, -1 if no match. |
  3302. | endpos : returns index of the last character matching the |
  3303. | regular expression, -1 if no match. |
  3304. | length : returns length of the matched string. |
  3305. `-------------------------------------------------------------------*/
  3306. static void
  3307. mp4h_bp_match (MP4H_BUILTIN_ARGS)
  3308. {
  3309. const char *action;
  3310. int extra_re_flags;
  3311. extra_re_flags = regex_attributes (&argc, argv);
  3312. action = predefined_attribute ("action", &argc, argv, TRUE);
  3313. if (!action)
  3314. action = "report";
  3315. if (bad_argc (argv[0], argc, 3, 4))
  3316. return;
  3317. string_regexp (obs, argc, argv, extra_re_flags, action);
  3318. }
  3319. /* Operation on variables: define, undefine, search, insert,...
  3320. Variables are either strings or array of strings. */
  3321. /*--------------------------------------------------------------.
  3322. | The function generic_variable_lookup is the generic function |
  3323. | used by mp4h_bp_get_var and mp4h_bp_get_var_once. |
  3324. `--------------------------------------------------------------*/
  3325. static void
  3326. generic_variable_lookup (MP4H_BUILTIN_ARGS, boolean verbatim)
  3327. {
  3328. char *cp, *ptr_index;
  3329. symbol *var, *index_var;
  3330. int i;
  3331. int length;
  3332. int array_index;
  3333. if (argc < 2)
  3334. return;
  3335. for (i = 1; i < argc; i++)
  3336. {
  3337. array_index = -1;
  3338. ptr_index = strchr (ARG (i), ']');
  3339. if (ptr_index != NULL)
  3340. {
  3341. if (*(ptr_index-1) == '[')
  3342. {
  3343. *(ptr_index-1) = '\0';
  3344. ptr_index = NULL;
  3345. }
  3346. else
  3347. {
  3348. *ptr_index = '\0';
  3349. ptr_index = strchr (ARG (i), '[');
  3350. if (!ptr_index)
  3351. {
  3352. MP4HERROR ((warning_status, 0, _("\
  3353. Warning:%s:%d: Wrong index declaration in <%s>"),
  3354. CURRENT_FILE_LINE, ARG (0)));
  3355. return;
  3356. }
  3357. *ptr_index = '\0';
  3358. ptr_index++;
  3359. if (!numeric_arg (argv[0], ptr_index, FALSE, &array_index))
  3360. {
  3361. /* Maybe there is an implicit index like in
  3362. <get-var foo[i]>. */
  3363. index_var = lookup_variable (ptr_index, SYMBOL_LOOKUP);
  3364. if (index_var)
  3365. {
  3366. if (!numeric_arg (argv[0], SYMBOL_TEXT (index_var),
  3367. FALSE, &array_index))
  3368. index_var = NULL;
  3369. }
  3370. if (!index_var)
  3371. {
  3372. MP4HERROR ((warning_status, 0, _("\
  3373. Warning:%s:%d: Wrong index declaration in <%s>"),
  3374. CURRENT_FILE_LINE, ARG (0)));
  3375. return;
  3376. }
  3377. }
  3378. if (array_index < 0)
  3379. continue;
  3380. }
  3381. }
  3382. var = lookup_variable (ARG (i), SYMBOL_LOOKUP);
  3383. if (var == NULL)
  3384. return;
  3385. if (array_index < 0)
  3386. {
  3387. cp = SYMBOL_TEXT (var);
  3388. length = strlen (cp);
  3389. }
  3390. else
  3391. cp = array_value (var, array_index, &length);
  3392. if (cp)
  3393. {
  3394. if (verbatim)
  3395. obstack_1grow (obs, CHAR_LQUOTE);
  3396. obstack_grow (obs, cp, length);
  3397. if (verbatim)
  3398. obstack_1grow (obs, CHAR_RQUOTE);
  3399. }
  3400. }
  3401. }
  3402. /*-------------------------------------------------------.
  3403. | Define a variable. Value is found in the body. |
  3404. | Intentionally, variables cannot be indexed with this |
  3405. | tag, because an entire array can be assigned using |
  3406. | this. |
  3407. `-------------------------------------------------------*/
  3408. static void
  3409. mp4h_bp_set_var_x (MP4H_BUILTIN_ARGS)
  3410. {
  3411. const char *name;
  3412. symbol *var;
  3413. name = predefined_attribute ("name", &argc, argv, FALSE);
  3414. if (name == NULL)
  3415. {
  3416. MP4HERROR ((warning_status, 0,
  3417. _("Error:%s:%d: In <%s>, required attribute `%s' is not specified"),
  3418. CURRENT_FILE_LINE, ARG (0), "name"));
  3419. return;
  3420. }
  3421. var = lookup_variable (name, SYMBOL_INSERT);
  3422. if (SYMBOL_TYPE (var) == TOKEN_TEXT)
  3423. xfree ((voidstar) SYMBOL_TEXT (var));
  3424. SYMBOL_TEXT (var) = xstrdup (ARGBODY);
  3425. SYMBOL_TYPE (var) = TOKEN_TEXT;
  3426. }
  3427. /*----------------------------------------------------------------------.
  3428. | Define a variable. Argument is evaluated or not depending on whether |
  3429. | the 'expand attributes' flag is on in the builtin_tab above. |
  3430. `----------------------------------------------------------------------*/
  3431. static void
  3432. mp4h_bp_set_var (MP4H_BUILTIN_ARGS)
  3433. {
  3434. char *value, *cp, *ptr_index, *old_value, *new_value;
  3435. symbol *var, *index_var;
  3436. register int i;
  3437. register int j;
  3438. int length, istep, size;
  3439. int array_index;
  3440. if (argc < 2)
  3441. return;
  3442. for (i = 1; i < argc; i++)
  3443. {
  3444. array_index = -1;
  3445. istep = 0;
  3446. value = strchr (ARG (i), '=');
  3447. if (value == NULL)
  3448. {
  3449. /* Look for spaces like in <set-var i = 0> */
  3450. if (i+1 < argc && *(ARG (i+1)) == '=')
  3451. {
  3452. istep++;
  3453. if (i+2 < argc && *(ARG (i+1) +1) == '\0')
  3454. {
  3455. istep++;
  3456. value = ARG (i+2);
  3457. }
  3458. else
  3459. value = ARG (i+1) + 1;
  3460. }
  3461. else
  3462. value = ARG (i) + strlen (ARG (i));
  3463. }
  3464. else
  3465. {
  3466. *value = '\0';
  3467. value++;
  3468. }
  3469. /* Remove special quote characters. */
  3470. remove_special_chars (value, FALSE);
  3471. ptr_index = strchr (ARG (i), ']');
  3472. if (ptr_index != NULL)
  3473. {
  3474. if (*(ptr_index-1) == '[')
  3475. {
  3476. *(ptr_index-1) = '\0';
  3477. ptr_index = NULL;
  3478. }
  3479. else
  3480. {
  3481. *ptr_index = '\0';
  3482. ptr_index = strchr (ARG (i), '[');
  3483. if (!ptr_index)
  3484. {
  3485. MP4HERROR ((warning_status, 0, _("\
  3486. Warning:%s:%d: Wrong index declaration in <%s>"),
  3487. CURRENT_FILE_LINE, ARG (0)));
  3488. return;
  3489. }
  3490. *ptr_index = '\0';
  3491. ptr_index++;
  3492. if (!numeric_arg (argv[0], ptr_index, FALSE, &array_index))
  3493. {
  3494. /* Maybe there is an implicit index like in
  3495. <set-var foo[i]=bar>. */
  3496. index_var = lookup_variable (ptr_index, SYMBOL_LOOKUP);
  3497. if (index_var)
  3498. {
  3499. if (!numeric_arg (argv[0], SYMBOL_TEXT (index_var),
  3500. FALSE, &array_index))
  3501. index_var = NULL;
  3502. }
  3503. if (!index_var)
  3504. {
  3505. MP4HERROR ((warning_status, 0, _("\
  3506. Warning:%s:%d: Wrong index declaration in <%s>"),
  3507. CURRENT_FILE_LINE, ARG (0)));
  3508. return;
  3509. }
  3510. }
  3511. }
  3512. }
  3513. var = lookup_variable (ARG (i), SYMBOL_INSERT);
  3514. if (ptr_index == NULL)
  3515. {
  3516. /* single value. */
  3517. if (SYMBOL_TYPE (var) == TOKEN_TEXT)
  3518. xfree ((voidstar) SYMBOL_TEXT (var));
  3519. SYMBOL_TEXT (var) = xstrdup (value);
  3520. }
  3521. else if (array_index >= 0)
  3522. {
  3523. /* an index has been specified. */
  3524. if (SYMBOL_TYPE (var) == TOKEN_TEXT)
  3525. old_value = SYMBOL_TEXT (var);
  3526. else
  3527. old_value = "";
  3528. length = strlen (old_value) + strlen (value) + array_index + 1;
  3529. new_value = (char *) xmalloc (length + 1);
  3530. *new_value = '\0';
  3531. if (array_index == 0)
  3532. {
  3533. strcat (new_value, value);
  3534. cp = strchr (old_value, '\n');
  3535. if (cp)
  3536. strcat (new_value, cp);
  3537. }
  3538. else
  3539. {
  3540. size = array_size (var);
  3541. if (size == 0)
  3542. size = 1;
  3543. if (size <= array_index)
  3544. {
  3545. strcat (new_value, old_value);
  3546. for (j=size; j<=array_index; j++)
  3547. strcat (new_value, "\n");
  3548. strcat (new_value, value);
  3549. }
  3550. else
  3551. {
  3552. cp = array_value (var, array_index, 0);
  3553. strncat (new_value, old_value, cp - SYMBOL_TEXT (var));
  3554. strcat (new_value, value);
  3555. cp = strchr (cp, '\n');
  3556. if (cp)
  3557. strcat (new_value, cp);
  3558. }
  3559. }
  3560. if (SYMBOL_TYPE (var) == TOKEN_TEXT)
  3561. xfree ((voidstar) SYMBOL_TEXT (var));
  3562. SYMBOL_TEXT (var) = new_value;
  3563. }
  3564. SYMBOL_TYPE (var) = TOKEN_TEXT;
  3565. i += istep;
  3566. }
  3567. }
  3568. /*------------------------------.
  3569. | Get the value of a variable. |
  3570. `------------------------------*/
  3571. static void
  3572. mp4h_bp_get_var (MP4H_BUILTIN_ARGS)
  3573. {
  3574. generic_variable_lookup (MP4H_BUILTIN_RECUR, FALSE);
  3575. }
  3576. /*--------------------------------------------------------------.
  3577. | Get the value of a variable without expanding its attribute. |
  3578. `--------------------------------------------------------------*/
  3579. static void
  3580. mp4h_bp_get_var_once (MP4H_BUILTIN_ARGS)
  3581. {
  3582. generic_variable_lookup (MP4H_BUILTIN_RECUR, TRUE);
  3583. }
  3584. /*----------------------.
  3585. | Undefine a variable. |
  3586. `----------------------*/
  3587. static void
  3588. mp4h_bp_unset_var (MP4H_BUILTIN_ARGS)
  3589. {
  3590. int i;
  3591. for (i=1; i<argc; i++)
  3592. lookup_variable (ARG (i), SYMBOL_DELETE);
  3593. }
  3594. /*-----------------------------------------------------.
  3595. | Push a variable to a stack and reset this variable. |
  3596. `-----------------------------------------------------*/
  3597. static void
  3598. mp4h_bp_preserve (MP4H_BUILTIN_ARGS)
  3599. {
  3600. symbol *var;
  3601. var_stack *next;
  3602. int i;
  3603. if (bad_argc (argv[0], argc, 2, 0))
  3604. return;
  3605. for (i=argc-1; i>0; i--)
  3606. {
  3607. next = (var_stack *) xmalloc (sizeof (var_stack));
  3608. var = lookup_variable (ARG (i), SYMBOL_LOOKUP);
  3609. if (var)
  3610. {
  3611. next->text = xstrdup (SYMBOL_TEXT (var));
  3612. *(SYMBOL_TEXT (var)) = '\0';
  3613. }
  3614. else
  3615. next->text = xstrdup ("");
  3616. next->prev = vs;
  3617. vs = next;
  3618. }
  3619. }
  3620. /*-----------------------------------------.
  3621. | Pop a variable from the variable stack. |
  3622. `-----------------------------------------*/
  3623. static void
  3624. mp4h_bp_restore (MP4H_BUILTIN_ARGS)
  3625. {
  3626. symbol *var;
  3627. var_stack *prev;
  3628. int i;
  3629. if (bad_argc (argv[0], argc, 2, 0))
  3630. return;
  3631. for (i=1; i<argc; i++)
  3632. {
  3633. var = lookup_variable (ARG (i), SYMBOL_INSERT);
  3634. if (!vs)
  3635. {
  3636. MP4HERROR ((warning_status, 0, _("\
  3637. Warning:%s:%d: Variable stack empty, it means <%s> already gobbled all data"),
  3638. CURRENT_FILE_LINE, ARG (0)));
  3639. return;
  3640. }
  3641. if (SYMBOL_TYPE (var) == TOKEN_TEXT)
  3642. xfree ((voidstar) SYMBOL_TEXT (var));
  3643. SYMBOL_TEXT (var) = xstrdup (vs->text);
  3644. SYMBOL_TYPE (var) = TOKEN_TEXT;
  3645. prev = vs->prev;
  3646. xfree ((voidstar) vs->text);
  3647. xfree ((voidstar) vs);
  3648. vs = prev;
  3649. }
  3650. }
  3651. static void
  3652. varstack_check (void)
  3653. {
  3654. if (vs)
  3655. {
  3656. MP4HERROR ((warning_status, 0, _("\
  3657. Warning:%s:%d: Variable stack not empty, it means <preserve> pushed more items than <restore> popped"),
  3658. CURRENT_FILE_LINE));
  3659. }
  3660. }
  3661. /*----------------------------.
  3662. | Test if a variable exists. |
  3663. `----------------------------*/
  3664. static void
  3665. mp4h_bp_var_exists (MP4H_BUILTIN_ARGS)
  3666. {
  3667. symbol *var;
  3668. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3669. if (var != NULL)
  3670. shipout_text (obs, "true");
  3671. }
  3672. /*-----------------------.
  3673. | Increment a variable. |
  3674. `-----------------------*/
  3675. static void
  3676. mp4h_bp_increment (MP4H_BUILTIN_ARGS)
  3677. {
  3678. int value, incr;
  3679. symbol *var;
  3680. char buf[128];
  3681. const char *by;
  3682. by = predefined_attribute ("by", &argc, argv, FALSE);
  3683. if (bad_argc (argv[0], argc, 2, 2))
  3684. return;
  3685. if (!by)
  3686. by = "1";
  3687. if (bad_argc (argv[0], argc, 2, 2))
  3688. return;
  3689. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3690. if (var == NULL)
  3691. return;
  3692. if (!numeric_arg (argv[0], SYMBOL_TEXT (var), TRUE, &value))
  3693. return;
  3694. if (!numeric_arg (argv[0], by, TRUE, &incr))
  3695. return;
  3696. if (SYMBOL_TYPE (var) == TOKEN_TEXT)
  3697. xfree ((voidstar) SYMBOL_TEXT (var));
  3698. sprintf (buf, "%d", value+incr);
  3699. SYMBOL_TEXT (var) = xstrdup(buf);
  3700. }
  3701. static void
  3702. mp4h_bp_decrement (MP4H_BUILTIN_ARGS)
  3703. {
  3704. int value, incr;
  3705. symbol *var;
  3706. char buf[128];
  3707. const char *by;
  3708. by = predefined_attribute ("by", &argc, argv, FALSE);
  3709. if (bad_argc (argv[0], argc, 2, 2))
  3710. return;
  3711. if (!by)
  3712. by = "1";
  3713. if (bad_argc (argv[0], argc, 2, 2))
  3714. return;
  3715. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3716. if (var == NULL)
  3717. return;
  3718. if (!numeric_arg (argv[0], SYMBOL_TEXT (var), TRUE, &value))
  3719. return;
  3720. if (!numeric_arg (argv[0], by, TRUE, &incr))
  3721. return;
  3722. if (SYMBOL_TYPE (var) == TOKEN_TEXT)
  3723. xfree ((voidstar) SYMBOL_TEXT (var));
  3724. sprintf (buf, "%d", value-incr);
  3725. SYMBOL_TEXT (var) = xstrdup(buf);
  3726. }
  3727. /*--------------------------------.
  3728. | Dumps informations of symbols. |
  3729. `--------------------------------*/
  3730. static void
  3731. mp4h_bp_symbol_info (MP4H_BUILTIN_ARGS)
  3732. {
  3733. symbol *var;
  3734. int size;
  3735. if (bad_argc (argv[0], argc, 0, 2))
  3736. return;
  3737. /* First look if this variable is defined. */
  3738. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3739. if (var != NULL)
  3740. {
  3741. size = array_size (var);
  3742. if (size == 0)
  3743. size = 1;
  3744. obstack_grow (obs, "STRING\n", 7);
  3745. shipout_int (obs, size);
  3746. return;
  3747. }
  3748. var = lookup_symbol (ARG (1), SYMBOL_LOOKUP);
  3749. if (var != NULL)
  3750. {
  3751. if (SYMBOL_TYPE (var) == TOKEN_FUNC)
  3752. {
  3753. if (SYMBOL_CONTAINER (var))
  3754. obstack_grow (obs, "PRIM COMPLEX", 12);
  3755. else
  3756. obstack_grow (obs, "PRIM TAG", 8);
  3757. }
  3758. else if (SYMBOL_TYPE (var) == TOKEN_TEXT)
  3759. {
  3760. if (SYMBOL_CONTAINER (var))
  3761. obstack_grow (obs, "USER COMPLEX", 12);
  3762. else
  3763. obstack_grow (obs, "USER TAG", 8);
  3764. }
  3765. }
  3766. }
  3767. /*--------------------------------.
  3768. | Copy a variable to another one. |
  3769. `--------------------------------*/
  3770. static void
  3771. mp4h_bp_copy_var (MP4H_BUILTIN_ARGS)
  3772. {
  3773. symbol *var1, *var2;
  3774. if (bad_argc (argv[0], argc, 3, 3))
  3775. return;
  3776. var1 = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3777. if (var1 == NULL)
  3778. {
  3779. MP4HERROR ((warning_status, 0,
  3780. _("Warning:%s:%d: Variable `%s' not defined in <%s>"),
  3781. CURRENT_FILE_LINE, ARG (1), ARG (0)));
  3782. return;
  3783. }
  3784. var2 = lookup_variable (ARG (2), SYMBOL_INSERT);
  3785. if (SYMBOL_TYPE (var2) == TOKEN_TEXT)
  3786. xfree ((voidstar) SYMBOL_TEXT (var2));
  3787. SYMBOL_TEXT (var2) = xstrdup(SYMBOL_TEXT (var1));
  3788. SYMBOL_TYPE (var2) = TOKEN_TEXT;
  3789. }
  3790. /*--------------------------------.
  3791. | Defines a variable only if it |
  3792. | is not set yet. |
  3793. `--------------------------------*/
  3794. static void
  3795. mp4h_bp_defvar (MP4H_BUILTIN_ARGS)
  3796. {
  3797. symbol *var;
  3798. if (bad_argc (argv[0], argc, 0, 3))
  3799. return;
  3800. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3801. if (var == NULL)
  3802. {
  3803. var = lookup_variable (ARG (1), SYMBOL_INSERT);
  3804. SYMBOL_TYPE (var) = TOKEN_TEXT;
  3805. SYMBOL_TEXT (var) = xstrdup(ARG (2));
  3806. }
  3807. else if (SYMBOL_TYPE (var) == TOKEN_TEXT && *(SYMBOL_TEXT (var)) == '\0')
  3808. {
  3809. xfree ((voidstar) SYMBOL_TEXT (var));
  3810. SYMBOL_TEXT (var) = xstrdup(ARG (2));
  3811. }
  3812. }
  3813. /* Array functions: */
  3814. /*------------------------------------------------------------.
  3815. | An array is a representation of variables, it is a newline |
  3816. | separated list of strings. |
  3817. `------------------------------------------------------------*/
  3818. /*-----------------------------------------.
  3819. | Returns number of elements of an array. |
  3820. `-----------------------------------------*/
  3821. static int
  3822. array_size (symbol *var)
  3823. {
  3824. char *cp;
  3825. int result = 0;
  3826. if (var != NULL && SYMBOL_TYPE (var) == TOKEN_TEXT
  3827. && SYMBOL_TEXT (var) != NULL && *(SYMBOL_TEXT (var)) != '\0')
  3828. {
  3829. result++;
  3830. for (cp=SYMBOL_TEXT (var); *cp != '\0'; cp++)
  3831. if (*cp == '\n')
  3832. result++;
  3833. }
  3834. return result;
  3835. }
  3836. /*------------------------------------.
  3837. | Returns the nth value of an array. |
  3838. | Returns NULL if array is too small. |
  3839. `------------------------------------*/
  3840. static char *
  3841. array_value (symbol *var, int offset, int *length)
  3842. {
  3843. char *cp, *value;
  3844. int i;
  3845. value = NULL;
  3846. if (offset == 0)
  3847. value = SYMBOL_TEXT (var);
  3848. else if (offset > 0)
  3849. {
  3850. value = SYMBOL_TEXT (var);
  3851. for (i=0; i<offset; i++)
  3852. {
  3853. value = strchr (value, '\n');
  3854. if (!value)
  3855. break;
  3856. value++;
  3857. }
  3858. }
  3859. if (length != NULL)
  3860. {
  3861. if (value)
  3862. {
  3863. cp = strchr (value, '\n');
  3864. if (cp != NULL)
  3865. *length = (int) (cp - value);
  3866. else
  3867. *length = strlen (value);
  3868. }
  3869. else
  3870. *length = 0;
  3871. }
  3872. return value;
  3873. }
  3874. /*----------------------------------------.
  3875. | Prints number of elements of an array. |
  3876. `----------------------------------------*/
  3877. static void
  3878. mp4h_bp_array_size (MP4H_BUILTIN_ARGS)
  3879. {
  3880. symbol *var;
  3881. int result = -1;
  3882. if (bad_argc (argv[0], argc, 2, 2))
  3883. return;
  3884. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3885. if (var != NULL)
  3886. result = array_size (var);
  3887. shipout_int (obs, result);
  3888. }
  3889. /*-------------------------------------------------------------.
  3890. | Search an element in an array. If it is found, returns its |
  3891. | index, otherwise returns -1. |
  3892. `-------------------------------------------------------------*/
  3893. static int
  3894. array_member (const char *text, symbol *var, boolean caseless)
  3895. {
  3896. char *cp, *next_item, *value;
  3897. int result = -1;
  3898. boolean found = FALSE;
  3899. value = xstrdup (SYMBOL_TEXT (var));
  3900. cp = value;
  3901. while (1)
  3902. {
  3903. result++;
  3904. next_item = strchr (cp, '\n');
  3905. if (next_item)
  3906. *next_item = '\0';
  3907. if (caseless)
  3908. found = (strcasecmp (cp, text) == 0);
  3909. else
  3910. found = (strcmp (cp, text) == 0);
  3911. if (next_item == NULL || found)
  3912. break;
  3913. cp = next_item + 1;
  3914. }
  3915. xfree ((voidstar) value);
  3916. if (found)
  3917. return result;
  3918. else
  3919. return -1;
  3920. }
  3921. static void
  3922. mp4h_bp_array_member (MP4H_BUILTIN_ARGS)
  3923. {
  3924. int result = -1;
  3925. symbol *var;
  3926. const char *caseless;
  3927. caseless = predefined_attribute ("caseless", &argc, argv, TRUE);
  3928. if (! bad_argc (argv[0], argc, 3, 3))
  3929. {
  3930. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3931. if (var != NULL)
  3932. result = array_member (ARG (2), var, (caseless != NULL));
  3933. }
  3934. shipout_int (obs, result);
  3935. }
  3936. /*-------------------------------------------------------------.
  3937. | Adds an element to an array only if it is not yet present. |
  3938. `-------------------------------------------------------------*/
  3939. static void
  3940. mp4h_bp_array_add_unique (MP4H_BUILTIN_ARGS)
  3941. {
  3942. symbol *var;
  3943. char *value;
  3944. const char *caseless;
  3945. int exists;
  3946. caseless = predefined_attribute ("caseless", &argc, argv, TRUE);
  3947. if (bad_argc (argv[0], argc, 3, 3))
  3948. return;
  3949. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  3950. if (var == NULL)
  3951. {
  3952. var = lookup_variable (ARG (1), SYMBOL_INSERT);
  3953. SYMBOL_TEXT (var) = xstrdup (ARG (2));
  3954. SYMBOL_TYPE (var) = TOKEN_TEXT;
  3955. }
  3956. else if (SYMBOL_TYPE (var) != TOKEN_TEXT || *(SYMBOL_TEXT (var)) == '\0')
  3957. {
  3958. SYMBOL_TEXT (var) = xstrdup (ARG (2));
  3959. SYMBOL_TYPE (var) = TOKEN_TEXT;
  3960. }
  3961. else
  3962. {
  3963. exists = array_member (ARG (2), var, (caseless != NULL));
  3964. if (exists == -1)
  3965. {
  3966. value = (char *) xmalloc (strlen (SYMBOL_TEXT (var)) + strlen (ARG (2)) + 2);
  3967. sprintf (value, "%s\n%s", SYMBOL_TEXT (var), ARG (2));
  3968. xfree ((voidstar) SYMBOL_TEXT (var));
  3969. SYMBOL_TEXT (var) = xstrdup (value);
  3970. xfree ((voidstar) value);
  3971. }
  3972. }
  3973. }
  3974. /*------------------------------.
  3975. | Adds an element to an array. |
  3976. `------------------------------*/
  3977. static void
  3978. mp4h_bp_array_push (MP4H_BUILTIN_ARGS)
  3979. {
  3980. symbol *var;
  3981. char *old_value;
  3982. if (bad_argc (argv[0], argc, 2, 3))
  3983. return;
  3984. var = lookup_variable (ARG (1), SYMBOL_INSERT);
  3985. if (SYMBOL_TYPE (var) != TOKEN_TEXT)
  3986. {
  3987. SYMBOL_TEXT (var) = xstrdup (ARG (2));
  3988. SYMBOL_TYPE (var) = TOKEN_TEXT;
  3989. }
  3990. else
  3991. {
  3992. if (*(SYMBOL_TEXT (var)) == '\0')
  3993. {
  3994. xfree ((voidstar) SYMBOL_TEXT (var));
  3995. SYMBOL_TEXT (var) = xstrdup (ARG (2));
  3996. }
  3997. else
  3998. {
  3999. old_value = (char *) xmalloc (strlen (SYMBOL_TEXT (var)) + strlen (ARG (2)) + 2);
  4000. sprintf (old_value, "%s\n%s", SYMBOL_TEXT (var), ARG (2));
  4001. xfree ((voidstar) SYMBOL_TEXT (var));
  4002. SYMBOL_TEXT (var) = old_value;
  4003. }
  4004. }
  4005. }
  4006. /*---------------------------------------------------------.
  4007. | Remove the top level value from an array and return it. |
  4008. `---------------------------------------------------------*/
  4009. static void
  4010. mp4h_bp_array_pop (MP4H_BUILTIN_ARGS)
  4011. {
  4012. symbol *var;
  4013. char *cp;
  4014. if (bad_argc (argv[0], argc, 2, 2))
  4015. return;
  4016. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  4017. if (var == NULL)
  4018. return;
  4019. cp = strrchr (SYMBOL_TEXT (var), '\n');
  4020. if (cp)
  4021. {
  4022. *cp = '\0';
  4023. shipout_string (obs, cp+1, 0);
  4024. }
  4025. else
  4026. {
  4027. shipout_string (obs, SYMBOL_TEXT (var), 0);
  4028. *(SYMBOL_TEXT (var)) = '\0';
  4029. }
  4030. }
  4031. /*-------------------------------------------.
  4032. | Prints the value with the highest index. |
  4033. `-------------------------------------------*/
  4034. static void
  4035. mp4h_bp_array_topvalue (MP4H_BUILTIN_ARGS)
  4036. {
  4037. symbol *var;
  4038. char *cp;
  4039. if (bad_argc (argv[0], argc, 2, 2))
  4040. return;
  4041. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  4042. if (var == NULL)
  4043. return;
  4044. cp = strrchr (SYMBOL_TEXT (var), '\n');
  4045. if (cp)
  4046. shipout_string (obs, cp+1, 0);
  4047. else
  4048. shipout_string (obs, SYMBOL_TEXT (var), 0);
  4049. }
  4050. /*-----------------------------------------------------------------.
  4051. | Shifts an array. If offset is negative, first values are lost. |
  4052. | Otherwise, the array is shifted and null values are inserted at |
  4053. | the beginning of this array. |
  4054. | If the "start" attribute is set, elements below this one are |
  4055. | left unchanged. |
  4056. `-----------------------------------------------------------------*/
  4057. static void
  4058. mp4h_bp_array_shift (MP4H_BUILTIN_ARGS)
  4059. {
  4060. int offset, ind_start;
  4061. symbol *var;
  4062. char *cp, *value, *old_value;
  4063. const char *start;
  4064. int i;
  4065. if (bad_argc (argv[0], argc, 3, 4))
  4066. return;
  4067. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  4068. if (var == NULL)
  4069. {
  4070. MP4HERROR ((warning_status, 0,
  4071. _("Warning:%s:%d: Variable `%s' not defined in <%s>"),
  4072. CURRENT_FILE_LINE, ARG (1), ARG (0)));
  4073. }
  4074. if (!numeric_arg (argv[0], ARG (2), TRUE, &offset))
  4075. return;
  4076. if (SYMBOL_TYPE (var) != TOKEN_TEXT)
  4077. {
  4078. SYMBOL_TYPE (var) = TOKEN_TEXT;
  4079. SYMBOL_TEXT (var) = xstrdup ("");
  4080. }
  4081. ind_start = 0;
  4082. start = predefined_attribute ("start", &argc, argv, FALSE);
  4083. if (start)
  4084. {
  4085. if (!numeric_arg (argv[0], start, TRUE, &ind_start))
  4086. return;
  4087. }
  4088. if (offset == 0)
  4089. return;
  4090. value = (char *) xmalloc (strlen (SYMBOL_TEXT (var)) + offset + 1);
  4091. if (ind_start > 0)
  4092. {
  4093. old_value = array_value (var, ind_start, 0);
  4094. if (!old_value)
  4095. return;
  4096. *(old_value-1) = '\0';
  4097. strcpy ((char *) value, SYMBOL_TEXT (var));
  4098. cp = value + strlen (SYMBOL_TEXT (var)) + 1;
  4099. *(cp-1) = '\n';
  4100. *(old_value-1) = '\n';
  4101. }
  4102. else
  4103. {
  4104. old_value = SYMBOL_TEXT (var);
  4105. cp = value;
  4106. }
  4107. *cp = '\0';
  4108. if (offset > 0)
  4109. {
  4110. for (i=0; i<offset; i++)
  4111. *(cp+i) = '\n';
  4112. *(cp+offset) = '\0';
  4113. strcpy ((char *) (cp+offset), old_value);
  4114. }
  4115. else
  4116. {
  4117. old_value = array_value (var, ind_start-offset, 0);
  4118. if (!old_value)
  4119. return;
  4120. strcpy ((char *) cp, old_value);
  4121. }
  4122. xfree ((voidstar) SYMBOL_TEXT (var));
  4123. SYMBOL_TEXT (var) = value;
  4124. }
  4125. /*------------------------------.
  4126. | Concatenate multiple arrays. |
  4127. `------------------------------*/
  4128. static void
  4129. mp4h_bp_array_concat (MP4H_BUILTIN_ARGS)
  4130. {
  4131. symbol *var, *varadd;
  4132. char *value;
  4133. int i;
  4134. if (bad_argc (argv[0], argc, 3, 0))
  4135. return;
  4136. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  4137. if (var == NULL)
  4138. {
  4139. var = lookup_variable (ARG (1), SYMBOL_INSERT);
  4140. SYMBOL_TEXT (var) = xstrdup ("");
  4141. SYMBOL_TYPE (var) = TOKEN_TEXT;
  4142. }
  4143. for (i=2; i<argc; i++)
  4144. {
  4145. varadd = lookup_variable (ARG (i), SYMBOL_LOOKUP);
  4146. if (varadd == NULL)
  4147. continue;
  4148. if (SYMBOL_TYPE (varadd) != TOKEN_TEXT)
  4149. continue;
  4150. value = (char *) xmalloc (strlen (SYMBOL_TEXT (var)) +
  4151. strlen (SYMBOL_TEXT (varadd)) + 2);
  4152. sprintf (value, "%s\n%s", SYMBOL_TEXT (var), SYMBOL_TEXT (varadd));
  4153. xfree ((voidstar) SYMBOL_TEXT (var));
  4154. SYMBOL_TEXT (var) = xstrdup (value);
  4155. xfree ((voidstar) value);
  4156. }
  4157. }
  4158. /*---------------------------------------------------------------------.
  4159. | This function is used to sort arrays. Arguments are either strings |
  4160. | or numerical values. |
  4161. `---------------------------------------------------------------------*/
  4162. static int
  4163. sort_function (const void *item1, const void *item2)
  4164. {
  4165. char *string1, *string2;
  4166. int result;
  4167. double val1, val2;
  4168. string1 = *(char **)item1;
  4169. string2 = *(char **)item2;
  4170. if (sort_numeric)
  4171. {
  4172. val1 = strtod (string1, 0);
  4173. val2 = strtod (string2, 0);
  4174. if ( val1 == val2 )
  4175. result = 0;
  4176. if ( val1 > val2 )
  4177. result = 1;
  4178. else
  4179. result = -1;
  4180. }
  4181. else
  4182. {
  4183. if (sort_caseless)
  4184. result = strcasecmp (string1, string2);
  4185. else
  4186. result = strcmp (string1, string2);
  4187. }
  4188. if (sort_sortorder)
  4189. result = - result;
  4190. return result;
  4191. }
  4192. static void
  4193. mp4h_bp_sort (MP4H_BUILTIN_ARGS)
  4194. {
  4195. symbol *var;
  4196. const char *caseless, *numeric, *sortorder;
  4197. char *cp, *value;
  4198. char **array;
  4199. int length, size, i;
  4200. caseless = predefined_attribute ("caseless", &argc, argv, TRUE);
  4201. sortorder = predefined_attribute ("sortorder", &argc, argv, TRUE);
  4202. numeric = predefined_attribute ("numeric", &argc, argv, TRUE);
  4203. if (bad_argc (argv[0], argc, 2, 2))
  4204. return;
  4205. var = lookup_variable (ARG (1), SYMBOL_LOOKUP);
  4206. if (var == NULL)
  4207. return;
  4208. sort_caseless = (caseless != NULL);
  4209. sort_numeric = (numeric != NULL);
  4210. if (sortorder && strcmp (sortorder, "reverse") == 0)
  4211. sort_sortorder = TRUE;
  4212. else
  4213. sort_sortorder = FALSE;
  4214. length = strlen (SYMBOL_TEXT (var));
  4215. size = array_size (var);
  4216. /* Build a pointer to array values. All newlines are replaced by
  4217. NULL chars to use standard string comparison functions. */
  4218. array = (char **) xmalloc ((size+1) * sizeof (char *));
  4219. i = 0;
  4220. value = xstrdup (SYMBOL_TEXT (var));
  4221. array[i] = value;
  4222. for (cp=value; *cp != '\0'; cp++)
  4223. {
  4224. if (*cp == '\n')
  4225. {
  4226. *cp = '\0';
  4227. i++;
  4228. array[i] = cp + 1;
  4229. }
  4230. }
  4231. qsort ((void *)array, size, sizeof (char *), sort_function);
  4232. strcpy (SYMBOL_TEXT (var), array[0]);
  4233. for (i=1; i<size; i++)
  4234. {
  4235. strcat (SYMBOL_TEXT (var), "\n");
  4236. strcat (SYMBOL_TEXT (var), array[i]);
  4237. }
  4238. xfree ((voidstar) array);
  4239. xfree ((voidstar) value);
  4240. }
  4241. /* This section contains the macros "divert", "undivert" and "divnum" for
  4242. handling diversion. The utility functions used lives in output.c. */
  4243. /*-----------------------------------------------------------------------.
  4244. | Divert further output to the diversion given by ARGV[1]. Out of range |
  4245. | means discard further output. |
  4246. `-----------------------------------------------------------------------*/
  4247. static void
  4248. mp4h_bp_divert (MP4H_BUILTIN_ARGS)
  4249. {
  4250. const char *divnum;
  4251. int i = 0;
  4252. divnum = predefined_attribute ("divnum", &argc, argv, TRUE);
  4253. if (!divnum || numeric_arg (argv[0], divnum, TRUE, &i))
  4254. make_diversion (i);
  4255. }
  4256. /*-----------------------------------------------------.
  4257. | Expand to the current diversion number, -1 if none. |
  4258. `-----------------------------------------------------*/
  4259. static void
  4260. mp4h_bp_divnum (MP4H_BUILTIN_ARGS)
  4261. {
  4262. if (bad_argc (argv[0], argc, 1, 1))
  4263. return;
  4264. shipout_int (obs, current_diversion);
  4265. }
  4266. /*-----------------------------------------------------------------------.
  4267. | Bring back the diversion given by the divnum attribute. If none is |
  4268. | specified, bring back all diversions. |
  4269. `-----------------------------------------------------------------------*/
  4270. static void
  4271. mp4h_bp_undivert (MP4H_BUILTIN_ARGS)
  4272. {
  4273. int i;
  4274. const char *divnum;
  4275. divnum = predefined_attribute ("divnum", &argc, argv, FALSE);
  4276. if (!divnum)
  4277. undivert_all ();
  4278. else
  4279. if (numeric_arg (argv[0], divnum, TRUE, &i))
  4280. insert_diversion (i);
  4281. }
  4282. /*-------------------------------------------------------------------------.
  4283. | This function handles all expansion of user defined and predefined |
  4284. | macros. It is called with an obstack OBS, where the macros expansion |
  4285. | will be placed, as an unfinished object. SYM points to the macro |
  4286. | definition, giving the expansion text. ARGC and ARGV are the arguments, |
  4287. | as usual. |
  4288. | This function is called by call_macro (). |
  4289. `-------------------------------------------------------------------------*/
  4290. void
  4291. expand_user_macro (struct obstack *obs, symbol *sym, int argc,
  4292. token_data **argv, read_type expansion)
  4293. {
  4294. const char *text, *save;
  4295. int i, len;
  4296. boolean unexpanded;
  4297. char sep[2];
  4298. sep[1] = '\0';
  4299. for (text = SYMBOL_TEXT (sym); *text != '\0';)
  4300. {
  4301. if (*text != '%')
  4302. {
  4303. save = text;
  4304. len = 1;
  4305. for (text++ ; *text != '%' && *text != '\0'; text++)
  4306. len++;
  4307. obstack_grow (obs, save, len);
  4308. if (*text == '\0')
  4309. break;
  4310. }
  4311. text++;
  4312. unexpanded = FALSE;
  4313. sep[0] = ' ';
  4314. save = text;
  4315. if (*text == '#')
  4316. {
  4317. shipout_int (obs, argc - 1);
  4318. text++;
  4319. continue;
  4320. }
  4321. else if (*text == '%')
  4322. {
  4323. obstack_1grow (obs, '%');
  4324. text++;
  4325. continue;
  4326. }
  4327. while (*text == 'U' || *text == 'A' || *text == 'u' || *text == 'y')
  4328. {
  4329. if (*text == 'U' || *text == 'u')
  4330. unexpanded = TRUE;
  4331. if (*text == 'A' || *text == 'y')
  4332. sep[0] = '\n';
  4333. text++;
  4334. }
  4335. if (isdigit ((int) *text))
  4336. {
  4337. char *endp;
  4338. i = (int)strtol (text, &endp, 10) + 1;
  4339. text = endp;
  4340. obstack_1grow (obs, CHAR_BGROUP);
  4341. if (i < argc)
  4342. obstack_grow (obs, ARG (i), strlen (ARG (i)));
  4343. obstack_1grow (obs, CHAR_EGROUP);
  4344. }
  4345. else if (strncmp (text, "body", 4) == 0
  4346. || strncmp (text, "xbody", 5) == 0
  4347. || strncmp (text, "qbody", 5) == 0)
  4348. {
  4349. if (unexpanded)
  4350. obstack_1grow (obs, CHAR_LQUOTE);
  4351. else
  4352. obstack_1grow (obs, CHAR_BGROUP);
  4353. if (SYMBOL_CONTAINER (sym))
  4354. dump_args (obs, 2, (argv+argc-1), sep);
  4355. else
  4356. dump_args (obs, argc, argv, sep);
  4357. if (unexpanded)
  4358. obstack_1grow (obs, CHAR_RQUOTE);
  4359. else
  4360. obstack_1grow (obs, CHAR_EGROUP);
  4361. if (*text == 'b')
  4362. text += 4;
  4363. else
  4364. text += 5;
  4365. }
  4366. else if (strncmp (text, "attributes", 10) == 0
  4367. || strncmp (text, "xattributes", 11) == 0
  4368. || strncmp (text, "qattributes", 11) == 0)
  4369. {
  4370. if (*text == 'a')
  4371. text += 10;
  4372. else
  4373. text += 11;
  4374. if (unexpanded)
  4375. {
  4376. for (i = 1; i < argc; i++)
  4377. {
  4378. if (i > 1)
  4379. obstack_grow (obs, sep, strlen (sep));
  4380. obstack_1grow (obs, CHAR_LQUOTE);
  4381. obstack_grow (obs, ARG (i), strlen (ARG (i)));
  4382. obstack_1grow (obs, CHAR_RQUOTE);
  4383. }
  4384. }
  4385. else
  4386. dump_args (obs, argc, argv, sep);
  4387. }
  4388. else if (strncmp (text, "name", 4) == 0)
  4389. {
  4390. obstack_grow (obs, SYMBOL_NAME (sym), strlen (SYMBOL_NAME (sym)));
  4391. text += 4;
  4392. }
  4393. else
  4394. {
  4395. obstack_1grow (obs, '%');
  4396. text = save;
  4397. }
  4398. }
  4399. }