~ubuntu-branches/ubuntu/feisty/mp4h/feisty

« back to all changes in this revision

Viewing changes to src/macro.c

  • Committer: Bazaar Package Importer
  • Author(s): Denis Barbier
  • Date: 2003-11-06 20:34:24 UTC
  • Revision ID: james.westby@ubuntu.com-20031106203424-nqx14tkfeirx01r3
Tags: upstream-1.3.1
ImportĀ upstreamĀ versionĀ 1.3.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* mp4h -- A macro processor for HTML documents
 
2
   Copyright 2000-2003, Denis Barbier
 
3
   All rights reserved.
 
4
 
 
5
   This program is free software; you can redistribute it and/or modify
 
6
   it under the terms of the GNU General Public License as published by
 
7
   the Free Software Foundation; either version 2, or (at your option)
 
8
   any later version.
 
9
 
 
10
   This program is a work based on GNU m4 version 1.4n. Below is the
 
11
   original copyright.
 
12
*/
 
13
/* GNU m4 -- A simple macro processor
 
14
   Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc.
 
15
  
 
16
   This program is free software; you can redistribute it and/or modify
 
17
   it under the terms of the GNU General Public License as published by
 
18
   the Free Software Foundation; either version 2, or (at your option)
 
19
   any later version.
 
20
  
 
21
   This program is distributed in the hope that it will be useful,
 
22
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
23
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
24
   GNU General Public License for more details.
 
25
  
 
26
   You should have received a copy of the GNU General Public License
 
27
   along with this program; if not, write to the Free Software
 
28
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
29
*/
 
30
 
 
31
/* This file contains the functions, that performs the basic argument
 
32
   parsing and macro expansion.  */
 
33
 
 
34
#include "mp4h.h"
 
35
 
 
36
static void expand_macro __P ((symbol *, read_type));
 
37
static void expand_unknown_tag __P ((char *, read_type));
 
38
static void expand_entity __P ((symbol *, read_type));
 
39
static void expand_token __P ((struct obstack *, read_type, token_type, token_data *));
 
40
 
 
41
/* Current recursion level in expand_macro ().  */
 
42
int expansion_level = 0;
 
43
 
 
44
/* The number of the current call of expand_macro ().  */
 
45
static int macro_call_id = 0;
 
46
 
 
47
/* global variable to abort expansion */
 
48
static boolean internal_abort = FALSE;
 
49
 
 
50
/*----------------------------------------------------------------------.
 
51
| This function read all input, and expands each token, one at a time.  |
 
52
`----------------------------------------------------------------------*/
 
53
 
 
54
void
 
55
expand_input (void)
 
56
{
 
57
  token_type t;
 
58
  token_data td;
 
59
 
 
60
  while ((t = next_token (&td, READ_NORMAL, FALSE)) != TOKEN_EOF)
 
61
    expand_token ((struct obstack *) NULL, READ_NORMAL, t, &td);
 
62
}
 
63
 
 
64
 
 
65
/*------------------------------------------------------------------------.
 
66
| Expand one token, according to its type.  Potential macro names         |
 
67
| (TOKEN_WORD) are looked up in the symbol table, to see if they have a   |
 
68
| macro definition.  If they have, they are expanded as macros, otherwise |
 
69
| the text are just copied to the output.                                 |
 
70
`------------------------------------------------------------------------*/
 
71
 
 
72
static void
 
73
expand_token (struct obstack *obs, read_type expansion, token_type t,
 
74
        token_data *td)
 
