wpp: Add I/O callbacks.
[wine] / libs / wpp / ppy.y
1 /*
2  * Wrc preprocessor syntax analysis
3  *
4  * Copyright 1999-2000  Bertho A. Stultiens (BS)
5  *
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * History:
22  * 24-Apr-2000 BS       Restructured the lot to fit the new scanner
23  *                      and reintegrate into the wine-tree.
24  * 01-Jan-2000 BS       FIXME: win16 preprocessor calculates with
25  *                      16 bit ints and overflows...?
26  * 26-Dec-1999 BS       Started this file
27  *
28  */
29
30 %{
31 #include "config.h"
32 #include "wine/port.h"
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <assert.h>
38 #include <ctype.h>
39 #include <string.h>
40
41 #include "wpp_private.h"
42
43
44 #define UNARY_OP(r, v, OP)                                      \
45         switch(v.type)                                          \
46         {                                                       \
47         case cv_sint:   r.val.si  = OP v.val.si; break;         \
48         case cv_uint:   r.val.ui  = OP v.val.ui; break;         \
49         case cv_slong:  r.val.sl  = OP v.val.sl; break;         \
50         case cv_ulong:  r.val.ul  = OP v.val.ul; break;         \
51         case cv_sll:    r.val.sll = OP v.val.sll; break;        \
52         case cv_ull:    r.val.ull = OP v.val.ull; break;        \
53         }
54
55 #define cv_signed(v)    ((v.type & FLAG_SIGNED) != 0)
56
57 #define BIN_OP_INT(r, v1, v2, OP)                       \
58         r.type = v1.type;                               \
59         if(cv_signed(v1) && cv_signed(v2))              \
60                 r.val.si = v1.val.si OP v2.val.si;      \
61         else if(cv_signed(v1) && !cv_signed(v2))        \
62                 r.val.si = v1.val.si OP v2.val.ui;      \
63         else if(!cv_signed(v1) && cv_signed(v2))        \
64                 r.val.ui = v1.val.ui OP v2.val.si;      \
65         else                                            \
66                 r.val.ui = v1.val.ui OP v2.val.ui;
67
68 #define BIN_OP_LONG(r, v1, v2, OP)                      \
69         r.type = v1.type;                               \
70         if(cv_signed(v1) && cv_signed(v2))              \
71                 r.val.sl = v1.val.sl OP v2.val.sl;      \
72         else if(cv_signed(v1) && !cv_signed(v2))        \
73                 r.val.sl = v1.val.sl OP v2.val.ul;      \
74         else if(!cv_signed(v1) && cv_signed(v2))        \
75                 r.val.ul = v1.val.ul OP v2.val.sl;      \
76         else                                            \
77                 r.val.ul = v1.val.ul OP v2.val.ul;
78
79 #define BIN_OP_LONGLONG(r, v1, v2, OP)                  \
80         r.type = v1.type;                               \
81         if(cv_signed(v1) && cv_signed(v2))              \
82                 r.val.sll = v1.val.sll OP v2.val.sll;   \
83         else if(cv_signed(v1) && !cv_signed(v2))        \
84                 r.val.sll = v1.val.sll OP v2.val.ull;   \
85         else if(!cv_signed(v1) && cv_signed(v2))        \
86                 r.val.ull = v1.val.ull OP v2.val.sll;   \
87         else                                            \
88                 r.val.ull = v1.val.ull OP v2.val.ull;
89
90 #define BIN_OP(r, v1, v2, OP)                                           \
91         switch(v1.type & SIZE_MASK)                                     \
92         {                                                               \
93         case SIZE_INT:          BIN_OP_INT(r, v1, v2, OP); break;       \
94         case SIZE_LONG:         BIN_OP_LONG(r, v1, v2, OP); break;      \
95         case SIZE_LONGLONG:     BIN_OP_LONGLONG(r, v1, v2, OP); break;  \
96         default: pp_internal_error(__FILE__, __LINE__, "Invalid type indicator (0x%04x)", v1.type);     \
97         }
98
99
100 /*
101  * Prototypes
102  */
103 static int boolean(cval_t *v);
104 static void promote_equal_size(cval_t *v1, cval_t *v2);
105 static void cast_to_sint(cval_t *v);
106 static void cast_to_uint(cval_t *v);
107 static void cast_to_slong(cval_t *v);
108 static void cast_to_ulong(cval_t *v);
109 static void cast_to_sll(cval_t *v);
110 static void cast_to_ull(cval_t *v);
111 static marg_t *new_marg(char *str, def_arg_t type);
112 static marg_t *add_new_marg(char *str, def_arg_t type);
113 static int marg_index(char *id);
114 static mtext_t *new_mtext(char *str, int idx, def_exp_t type);
115 static mtext_t *combine_mtext(mtext_t *tail, mtext_t *mtp);
116 static char *merge_text(char *s1, char *s2);
117
118 /*
119  * Local variables
120  */
121 static marg_t **macro_args;     /* Macro parameters array while parsing */
122 static int      nmacro_args;
123
124 %}
125
126 %union{
127         int             sint;
128         unsigned int    uint;
129         long            slong;
130         unsigned long   ulong;
131         wrc_sll_t       sll;
132         wrc_ull_t       ull;
133         int             *iptr;
134         char            *cptr;
135         cval_t          cval;
136         marg_t          *marg;
137         mtext_t         *mtext;
138 }
139
140 %token tRCINCLUDE
141 %token tIF tIFDEF tIFNDEF tELSE tELIF tENDIF tDEFINED tNL
142 %token tINCLUDE tLINE tGCCLINE tERROR tWARNING tPRAGMA tPPIDENT
143 %token tUNDEF tMACROEND tCONCAT tELIPSIS tSTRINGIZE
144 %token <cptr> tIDENT tLITERAL tMACRO tDEFINE
145 %token <cptr> tDQSTRING tSQSTRING tIQSTRING
146 %token <uint> tUINT
147 %token <sint> tSINT
148 %token <ulong> tULONG
149 %token <slong> tSLONG
150 %token <ull> tULONGLONG
151 %token <sll> tSLONGLONG
152 %token <cptr> tRCINCLUDEPATH
153
154 %right '?' ':'
155 %left tLOGOR
156 %left tLOGAND
157 %left '|'
158 %left '^'
159 %left '&'
160 %left tEQ tNE
161 %left '<' tLTE '>' tGTE
162 %left tLSHIFT tRSHIFT
163 %left '+' '-'
164 %left '*' '/'
165 %right '~' '!'
166
167 %type <cval>    pp_expr
168 %type <marg>    emargs margs
169 %type <mtext>   opt_mtexts mtexts mtext
170 %type <sint>    allmargs
171 %type <cptr>    opt_text text
172
173 /*
174  **************************************************************************
175  * The parser starts here
176  **************************************************************************
177  */
178
179 %%
180
181 pp_file : /* Empty */
182         | pp_file preprocessor
183         ;
184
185 preprocessor
186         : tINCLUDE tDQSTRING tNL        { pp_do_include($2, 1); }
187         | tINCLUDE tIQSTRING tNL        { pp_do_include($2, 0); }
188         | tIF pp_expr tNL       { pp_next_if_state(boolean(&$2)); }
189         | tIFDEF tIDENT tNL     { pp_next_if_state(pplookup($2) != NULL); free($2); }
190         | tIFNDEF tIDENT tNL    {
191                 int t = pplookup($2) == NULL;
192                 if(pp_incl_state.state == 0 && t && !pp_incl_state.seen_junk)
193                 {
194                         pp_incl_state.state     = 1;
195                         pp_incl_state.ppp       = $2;
196                         pp_incl_state.ifdepth   = pp_get_if_depth();
197                 }
198                 else if(pp_incl_state.state != 1)
199                 {
200                         pp_incl_state.state = -1;
201                         free($2);
202                 }
203                 else
204                         free($2);
205                 pp_next_if_state(t);
206                 if(pp_status.debug)
207                         fprintf(stderr, "tIFNDEF: %s:%d: include_state=%d, include_ppp='%s', include_ifdepth=%d\n",
208                                 pp_status.input, pp_status.line_number, pp_incl_state.state, pp_incl_state.ppp, pp_incl_state.ifdepth);
209                 }
210         | tELIF pp_expr tNL     {
211                 pp_if_state_t s = pp_pop_if();
212                 switch(s)
213                 {
214                 case if_true:
215                 case if_elif:
216                         pp_push_if(if_elif);
217                         break;
218                 case if_false:
219                         pp_push_if(boolean(&$2) ? if_true : if_false);
220                         break;
221                 case if_ignore:
222                         pp_push_if(if_ignore);
223                         break;
224                 case if_elsetrue:
225                 case if_elsefalse:
226                         ppy_error("#elif cannot follow #else");
227                         break;
228                 case if_error:
229                         break;
230                 default:
231                         pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #elif directive", s);
232                 }
233                 }
234         | tELSE tNL             {
235                 pp_if_state_t s = pp_pop_if();
236                 switch(s)
237                 {
238                 case if_true:
239                         pp_push_if(if_elsefalse);
240                         break;
241                 case if_elif:
242                         pp_push_if(if_elif);
243                         break;
244                 case if_false:
245                         pp_push_if(if_elsetrue);
246                         break;
247                 case if_ignore:
248                         pp_push_if(if_ignore);
249                         break;
250                 case if_elsetrue:
251                 case if_elsefalse:
252                         ppy_error("#else clause already defined");
253                         break;
254                 case if_error:
255                         break;
256                 default:
257                         pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #else directive", s);
258                 }
259                 }
260         | tENDIF tNL            {
261                 if(pp_pop_if() != if_error)
262                 {
263                         if(pp_incl_state.ifdepth == pp_get_if_depth() && pp_incl_state.state == 1)
264                         {
265                                 pp_incl_state.state = 2;
266                                 pp_incl_state.seen_junk = 0;
267                         }
268                         else if(pp_incl_state.state != 1)
269                         {
270                                 pp_incl_state.state = -1;
271                         }
272                         if(pp_status.debug)
273                                 fprintf(stderr, "tENDIF: %s:%d: include_state=%d, include_ppp='%s', include_ifdepth=%d\n",
274                                         pp_status.input, pp_status.line_number, pp_incl_state.state, pp_incl_state.ppp, pp_incl_state.ifdepth);
275                 }
276                 }
277         | tUNDEF tIDENT tNL     { pp_del_define($2); free($2); }
278         | tDEFINE opt_text tNL  { pp_add_define($1, $2); }
279         | tMACRO res_arg allmargs tMACROEND opt_mtexts tNL      {
280                 pp_add_macro($1, macro_args, nmacro_args, $5);
281                 }
282         | tLINE tSINT tDQSTRING tNL     { if($3) pp_writestring("# %d %s\n", $2 , $3); free($3); }
283         | tGCCLINE tSINT tDQSTRING tNL  { if($3) pp_writestring("# %d %s\n", $2 , $3); free($3); }
284         | tGCCLINE tSINT tDQSTRING tSINT tNL
285                 { if($3) pp_writestring("# %d %s %d\n", $2, $3, $4); free($3); }
286         | tGCCLINE tSINT tDQSTRING tSINT tSINT tNL
287                 { if($3) pp_writestring("# %d %s %d %d\n", $2 ,$3, $4, $5); free($3); }
288         | tGCCLINE tSINT tDQSTRING tSINT tSINT tSINT  tNL
289                 { if($3) pp_writestring("# %d %s %d %d %d\n", $2 ,$3 ,$4 ,$5, $6); free($3); }
290         | tGCCLINE tSINT tDQSTRING tSINT tSINT tSINT tSINT tNL
291                 { if($3) pp_writestring("# %d %s %d %d %d %d\n", $2 ,$3 ,$4 ,$5, $6, $7); free($3); }
292         | tGCCLINE tNL          /* The null-token */
293         | tERROR opt_text tNL   { ppy_error("#error directive: '%s'", $2); free($2); }
294         | tWARNING opt_text tNL { ppy_warning("#warning directive: '%s'", $2); free($2); }
295         | tPRAGMA opt_text tNL  { pp_writestring("#pragma %s\n", $2 ? $2 : ""); free($2); }
296         | tPPIDENT opt_text tNL { if(pp_status.pedantic) ppy_warning("#ident ignored (arg: '%s')", $2); free($2); }
297         | tRCINCLUDE tRCINCLUDEPATH {
298                 if($2)
299                 {
300                         int nl=strlen($2) +3;
301                         char *fn=pp_xmalloc(nl);
302                         if(fn)
303                         {
304                                 sprintf(fn,"\"%s\"",$2);
305                                 pp_do_include(fn,1);
306                         }
307                         free($2);
308                 }
309         }
310         | tRCINCLUDE tDQSTRING {
311                 pp_do_include($2,1);
312         }
313         /*| tNL*/
314         ;
315
316 opt_text: /* Empty */   { $$ = NULL; }
317         | text          { $$ = $1; }
318         ;
319
320 text    : tLITERAL              { $$ = $1; }
321         | tDQSTRING             { $$ = $1; }
322         | tSQSTRING             { $$ = $1; }
323         | text tLITERAL         { $$ = merge_text($1, $2); }
324         | text tDQSTRING        { $$ = merge_text($1, $2); }
325         | text tSQSTRING        { $$ = merge_text($1, $2); }
326         ;
327
328 res_arg : /* Empty */   { macro_args = NULL; nmacro_args = 0; }
329         ;
330
331 allmargs: /* Empty */           { $$ = 0; macro_args = NULL; nmacro_args = 0; }
332         | emargs                { $$ = nmacro_args; }
333         ;
334
335 emargs  : margs                 { $$ = $1; }
336         | margs ',' tELIPSIS    { $$ = add_new_marg(NULL, arg_list); nmacro_args *= -1; }
337         ;
338
339 margs   : margs ',' tIDENT      { $$ = add_new_marg($3, arg_single); }
340         | tIDENT                { $$ = add_new_marg($1, arg_single); }
341         ;
342
343 opt_mtexts
344         : /* Empty */   { $$ = NULL; }
345         | mtexts        {
346                 for($$ = $1; $$ && $$->prev; $$ = $$->prev)
347                         ;
348                 }
349         ;
350
351 mtexts  : mtext         { $$ = $1; }
352         | mtexts mtext  { $$ = combine_mtext($1, $2); }
353         ;
354
355 mtext   : tLITERAL      { $$ = new_mtext($1, 0, exp_text); }
356         | tDQSTRING     { $$ = new_mtext($1, 0, exp_text); }
357         | tSQSTRING     { $$ = new_mtext($1, 0, exp_text); }
358         | tCONCAT       { $$ = new_mtext(NULL, 0, exp_concat); }
359         | tSTRINGIZE tIDENT     {
360                 int mat = marg_index($2);
361                 if(mat < 0)
362                         ppy_error("Stringification identifier must be an argument parameter");
363                 else
364                         $$ = new_mtext(NULL, mat, exp_stringize);
365                 }
366         | tIDENT        {
367                 int mat = marg_index($1);
368                 if(mat >= 0)
369                         $$ = new_mtext(NULL, mat, exp_subst);
370                 else if($1)
371                         $$ = new_mtext($1, 0, exp_text);
372                 }
373         ;
374
375 pp_expr : tSINT                         { $$.type = cv_sint;  $$.val.si = $1; }
376         | tUINT                         { $$.type = cv_uint;  $$.val.ui = $1; }
377         | tSLONG                        { $$.type = cv_slong; $$.val.sl = $1; }
378         | tULONG                        { $$.type = cv_ulong; $$.val.ul = $1; }
379         | tSLONGLONG                    { $$.type = cv_sll;   $$.val.sll = $1; }
380         | tULONGLONG                    { $$.type = cv_ull;   $$.val.ull = $1; }
381         | tDEFINED tIDENT               { $$.type = cv_sint;  $$.val.si = pplookup($2) != NULL; }
382         | tDEFINED '(' tIDENT ')'       { $$.type = cv_sint;  $$.val.si = pplookup($3) != NULL; }
383         | tIDENT                        { $$.type = cv_sint;  $$.val.si = 0; }
384         | pp_expr tLOGOR pp_expr        { $$.type = cv_sint; $$.val.si = boolean(&$1) || boolean(&$3); }
385         | pp_expr tLOGAND pp_expr       { $$.type = cv_sint; $$.val.si = boolean(&$1) && boolean(&$3); }
386         | pp_expr tEQ pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, ==) }
387         | pp_expr tNE pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, !=) }
388         | pp_expr '<' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  <) }
389         | pp_expr '>' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  >) }
390         | pp_expr tLTE pp_expr          { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, <=) }
391         | pp_expr tGTE pp_expr          { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, >=) }
392         | pp_expr '+' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  +) }
393         | pp_expr '-' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  -) }
394         | pp_expr '^' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  ^) }
395         | pp_expr '&' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  &) }
396         | pp_expr '|' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  |) }
397         | pp_expr '*' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  *) }
398         | pp_expr '/' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  /) }
399         | pp_expr tLSHIFT pp_expr       { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, <<) }
400         | pp_expr tRSHIFT pp_expr       { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, >>) }
401         | '+' pp_expr                   { $$ =  $2; }
402         | '-' pp_expr                   { UNARY_OP($$, $2, -) }
403         | '~' pp_expr                   { UNARY_OP($$, $2, ~) }
404         | '!' pp_expr                   { $$.type = cv_sint; $$.val.si = !boolean(&$2); }
405         | '(' pp_expr ')'               { $$ =  $2; }
406         | pp_expr '?' pp_expr ':' pp_expr { $$ = boolean(&$1) ? $3 : $5; }
407         ;
408
409 %%
410
411 /*
412  **************************************************************************
413  * Support functions
414  **************************************************************************
415  */
416
417 static void cast_to_sint(cval_t *v)
418 {
419         switch(v->type)
420         {
421         case cv_sint:   break;
422         case cv_uint:   break;
423         case cv_slong:  v->val.si = v->val.sl;  break;
424         case cv_ulong:  v->val.si = v->val.ul;  break;
425         case cv_sll:    v->val.si = v->val.sll; break;
426         case cv_ull:    v->val.si = v->val.ull; break;
427         }
428         v->type = cv_sint;
429 }
430
431 static void cast_to_uint(cval_t *v)
432 {
433         switch(v->type)
434         {
435         case cv_sint:   break;
436         case cv_uint:   break;
437         case cv_slong:  v->val.ui = v->val.sl;  break;
438         case cv_ulong:  v->val.ui = v->val.ul;  break;
439         case cv_sll:    v->val.ui = v->val.sll; break;
440         case cv_ull:    v->val.ui = v->val.ull; break;
441         }
442         v->type = cv_uint;
443 }
444
445 static void cast_to_slong(cval_t *v)
446 {
447         switch(v->type)
448         {
449         case cv_sint:   v->val.sl = v->val.si;  break;
450         case cv_uint:   v->val.sl = v->val.ui;  break;
451         case cv_slong:  break;
452         case cv_ulong:  break;
453         case cv_sll:    v->val.sl = v->val.sll; break;
454         case cv_ull:    v->val.sl = v->val.ull; break;
455         }
456         v->type = cv_slong;
457 }
458
459 static void cast_to_ulong(cval_t *v)
460 {
461         switch(v->type)
462         {
463         case cv_sint:   v->val.ul = v->val.si;  break;
464         case cv_uint:   v->val.ul = v->val.ui;  break;
465         case cv_slong:  break;
466         case cv_ulong:  break;
467         case cv_sll:    v->val.ul = v->val.sll; break;
468         case cv_ull:    v->val.ul = v->val.ull; break;
469         }
470         v->type = cv_ulong;
471 }
472
473 static void cast_to_sll(cval_t *v)
474 {
475         switch(v->type)
476         {
477         case cv_sint:   v->val.sll = v->val.si; break;
478         case cv_uint:   v->val.sll = v->val.ui; break;
479         case cv_slong:  v->val.sll = v->val.sl; break;
480         case cv_ulong:  v->val.sll = v->val.ul; break;
481         case cv_sll:    break;
482         case cv_ull:    break;
483         }
484         v->type = cv_sll;
485 }
486
487 static void cast_to_ull(cval_t *v)
488 {
489         switch(v->type)
490         {
491         case cv_sint:   v->val.ull = v->val.si; break;
492         case cv_uint:   v->val.ull = v->val.ui; break;
493         case cv_slong:  v->val.ull = v->val.sl; break;
494         case cv_ulong:  v->val.ull = v->val.ul; break;
495         case cv_sll:    break;
496         case cv_ull:    break;
497         }
498         v->type = cv_ull;
499 }
500
501
502 static void promote_equal_size(cval_t *v1, cval_t *v2)
503 {
504 #define cv_sizeof(v)    ((int)(v->type & SIZE_MASK))
505         int s1 = cv_sizeof(v1);
506         int s2 = cv_sizeof(v2);
507 #undef cv_sizeof
508
509         if(s1 == s2)
510                 return;
511         else if(s1 > s2)
512         {
513                 switch(v1->type)
514                 {
515                 case cv_sint:   cast_to_sint(v2); break;
516                 case cv_uint:   cast_to_uint(v2); break;
517                 case cv_slong:  cast_to_slong(v2); break;
518                 case cv_ulong:  cast_to_ulong(v2); break;
519                 case cv_sll:    cast_to_sll(v2); break;
520                 case cv_ull:    cast_to_ull(v2); break;
521                 }
522         }
523         else
524         {
525                 switch(v2->type)
526                 {
527                 case cv_sint:   cast_to_sint(v1); break;
528                 case cv_uint:   cast_to_uint(v1); break;
529                 case cv_slong:  cast_to_slong(v1); break;
530                 case cv_ulong:  cast_to_ulong(v1); break;
531                 case cv_sll:    cast_to_sll(v1); break;
532                 case cv_ull:    cast_to_ull(v1); break;
533                 }
534         }
535 }
536
537
538 static int boolean(cval_t *v)
539 {
540         switch(v->type)
541         {
542         case cv_sint:   return v->val.si != (int)0;
543         case cv_uint:   return v->val.ui != (unsigned int)0;
544         case cv_slong:  return v->val.sl != (long)0;
545         case cv_ulong:  return v->val.ul != (unsigned long)0;
546         case cv_sll:    return v->val.sll != (wrc_sll_t)0;
547         case cv_ull:    return v->val.ull != (wrc_ull_t)0;
548         }
549         return 0;
550 }
551
552 static marg_t *new_marg(char *str, def_arg_t type)
553 {
554         marg_t *ma = pp_xmalloc(sizeof(marg_t));
555         if(!ma)
556                 return NULL;
557         ma->arg = str;
558         ma->type = type;
559         ma->nnl = 0;
560         return ma;
561 }
562
563 static marg_t *add_new_marg(char *str, def_arg_t type)
564 {
565         marg_t **new_macro_args;
566         marg_t *ma;
567         if(!str)
568                 return NULL;
569         new_macro_args = pp_xrealloc(macro_args, (nmacro_args+1) * sizeof(macro_args[0]));
570         if(!new_macro_args)
571                 return NULL;
572         macro_args = new_macro_args;
573         ma = new_marg(str, type);
574         if(!ma)
575                 return NULL;
576         macro_args[nmacro_args] = ma;
577         nmacro_args++;
578         return ma;
579 }
580
581 static int marg_index(char *id)
582 {
583         int t;
584         if(!id)
585                 return -1;
586         for(t = 0; t < nmacro_args; t++)
587         {
588                 if(!strcmp(id, macro_args[t]->arg))
589                         break;
590         }
591         return t < nmacro_args ? t : -1;
592 }
593
594 static mtext_t *new_mtext(char *str, int idx, def_exp_t type)
595 {
596         mtext_t *mt = pp_xmalloc(sizeof(mtext_t));
597         if(!mt)
598                 return NULL;
599         if(str == NULL)
600                 mt->subst.argidx = idx;
601         else
602                 mt->subst.text = str;
603         mt->type = type;
604         mt->next = mt->prev = NULL;
605         return mt;
606 }
607
608 static mtext_t *combine_mtext(mtext_t *tail, mtext_t *mtp)
609 {
610         if(!tail)
611                 return mtp;
612
613         if(!mtp)
614                 return tail;
615
616         if(tail->type == exp_text && mtp->type == exp_text)
617         {
618                 char *new_text;
619                 new_text = pp_xrealloc(tail->subst.text, strlen(tail->subst.text)+strlen(mtp->subst.text)+1);
620                 if(!new_text)
621                         return mtp;
622                 tail->subst.text = new_text;
623                 strcat(tail->subst.text, mtp->subst.text);
624                 free(mtp->subst.text);
625                 free(mtp);
626                 return tail;
627         }
628
629         if(tail->type == exp_concat && mtp->type == exp_concat)
630         {
631                 free(mtp);
632                 return tail;
633         }
634
635         if(tail->type == exp_concat && mtp->type == exp_text)
636         {
637                 int len = strlen(mtp->subst.text);
638                 while(len)
639                 {
640 /* FIXME: should delete space from head of string */
641                         if(isspace(mtp->subst.text[len-1] & 0xff))
642                                 mtp->subst.text[--len] = '\0';
643                         else
644                                 break;
645                 }
646
647                 if(!len)
648                 {
649                         free(mtp->subst.text);
650                         free(mtp);
651                         return tail;
652                 }
653         }
654
655         if(tail->type == exp_text && mtp->type == exp_concat)
656         {
657                 int len = strlen(tail->subst.text);
658                 while(len)
659                 {
660                         if(isspace(tail->subst.text[len-1] & 0xff))
661                                 tail->subst.text[--len] = '\0';
662                         else
663                                 break;
664                 }
665
666                 if(!len)
667                 {
668                         mtp->prev = tail->prev;
669                         mtp->next = tail->next;
670                         if(tail->prev)
671                                 tail->prev->next = mtp;
672                         free(tail->subst.text);
673                         free(tail);
674                         return mtp;
675                 }
676         }
677
678         tail->next = mtp;
679         mtp->prev = tail;
680
681         return mtp;
682 }
683
684 static char *merge_text(char *s1, char *s2)
685 {
686         int l1;
687         int l2;
688         char *snew;
689         if(!s1)
690                 return s2;
691         if(!s2)
692                 return s1;
693         l1 = strlen(s1);
694         l2 = strlen(s2);
695         snew = pp_xrealloc(s1, l1+l2+1);
696         if(!snew)
697         {
698                 free(s2);
699                 return s1;
700         }
701         s1 = snew;
702         memcpy(s1+l1, s2, l2+1);
703         free(s2);
704         return s1;
705 }