/** * Parse (and execute) new macro definition. * @param mb macro expansion state * @param se macro definition to parse * @param slen length of se argument * @param level macro recursion level * @param expandbody should body be expanded? * @return address to continue parsing */ static const char * doDefine(MacroBuf mb, const char * se, size_t slen, int level, int expandbody) { const char *s = se; char *buf = xmalloc(slen + 3); /* Some leeway for termination issues... */ char *n = buf, *ne = n; char *o = NULL, *oe; char *b, *be, *ebody = NULL; int c; int oc = ')'; const char *sbody; /* as-is body start */ /* Copy name */ COPYNAME(ne, s, c); /* Copy opts (if present) */ oe = ne + 1; if (*s == '(') { s++; /* skip ( */ /* Options must be terminated with ')' */ if (strchr(s, ')')) { o = oe; COPYOPTS(oe, s, oc); s++; /* skip ) */ } else { rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n); goto exit; } } /* Copy body, skipping over escaped newlines */ b = be = oe + 1; sbody = s; SKIPBLANK(s, c); if (c == '{') { /* XXX permit silent {...} grouping */ if ((se = matchchar(s, c, '}')) == NULL) { rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated body\n"), n); se = s; /* XXX W2DO? */ goto exit; } s++; /* XXX skip { */ strncpy(b, s, (se - s)); b[se - s] = '\0'; be += strlen(b); se++; /* XXX skip } */ s = se; /* move scan forward */ } else { /* otherwise free-field */ int bc = 0, pc = 0; while (*s && (bc || pc || !iseol(*s))) { switch (*s) { case '\\': switch (*(s+1)) { case '\0': break; default: s++; break; } break; case '%': switch (*(s+1)) { case '{': *be++ = *s++; bc++; break; case '(': *be++ = *s++; pc++; break; case '%': *be++ = *s++; break; } break; case '{': if (bc > 0) bc++; break; case '}': if (bc > 0) bc--; break; case '(': if (pc > 0) pc++; break; case ')': if (pc > 0) pc--; break; } *be++ = *s++; } *be = '\0'; if (bc || pc) { rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated body\n"), n); se = s; /* XXX W2DO? */ goto exit; } /* Trim trailing blanks/newlines */ while (--be >= b && (c = *be) && (isblank(c) || iseol(c))) {}; *(++be) = '\0'; /* one too far */ } /* Move scan over body */ while (iseol(*s)) s++; se = s; /* Names must start with alphabetic or _ and be at least 3 chars */ if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) { rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%%define)\n"), n); goto exit; } if ((be - b) < 1) { rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n); goto exit; } if (!isblank(*sbody) && !(*sbody == '\\' && iseol(sbody[1]))) rpmlog(RPMLOG_WARNING, _("Macro %%%s needs whitespace before body\n"), n); if (expandbody) { if (expandThis(mb, b, 0, &ebody)) { rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n); goto exit; } b = ebody; } pushMacro(mb->mc, n, o, b, (level - 1), ME_NONE); exit: _free(buf); _free(ebody); return se; }
/** * Parse (and execute) new macro definition. * @param mb macro expansion state * @param se macro definition to parse * @param level macro recursion level * @param expandbody should body be expanded? * @return address to continue parsing */ static const char * doDefine(MacroBuf mb, const char * se, int level, int expandbody) { const char *s = se; size_t blen = MACROBUFSIZ; char *buf = xmalloc(blen); char *n = buf, *ne = n; char *o = NULL, *oe; char *b, *be; int c; int oc = ')'; /* Copy name */ COPYNAME(ne, s, c); /* Copy opts (if present) */ oe = ne + 1; if (*s == '(') { s++; /* skip ( */ o = oe; COPYOPTS(oe, s, oc); s++; /* skip ) */ } /* Copy body, skipping over escaped newlines */ b = be = oe + 1; SKIPBLANK(s, c); if (c == '{') { /* XXX permit silent {...} grouping */ if ((se = matchchar(s, c, '}')) == NULL) { rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated body\n"), n); se = s; /* XXX W2DO? */ return se; } s++; /* XXX skip { */ strncpy(b, s, (se - s)); b[se - s] = '\0'; be += strlen(b); se++; /* XXX skip } */ s = se; /* move scan forward */ } else { /* otherwise free-field */ int bc = 0, pc = 0; while (*s && (bc || pc || !iseol(*s))) { switch (*s) { case '\\': switch (*(s+1)) { case '\0': break; default: s++; break; } break; case '%': switch (*(s+1)) { case '{': *be++ = *s++; bc++; break; case '(': *be++ = *s++; pc++; break; case '%': *be++ = *s++; break; } break; case '{': if (bc > 0) bc++; break; case '}': if (bc > 0) bc--; break; case '(': if (pc > 0) pc++; break; case ')': if (pc > 0) pc--; break; } *be++ = *s++; } *be = '\0'; if (bc || pc) { rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated body\n"), n); se = s; /* XXX W2DO? */ return se; } /* Trim trailing blanks/newlines */ while (--be >= b && (c = *be) && (isblank(c) || iseol(c))) {}; *(++be) = '\0'; /* one too far */ } /* Move scan over body */ while (iseol(*s)) s++; se = s; /* Names must start with alphabetic or _ and be at least 3 chars */ if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) { rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%%define)\n"), n); return se; } /* Options must be terminated with ')' */ if (o && oc != ')') { rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n); goto exit; } if ((be - b) < 1) { rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n); goto exit; } if (expandbody && expandU(mb, b, (&buf[blen] - b))) { rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n); goto exit; } addMacro(mb->mc, n, o, b, (level - 1)); exit: _free(buf); return se; }