75
{
 
76
  symbol *sym;
 
77
  char *append_semicolon;
 
78
  char *text = TOKEN_DATA_TEXT (td);
 
79
 
 
80
  switch (t)
 
81
    {                           /* TOKSW */
 
82
    case TOKEN_EOF:
 
83
    case TOKEN_MACDEF:
 
84
    case TOKEN_BGROUP:
 
85
    case TOKEN_EGROUP:
 
86
      break;
 
87
 
 
88
    case TOKEN_QUOTED:
 
89
    case TOKEN_SIMPLE:
 
90
    case TOKEN_SPACE:
 
91
    case TOKEN_STRING:
 
92
      if (expansion_level > 0 && t == TOKEN_QUOTED)
 
93
        obstack_1grow (obs, CHAR_LQUOTE);
 
94
      shipout_text (obs, text);
 
95
      if (expansion_level > 0 && t == TOKEN_QUOTED)
 
96
        obstack_1grow (obs, CHAR_RQUOTE);
 
97
      break;
 
98
 
 
99
    case TOKEN_WORD:
 
100
      if (IS_TAG(*text))
 
101
        text++;
 
102
      else
 
103
        MP4HERROR ((warning_status, 0,
 
104
          "INTERNAL ERROR: macro has no leading '<' in expand_token ()"));
 
105
 
 
106
      /* macro names must begin with a letter or an underscore.
 
107
         If another character is found, this string is not a
 
108
         macro.   */
 
109
 
 
110
      if (IS_ALPHA (*text))
 
111
        {
 
112
          sym = lookup_symbol (text, SYMBOL_LOOKUP);
 
113
          if (sym == NULL || SYMBOL_TYPE (sym) == TOKEN_VOID)
 
114
            {
 
115
              if (exp_flags & EXP_NO_HTMLTAG)
 
116
                shipout_text (obs, TOKEN_DATA_TEXT (td));
 
117
              else
 
118
                expand_unknown_tag (text, expansion);
 
119
            }
 
120
          else
 
121
            expand_macro (sym, expansion);
 
122
        }
 
123
      else if (*text == '/' && IS_ALPHA (*(text+1)))
 
124
        {
 
125
          sym = lookup_symbol (text+1, SYMBOL_LOOKUP);
 
126
          if (sym != NULL)
 
127
            {
 
128
              MP4HERROR ((warning_status, 0,
 
129
                _("Warning:%s:%d: `<%s>' found without begin tag"),
 
130
                    CURRENT_FILE_LINE, text));
 
131
            }
 
132
          expand_unknown_tag (text, expansion);
 
133
        }
 
134
      else
 
135
        shipout_text (obs, TOKEN_DATA_TEXT (td));
 
136
      break;
 
137
 
 
138
    case TOKEN_ENTITY:
 
139
      /* entity names must begin with a letter or an underscore.
 
140
         If another character is found, this string is not an
 
141
         entity.   */
 
142
 
 
143
      if (IS_ENTITY(*text))
 
144
        text++;
 
145
      else
 
146
        MP4HERROR ((warning_status, 0,
 
147
          "INTERNAL ERROR: entity has no leading '&' in expand_token ()"));
 
148
 
 
149
      if (IS_ALPHA (*text))
 
150
        {
 
151
          append_semicolon = NULL;
 
152
          if (LAST_CHAR (text) == ';')
 
153
            {
 
154
              append_semicolon = text + strlen (text) - 1;
 
155
              *append_semicolon = '\0';
 
156
            }
 
157
 
 
158
          sym = lookup_entity (text, SYMBOL_LOOKUP);
 
159
          if (sym == NULL || SYMBOL_TYPE (sym) == TOKEN_VOID)
 
160
            {
 
161
              if (append_semicolon)
 
162
                *(append_semicolon) = ';';
 
163
              shipout_text (obs, TOKEN_DATA_TEXT (td));
 
164
            }
 
165
          else
 
166
            expand_entity (sym, expansion);
 
167
        }
 
168
      else
 
169
        shipout_text (obs, TOKEN_DATA_TEXT (td));
 
170
      break;
 
171
 
 
172
    default:
 
173
      MP4HERROR ((warning_status, 0,
 
174
        "INTERNAL ERROR: Bad token type in expand_token ()"));
 
175
      exit (1);
 
176
    }
 
177
}
 
178
 
 
179
 
 
180
 
 
181
/*-------------------------------------------------------------------------.
 
182
| This function parses one argument to a macro call.  It skips leading     |
 
183
| whitespace and reads and expands tokens until it finds a space outside   |
 
184
| of any group, or a right angle bracket.  It returns a flag indicating    |
 
185
| whether the argument read are the last for the active macro call.  The   |
 
186
| argument read are the last for the active macro call.  The argument are  |
 
187
| on the obstack OBS, indirectly through expand_token ().                  |
 
188
| On exit, this function returns                                           |
 
189
|      0: close bracket found                                              |
 
190
|      1: last argument                                                    |
 
191
|      2: other arguments follow                                           |
 
192
`-------------------------------------------------------------------------*/
 
193
 
 
194
static int
 
195
expand_argument (struct obstack *obs, read_type expansion, token_data *argp,
 
196
                 char *last_char_ptr)
 
197
{
 
198
  char *text;
 
199
  token_type t;
 
200
  token_data td;
 
201
  int group_level = 0;
 
202
  int rc;
 
203
  boolean in_string = FALSE;
 
204
 
 
205
  *last_char_ptr = ' ';
 
206
  TOKEN_DATA_TYPE (argp) = TOKEN_VOID;
 
207
 
 
208
  /* Skip leading white space.  */
 
209
  t = next_token (&td, expansion, FALSE);
 
210
  if (t == TOKEN_SPACE)
 
211
    {
 
212
      if (expansion == READ_ATTR_ASIS || expansion == READ_ATTR_QUOT)
 
213
        {
 
214
          /*  We are parsing attributes of a command which will be
 
215
              rescanned, so whitepsaces are preserved.  */
 
216
          obstack_grow (obs, TOKEN_DATA_TEXT (&td),
 
217
                  strlen (TOKEN_DATA_TEXT (&td)));
 
218
        }
 
219
      t = next_token (&td, expansion, FALSE);
 
220
    }
 
221
 
 
222
  rc = 0;
 
223
  while (1)
 
224
    {
 
225
      switch (t)
 
226
        {                       /* TOKSW */
 
227
        case TOKEN_SPACE:
 
228
        case TOKEN_SIMPLE:
 
229
          text = TOKEN_DATA_TEXT (&td);
 
230
          if ((IS_SPACE(*text) || IS_CLOSE(*text))
 
231
               && !in_string && (group_level == 0))
 
232
            {
 
233
 
 
234
              if (t == TOKEN_SPACE &&
 
235
                  (expansion == READ_ATTR_ASIS || expansion == READ_ATTR_QUOT))
 
236
                {
 
237
                  obstack_grow (obs, TOKEN_DATA_TEXT (&td),
 
238
                          strlen (TOKEN_DATA_TEXT (&td)));
 
239
                }
 
240
 
 
241
              /* The argument MUST be finished, whether we want it or not.  */
 
242
              obstack_1grow (obs, '\0');
 
243
 
 
244
              if (TOKEN_DATA_TYPE (argp) == TOKEN_VOID)
 
245
                {
 
246
                  TOKEN_DATA_TYPE (argp) = TOKEN_TEXT;
 
247
                  TOKEN_DATA_TEXT (argp) = obstack_finish (obs);
 
248
                }
 
249
              if (IS_SPACE(*TOKEN_DATA_TEXT (&td)))
 
250
                rc = 2;
 
251
              return rc;
 
252
            }
 
253
          expand_token (obs, expansion, t, &td);
 
254
          break;
 
255
 
 
256
        case TOKEN_EOF:
 
257
          internal_abort = TRUE;
 
258
          return FALSE;
 
259
          break;
 
260
 
 
261
        case TOKEN_BGROUP:
 
262
          if (expansion == READ_ATTR_ASIS && group_level == 0)
 
263
            obstack_1grow (obs, CHAR_BGROUP);
 
264
          group_level++;
 
265
          break;
 
266
 
 
267
        case TOKEN_EGROUP:
 
268
          group_level--;
 
269
          if (expansion == READ_ATTR_ASIS && group_level == 0)
 
270
            obstack_1grow (obs, CHAR_EGROUP);
 
271
          break;
 
272
 
 
273
        case TOKEN_QUOTE:
 
274
          in_string = !in_string;
 
275
          obstack_grow (obs, TOKEN_DATA_TEXT (&td),
 
276
                  strlen (TOKEN_DATA_TEXT (&td)));
 
277
          break;
 
278
 
 
279
        case TOKEN_QUOTED:
 
280
        case TOKEN_STRING:
 
281
          if (expansion_level > 0 && t == TOKEN_QUOTED)
 
282
            obstack_1grow (obs, CHAR_LQUOTE);
 
283
          obstack_grow (obs, TOKEN_DATA_TEXT (&td),
 
284
                  strlen (TOKEN_DATA_TEXT (&td)));
 
285
          if (expansion_level > 0 && t == TOKEN_QUOTED)
 
286
            obstack_1grow (obs, CHAR_RQUOTE);
 
287
          break;
 
288
 
 
289
        case TOKEN_WORD:
 
290
        case TOKEN_ENTITY:
 
291
          expand_token (obs, expansion, t, &td);
 
292
          break;
 
293
 
 
294
        case TOKEN_MACDEF:
 
295
          if (obstack_object_size (obs) == 0)
 
296
            {
 
297
              TOKEN_DATA_TYPE (argp) = TOKEN_FUNC;
 
298
              TOKEN_DATA_FUNC (argp) = TOKEN_DATA_FUNC (&td);
 
299
              TOKEN_DATA_FUNC_TRACED (argp) = TOKEN_DATA_FUNC_TRACED (&td);
 
300
            }
 
301
          break;
 
302
 
 
303
        default:
 
304
          MP4HERROR ((warning_status, 0,
 
305
            "INTERNAL ERROR: Bad token type in expand_argument ()"));
 
306
          exit (1);
 
307
        }
 
308
 
 
309
      text = TOKEN_DATA_TEXT (&td);
 
310
      if (*text == '\0')
 
311
        *last_char_ptr = '\0';
 
312
      else
 
313
        *last_char_ptr = LAST_CHAR (text);
 
314
      t = next_token (&td, expansion, in_string);
 
315
      rc = 1;
 
316
    }
 
317
}
 
318
 
 
319
/*-------------------------------------------------------------------------.
 
320
| Collect all the arguments to a call of the macro SYM.  The arguments are |
 
321
| stored on the obstack ARGUMENTS and a table of pointers to the arguments |
 
322
| on the obstack ARGPTR.                                                   |
 
323
| If there is a slash character before closing bracket, this function      |
 
324
| returns TRUE, otherwise FALSE.                                           |
 
325
`-------------------------------------------------------------------------*/
 
326
 
 
327
static boolean
 
328
collect_arguments (char *symbol_name, read_type expansion,
 
329
                   struct obstack *argptr, struct obstack *arguments)
 
