1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1986-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * Glenn Fowler <gsf@research.att.com> * 18*4887Schin * * 19*4887Schin ***********************************************************************/ 20*4887Schin #pragma prototyped 21*4887Schin /* 22*4887Schin * Glenn Fowler 23*4887Schin * AT&T Research 24*4887Schin * 25*4887Schin * preprocessor macro call 26*4887Schin */ 27*4887Schin 28*4887Schin #include "pplib.h" 29*4887Schin 30*4887Schin #include <ctype.h> 31*4887Schin 32*4887Schin /* 33*4887Schin * call a macro by pushing its value on the input stream 34*4887Schin * only the macro token itself has been consumed 35*4887Schin * -1 returned if macro disabled 36*4887Schin * 0 returned if tok==0 and sym->mac->value to be copied to output by caller 37*4887Schin * 1 returned if value pushed on input 38*4887Schin */ 39*4887Schin 40*4887Schin int 41*4887Schin ppcall(register struct ppsymbol* sym, int tok) 42*4887Schin { 43*4887Schin register int c; 44*4887Schin register char* p; 45*4887Schin register char* q; 46*4887Schin register struct ppmacro* mac; 47*4887Schin int n; 48*4887Schin int m; 49*4887Schin int ret; 50*4887Schin int old_hidden; 51*4887Schin int last_line; 52*4887Schin long old_state; 53*4887Schin char* last_file; 54*4887Schin char* old_token; 55*4887Schin struct ppmacstk* mp; 56*4887Schin struct ppinstk* old_in; 57*4887Schin struct ppinstk* kp; 58*4887Schin struct pptuple* tp; 59*4887Schin 60*4887Schin ret = -1; 61*4887Schin sym->flags |= SYM_NOTICED; 62*4887Schin if (mac = sym->macro) 63*4887Schin { 64*4887Schin count(macro); 65*4887Schin if ((sym->flags & SYM_PREDICATE) && (pp.state & (CONDITIONAL|WARN)) == (CONDITIONAL|WARN)) 66*4887Schin error(1, "%s: macro definition overrides assertion: use #%s ...", sym->name, sym->name); 67*4887Schin if (sym->flags & SYM_DISABLED) 68*4887Schin #if COMPATIBLE 69*4887Schin if ((pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY || !mac->arity) 70*4887Schin #endif 71*4887Schin { 72*4887Schin pp.mode |= MARKMACRO; 73*4887Schin #if COMPATIBLE 74*4887Schin if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT)) 75*4887Schin error(1, "%s: macro recursion inhibited", sym->name); 76*4887Schin #endif 77*4887Schin goto disable; 78*4887Schin } 79*4887Schin if ((sym->flags & SYM_PREDEFINED) && !(pp.mode & (HOSTED|INACTIVE))) 80*4887Schin { 81*4887Schin #if COMPATIBLE 82*4887Schin if (*sym->name != '_' && !(pp.state & COMPATIBILITY)) 83*4887Schin #else 84*4887Schin if (*sym->name != '_') 85*4887Schin #endif 86*4887Schin { 87*4887Schin if (pp.state & STRICT) 88*4887Schin { 89*4887Schin error(1, "%s: obsolete predefined symbol expansion disabled", sym->name); 90*4887Schin goto disable; 91*4887Schin } 92*4887Schin error(1, "%s: obsolete predefined symbol expanded%s", sym->name, (pp.state & DIRECTIVE) ? "" : " outside of directive"); 93*4887Schin } 94*4887Schin else if (!(pp.state & DIRECTIVE) && mac->value && (ppisdig(*mac->value) || *mac->value == '#')) 95*4887Schin error(1, "%s: predefined symbol expanded outside of directive", sym->name); 96*4887Schin } 97*4887Schin debug((-5, "macro %s = %s", sym->name, mac->value)); 98*4887Schin if (pp.macref) 99*4887Schin (*pp.macref)(sym, error_info.file, error_info.line, (pp.state & CONDITIONAL) ? REF_IF : REF_NORMAL, 0L); 100*4887Schin if (tp = mac->tuple) 101*4887Schin { 102*4887Schin old_state = pp.state; 103*4887Schin pp.state |= DEFINITION|NOSPACE; 104*4887Schin old_token = pp.token; 105*4887Schin n = 2 * MAXTOKEN; 106*4887Schin pp.token = p = oldof(0, char, 0, n); 107*4887Schin q = p + MAXTOKEN; 108*4887Schin *pp.token++ = ' '; 109*4887Schin old_hidden = pp.hidden; 110*4887Schin while (c = pplex()) 111*4887Schin { 112*4887Schin if (c == '\n') 113*4887Schin { 114*4887Schin pp.hidden++; 115*4887Schin pp.state |= HIDDEN|NEWLINE; 116*4887Schin old_state |= HIDDEN|NEWLINE; 117*4887Schin error_info.line++; 118*4887Schin } 119*4887Schin else if (c == '#') 120*4887Schin { 121*4887Schin ungetchr(c); 122*4887Schin break; 123*4887Schin } 124*4887Schin else 125*4887Schin { 126*4887Schin for (;;) 127*4887Schin { 128*4887Schin if (streq(pp.token, tp->token)) 129*4887Schin { 130*4887Schin if (!(tp = tp->match)) 131*4887Schin break; 132*4887Schin if (!tp->nomatch) 133*4887Schin { 134*4887Schin free(p); 135*4887Schin pp.state = old_state; 136*4887Schin pp.token = old_token; 137*4887Schin PUSH_TUPLE(sym, tp->token); 138*4887Schin ret = 1; 139*4887Schin goto disable; 140*4887Schin } 141*4887Schin } 142*4887Schin else if (!(tp = tp->nomatch)) 143*4887Schin break; 144*4887Schin } 145*4887Schin if (!tp) 146*4887Schin { 147*4887Schin pp.token = pp.toknxt; 148*4887Schin break; 149*4887Schin } 150*4887Schin } 151*4887Schin if ((pp.token = pp.toknxt) > q) 152*4887Schin { 153*4887Schin c = pp.token - p; 154*4887Schin p = newof(p, char, n += MAXTOKEN, 0); 155*4887Schin q = p + n - MAXTOKEN; 156*4887Schin pp.token = p + c; 157*4887Schin } 158*4887Schin *pp.token++ = ' '; 159*4887Schin } 160*4887Schin if (pp.token > p && *(pp.token - 1) == ' ') 161*4887Schin pp.token--; 162*4887Schin if (pp.hidden != old_hidden) 163*4887Schin *pp.token++ = '\n'; 164*4887Schin else 165*4887Schin *pp.token++ = ' '; 166*4887Schin *pp.token = 0; 167*4887Schin pp.state = old_state; 168*4887Schin pp.token = old_token; 169*4887Schin if (*p) 170*4887Schin PUSH_RESCAN(p); 171*4887Schin else 172*4887Schin free(p); 173*4887Schin if (!mac->value) 174*4887Schin goto disable; 175*4887Schin } 176*4887Schin if (sym->flags & SYM_FUNCTION) 177*4887Schin { 178*4887Schin /* 179*4887Schin * a quick and dirty '(' peek to avoid possibly 180*4887Schin * inappropriate ungetchr()'s below 181*4887Schin */ 182*4887Schin 183*4887Schin for (p = pp.in->nextchr; isspace(*p); p++); 184*4887Schin if ((c = *p) != '(' && c != '/' && c != 0 && c != MARK) 185*4887Schin goto disable; 186*4887Schin old_token = pp.token; 187*4887Schin mp = pp.macp->next; 188*4887Schin if ((pp.token = (char*)&mp->arg[mac->arity + 1]) > pp.maxmac) 189*4887Schin error(3, "%s: too many nested function-like macros", sym->name); 190*4887Schin old_hidden = pp.hidden; 191*4887Schin old_state = pp.state; 192*4887Schin pp.state |= DEFINITION|FILEPOP|NOSPACE; 193*4887Schin while ((c = pplex()) == '\n') 194*4887Schin { 195*4887Schin pp.hidden++; 196*4887Schin pp.state |= HIDDEN|NEWLINE; 197*4887Schin old_state |= HIDDEN|NEWLINE; 198*4887Schin error_info.line++; 199*4887Schin } 200*4887Schin if (c != '(') 201*4887Schin { 202*4887Schin pp.state = old_state; 203*4887Schin if (c) 204*4887Schin { 205*4887Schin p = pp.toknxt; 206*4887Schin while (p > pp.token) 207*4887Schin ungetchr(*--p); 208*4887Schin #if COMPATIBLE 209*4887Schin if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT)) 210*4887Schin error(1, "%s: macro arguments omitted", sym->name); 211*4887Schin #endif 212*4887Schin if (c == T_ID && !(pp.state & HIDDEN)) 213*4887Schin ungetchr(' '); 214*4887Schin } 215*4887Schin if (pp.hidden != old_hidden) 216*4887Schin { 217*4887Schin ungetchr('\n'); 218*4887Schin error_info.line--; 219*4887Schin if (pp.hidden && !--pp.hidden) 220*4887Schin pp.state &= ~HIDDEN; 221*4887Schin } 222*4887Schin pp.token = old_token; 223*4887Schin goto disable; 224*4887Schin } 225*4887Schin pp.state = old_state; 226*4887Schin 227*4887Schin /* 228*4887Schin * arg[i][-1] is an extra char for each actual i 229*4887Schin * for a possible ungetchr('"') during IN_QUOTE 230*4887Schin * arg[i][-1]==0 if arg i need not be expanded 231*4887Schin * arg[0][-2] holds the actual arg count 232*4887Schin */ 233*4887Schin 234*4887Schin c = 0; 235*4887Schin m = 0; 236*4887Schin n = 0; 237*4887Schin mp = pp.macp->next; 238*4887Schin p = pp.token = (char*)&mp->arg[mac->arity + 1]; 239*4887Schin pp.state |= COLLECTING|NOEXPAND; 240*4887Schin pp.state &= ~FILEPOP; 241*4887Schin sym->flags |= SYM_ACTIVE; 242*4887Schin old_in = pp.in; 243*4887Schin last_line = error_info.line; 244*4887Schin last_file = error_info.file; 245*4887Schin mp->line = error_info.line; 246*4887Schin #if MACKEYARGS 247*4887Schin if (pp.option & KEYARGS) 248*4887Schin { 249*4887Schin for (c = 0; c < mac->arity; c++) 250*4887Schin mp->arg[c] = mac->args.key[c].value + 1; 251*4887Schin mp->arg[0]++; 252*4887Schin } 253*4887Schin else 254*4887Schin #endif 255*4887Schin { 256*4887Schin *++p = ' '; 257*4887Schin mp->arg[0] = ++p; 258*4887Schin } 259*4887Schin #if MACKEYARGS 260*4887Schin keyarg: 261*4887Schin if (pp.option & KEYARGS) 262*4887Schin { 263*4887Schin pp.state |= NOSPACE; 264*4887Schin switch (pplex()) 265*4887Schin { 266*4887Schin case T_ID: 267*4887Schin break; 268*4887Schin case ')': /* no actual key args */ 269*4887Schin if (!(pp.state & NOEXPAND)) 270*4887Schin pp.state |= NOEXPAND; 271*4887Schin for (c = 0; c < mac->arity; c++) 272*4887Schin mp->arg[c][-1] = 0; 273*4887Schin c = 0; 274*4887Schin goto endactuals; 275*4887Schin default: 276*4887Schin error(3, "%s: invalid keyword macro argument", pp.token); 277*4887Schin break; 278*4887Schin } 279*4887Schin for (c = 0; c < mac->arity; c++) 280*4887Schin if (streq(pp.token, mac->args.key[c].name)) break; 281*4887Schin if (c >= mac->arity) 282*4887Schin error(2, "%s: invalid macro argument keyword", pp.token); 283*4887Schin if (pplex() != '=') 284*4887Schin error(2, "= expected in keyword macro argument"); 285*4887Schin pp.state &= ~NOSPACE; 286*4887Schin if (!c) 287*4887Schin p++; 288*4887Schin pp.token = mp->arg[c] = ++p; 289*4887Schin } 290*4887Schin #endif 291*4887Schin for (;;) 292*4887Schin { 293*4887Schin if ((pp.mactop = pp.token = p) >= pp.maxmac) 294*4887Schin error(3, "%s: too many nested function-like macros", sym->name); 295*4887Schin switch (pplex()) 296*4887Schin { 297*4887Schin case '(': 298*4887Schin n++; 299*4887Schin break; 300*4887Schin case ')': 301*4887Schin if (!n--) 302*4887Schin { 303*4887Schin if (p > mp->arg[c] && *(p - 1) == ' ') 304*4887Schin p--; 305*4887Schin if (p > mp->arg[c] && *(p - 1) == '\\') 306*4887Schin { 307*4887Schin for (q = mp->arg[c]; q < p; q++) 308*4887Schin if (*q == '\\') 309*4887Schin q++; 310*4887Schin if (q > p) 311*4887Schin *p++ = '\\'; 312*4887Schin } 313*4887Schin #if MACKEYARGS 314*4887Schin *p = 0; 315*4887Schin m++; 316*4887Schin #endif 317*4887Schin goto endactuals; 318*4887Schin } 319*4887Schin break; 320*4887Schin case ',': 321*4887Schin if (!n && (m++, (c < mac->arity - 1 || !(sym->flags & SYM_VARIADIC)))) 322*4887Schin { 323*4887Schin if (p > mp->arg[c] && *(p - 1) == ' ') 324*4887Schin p--; 325*4887Schin *p++ = 0; 326*4887Schin if (!(pp.state & NOEXPAND)) 327*4887Schin pp.state |= NOEXPAND; 328*4887Schin else 329*4887Schin mp->arg[c][-1] = 0; 330*4887Schin #if MACKEYARGS 331*4887Schin if (pp.option & KEYARGS) 332*4887Schin { 333*4887Schin pp.token = p + 1; 334*4887Schin goto keyarg; 335*4887Schin } 336*4887Schin #endif 337*4887Schin { 338*4887Schin if ((pp.state & STRICT) && p == mp->arg[c]) 339*4887Schin error(1, "%s: macro call argument %d is null", sym->name, c + 1); 340*4887Schin if (c < mac->arity) 341*4887Schin c++; 342*4887Schin *p++ = ' '; 343*4887Schin } 344*4887Schin pp.toknxt = mp->arg[c] = p; 345*4887Schin } 346*4887Schin break; 347*4887Schin case 0: 348*4887Schin if (pp.in == old_in) 349*4887Schin kp = 0; 350*4887Schin else 351*4887Schin for (kp = pp.in; kp && kp != old_in; kp = kp->prev); 352*4887Schin if (!kp) 353*4887Schin { 354*4887Schin error( 355*4887Schin #if COMPATIBLE 356*4887Schin (pp.state & COMPATIBILITY) ? 3 : 357*4887Schin #endif 358*4887Schin 2, "%s: %s in macro argument list", sym->name, pptokchr(0)); 359*4887Schin goto endactuals; 360*4887Schin } 361*4887Schin continue; 362*4887Schin case '\n': 363*4887Schin pp.state |= HIDDEN; 364*4887Schin error_info.line++; 365*4887Schin pp.hidden++; 366*4887Schin /*FALLTHROUGH*/ 367*4887Schin case ' ': 368*4887Schin if (p > mp->arg[c] && *(p - 1) != ' ') *p++ = ' '; 369*4887Schin continue; 370*4887Schin } 371*4887Schin p = pp.toknxt; 372*4887Schin if (error_info.line != last_line) 373*4887Schin { 374*4887Schin SETLINE(p, error_info.line); 375*4887Schin last_line = error_info.line; 376*4887Schin } 377*4887Schin if (error_info.file != last_file) 378*4887Schin { 379*4887Schin SETFILE(p, error_info.file); 380*4887Schin last_file = error_info.file; 381*4887Schin } 382*4887Schin } 383*4887Schin endactuals: 384*4887Schin if (pp.state & NOEXPAND) 385*4887Schin mp->arg[c][-1] = 0; 386*4887Schin pp.token = old_token; 387*4887Schin if (pp.in != old_in) 388*4887Schin { 389*4887Schin for (kp = pp.in; kp && kp != old_in; kp = kp->prev); 390*4887Schin if (kp) 391*4887Schin error(2, "%s: macro call starts and ends in different files", sym->name); 392*4887Schin } 393*4887Schin pp.state &= ~(COLLECTING|FILEPOP|NOEXPAND); 394*4887Schin sym->flags &= ~SYM_ACTIVE; 395*4887Schin #if MACKEYARGS 396*4887Schin if (!(pp.option & KEYARGS)) 397*4887Schin #endif 398*4887Schin { 399*4887Schin if (p > mp->arg[0] && ++m || (sym->flags & SYM_VARIADIC)) 400*4887Schin c++; 401*4887Schin if (c != mac->arity && !(sym->flags & SYM_EMPTY)) 402*4887Schin { 403*4887Schin n = mac->arity; 404*4887Schin if (!(sym->flags & SYM_VARIADIC)) 405*4887Schin error(1, "%s: %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s"); 406*4887Schin else if (c < --n) 407*4887Schin error(1, "%s: at least %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s"); 408*4887Schin #if COMPATIBLE 409*4887Schin if (!c && (pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT)) 410*4887Schin goto disable; 411*4887Schin #endif 412*4887Schin } 413*4887Schin if (!c) 414*4887Schin ++c; 415*4887Schin while (c < mac->arity) 416*4887Schin mp->arg[c++] = (char*)"\0" + 1; 417*4887Schin } 418*4887Schin mp->arg[0][-2] = m; 419*4887Schin *p++ = 0; 420*4887Schin nextframe(mp, p); 421*4887Schin count(function); 422*4887Schin } 423*4887Schin if (!tok && (sym->flags & SYM_NOEXPAND)) 424*4887Schin { 425*4887Schin if (sym->flags & SYM_FUNCTION) 426*4887Schin popframe(mp); 427*4887Schin ret = !mac->size; 428*4887Schin } 429*4887Schin else if (!(pp.state & HEADER) || (pp.option & HEADEREXPANDALL) || pp.in->type != IN_COPY) 430*4887Schin { 431*4887Schin if (sym->flags & SYM_MULTILINE) 432*4887Schin PUSH_MULTILINE(sym); 433*4887Schin else 434*4887Schin PUSH_MACRO(sym); 435*4887Schin ret = 1; 436*4887Schin } 437*4887Schin } 438*4887Schin disable: 439*4887Schin if (ret < 0 && sym->hidden && !(pp.mode & EXPOSE) && !(pp.state & HEADER) && (pp.in->type == IN_FILE || pp.in->type == IN_MACRO || pp.in->type == IN_EXPAND)) 440*4887Schin { 441*4887Schin struct ppinstk* inp; 442*4887Schin 443*4887Schin for (inp = pp.in; inp->type != IN_FILE && inp->prev; inp = inp->prev); 444*4887Schin sfsprintf(pp.hidebuf, MAXTOKEN, "_%d_%s_hIDe", inp->index, sym->name); 445*4887Schin PUSH_STRING(pp.hidebuf); 446*4887Schin ret = 1; 447*4887Schin } 448*4887Schin pp.state &= ~NEWLINE; 449*4887Schin pp.in->flags |= IN_tokens; 450*4887Schin count(token); 451*4887Schin return ret; 452*4887Schin } 453