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