330
{
 
331
  int ch;
 
332
  token_data td;
 
333
  token_data *tdp;
 
334
  char *last_addr;
 
335
  int more_args;
 
336
  char last_char = ' ';
 
337
 
 
338
  TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
 
339
  TOKEN_DATA_TEXT (&td) = symbol_name;
 
340
  tdp = (token_data *) obstack_copy (arguments, (voidstar) &td, sizeof (td));
 
341
  obstack_grow (argptr, (voidstar) &tdp, sizeof (tdp));
 
342
  TOKEN_DATA_TYPE (&td) = TOKEN_VOID;
 
343
 
 
344
  ch = peek_input ();
 
345
  if (IS_CLOSE (ch))
 
346
    (void) next_token (&td, READ_BODY, FALSE);
 
347
  else if (IS_SPACE(ch) || IS_TAG(ch) || IS_SLASH(ch))
 
348
    {
 
349
      do
 
350
        {
 
351
          /*  remember last address in use to remove the last
 
352
              argument if it is empty.  */
 
353
          last_addr = argptr->next_free;
 
354
          more_args = expand_argument (arguments, expansion, &td, &last_char);
 
355
          if (internal_abort)
 
356
            {
 
357
              MP4HERROR ((EXIT_FAILURE, 0,
 
358
                _("ERROR:%s:%d: EOF when reading argument of the <%s> tag"),
 
359
                     CURRENT_FILE_LINE, symbol_name));
 
360
            }
 
361
 
 
362
          tdp = (token_data *)
 
363
            obstack_copy (arguments, (voidstar) &td, sizeof (td));
 
364
          obstack_grow (argptr, (voidstar) &tdp, sizeof (tdp));
 
365
        }
 
366
      while (more_args == 2);
 
367
 
 
368
      /*  If the last argument is empty, it is removed.  We need it to
 
369
          remove white spaces before closing brackets.  */
 
370
      if (more_args == 0)
 
371
        argptr->next_free = last_addr;
 
372
    }
 
373
  else
 
374
    {
 
375
      MP4HERROR ((warning_status, 0,
 
376
        "INTERNAL ERROR: Bad tag expression in `%s'", symbol_name));
 
377
    }
 
378
  return (IS_SLASH(last_char));
 
379
}
 
380
 
 
381
/*-----------------------------------------------------------------.
 
382
| Collect the body of a container tag. No expansion is performed,  |
 
383
| but when a macro is found its arguments are collected.  This is  |
 
384
| necessary to deal with nested expressions.                       |
 
385
`-----------------------------------------------------------------*/
 
386
static void
 
387
collect_body (char *symbol_name, read_type expansion,
 
388
              struct obstack *argptr, struct obstack *bodyptr)
 
