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, int inmacro) 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, inmacro); 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, int inmacro) 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 if(!inmacro) 218 doconcat(&ntr); /* execute ## operators */ 219 hs = newhideset(trp->tp->hideset, np); 220 for (tp=ntr.bp; tp<ntr.lp; tp++) { /* distribute hidesets */ 221 if (tp->type==NAME) { 222 if (tp->hideset==0) 223 tp->hideset = hs; 224 else 225 tp->hideset = unionhideset(tp->hideset, hs); 226 } 227 } 228 ntr.tp = ntr.bp; 229 insertrow(trp, ntokc, &ntr); 230 trp->tp -= rowlen(&ntr); 231 dofree(ntr.bp); 232 return; 233 } 234 235 /* 236 * Gather an arglist, starting in trp with tp pointing at the macro name. 237 * Return total number of tokens passed, stash number of args found. 238 * trp->tp is not changed relative to the tokenrow. 239 */ 240 int 241 gatherargs(Tokenrow *trp, Tokenrow **atr, int dots, int *narg) 242 { 243 int parens = 1; 244 int ntok = 0; 245 Token *bp, *lp; 246 Tokenrow ttr; 247 int ntokp; 248 int needspace; 249 250 *narg = -1; /* means that there is no macro call */ 251 /* look for the ( */ 252 for (;;) { 253 trp->tp++; 254 ntok++; 255 if (trp->tp >= trp->lp) { 256 gettokens(trp, 0); 257 if ((trp->lp-1)->type==END) { 258 /* error(WARNING, "reach END\n"); */ 259 trp->lp -= 1; 260 if (*narg>=0) 261 trp->tp -= ntok; 262 return ntok; 263 } 264 } 265 if (trp->tp->type==LP) 266 break; 267 if (trp->tp->type!=NL) 268 return ntok; 269 } 270 *narg = 0; 271 ntok++; 272 ntokp = ntok; 273 trp->tp++; 274 /* search for the terminating ), possibly extending the row */ 275 needspace = 0; 276 while (parens>0) { 277 if (trp->tp >= trp->lp) 278 gettokens(trp, 0); 279 if (needspace) { 280 needspace = 0; 281 makespace(trp); 282 } 283 if (trp->tp->type==END) { 284 trp->lp -= 1; 285 trp->tp -= ntok; 286 error(ERROR, "EOF in macro arglist"); 287 return ntok; 288 } 289 if (trp->tp->type==NL) { 290 trp->tp += 1; 291 adjustrow(trp, -1); 292 trp->tp -= 1; 293 makespace(trp); 294 needspace = 1; 295 continue; 296 } 297 if (trp->tp->type==LP) 298 parens++; 299 else if (trp->tp->type==RP) 300 parens--; 301 trp->tp++; 302 ntok++; 303 } 304 trp->tp -= ntok; 305 /* Now trp->tp won't move underneath us */ 306 lp = bp = trp->tp+ntokp; 307 for (; parens>=0; lp++) { 308 if (lp->type == LP) { 309 parens++; 310 continue; 311 } 312 if (lp->type==RP) 313 parens--; 314 if (lp->type==DSHARP) 315 lp->type = DSHARP1; /* ## not special in arg */ 316 if ((lp->type==COMMA && parens==0) || (parens<0 && (lp-1)->type!=LP)) { 317 if (lp->type == COMMA && dots && *narg == dots-1) 318 continue; 319 if (*narg>=NARG-1) 320 error(FATAL, "Sorry, too many macro arguments"); 321 ttr.bp = ttr.tp = bp; 322 ttr.lp = lp; 323 atr[(*narg)++] = normtokenrow(&ttr); 324 bp = lp+1; 325 } 326 } 327 return ntok; 328 } 329 330 /* 331 * substitute the argument list into the replacement string 332 * This would be simple except for ## and # 333 */ 334 void 335 substargs(Nlist *np, Tokenrow *rtr, Tokenrow **atr) 336 { 337 Tokenrow tatr; 338 Token *tp; 339 int ntok, argno; 340 341 for (rtr->tp=rtr->bp; rtr->tp<rtr->lp; ) { 342 if (rtr->tp->type==SHARP) { /* string operator */ 343 tp = rtr->tp; 344 rtr->tp += 1; 345 if ((argno = lookuparg(np, rtr->tp))<0) { 346 error(ERROR, "# not followed by macro parameter"); 347 continue; 348 } 349 ntok = 1 + (rtr->tp - tp); 350 rtr->tp = tp; 351 insertrow(rtr, ntok, stringify(atr[argno])); 352 continue; 353 } 354 if (rtr->tp->type==NAME 355 && (argno = lookuparg(np, rtr->tp)) >= 0) { 356 if (rtr->tp < rtr->bp) 357 error(ERROR, "access out of bounds"); 358 if ((rtr->tp+1)->type==DSHARP 359 || rtr->tp!=rtr->bp && (rtr->tp-1)->type==DSHARP) 360 insertrow(rtr, 1, atr[argno]); 361 else { 362 copytokenrow(&tatr, atr[argno]); 363 expandrow(&tatr, "<macro>", Inmacro); 364 insertrow(rtr, 1, &tatr); 365 dofree(tatr.bp); 366 } 367 continue; 368 } 369 rtr->tp++; 370 } 371 } 372 373 /* 374 * Evaluate the ## operators in a tokenrow 375 */ 376 void 377 doconcat(Tokenrow *trp) 378 { 379 Token *ltp, *ntp; 380 Tokenrow ntr; 381 int len; 382 383 for (trp->tp=trp->bp; trp->tp<trp->lp; trp->tp++) { 384 if (trp->tp->type==DSHARP1) 385 trp->tp->type = DSHARP; 386 else if (trp->tp->type==DSHARP) { 387 char tt[128]; 388 ltp = trp->tp-1; 389 ntp = trp->tp+1; 390 if (ltp<trp->bp || ntp>=trp->lp) { 391 error(ERROR, "## occurs at border of replacement"); 392 continue; 393 } 394 len = ltp->len + ntp->len; 395 strncpy((char*)tt, (char*)ltp->t, ltp->len); 396 strncpy((char*)tt+ltp->len, (char*)ntp->t, ntp->len); 397 tt[len] = '\0'; 398 setsource("<##>", -1, tt); 399 maketokenrow(3, &ntr); 400 gettokens(&ntr, 1); 401 unsetsource(); 402 if (ntr.lp-ntr.bp!=2 || ntr.bp->type==UNCLASS) 403 error(WARNING, "Bad token %r produced by ##", &ntr); 404 ntr.lp = ntr.bp+1; 405 trp->tp = ltp; 406 makespace(&ntr); 407 insertrow(trp, (ntp-ltp)+1, &ntr); 408 dofree(ntr.bp); 409 trp->tp--; 410 } 411 } 412 } 413 414 /* 415 * tp is a potential parameter name of macro mac; 416 * look it up in mac's arglist, and if found, return the 417 * corresponding index in the argname array. Return -1 if not found. 418 */ 419 int 420 lookuparg(Nlist *mac, Token *tp) 421 { 422 Token *ap; 423 424 if (tp->type!=NAME || mac->ap==NULL) 425 return -1; 426 if((mac->flag & ISVARMAC) && strcmp((char*)tp->t, "__VA_ARGS__") == 0) 427 return rowlen(mac->ap) - 1; 428 for (ap=mac->ap->bp; ap<mac->ap->lp; ap++) { 429 if (ap->len==tp->len && strncmp((char*)ap->t,(char*)tp->t,ap->len)==0) 430 return ap - mac->ap->bp; 431 } 432 return -1; 433 } 434 435 /* 436 * Return a quoted version of the tokenrow (from # arg) 437 */ 438 #define STRLEN 512 439 Tokenrow * 440 stringify(Tokenrow *vp) 441 { 442 static Token t = { STRING }; 443 static Tokenrow tr = { &t, &t, &t+1, 1 }; 444 Token *tp; 445 uchar s[STRLEN]; 446 uchar *sp = s, *cp; 447 int i, instring; 448 449 *sp++ = '"'; 450 for (tp = vp->bp; tp < vp->lp; tp++) { 451 instring = tp->type==STRING || tp->type==CCON; 452 if (sp+2*tp->len >= &s[STRLEN-10]) { 453 error(ERROR, "Stringified macro arg is too long"); 454 break; 455 } 456 if (tp->wslen /* && (tp->flag&XPWS)==0 */) 457 *sp++ = ' '; 458 for (i=0, cp=tp->t; i<tp->len; i++) { 459 if (instring && (*cp=='"' || *cp=='\\')) 460 *sp++ = '\\'; 461 *sp++ = *cp++; 462 } 463 } 464 *sp++ = '"'; 465 *sp = '\0'; 466 sp = s; 467 t.len = strlen((char*)sp); 468 t.t = newstring(sp, t.len, 0); 469 return &tr; 470 } 471 472 /* 473 * expand a builtin name 474 */ 475 void 476 builtin(Tokenrow *trp, int biname) 477 { 478 char *op; 479 Token *tp; 480 Source *s; 481 482 tp = trp->tp; 483 trp->tp++; 484 /* need to find the real source */ 485 s = cursource; 486 while (s && s->fd==-1) 487 s = s->next; 488 if (s==NULL) 489 s = cursource; 490 /* most are strings */ 491 tp->type = STRING; 492 if (tp->wslen) { 493 *outp++ = ' '; 494 tp->wslen = 1; 495 } 496 op = outp; 497 *op++ = '"'; 498 switch (biname) { 499 500 case KLINENO: 501 tp->type = NUMBER; 502 op = outnum(op-1, s->line); 503 break; 504 505 case KFILE: 506 strcpy(op, s->filename); 507 op += strlen(s->filename); 508 break; 509 510 case KDATE: 511 strncpy(op, curtime+4, 7); 512 strncpy(op+7, curtime+24, 4); /* Plan 9 asctime disobeys standard */ 513 op += 11; 514 break; 515 516 case KTIME: 517 strncpy(op, curtime+11, 8); 518 op += 8; 519 break; 520 521 default: 522 error(ERROR, "cpp botch: unknown internal macro"); 523 return; 524 } 525 if (tp->type==STRING) 526 *op++ = '"'; 527 tp->t = (uchar*)outp; 528 tp->len = op - outp; 529 outp = op; 530 } 531