1 #include <u.h> 2 #include <libc.h> 3 #include <stdio.h> 4 #include "cpp.h" 5 6 /* 7 * do a macro definition. tp points to the name being defined in the line 8 */ 9 void 10 dodefine(Tokenrow *trp) 11 { 12 Token *tp; 13 Nlist *np; 14 Tokenrow *def, *args; 15 int dots; 16 17 dots = 0; 18 tp = trp->tp+1; 19 if (tp>=trp->lp || tp->type!=NAME) { 20 error(ERROR, "#defined token is not a name"); 21 return; 22 } 23 np = lookup(tp, 1); 24 if (np->flag&ISUNCHANGE) { 25 error(ERROR, "#defined token %t can't be redefined", tp); 26 return; 27 } 28 /* collect arguments */ 29 tp += 1; 30 args = NULL; 31 if (tp<trp->lp && tp->type==LP && tp->wslen==0) { 32 /* macro with args */ 33 int narg = 0; 34 tp += 1; 35 args = new(Tokenrow); 36 maketokenrow(2, args); 37 if (tp->type!=RP) { 38 int err = 0; 39 for (;;) { 40 Token *atp; 41 if (tp->type == ELLIPS) 42 dots++; 43 else if (tp->type!=NAME) { 44 err++; 45 break; 46 } 47 if (narg>=args->max) 48 growtokenrow(args); 49 for (atp=args->bp; atp<args->lp; atp++) 50 if (atp->len==tp->len 51 && strncmp((char*)atp->t, (char*)tp->t, tp->len)==0) 52 error(ERROR, "Duplicate macro argument"); 53 *args->lp++ = *tp; 54 narg++; 55 tp += 1; 56 if (tp->type==RP) 57 break; 58 if (dots) 59 error(ERROR, "arguments after '...' in macro"); 60 if (tp->type!=COMMA) { 61 err++; 62 break; 63 } 64 tp += 1; 65 } 66 if (err) { 67 error(ERROR, "Syntax error in macro parameters"); 68 return; 69 } 70 } 71 tp += 1; 72 } 73 trp->tp = tp; 74 if (((trp->lp)-1)->type==NL) 75 trp->lp -= 1; 76 def = normtokenrow(trp); 77 if (np->flag&ISDEFINED) { 78 if (comparetokens(def, np->vp) 79 || (np->ap==NULL) != (args==NULL) 80 || np->ap && comparetokens(args, np->ap)) 81 error(ERROR, "Macro redefinition of %t", trp->bp+2); 82 } 83 if (args) { 84 Tokenrow *tap; 85 tap = normtokenrow(args); 86 dofree(args->bp); 87 args = tap; 88 } 89 np->ap = args; 90 np->vp = def; 91 np->flag |= ISDEFINED; 92 if(dots) 93 np->flag |= ISVARMAC; 94 } 95 96 /* 97 * Definition received via -D or -U 98 */ 99 void 100 doadefine(Tokenrow *trp, int type) 101 { 102 Nlist *np; 103 static unsigned char one[] = "1"; 104 static Token onetoken[1] = {{ NUMBER, 0, 0, 0, 1, one }}; 105 static Tokenrow onetr = { onetoken, onetoken, onetoken+1, 1 }; 106 107 trp->tp = trp->bp; 108 if (type=='U') { 109 if (trp->lp-trp->tp != 2 || trp->tp->type!=NAME) 110 goto syntax; 111 if ((np = lookup(trp->tp, 0)) == NULL) 112 return; 113 np->flag &= ~ISDEFINED; 114 return; 115 } 116 if (trp->tp >= trp->lp || trp->tp->type!=NAME) 117 goto syntax; 118 np = lookup(trp->tp, 1); 119 np->flag |= ISDEFINED; 120 trp->tp += 1; 121 if (trp->tp >= trp->lp || trp->tp->type==END) { 122 np->vp = &onetr; 123 return; 124 } 125 if (trp->tp->type!=ASGN) 126 goto syntax; 127 trp->tp += 1; 128 if ((trp->lp-1)->type == END) 129 trp->lp -= 1; 130 np->vp = normtokenrow(trp); 131 return; 132 syntax: 133 error(FATAL, "Illegal -D or -U argument %r", trp); 134 } 135 136 /* 137 * Do macro expansion in a row of tokens. 138 * Flag is NULL if more input can be gathered. 139 */ 140 void 141 expandrow(Tokenrow *trp, char *flag) 142 { 143 Token *tp; 144 Nlist *np; 145 146 if (flag) 147 setsource(flag, -1, ""); 148 for (tp = trp->tp; tp<trp->lp; ) { 149 if (tp->type!=NAME 150 || quicklook(tp->t[0], tp->len>1?tp->t[1]:0)==0 151 || (np = lookup(tp, 0))==NULL 152 || (np->flag&(ISDEFINED|ISMAC))==0 153 || tp->hideset && checkhideset(tp->hideset, np)) { 154 tp++; 155 continue; 156 } 157 trp->tp = tp; 158 if (np->val==KDEFINED) { 159 tp->type = DEFINED; 160 if ((tp+1)<trp->lp && (tp+1)->type==NAME) 161 (tp+1)->type = NAME1; 162 else if ((tp+3)<trp->lp && (tp+1)->type==LP 163 && (tp+2)->type==NAME && (tp+3)->type==RP) 164 (tp+2)->type = NAME1; 165 else 166 error(ERROR, "Incorrect syntax for `defined'"); 167 tp++; 168 continue; 169 } 170 if (np->flag&ISMAC) 171 builtin(trp, np->val); 172 else { 173 expand(trp, np); 174 } 175 tp = trp->tp; 176 } 177 if (flag) 178 unsetsource(); 179 } 180 181 /* 182 * Expand the macro whose name is np, at token trp->tp, in the tokenrow. 183 * Return trp->tp at the first token next to be expanded 184 * (ordinarily the beginning of the expansion) 185 */ 186 void 187 expand(Tokenrow *trp, Nlist *np) 188 { 189 Tokenrow ntr; 190 int ntokc, narg, i; 191 Token *tp; 192 Tokenrow *atr[NARG+1]; 193 int hs; 194 195 copytokenrow(&ntr, np->vp); /* copy macro value */ 196 if (np->ap==NULL) /* parameterless */ 197 ntokc = 1; 198 else { 199 ntokc = gatherargs(trp, atr, (np->flag&ISVARMAC) ? rowlen(np->ap) : 0, &narg); 200 if (narg<0) { /* not actually a call (no '(') */ 201 /* error(WARNING, "%d %r\n", narg, trp); */ 202 /* gatherargs has already pushed trp->tr to the next token */ 203 return; 204 } 205 if (narg != rowlen(np->ap)) { 206 error(ERROR, "Disagreement in number of macro arguments"); 207 trp->tp->hideset = newhideset(trp->tp->hideset, np); 208 trp->tp += ntokc; 209 return; 210 } 211 substargs(np, &ntr, atr); /* put args into replacement */ 212 for (i=0; i<narg; i++) { 213 dofree(atr[i]->bp); 214 dofree(atr[i]); 215 } 216 } 217 doconcat(&ntr); /* execute ## operators */ 218 hs = newhideset(trp->tp->hideset, np); 219 for (tp=ntr.bp; tp<ntr.lp; tp++) { /* distribute hidesets */ 220 if (tp->type==NAME) { 221 if (tp->hideset==0) 222 tp->hideset = hs; 223 else 224 tp->hideset = unionhideset(tp->hideset, hs); 225 } 226 } 227 ntr.tp = ntr.bp; 228 insertrow(trp, ntokc, &ntr); 229 trp->tp -= rowlen(&ntr); 230 dofree(ntr.bp); 231 return; 232 } 233 234 /* 235 * Gather an arglist, starting in trp with tp pointing at the macro name. 236 * Return total number of tokens passed, stash number of args found. 237 * trp->tp is not changed relative to the tokenrow. 238 */ 239 int 240 gatherargs(Tokenrow *trp, Tokenrow **atr, int dots, int *narg) 241 { 242 int parens = 1; 243 int ntok = 0; 244 Token *bp, *lp; 245 Tokenrow ttr; 246 int ntokp; 247 int needspace; 248 249 *narg = -1; /* means that there is no macro call */ 250 /* look for the ( */ 251 for (;;) { 252 trp->tp++; 253 ntok++; 254 if (trp->tp >= trp->lp) { 255 gettokens(trp, 0); 256 if ((trp->lp-1)->type==END) { 257 /* error(WARNING, "reach END\n"); */ 258 trp->lp -= 1; 259 if (*narg>=0) 260 trp->tp -= ntok; 261 return ntok; 262 } 263 } 264 if (trp->tp->type==LP) 265 break; 266 if (trp->tp->type!=NL) 267 return ntok; 268 } 269 *narg = 0; 270 ntok++; 271 ntokp = ntok; 272 trp->tp++; 273 /* search for the terminating ), possibly extending the row */ 274 needspace = 0; 275 while (parens>0) { 276 if (trp->tp >= trp->lp) 277 gettokens(trp, 0); 278 if (needspace) { 279 needspace = 0; 280 makespace(trp); 281 } 282 if (trp->tp->type==END) { 283 trp->lp -= 1; 284 trp->tp -= ntok; 285 error(ERROR, "EOF in macro arglist"); 286 return ntok; 287 } 288 if (trp->tp->type==NL) { 289 trp->tp += 1; 290 adjustrow(trp, -1); 291 trp->tp -= 1; 292 makespace(trp); 293 needspace = 1; 294 continue; 295 } 296 if (trp->tp->type==LP) 297 parens++; 298 else if (trp->tp->type==RP) 299 parens--; 300 trp->tp++; 301 ntok++; 302 } 303 trp->tp -= ntok; 304 /* Now trp->tp won't move underneath us */ 305 lp = bp = trp->tp+ntokp; 306 for (; parens>=0; lp++) { 307 if (lp->type == LP) { 308 parens++; 309 continue; 310 } 311 if (lp->type==RP) 312 parens--; 313 if (lp->type==DSHARP) 314 lp->type = DSHARP1; /* ## not special in arg */ 315 if ((lp->type==COMMA && parens==0) || (parens<0 && (lp-1)->type!=LP)) { 316 if (lp->type == COMMA && dots && *narg == dots-1) 317 continue; 318 if (*narg>=NARG-1) 319 error(FATAL, "Sorry, too many macro arguments"); 320 ttr.bp = ttr.tp = bp; 321 ttr.lp = lp; 322 atr[(*narg)++] = normtokenrow(&ttr); 323 bp = lp+1; 324 } 325 } 326 return ntok; 327 } 328 329 /* 330 * substitute the argument list into the replacement string 331 * This would be simple except for ## and # 332 */ 333 void 334 substargs(Nlist *np, Tokenrow *rtr, Tokenrow **atr) 335 { 336 Tokenrow tatr; 337 Token *tp; 338 int ntok, argno; 339 340 for (rtr->tp=rtr->bp; rtr->tp<rtr->lp; ) { 341 if (rtr->tp->type==SHARP) { /* string operator */ 342 tp = rtr->tp; 343 rtr->tp += 1; 344 if ((argno = lookuparg(np, rtr->tp))<0) { 345 error(ERROR, "# not followed by macro parameter"); 346 continue; 347 } 348 ntok = 1 + (rtr->tp - tp); 349 rtr->tp = tp; 350 insertrow(rtr, ntok, stringify(atr[argno])); 351 continue; 352 } 353 if (rtr->tp->type==NAME 354 && (argno = lookuparg(np, rtr->tp)) >= 0) { 355 if (rtr->tp < rtr->bp) 356 error(ERROR, "access out of bounds"); 357 if ((rtr->tp+1)->type==DSHARP 358 || rtr->tp!=rtr->bp && (rtr->tp-1)->type==DSHARP) 359 insertrow(rtr, 1, atr[argno]); 360 else { 361 copytokenrow(&tatr, atr[argno]); 362 expandrow(&tatr, "<macro>"); 363 insertrow(rtr, 1, &tatr); 364 dofree(tatr.bp); 365 } 366 continue; 367 } 368 rtr->tp++; 369 } 370 } 371 372 /* 373 * Evaluate the ## operators in a tokenrow 374 */ 375 void 376 doconcat(Tokenrow *trp) 377 { 378 Token *ltp, *ntp; 379 Tokenrow ntr; 380 int len; 381 382 for (trp->tp=trp->bp; trp->tp<trp->lp; trp->tp++) { 383 if (trp->tp->type==DSHARP1) 384 trp->tp->type = DSHARP; 385 else if (trp->tp->type==DSHARP) { 386 char tt[128]; 387 ltp = trp->tp-1; 388 ntp = trp->tp+1; 389 if (ltp<trp->bp || ntp>=trp->lp) { 390 error(ERROR, "## occurs at border of replacement"); 391 continue; 392 } 393 len = ltp->len + ntp->len; 394 strncpy((char*)tt, (char*)ltp->t, ltp->len); 395 strncpy((char*)tt+ltp->len, (char*)ntp->t, ntp->len); 396 tt[len] = '\0'; 397 setsource("<##>", -1, tt); 398 maketokenrow(3, &ntr); 399 gettokens(&ntr, 1); 400 unsetsource(); 401 if (ntr.lp-ntr.bp!=2 || ntr.bp->type==UNCLASS) 402 error(WARNING, "Bad token %r produced by ##", &ntr); 403 ntr.lp = ntr.bp+1; 404 trp->tp = ltp; 405 makespace(&ntr); 406 insertrow(trp, (ntp-ltp)+1, &ntr); 407 dofree(ntr.bp); 408 trp->tp--; 409 } 410 } 411 } 412 413 /* 414 * tp is a potential parameter name of macro mac; 415 * look it up in mac's arglist, and if found, return the 416 * corresponding index in the argname array. Return -1 if not found. 417 */ 418 int 419 lookuparg(Nlist *mac, Token *tp) 420 { 421 Token *ap; 422 423 if (tp->type!=NAME || mac->ap==NULL) 424 return -1; 425 if((mac->flag & ISVARMAC) && strcmp((char*)tp->t, "__VA_ARGS__") == 0) 426 return rowlen(mac->ap) - 1; 427 for (ap=mac->ap->bp; ap<mac->ap->lp; ap++) { 428 if (ap->len==tp->len && strncmp((char*)ap->t,(char*)tp->t,ap->len)==0) 429 return ap - mac->ap->bp; 430 } 431 return -1; 432 } 433 434 /* 435 * Return a quoted version of the tokenrow (from # arg) 436 */ 437 #define STRLEN 512 438 Tokenrow * 439 stringify(Tokenrow *vp) 440 { 441 static Token t = { STRING }; 442 static Tokenrow tr = { &t, &t, &t+1, 1 }; 443 Token *tp; 444 uchar s[STRLEN]; 445 uchar *sp = s, *cp; 446 int i, instring; 447 448 *sp++ = '"'; 449 for (tp = vp->bp; tp < vp->lp; tp++) { 450 instring = tp->type==STRING || tp->type==CCON; 451 if (sp+2*tp->len >= &s[STRLEN-10]) { 452 error(ERROR, "Stringified macro arg is too long"); 453 break; 454 } 455 if (tp->wslen && (tp->flag&XPWS)==0) 456 *sp++ = ' '; 457 for (i=0, cp=tp->t; i<tp->len; i++) { 458 if (instring && (*cp=='"' || *cp=='\\')) 459 *sp++ = '\\'; 460 *sp++ = *cp++; 461 } 462 } 463 *sp++ = '"'; 464 *sp = '\0'; 465 sp = s; 466 t.len = strlen((char*)sp); 467 t.t = newstring(sp, t.len, 0); 468 return &tr; 469 } 470 471 /* 472 * expand a builtin name 473 */ 474 void 475 builtin(Tokenrow *trp, int biname) 476 { 477 char *op; 478 Token *tp; 479 Source *s; 480 481 tp = trp->tp; 482 trp->tp++; 483 /* need to find the real source */ 484 s = cursource; 485 while (s && s->fd==-1) 486 s = s->next; 487 if (s==NULL) 488 s = cursource; 489 /* most are strings */ 490 tp->type = STRING; 491 if (tp->wslen) { 492 *outp++ = ' '; 493 tp->wslen = 1; 494 } 495 op = outp; 496 *op++ = '"'; 497 switch (biname) { 498 499 case KLINENO: 500 tp->type = NUMBER; 501 op = outnum(op-1, s->line); 502 break; 503 504 case KFILE: 505 strcpy(op, s->filename); 506 op += strlen(s->filename); 507 break; 508 509 case KDATE: 510 strncpy(op, curtime+4, 7); 511 strncpy(op+7, curtime+24, 4); /* Plan 9 asctime disobeys standard */ 512 op += 11; 513 break; 514 515 case KTIME: 516 strncpy(op, curtime+11, 8); 517 op += 8; 518 break; 519 520 default: 521 error(ERROR, "cpp botch: unknown internal macro"); 522 return; 523 } 524 if (tp->type==STRING) 525 *op++ = '"'; 526 tp->t = (uchar*)outp; 527 tp->len = op - outp; 528 outp = op; 529 } 530