389
{
 
390
  token_type t;
 
391
  token_data td;
 
392
  token_data *tdp;
 
393
  char *text;
 
394
  symbol *newsym;
 
395
 
 
396
  while (1)
 
397
    {
 
398
      t = next_token (&td, expansion, FALSE);
 
399
      text = TOKEN_DATA_TEXT (&td);
 
400
      switch (t)
 
401
        {                                /* TOKSW */
 
402
        case TOKEN_EOF:
 
403
        case TOKEN_MACDEF:
 
404
          MP4HERROR ((EXIT_FAILURE, 0,
 
405
            _("ERROR:%s:%d: EOF when reading body of the <%s> tag"),
 
406
                 CURRENT_FILE_LINE, symbol_name));
 
407
          break;
 
408
 
 
409
        case TOKEN_BGROUP:
 
410
        case TOKEN_EGROUP:
 
411
          break;
 
412
 
 
413
        case TOKEN_QUOTE:
 
414
          obstack_1grow (bodyptr, CHAR_QUOTE);
 
415
          break;
 
416
 
 
417
        case TOKEN_QUOTED:
 
418
        case TOKEN_SIMPLE:
 
419
        case TOKEN_SPACE:
 
420
        case TOKEN_STRING:
 
421
        case TOKEN_ENTITY:
 
422
          if (expansion_level > 0 && t == TOKEN_QUOTED)
 
423
            obstack_1grow (bodyptr, CHAR_LQUOTE);
 
424
          shipout_text (bodyptr, text);
 
425
          if (expansion_level > 0 && t == TOKEN_QUOTED)
 
426
            obstack_1grow (bodyptr, CHAR_RQUOTE);
 
427
          break;
 
428
 
 
429
        case TOKEN_WORD:
 
430
          if (IS_TAG(*text))
 
431
            text++;
 
432
 
 
433
          if (*text == '/')
 
434
            {
 
435
              text++;
 
436
              if (strcasecmp (text, symbol_name) == 0)
 
437
                {
 
438
                  /*  gobble closing bracket */
 
439
                  do
 
440
                    {
 
441
                      t = next_token (&td, expansion, FALSE);
 
442
                    }
 
443
                  while (! IS_CLOSE(*TOKEN_DATA_TEXT (&td)))
 
444
                    ;
 
445
 
 
446
                  obstack_1grow (bodyptr, '\0');
 
447
 
 
448
                  /* Add body to argptr */
 
449
                  TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
 
450
                  TOKEN_DATA_TEXT (&td) = obstack_finish (bodyptr);
 
451
                  tdp = (token_data *) obstack_copy (bodyptr,
 
452
                      (voidstar) &td, sizeof (td));
 
453
                  obstack_grow (argptr, (voidstar) &tdp, sizeof (tdp));
 
454
 
 
455
                  return;
 
456
                }
 
457
              else
 
458
                {
 
459
                  newsym = lookup_symbol (text, SYMBOL_LOOKUP);
 
460
                  if (newsym)
 
461
                    {
 
462
                      if (!(exp_flags & EXP_NOWARN_NEST))
 
463
                        MP4HERROR ((warning_status, 0,
 
464
                          _("Warning:%s:%d: `</%s>' found when `</%s>' expected"),
 
465
                               CURRENT_FILE_LINE, text, symbol_name));
 
466
                      if (exp_flags & EXP_UNM_BREAK)
 
467
                        {
 
468
                          /*  Premature end of body parsing.  */
 
469
                          obstack_1grow (bodyptr, '\0');
 
470
                          TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
 
471
                          TOKEN_DATA_TEXT (&td) = obstack_finish (bodyptr);
 
472
                          tdp = (token_data *) obstack_copy (bodyptr,
 
473
                              (voidstar) &td, sizeof (td));
 
474
                          obstack_grow (argptr, (voidstar) &tdp, sizeof (tdp));
 
475
 
 
476
                          /*  Push read input back on the stack  */
 
477
                          unget_string (text-2);
 
478
 
 
479
                          return;
 
480
                        }
 
481
                    }
 
482
                  obstack_grow (bodyptr, TOKEN_DATA_TEXT (&td),
 
483
                          strlen(TOKEN_DATA_TEXT (&td)) );
 
484
                  /*  gobble closing bracket */
 
485
                  do
 
486
                    {
 
487
                      t = next_token (&td, expansion, FALSE);
 
488
                      obstack_grow (bodyptr, TOKEN_DATA_TEXT (&td),
 
489
                              strlen(TOKEN_DATA_TEXT (&td)) );
 
490
                    }
 
491
                  while (! IS_CLOSE(*TOKEN_DATA_TEXT (&td)))
 
492
                    ;
 
493
                }
 
494
            }
 
495
          else
 
496
            {
 
497
              newsym = lookup_symbol (text, SYMBOL_LOOKUP);
 
498
              if (newsym == NULL || SYMBOL_TYPE (newsym) == TOKEN_VOID)
 
499
                {
 
500
                  if (exp_flags & EXP_NO_HTMLTAG)
 
501
                    shipout_text (bodyptr, TOKEN_DATA_TEXT (&td));
 
502
                  else
 
503
                    expand_unknown_tag (text, expansion);
 
504
                }
 
505
              else
 
506
                expand_macro (newsym, expansion);
 
507
            }
 
508
          break;
 
509
 
 
510
        default:
 
511
          MP4HERROR ((warning_status, 0,
 
512
            "INTERNAL ERROR:%d: Bad token type in collect_body ()", t));
 
513
          exit (1);
 
514
        }
 
515
    }
 
516
}
 
517
 
 
518
 
 
519
/*------------------------------------------------------------------------.
 
520
| The actual call of a macro is handled by call_macro ().  call_macro ()  |
 
521
| is passed a symbol SYM, whose type is used to call either a builtin     |
 
522
| function, or the user macro expansion function expand_user_macro ()     |
 
523
| (lives in builtin.c).  There are ARGC arguments to the call, stored in  |
 
524
| the ARGV table.  The expansion is left on the obstack EXPANSION.  Macro |
 
525
| tracing is also handled here.                                           |
 
526
`------------------------------------------------------------------------*/
 
527
 
 
528
void
 
529
call_macro (symbol *sym, struct obstack *obs, int argc, token_data **argv,
 
530
                 read_type expansion)
 
531
{
 
532
  if (SYMBOL_HOOK_BEGIN (sym))
 
533
    obstack_grow (obs, SYMBOL_HOOK_BEGIN (sym),
 
534
                        strlen (SYMBOL_HOOK_BEGIN (sym)));
 
535
  switch (SYMBOL_TYPE (sym))
 
536
    {
 
537
    case TOKEN_FUNC:
 
538
      (*SYMBOL_FUNC (sym)) (obs, argc, argv, expansion);
 
539
      break;
 
540
 
 
541
    case TOKEN_TEXT:
 
542
      expand_user_macro (obs, sym, argc, argv, expansion);
 
543
      break;
 
544
 
 
545
    default:
 
546
      MP4HERROR ((warning_status, 0,
 
547
        "INTERNAL ERROR: Bad symbol type in call_macro ()"));
 
548
      exit (1);
 
549
    }
 
550
  if (SYMBOL_HOOK_END (sym))
 
551
    obstack_grow (obs, SYMBOL_HOOK_END (sym),
 
552
                        strlen (SYMBOL_HOOK_END (sym)));
 
553
}
 
