/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #if defined(__IBMC__) || defined(__EMX__) || defined(_MSC_VER) # ifndef PATH_MAX # define PATH_MAX _MAX_PATH # endif #endif #include #include "cpp.h" #define NCONCAT 16384 /* * do a macro definition. tp points to the name being defined in the line */ void dodefine(Tokenrow * trp) { Token *tp; Nlist *np; Source *s; Tokenrow *def, *args; static uchar location[(PATH_MAX + 8) * NINC], *cp; tp = trp->tp + 1; if (tp >= trp->lp || tp->type != NAME) { error(ERROR, "#defined token is not a name"); return; } np = lookup(tp, 1); if (np->flag & ISUNCHANGE) { error(ERROR, "#defined token %t can't be redefined", tp); return; } /* collect arguments */ tp += 1; args = NULL; if (tp < trp->lp && tp->type == LP && tp->wslen == 0) { tp += 1; args = new(Tokenrow); maketokenrow(2, args); if (tp->type != RP) { /* macro with args */ size_t narg = 0; int err = 0; for (;;) { Token *atp; if (tp->type != NAME) { err++; break; } if (narg >= args->max) growtokenrow(args); for (atp = args->bp; atp < args->lp; atp++) if (atp->len == tp->len && strncmp((char *) atp->t, (char *) tp->t, tp->len) == 0) error(ERROR, "Duplicate macro argument"); *args->lp++ = *tp; narg++; tp += 1; if (tp->type == RP) break; if (tp->type != COMMA) { err++; break; } tp += 1; } if (err) { error(ERROR, "Syntax error in macro parameters"); return; } } tp += 1; } trp->tp = tp; if (((trp->lp) - 1)->type == NL) trp->lp -= 1; def = normtokenrow(trp); if (np->flag & ISDEFINED) { if (comparetokens(def, np->vp) || (np->ap == NULL) != (args == NULL) || (np->ap && comparetokens(args, np->ap))) { if ( np->loc ) error(ERROR, "Macro redefinition of %t (already defined at %s)", trp->bp + 2, np->loc); else error(ERROR, "Macro redefinition of %t (already defined at %s)", trp->bp + 2, "commandline" ); } } if (args) { Tokenrow *tap; tap = normtokenrow(args); dofree(args->bp); dofree(args); args = tap; } np->ap = args; np->vp = def; np->flag |= ISDEFINED; /* build location string of macro definition */ for (cp = location, s = cursource; s; s = s->next) if (*s->filename) { if (cp != location) *cp++ = ' '; sprintf((char *)cp, "%s:%d", s->filename, s->line); cp += strlen((char *)cp); } np->loc = newstring(location, strlen((char *)location), 0); if (Mflag) { if (np->ap) error(INFO, "Macro definition of %s(%r) [%r]", np->name, np->ap, np->vp); else error(INFO, "Macro definition of %s [%r]", np->name, np->vp); } } /* * Definition received via -D or -U */ void doadefine(Tokenrow * trp, int type) { Nlist *np; static uchar onestr[2] = "1"; static Token onetoken[1] = {{NUMBER, 0, 1, onestr, 0}}; static Tokenrow onetr = {onetoken, onetoken, onetoken + 1, 1}; trp->tp = trp->bp; if (type == 'U') { if (trp->lp - trp->tp != 2 || trp->tp->type != NAME) goto syntax; if ((np = lookup(trp->tp, 0)) == NULL) return; np->flag &= ~ISDEFINED; return; } if (type == 'A') { if (trp->tp >= trp->lp || trp->tp->type != NAME) goto syntax; trp->tp->type = ARCHITECTURE; np = lookup(trp->tp, 1); np->flag |= ISARCHITECTURE; trp->tp += 1; if (trp->tp >= trp->lp || trp->tp->type == END) { np->vp = &onetr; return; } else error(FATAL, "Illegal -A argument %r", trp); } if (trp->tp >= trp->lp || trp->tp->type != NAME) goto syntax; np = lookup(trp->tp, 1); np->flag |= ISDEFINED; trp->tp += 1; if (trp->tp >= trp->lp || trp->tp->type == END) { np->vp = &onetr; return; } if (trp->tp->type != ASGN) goto syntax; trp->tp += 1; if ((trp->lp - 1)->type == END) trp->lp -= 1; np->vp = normtokenrow(trp); return; syntax: error(FATAL, "Illegal -D or -U argument %r", trp); } /* * Do macro expansion in a row of tokens. * Flag is NULL if more input can be gathered. */ void expandrow(Tokenrow * trp, char *flag) { Token * tp; Nlist * np; MacroValidatorList validators; mvl_init(&validators); /* Sets all token-identifiers to 0 because tokens may not be initialised (never use C!) */ tokenrow_zeroTokenIdentifiers(trp); if (flag) setsource(flag, -1, -1, "", 0); for (tp = trp->tp; tp < trp->lp;) { mvl_check(&validators, tp); if (tp->type != NAME || quicklook(tp->t[0], tp->len > 1 ? tp->t[1] : 0) == 0 || (np = lookup(tp, 0)) == NULL || (np->flag & (ISDEFINED | ISMAC)) == 0 || (np->flag & ISACTIVE) != 0) { tp++; continue; } trp->tp = tp; if (np->val == KDEFINED) { tp->type = DEFINED; if ((tp + 1) < trp->lp && (tp + 1)->type == NAME) (tp + 1)->type = NAME1; else if ((tp + 3) < trp->lp && (tp + 1)->type == LP && (tp + 2)->type == NAME && (tp + 3)->type == RP) (tp + 2)->type = NAME1; else error(ERROR, "Incorrect syntax for `defined'"); tp++; continue; } else if (np->val == KMACHINE) { if (((tp - 1) >= trp->bp) && ((tp - 1)->type == SHARP)) { tp->type = ARCHITECTURE; if ((tp + 1) < trp->lp && (tp + 1)->type == NAME) (tp + 1)->type = NAME2; else if ((tp + 3) < trp->lp && (tp + 1)->type == LP && (tp + 2)->type == NAME && (tp + 3)->type == RP) (tp + 2)->type = NAME2; else error(ERROR, "Incorrect syntax for `#machine'"); } tp++; continue; } if (np->flag & ISMAC) builtin(trp, np->val); else { // coverity[overrun-buffer-arg: FALSE] - a multiple of trp->max is allocated, not trp->max itself expand(trp, np, &validators); } tp = trp->tp; } // end for if (flag) unsetsource(); mvl_destruct(&validators); } /* * Expand the macro whose name is np, at token trp->tp, in the tokenrow. * Return trp->tp at the first token next to be expanded * (ordinarily the beginning of the expansion) * I.e.: the same position as before! * Only one expansion is performed, then we return to the expandrow() * loop and start at same position. */ void expand(Tokenrow * trp, Nlist * np, MacroValidatorList * pValidators) { Tokenrow ntr; int ntokc, narg; Tokenrow *atr[NARG + 1]; if (Mflag == 2) { if (np->ap) error(INFO, "Macro expansion of %t with %s(%r)", trp->tp, np->name, np->ap); else error(INFO, "Macro expansion of %t with %s", trp->tp, np->name); } copytokenrow(&ntr, np->vp); /* copy macro value */ if (np->ap == NULL) /* parameterless */ ntokc = 1; else { int i; ntokc = gatherargs(trp, atr, &narg); if (narg < 0) { /* not actually a call (no '(') */ trp->tp++; return; } if (narg != rowlen(np->ap)) { error(ERROR, "Disagreement in number of macro arguments"); trp->tp += ntokc; return; } /** If gatherargs passed a macro validating token, this token must become valid here. trp->tp+0 was checked in expandrow(), so we don't need to do it again here: */ for (i = 1; i < ntokc; i++) { mvl_check(pValidators,trp->tp+i); } substargs(np, &ntr, atr); /* put args into replacement */ for (i = 0; i < narg; i++) { dofree(atr[i]->bp); dofree(atr[i]); } } doconcat(&ntr); /* execute ## operators */ ntr.tp = ntr.bp; makespace(&ntr, trp->tp); tokenrow_zeroTokenIdentifiers(&ntr); insertrow(trp, ntokc, &ntr); /* add validator for just invalidated macro: */ np->flag |= ISACTIVE; if (trp->tp != trp->lp) { /* tp is a valid pointer: */ mvl_add(pValidators,np,trp->tp); } else { /* tp is == lp, therefore does not point to valid memory: */ mvl_add(pValidators,np,NULL); } /* reset trp->tp to original position: */ trp->tp -= ntr.lp - ntr.bp; /* so the result will be tested for macros from the same position again */ dofree(ntr.bp); return; } /* * Gather an arglist, starting in trp with tp pointing at the macro name. * Return total number of tokens passed, stash number of args found. * trp->tp is not changed relative to the tokenrow. */ int gatherargs(Tokenrow * trp, Tokenrow ** atr, int *narg) { int parens = 1; int ntok = 0; Token *bp, *lp; Tokenrow ttr; int ntokp; int needspace; *narg = -1; /* means that there is no macro * call */ /* look for the ( */ for (;;) { trp->tp++; ntok++; if (trp->tp >= trp->lp) { gettokens(trp, 0); if ((trp->lp - 1)->type == END) { trp->lp -= 1; trp->tp -= ntok; return ntok; } } if (trp->tp->type == LP) break; if (trp->tp->type != NL) return ntok; } *narg = 0; ntok++; ntokp = ntok; trp->tp++; /* search for the terminating ), possibly extending the row */ needspace = 0; while (parens > 0) { if (trp->tp >= trp->lp) { // coverity[overrun-buffer-arg: FALSE] - a multiple of trp->max is allocated, not trp->max itself gettokens(trp, 0); } if (needspace) { needspace = 0; /* makespace(trp); [rh] */ } if (trp->tp->type == END) { trp->lp -= 1; trp->tp -= ntok; error(ERROR, "EOF in macro arglist"); return ntok; } if (trp->tp->type == NL) { trp->tp += 1; adjustrow(trp, -1); trp->tp -= 1; /* makespace(trp); [rh] */ needspace = 1; continue; } if (trp->tp->type == LP) parens++; else if (trp->tp->type == RP) parens--; trp->tp++; ntok++; } trp->tp -= ntok; /* Now trp->tp won't move underneath us */ lp = bp = trp->tp + ntokp; for (; parens >= 0; lp++) { if (lp->type == LP) { parens++; continue; } if (lp->type == RP) parens--; if (lp->type == DSHARP) lp->type = DSHARP1; /* ## not special in arg */ if ((lp->type == COMMA && parens == 0) || ( parens < 0 && ((lp - 1)->type != LP))) { if (*narg >= NARG - 1) error(FATAL, "Sorry, too many macro arguments"); ttr.bp = ttr.tp = bp; ttr.lp = lp; atr[(*narg)++] = normtokenrow(&ttr); bp = lp + 1; } } return ntok; } /* * substitute the argument list into the replacement string * This would be simple except for ## and # */ void substargs(Nlist * np, Tokenrow * rtr, Tokenrow ** atr) { Tokenrow tatr; Token *tp; int ntok, argno; for (rtr->tp = rtr->bp; rtr->tp < rtr->lp;) { if (rtr->tp->type == SHARP) { /* string operator */ tp = rtr->tp; rtr->tp += 1; if ((argno = lookuparg(np, rtr->tp)) < 0) { error(ERROR, "# not followed by macro parameter"); continue; } ntok = 1 + (int)(rtr->tp - tp); rtr->tp = tp; insertrow(rtr, ntok, stringify(atr[argno])); continue; } if (rtr->tp->type == NAME && (argno = lookuparg(np, rtr->tp)) >= 0) { if (((rtr->tp + 1) < rtr->lp && (rtr->tp + 1)->type == DSHARP) || (rtr->tp != rtr->bp && (rtr->tp - 1)->type == DSHARP)) { copytokenrow(&tatr, atr[argno]); makespace(&tatr, rtr->tp); insertrow(rtr, 1, &tatr); dofree(tatr.bp); } else { copytokenrow(&tatr, atr[argno]); makespace(&tatr, rtr->tp); expandrow(&tatr, ""); insertrow(rtr, 1, &tatr); dofree(tatr.bp); } continue; } rtr->tp++; } } /* * Evaluate the ## operators in a tokenrow */ void doconcat(Tokenrow * trp) { Token *ltp, *ntp; Tokenrow ntr; size_t len; for (trp->tp = trp->bp; trp->tp < trp->lp; trp->tp++) { if (trp->tp->type == DSHARP1) trp->tp->type = DSHARP; else if (trp->tp->type == DSHARP) { int i; char tt[NCONCAT]; ltp = trp->tp - 1; ntp = trp->tp + 1; if (ltp < trp->bp || ntp >= trp->lp) { error(ERROR, "## occurs at border of replacement"); continue; } ntp = ltp; i = 1; len = 0; do { if (len + ntp->len + ntp->wslen > sizeof(tt)) { error(ERROR, "## string concatenation buffer overrun"); break; } if (ntp != trp->tp + 1) { strncpy((char *) tt + len, (char *) ntp->t - ntp->wslen, ntp->len + ntp->wslen); len += ntp->len + ntp->wslen; } else { // remove spaces around ## strncpy((char *) tt + len, (char *) ntp->t, ntp->len); len += ntp->len; } ntp = trp->tp + i; i++; } while (ntp < trp->lp); tt[len] = '\0'; setsource("<##>", -1, -1, tt, 0); maketokenrow(3, &ntr); // coverity[overrun-buffer-arg: FALSE] - a multiple of trp->max is allocated, not trp->max itself gettokens(&ntr, 1); unsetsource(); if (ntr.bp->type == UNCLASS) error(WARNING, "Bad token %r produced by ##", &ntr); while ((ntr.lp-1)->len == 0 && ntr.lp != ntr.bp) ntr.lp--; doconcat(&ntr); trp->tp = ltp; makespace(&ntr, ltp); insertrow(trp, (int)(ntp - ltp), &ntr); dofree(ntr.bp); trp->tp--; } } } /* * tp is a potential parameter name of macro mac; * look it up in mac's arglist, and if found, return the * corresponding index in the argname array. Return -1 if not found. */ int lookuparg(Nlist * mac, Token const * tp) { Token *ap; if (tp->type != NAME || mac->ap == NULL) return -1; for (ap = mac->ap->bp; ap < mac->ap->lp; ap++) { if (ap->len == tp->len && strncmp((char *) ap->t, (char *) tp->t, ap->len) == 0) return (int)(ap - mac->ap->bp); } return -1; } /* * Return a quoted version of the tokenrow (from # arg) */ #define STRLEN 512 Tokenrow * stringify(Tokenrow * vp) { static Token t = {STRING, 0, 0, NULL, 0}; static Tokenrow tr = {&t, &t, &t + 1, 1}; Token *tp; uchar s[STRLEN]; uchar *sp = s, *cp; int i, instring; *sp++ = '"'; for (tp = vp->bp; tp < vp->lp; tp++) { instring = tp->type == STRING || tp->type == CCON; if (sp + 2 * tp->len + tp->wslen >= &s[STRLEN - 10]) { error(ERROR, "Stringified macro arg is too long"); break; } // Change by np 31.10.2001, #93725 - begin if ( tp->wslen > 0 ) *sp++ = ' '; // change end. for (i = 0, cp = tp->t; (unsigned int)i < tp->len; i++) { if (instring && (*cp == '"' || *cp == '\\')) *sp++ = '\\'; *sp++ = *cp++; } } *sp++ = '"'; *sp = '\0'; sp = s; t.len = strlen((char *) sp); t.t = newstring(sp, t.len, 0); return &tr; } /* * expand a builtin name */ void builtin(Tokenrow * trp, int biname) { char *op; Token *tp; Source *s; tp = trp->tp; trp->tp++; /* need to find the real source */ s = cursource; while (s && s->fd == -1) s = s->next; if (s == NULL) s = cursource; /* most are strings */ tp->type = STRING; if (tp->wslen) { *outptr++ = ' '; tp->wslen = 1; } op = outptr; *op++ = '"'; switch (biname) { case KLINENO: tp->type = NUMBER; op = outnum(op - 1, s->line); break; case KFILE: { char *src = s->filename; while ((*op++ = *src++) != 0) if (src[-1] == '\\') *op++ = '\\'; op--; break; } case KDATE: strncpy(op, curtime + 4, 7); strncpy(op + 7, curtime + 20, 4); op += 11; break; case KTIME: strncpy(op, curtime + 11, 8); op += 8; break; default: error(ERROR, "cpp botch: unknown internal macro"); return; } if (tp->type == STRING) *op++ = '"'; tp->t = (uchar *) outptr; tp->len = op - outptr; outptr = op; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */