Moved all references to file descriptors out of the generic object
[wine] / tools / 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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                         pperror("#elif cannot follow #else");
227                 default:
228                         pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #elif directive", s);
229                 }
230                 }
231         | tELSE tNL             {
232                 pp_if_state_t s = pp_pop_if();
233                 switch(s)
234                 {
235                 case if_true:
236                         pp_push_if(if_elsefalse);
237                         break;
238                 case if_elif:
239                         pp_push_if(if_elif);
240                         break;
241                 case if_false:
242                         pp_push_if(if_elsetrue);
243                         break;
244                 case if_ignore:
245                         pp_push_if(if_ignore);
246                         break;
247                 case if_elsetrue:
248                 case if_elsefalse:
249                         pperror("#else clause already defined");
250                 default:
251                         pp_internal_error(__FILE__, __LINE__, "Invalid pp_if_state (%d) in #else directive", s);
252                 }
253                 }
254         | tENDIF tNL            {
255                 pp_pop_if();
256                 if(pp_incl_state.ifdepth == pp_get_if_depth() && pp_incl_state.state == 1)
257                 {
258                         pp_incl_state.state = 2;
259                         pp_incl_state.seen_junk = 0;
260                 }
261                 else if(pp_incl_state.state != 1)
262                 {
263                         pp_incl_state.state = -1;
264                 }
265                 if(pp_status.debug)
266                         fprintf(stderr, "tENDIF: %s:%d: include_state=%d, include_ppp='%s', include_ifdepth=%d\n",
267                                 pp_status.input, pp_status.line_number, pp_incl_state.state, pp_incl_state.ppp, pp_incl_state.ifdepth);
268                 }
269         | tUNDEF tIDENT tNL     { pp_del_define($2); free($2); }
270         | tDEFINE opt_text tNL  { pp_add_define($1, $2); }
271         | tMACRO res_arg allmargs tMACROEND opt_mtexts tNL      {
272                 pp_add_macro($1, macro_args, nmacro_args, $5);
273                 }
274         | tLINE tSINT tDQSTRING tNL     { fprintf(ppout, "# %d %s\n", $2 , $3); free($3); }
275         | tGCCLINE tSINT tDQSTRING tNL  { fprintf(ppout, "# %d %s\n", $2 , $3); free($3); }
276         | tGCCLINE tSINT tDQSTRING tSINT tNL
277                 { fprintf(ppout, "# %d %s %d\n", $2, $3, $4); free($3); }
278         | tGCCLINE tSINT tDQSTRING tSINT tSINT tNL
279                 { fprintf(ppout, "# %d %s %d %d\n", $2 ,$3, $4, $5); free($3); }
280         | tGCCLINE tSINT tDQSTRING tSINT tSINT tSINT  tNL
281                 { fprintf(ppout, "# %d %s %d %d %d\n", $2 ,$3 ,$4 ,$5, $6); free($3); }
282         | tGCCLINE tSINT tDQSTRING tSINT tSINT tSINT tSINT tNL
283                 { fprintf(ppout, "# %d %s %d %d %d %d\n", $2 ,$3 ,$4 ,$5, $6, $7); free($3); }
284         | tGCCLINE tNL          /* The null-token */
285         | tERROR opt_text tNL   { pperror("#error directive: '%s'", $2); if($2) free($2); }
286         | tWARNING opt_text tNL { ppwarning("#warning directive: '%s'", $2); if($2) free($2); }
287         | tPRAGMA opt_text tNL  { fprintf(ppout, "#pragma %s\n", $2 ? $2 : ""); if ($2) free($2); }
288         | tPPIDENT opt_text tNL { if(pp_status.pedantic) ppwarning("#ident ignored (arg: '%s')", $2); if($2) free($2); }
289         | tRCINCLUDE tRCINCLUDEPATH {
290                 int nl=strlen($2) +3;
291                 char *fn=pp_xmalloc(nl);
292                 sprintf(fn,"\"%s\"",$2);
293                 free($2);
294                 pp_do_include(fn,1);
295         }
296         | tRCINCLUDE tDQSTRING {
297                 pp_do_include($2,1);
298         }
299         /*| tNL*/
300         ;
301
302 opt_text: /* Empty */   { $$ = NULL; }
303         | text          { $$ = $1; }
304         ;
305
306 text    : tLITERAL              { $$ = $1; }
307         | tDQSTRING             { $$ = $1; }
308         | tSQSTRING             { $$ = $1; }
309         | text tLITERAL         { $$ = merge_text($1, $2); }
310         | text tDQSTRING        { $$ = merge_text($1, $2); }
311         | text tSQSTRING        { $$ = merge_text($1, $2); }
312         ;
313
314 res_arg : /* Empty */   { macro_args = NULL; nmacro_args = 0; }
315         ;
316
317 allmargs: /* Empty */           { $$ = 0; macro_args = NULL; nmacro_args = 0; }
318         | emargs                { $$ = nmacro_args; }
319         ;
320
321 emargs  : margs                 { $$ = $1; }
322         | margs ',' tELIPSIS    { $$ = add_new_marg(NULL, arg_list); nmacro_args *= -1; }
323         ;
324
325 margs   : margs ',' tIDENT      { $$ = add_new_marg($3, arg_single); }
326         | tIDENT                { $$ = add_new_marg($1, arg_single); }
327         ;
328
329 opt_mtexts
330         : /* Empty */   { $$ = NULL; }
331         | mtexts        {
332                 for($$ = $1; $$ && $$->prev; $$ = $$->prev)
333                         ;
334                 }
335         ;
336
337 mtexts  : mtext         { $$ = $1; }
338         | mtexts mtext  { $$ = combine_mtext($1, $2); }
339         ;
340
341 mtext   : tLITERAL      { $$ = new_mtext($1, 0, exp_text); }
342         | tDQSTRING     { $$ = new_mtext($1, 0, exp_text); }
343         | tSQSTRING     { $$ = new_mtext($1, 0, exp_text); }
344         | tCONCAT       { $$ = new_mtext(NULL, 0, exp_concat); }
345         | tSTRINGIZE tIDENT     {
346                 int mat = marg_index($2);
347                 if(mat < 0)
348                         pperror("Stringification identifier must be an argument parameter");
349                 $$ = new_mtext(NULL, mat, exp_stringize);
350                 }
351         | tIDENT        {
352                 int mat = marg_index($1);
353                 if(mat >= 0)
354                         $$ = new_mtext(NULL, mat, exp_subst);
355                 else
356                         $$ = new_mtext($1, 0, exp_text);
357                 }
358         ;
359
360 pp_expr : tSINT                         { $$.type = cv_sint;  $$.val.si = $1; }
361         | tUINT                         { $$.type = cv_uint;  $$.val.ui = $1; }
362         | tSLONG                        { $$.type = cv_slong; $$.val.sl = $1; }
363         | tULONG                        { $$.type = cv_ulong; $$.val.ul = $1; }
364         | tSLONGLONG                    { $$.type = cv_sll;   $$.val.sl = $1; }
365         | tULONGLONG                    { $$.type = cv_ull;   $$.val.ul = $1; }
366         | tDEFINED tIDENT               { $$.type = cv_sint;  $$.val.si = pplookup($2) != NULL; }
367         | tDEFINED '(' tIDENT ')'       { $$.type = cv_sint;  $$.val.si = pplookup($3) != NULL; }
368         | tIDENT                        { $$.type = cv_sint;  $$.val.si = 0; }
369         | pp_expr tLOGOR pp_expr        { $$.type = cv_sint; $$.val.si = boolean(&$1) || boolean(&$3); }
370         | pp_expr tLOGAND pp_expr       { $$.type = cv_sint; $$.val.si = boolean(&$1) && boolean(&$3); }
371         | pp_expr tEQ pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, ==) }
372         | pp_expr tNE pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, !=) }
373         | pp_expr '<' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  <) }
374         | pp_expr '>' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  >) }
375         | pp_expr tLTE pp_expr          { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, <=) }
376         | pp_expr tGTE pp_expr          { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, >=) }
377         | pp_expr '+' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  +) }
378         | pp_expr '-' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  -) }
379         | pp_expr '^' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  ^) }
380         | pp_expr '&' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  &) }
381         | pp_expr '|' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  |) }
382         | pp_expr '*' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  *) }
383         | pp_expr '/' pp_expr           { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3,  /) }
384         | pp_expr tLSHIFT pp_expr       { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, <<) }
385         | pp_expr tRSHIFT pp_expr       { promote_equal_size(&$1, &$3); BIN_OP($$, $1, $3, >>) }
386         | '+' pp_expr                   { $$ =  $2; }
387         | '-' pp_expr                   { UNARY_OP($$, $2, -) }
388         | '~' pp_expr                   { UNARY_OP($$, $2, ~) }
389         | '!' pp_expr                   { $$.type = cv_sint; $$.val.si = !boolean(&$2); }
390         | '(' pp_expr ')'               { $$ =  $2; }
391         | pp_expr '?' pp_expr ':' pp_expr { $$ = boolean(&$1) ? $3 : $5; }
392         ;
393
394 %%
395
396 /*
397  **************************************************************************
398  * Support functions
399  **************************************************************************
400  */
401
402 static void cast_to_sint(cval_t *v)
403 {
404         switch(v->type)
405         {
406         case cv_sint:   break;
407         case cv_uint:   break;
408         case cv_slong:  v->val.si = v->val.sl;  break;
409         case cv_ulong:  v->val.si = v->val.ul;  break;
410         case cv_sll:    v->val.si = v->val.sll; break;
411         case cv_ull:    v->val.si = v->val.ull; break;
412         }
413         v->type = cv_sint;
414 }
415
416 static void cast_to_uint(cval_t *v)
417 {
418         switch(v->type)
419         {
420         case cv_sint:   break;
421         case cv_uint:   break;
422         case cv_slong:  v->val.ui = v->val.sl;  break;
423         case cv_ulong:  v->val.ui = v->val.ul;  break;
424         case cv_sll:    v->val.ui = v->val.sll; break;
425         case cv_ull:    v->val.ui = v->val.ull; break;
426         }
427         v->type = cv_uint;
428 }
429
430 static void cast_to_slong(cval_t *v)
431 {
432         switch(v->type)
433         {
434         case cv_sint:   v->val.sl = v->val.si;  break;
435         case cv_uint:   v->val.sl = v->val.ui;  break;
436         case cv_slong:  break;
437         case cv_ulong:  break;
438         case cv_sll:    v->val.sl = v->val.sll; break;
439         case cv_ull:    v->val.sl = v->val.ull; break;
440         }
441         v->type = cv_slong;
442 }
443
444 static void cast_to_ulong(cval_t *v)
445 {
446         switch(v->type)
447         {
448         case cv_sint:   v->val.ul = v->val.si;  break;
449         case cv_uint:   v->val.ul = v->val.ui;  break;
450         case cv_slong:  break;
451         case cv_ulong:  break;
452         case cv_sll:    v->val.ul = v->val.sll; break;
453         case cv_ull:    v->val.ul = v->val.ull; break;
454         }
455         v->type = cv_ulong;
456 }
457
458 static void cast_to_sll(cval_t *v)
459 {
460         switch(v->type)
461         {
462         case cv_sint:   v->val.sll = v->val.si; break;
463         case cv_uint:   v->val.sll = v->val.ui; break;
464         case cv_slong:  v->val.sll = v->val.sl; break;
465         case cv_ulong:  v->val.sll = v->val.ul; break;
466         case cv_sll:    break;
467         case cv_ull:    break;
468         }
469         v->type = cv_sll;
470 }
471
472 static void cast_to_ull(cval_t *v)
473 {
474         switch(v->type)
475         {
476         case cv_sint:   v->val.ull = v->val.si; break;
477         case cv_uint:   v->val.ull = v->val.ui; break;
478         case cv_slong:  v->val.ull = v->val.sl; break;
479         case cv_ulong:  v->val.ull = v->val.ul; break;
480         case cv_sll:    break;
481         case cv_ull:    break;
482         }
483         v->type = cv_ull;
484 }
485
486
487 static void promote_equal_size(cval_t *v1, cval_t *v2)
488 {
489 #define cv_sizeof(v)    ((int)(v->type & SIZE_MASK))
490         int s1 = cv_sizeof(v1);
491         int s2 = cv_sizeof(v2);
492 #undef cv_sizeof
493
494         if(s1 == s2)
495                 return;
496         else if(s1 > s2)
497         {
498                 switch(v1->type)
499                 {
500                 case cv_sint:   cast_to_sint(v2); break;
501                 case cv_uint:   cast_to_uint(v2); break;
502                 case cv_slong:  cast_to_slong(v2); break;
503                 case cv_ulong:  cast_to_ulong(v2); break;
504                 case cv_sll:    cast_to_sll(v2); break;
505                 case cv_ull:    cast_to_ull(v2); break;
506                 }
507         }
508         else
509         {
510                 switch(v2->type)
511                 {
512                 case cv_sint:   cast_to_sint(v1); break;
513                 case cv_uint:   cast_to_uint(v1); break;
514                 case cv_slong:  cast_to_slong(v1); break;
515                 case cv_ulong:  cast_to_ulong(v1); break;
516                 case cv_sll:    cast_to_sll(v1); break;
517                 case cv_ull:    cast_to_ull(v1); break;
518                 }
519         }
520 }
521
522
523 static int boolean(cval_t *v)
524 {
525         switch(v->type)
526         {
527         case cv_sint:   return v->val.si != (int)0;
528         case cv_uint:   return v->val.ui != (unsigned int)0;
529         case cv_slong:  return v->val.sl != (long)0;
530         case cv_ulong:  return v->val.ul != (unsigned long)0;
531         case cv_sll:    return v->val.sll != (wrc_sll_t)0;
532         case cv_ull:    return v->val.ull != (wrc_ull_t)0;
533         }
534         return 0;
535 }
536
537 static marg_t *new_marg(char *str, def_arg_t type)
538 {
539         marg_t *ma = pp_xmalloc(sizeof(marg_t));
540         ma->arg = str;
541         ma->type = type;
542         ma->nnl = 0;
543         return ma;
544 }
545
546 static marg_t *add_new_marg(char *str, def_arg_t type)
547 {
548         marg_t *ma = new_marg(str, type);
549         nmacro_args++;
550         macro_args = pp_xrealloc(macro_args, nmacro_args * sizeof(macro_args[0]));
551         macro_args[nmacro_args-1] = ma;
552         return ma;
553 }
554
555 static int marg_index(char *id)
556 {
557         int t;
558         for(t = 0; t < nmacro_args; t++)
559         {
560                 if(!strcmp(id, macro_args[t]->arg))
561                         break;
562         }
563         return t < nmacro_args ? t : -1;
564 }
565
566 static mtext_t *new_mtext(char *str, int idx, def_exp_t type)
567 {
568         mtext_t *mt = pp_xmalloc(sizeof(mtext_t));
569         if(str == NULL)
570                 mt->subst.argidx = idx;
571         else
572                 mt->subst.text = str;
573         mt->type = type;
574         mt->next = mt->prev = NULL;
575         return mt;
576 }
577
578 static mtext_t *combine_mtext(mtext_t *tail, mtext_t *mtp)
579 {
580         if(!tail)
581                 return mtp;
582
583         if(!mtp)
584                 return tail;
585
586         if(tail->type == exp_text && mtp->type == exp_text)
587         {
588                 tail->subst.text = pp_xrealloc(tail->subst.text, strlen(tail->subst.text)+strlen(mtp->subst.text)+1);
589                 strcat(tail->subst.text, mtp->subst.text);
590                 free(mtp->subst.text);
591                 free(mtp);
592                 return tail;
593         }
594
595         if(tail->type == exp_concat && mtp->type == exp_concat)
596         {
597                 free(mtp);
598                 return tail;
599         }
600
601         if(tail->type == exp_concat && mtp->type == exp_text)
602         {
603                 int len = strlen(mtp->subst.text);
604                 while(len)
605                 {
606 /* FIXME: should delete space from head of string */
607                         if(isspace(mtp->subst.text[len-1] & 0xff))
608                                 mtp->subst.text[--len] = '\0';
609                         else
610                                 break;
611                 }
612
613                 if(!len)
614                 {
615                         free(mtp->subst.text);
616                         free(mtp);
617                         return tail;
618                 }
619         }
620
621         if(tail->type == exp_text && mtp->type == exp_concat)
622         {
623                 int len = strlen(tail->subst.text);
624                 while(len)
625                 {
626                         if(isspace(tail->subst.text[len-1] & 0xff))
627                                 tail->subst.text[--len] = '\0';
628                         else
629                                 break;
630                 }
631
632                 if(!len)
633                 {
634                         mtp->prev = tail->prev;
635                         mtp->next = tail->next;
636                         if(tail->prev)
637                                 tail->prev->next = mtp;
638                         free(tail->subst.text);
639                         free(tail);
640                         return mtp;
641                 }
642         }
643
644         tail->next = mtp;
645         mtp->prev = tail;
646
647         return mtp;
648 }
649
650 static char *merge_text(char *s1, char *s2)
651 {
652         int l1 = strlen(s1);
653         int l2 = strlen(s2);
654         s1 = pp_xrealloc(s1, l1+l2+1);
655         memcpy(s1+l1, s2, l2+1);
656         free(s2);
657         return s1;
658 }