554
 
 
555
/*-------------------------------------------------------------------------.
 
556
| The macro expansion is handled by expand_macro ().  It parses the        |
 
557
| arguments, using collect_arguments (), and builds a table of pointers to |
 
558
| the arguments.  The arguments themselves are stored on a local obstack.  |
 
559
| Expand_macro () uses call_macro () to do the call of the macro.          |
 
560
|                                                                          |
 
561
| Expand_macro () is potentially recursive, since it calls expand_argument |
 
562
| (), which might call expand_token (), which might call expand_macro ().  |
 
563
`-------------------------------------------------------------------------*/
 
564
 
 
565
static void
 
566
expand_macro (symbol *sym, read_type expansion)
 
567
{
 
568
  struct obstack arguments, argptr, body;
 
569
  token_data **argv;
 
570
  token_data td;
 
571
  token_data *tdp;
 
572
  int argc, i;
 
573
  struct obstack *obs_expansion;
 
574
  const char *expanded;
 
575
  char *cp;
 
576
  boolean traced, slash;
 
577
  int my_call_id;
 
578
  read_type attr_expansion;
 
579
 
 
580
  expansion_level++;
 
581
  if (expansion_level > nesting_limit)
 
582
    MP4HERROR ((EXIT_FAILURE, 0,
 
583
      _("ERROR: Recursion limit of %d exceeded, use -L<N> to change it"),
 
584
           nesting_limit));
 
585
 
 
586
  array_current_line[expansion_level] = current_line;
 
587
  xfree((voidstar) array_current_file[expansion_level]);
 
588
  array_current_file[expansion_level] = xstrdup(current_file);
 
589
 
 
590
  macro_call_id++;
 
591
  my_call_id = macro_call_id;
 
592
 
 
593
  traced = (boolean) ((debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym));
 
594
 
 
595
  obstack_init (&arguments);
 
596
  obstack_init (&argptr);
 
597
  obstack_init (&body);
 
598
 
 
599
  if (traced && (debug_level & DEBUG_TRACE_CALL))
 
600
    trace_prepre (SYMBOL_NAME (sym), my_call_id);
 
601
 
 
602
  if (expansion == READ_ATTR_ASIS || expansion == READ_ATTR_VERB
 
603
      || expansion == READ_BODY)
 
604
    attr_expansion = READ_ATTR_ASIS;
 
605
  else if (! SYMBOL_EXPAND_ARGS (sym))
 
606
    attr_expansion = READ_ATTR_VERB;
 
607
  else
 
608
    attr_expansion = READ_ATTRIBUTE;
 
609
 
 
610
  slash = collect_arguments (SYMBOL_NAME (sym), attr_expansion, &argptr,
 
611
                             &arguments);
 
612
  argc = obstack_object_size (&argptr) / sizeof (token_data *);
 
613
 
 
614
  if (SYMBOL_CONTAINER (sym) && !slash)
 
615
    {
 
616
      collect_body (SYMBOL_NAME (sym), READ_BODY, &argptr, &body);
 
617
      argv = (token_data **) obstack_finish (&argptr);
 
618
    }
 
619
  else
 
620
    {
 
621
      if (SYMBOL_CONTAINER (sym))
 
622
        {
 
623
          TOKEN_DATA_TYPE (&td) = TOKEN_TEXT;
 
624
          TOKEN_DATA_TEXT (&td) = "";
 
625
          tdp = (token_data *) obstack_copy (&arguments,
 
626
                  (voidstar) &td, sizeof (td));
 
627
          obstack_grow (&argptr, (voidstar) &tdp, sizeof (tdp));
 
628
        }
 
629
 
 
630
      argv = (token_data **) obstack_finish (&argptr);
 
631
      if (slash)
 
632
        {
 
633
          cp = TOKEN_DATA_TEXT (argv[argc-1]);
 
634
          if (IS_SLASH(*cp) && *(cp+1) == '\0')
 
635
            {
 
636
              *cp = '\0';
 
637
              argc--;
 
638
            }
 
639
          else if (IS_SLASH (LAST_CHAR (cp)))
 
640
            {
 
641
              if (IS_SPACE (*(cp+strlen(cp)-2)))
 
642
                *(cp+strlen(cp)-2) = '\0';
 
643
              else
 
644
                LAST_CHAR (cp) = '\0';
 
645
            }
 
646
        }
 
647
      else if (!(exp_flags & EXP_NOWARN_SLASH))
 
648
        MP4HERROR ((warning_status, 0,
 
649
          _("Warning:%s:%d: `<%s>' requires a trailing slash"),
 
650
               CURRENT_FILE_LINE, SYMBOL_NAME (sym)));
 
651
    }
 
652
 
 
653
  if (traced)
 
654
    trace_pre (SYMBOL_NAME (sym), my_call_id, argc, argv);
 
655
 
 
656
  obs_expansion = push_string_init ();
 
657
  if (expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
 
658
          || expansion == READ_ATTR_QUOT)
 
659
    {
 
660
      if (expansion_level > 1)
 
661
        obstack_1grow (obs_expansion, CHAR_BGROUP);
 
662
      call_macro (sym, obs_expansion, argc, argv, expansion);
 
663
      if (expansion_level > 1)
 
664
        obstack_1grow (obs_expansion, CHAR_EGROUP);
 
665
    }
 
666
  else
 
667
    {
 
668
      obstack_1grow (obs_expansion, '<');
 
669
      shipout_string (obs_expansion, SYMBOL_NAME (sym), 0);
 
670
 
 
671
      for (i = 1; i < argc; i++)
 
672
        {
 
673
          shipout_string (obs_expansion, TOKEN_DATA_TEXT (argv[i]), 0);
 
674
        }
 
675
      if (slash)
 
676
        {
 
677
          obstack_1grow (obs_expansion, CHAR_SLASH);
 
678
        }
 
679
 
 
680
      obstack_1grow (obs_expansion, '>');
 
681
      if (SYMBOL_CONTAINER (sym) && !slash)
 
682
        {
 
683
          shipout_string (obs_expansion, TOKEN_DATA_TEXT (argv[argc]), 0);
 
684
          obstack_grow (obs_expansion, "</", 2);
 
685
          shipout_string (obs_expansion, SYMBOL_NAME (sym), 0);
 
686
          obstack_1grow (obs_expansion, '>');
 
687
        }
 
688
    }
 
689
  expanded = push_string_finish (expansion);
 
690
 
 
691
  if (traced)
 
692
    trace_post (SYMBOL_NAME (sym), my_call_id, argc, argv, expanded);
 
693
 
 
694
  --expansion_level;
 
695
 
 
696
  obstack_free (&arguments, NULL);
 
697
  obstack_free (&argptr, NULL);
 
698
  obstack_free (&body, NULL);
 
699
 
 
700
  if (expansion_level == 0)
 
701
    clear_tag_attr ();
 
702
}
 
703
 
 
704
/*-------------------------------------------------------------------------.
 
705
| This macro reads attributes without expanding macro.  It is useful to    |
 
706
| print unknown tags.                                                      |
 
707
`-------------------------------------------------------------------------*/
 
708
 
 
709
static void
 
710
expand_unknown_tag (char *name, read_type expansion)
 
711
{
 
712
  struct obstack arguments, argptr, body;
 
713
  token_data **argv;
 
714
  int argc, i;
 
715
  struct obstack *obs_expansion;
 
716
  const char *expanded;
 
717
  char *symbol_name, *cp;
 
718
  read_type attr_expansion;
 
719
  boolean slash, single;
 
720
 
 
721
  expansion_level++;
 
722
  if (expansion_level > nesting_limit)
 
723
    MP4HERROR ((EXIT_FAILURE, 0,
 
724
      _("ERROR: Recursion limit of %d exceeded, use -L<N> to change it"),
 
725
           nesting_limit));
 
726
 
 
727
  array_current_line[expansion_level] = current_line;
 
728
  xfree((voidstar) array_current_file[expansion_level]);
 
729
  array_current_file[expansion_level] = xstrdup(current_file);
 
730
 
 
731
  symbol_name = xstrdup (name);
 
732
 
 
733
  obstack_init (&arguments);
 
734
  obstack_init (&argptr);
 
735
  obstack_init (&body);
 
736
 
 
737
  if (expansion == READ_NORMAL || expansion == READ_ATTRIBUTE
 
738
          || expansion == READ_ATTR_QUOT)
 
739
    attr_expansion = READ_ATTR_QUOT;
 
740
  else
 
741
    attr_expansion = READ_ATTR_ASIS;
 
742
 
 
743
  slash = collect_arguments (symbol_name, attr_expansion, &argptr, &arguments);
 
744
  argc  = obstack_object_size (&argptr) / sizeof (token_data *);
 
745
 
 
746
  /*
 
747
     This tag is a simple tag if either
 
748
         - tag name begins with a slash (i.e. this is an end tag)
 
749
         - tag name last character is a star
 
750
         - attributes are ended by a trailing slash
 
751
  */
 
752
  single = slash;
 
753
  if (*(symbol_name) == '/')
 
754
    single = TRUE;
 
755
 
 
756
  if (!single && !(exp_flags & EXP_STAR_COMPLEX))
 
757
    single = (LAST_CHAR (symbol_name) == '*');
 
758
 
 
759
  if (!single && !(exp_flags & EXP_DFT_SIMPLE))
 
760
    collect_body (symbol_name, expansion, &argptr, &body);
 
761
 
 
762
  argv = (token_data **) obstack_finish (&argptr);
 
763
 
 
764
  /*  Remove the trailing slash in single tags.  */
 
765
  if (slash)
 
766
    {
 
767
      cp = TOKEN_DATA_TEXT (argv[argc-1]);
 
768
      if (IS_SLASH (*cp) && *(cp+1) == '\0')
 
769
        argc--;
 
770
      else if (IS_SLASH (LAST_CHAR (cp)))
 
771
        {
 
772
          if (IS_SPACE (*(cp+strlen(cp)-2)))
 
773
            *(cp+strlen(cp)-2) = '\0';
 
774
          else
 
775
            LAST_CHAR (cp) = '\0';
 
776
        }
 
777
    }
 
778
 
 
779
  if (expansion == READ_ATTR_ASIS
 
780
     || expansion == READ_ATTR_VERB || expansion == READ_BODY)
 
781
    expansion = READ_BODY;
 
782
 
 
783
  obs_expansion = push_string_init ();
 
784
 
 
785
  if (expansion != READ_BODY)
 
786
    obstack_1grow (obs_expansion, CHAR_LQUOTE);
 
787
  obstack_1grow (obs_expansion, '<');
 
788
  shipout_string (obs_expansion, symbol_name, 0);
 
789
 
 
790
  /*  Whitespaces between attributes have been put into
 
791
      attributes text, so there is no need to insert a space
 
792
      between them.  */
 
793
  for (i = 1; i < argc; i++)
 
794
    shipout_string (obs_expansion, TOKEN_DATA_TEXT (argv[i]), 0);
 
795
 
 
796
  if (slash)
 
797
    {
 
798
      if (! (exp_flags & EXP_NOSPACE_BSLASH))
 
799
        obstack_1grow (obs_expansion, ' ');
 
800
      obstack_1grow (obs_expansion, CHAR_SLASH);
 
801
    }
 
802
  obstack_1grow (obs_expansion, '>');
 
803
  if (expansion != READ_BODY)
 
804
    obstack_1grow (obs_expansion, CHAR_RQUOTE);
 
805
  if (!single && !(exp_flags & EXP_DFT_SIMPLE))
 
806
    {
 
807
      shipout_string (obs_expansion, TOKEN_DATA_TEXT (argv[argc]), 0);
 
808
      if (expansion != READ_BODY)
 
809
        obstack_1grow (obs_expansion, CHAR_LQUOTE);
 
810
      obstack_grow (obs_expansion, "</", 2);
 
811
      shipout_string (obs_expansion, symbol_name, 0);
 
812
      obstack_1grow (obs_expansion, '>');
 
813
      if (expansion != READ_BODY)
 
814
        obstack_1grow (obs_expansion, CHAR_RQUOTE);
 
815
    }
 
816
 
 
817
  expanded = push_string_finish (expansion);
 
818
 
 
819
  --expansion_level;
 
820
 
 
821
  obstack_free (&arguments, NULL);
 
822
  obstack_free (&argptr, NULL);
 
823
  obstack_free (&body, NULL);
 
824
 
 
825
  xfree ((voidstar) symbol_name);
 
826
}
 
827
 
 
828
 
 
829
/*-------------------------------------------------------------------------.
 
830
| The entity expansion is handled by expand_entity ().  Entities are       |
 
831
| treated similarly to macros, they just do not have arguments and the     |
 
832
| symbol text is output directly.                                          |
 
833
|                                                                          |
 
834
| Expand_entity () is not recursive.                                       |
 
835
`-------------------------------------------------------------------------*/
 
836
 
 
837
static void
 
838
expand_entity (symbol *sym, read_type expansion)
 
839
{
 
840
  struct obstack *obs_expansion;
 
841
  const char *expanded;
 
842
  boolean traced;
 
843
  int my_call_id;
 
844
 
 
845
  expansion_level++;
 
846
  if (expansion_level > nesting_limit)
 
847
    MP4HERROR ((EXIT_FAILURE, 0,
 
848
      _("ERROR: Recursion limit of %d exceeded, use -L<N> to change it"),
 
849
           nesting_limit));
 
850
 
 
851
  array_current_line[expansion_level] = current_line;
 
852
  xfree((voidstar) array_current_file[expansion_level]);
 
853
  array_current_file[expansion_level] = xstrdup(current_file);
 
854
 
 
855
  macro_call_id++;
 
856
  my_call_id = macro_call_id;
 
857
 
 
858
  traced = (boolean) ((debug_level & DEBUG_TRACE_ALL) || SYMBOL_TRACED (sym));
 
859
 
 
860
  if (traced && (debug_level & DEBUG_TRACE_CALL))
 
861
    trace_prepre (SYMBOL_NAME (sym), my_call_id);
 
862
 
 
863
  obs_expansion = push_string_init ();
 
864
  if (expansion_level > 1)
 
865
    obstack_1grow (obs_expansion, CHAR_BGROUP);
 
866
  shipout_string (obs_expansion, SYMBOL_TEXT (sym), 0);
 
867
  if (expansion_level > 1)
 
868
    obstack_1grow (obs_expansion, CHAR_EGROUP);
 
869
  expanded = push_string_finish (expansion);
 
870
 
 
871
  if (traced)
 
872
    trace_post (SYMBOL_NAME (sym), my_call_id, 0, NULL, expanded);
 
873
 
 
874
  --expansion_level;
 
875
}
 
876