2000-08-28 02:34:02 +08:00
|
|
|
/** \ingroup rpmrc rpmio
|
|
|
|
* \file rpmio/macro.c
|
|
|
|
*/
|
|
|
|
|
2000-12-13 04:03:45 +08:00
|
|
|
#include "system.h"
|
1999-03-28 08:47:40 +08:00
|
|
|
#include <stdarg.h>
|
2013-05-22 19:24:29 +08:00
|
|
|
#include <pthread.h>
|
2009-12-23 20:02:19 +08:00
|
|
|
#ifdef HAVE_GETOPT_H
|
|
|
|
#include <getopt.h>
|
|
|
|
#else
|
|
|
|
extern char *optarg;
|
|
|
|
extern int optind;
|
|
|
|
#endif
|
1998-01-13 05:31:29 +08:00
|
|
|
|
1999-10-28 07:18:10 +08:00
|
|
|
#if !defined(isblank)
|
1998-07-09 01:50:48 +08:00
|
|
|
#define isblank(_c) ((_c) == ' ' || (_c) == '\t')
|
1999-10-28 07:18:10 +08:00
|
|
|
#endif
|
1999-08-17 02:57:37 +08:00
|
|
|
#define iseol(_c) ((_c) == '\n' || (_c) == '\r')
|
|
|
|
|
2009-08-31 16:15:16 +08:00
|
|
|
#define STREQ(_t, _f, _fn) ((_fn) == (sizeof(_t)-1) && rstreqn((_t), (_f), (_fn)))
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2008-02-01 17:36:13 +08:00
|
|
|
#define MACROBUFSIZ (BUFSIZ * 2)
|
|
|
|
|
2007-12-08 20:02:32 +08:00
|
|
|
#include <rpm/rpmio.h>
|
|
|
|
#include <rpm/rpmstring.h>
|
|
|
|
#include <rpm/rpmfileutil.h>
|
|
|
|
#include <rpm/rpmurl.h>
|
|
|
|
#include <rpm/rpmlog.h>
|
2008-01-30 23:05:29 +08:00
|
|
|
#include <rpm/rpmmacro.h>
|
2008-04-18 19:54:58 +08:00
|
|
|
#include <rpm/argv.h>
|
2004-10-10 01:29:22 +08:00
|
|
|
|
|
|
|
#ifdef WITH_LUA
|
2007-11-23 18:41:29 +08:00
|
|
|
#include "rpmio/rpmlua.h"
|
2004-10-10 01:29:22 +08:00
|
|
|
#endif
|
1999-11-13 01:20:49 +08:00
|
|
|
|
2000-12-13 04:03:45 +08:00
|
|
|
#include "debug.h"
|
|
|
|
|
2007-11-02 16:40:45 +08:00
|
|
|
/*! The structure used to store a macro. */
|
|
|
|
struct rpmMacroEntry_s {
|
|
|
|
struct rpmMacroEntry_s *prev;/*!< Macro entry stack. */
|
2013-04-04 16:22:22 +08:00
|
|
|
const char *name; /*!< Macro name. */
|
|
|
|
const char *opts; /*!< Macro parameters (a la getopt) */
|
|
|
|
const char *body; /*!< Macro body. */
|
2007-11-02 16:40:45 +08:00
|
|
|
int used; /*!< No. of expansions. */
|
|
|
|
int level; /*!< Scoping level. */
|
2013-04-04 16:22:22 +08:00
|
|
|
char arena[]; /*!< String arena. */
|
2007-11-02 16:40:45 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*! The structure used to store the set of macros in a context. */
|
|
|
|
struct rpmMacroContext_s {
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
rpmMacroEntry *tab; /*!< Macro entry table (array of pointers). */
|
|
|
|
int n; /*!< No. of macros. */
|
2013-05-22 19:24:29 +08:00
|
|
|
pthread_mutex_t lock;
|
|
|
|
pthread_mutexattr_t lockattr;
|
2007-11-02 16:40:45 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2007-09-20 20:52:03 +08:00
|
|
|
static struct rpmMacroContext_s rpmGlobalMacroContext_s;
|
|
|
|
rpmMacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
|
2001-10-15 11:22:10 +08:00
|
|
|
|
2007-09-20 20:52:03 +08:00
|
|
|
static struct rpmMacroContext_s rpmCLIMacroContext_s;
|
|
|
|
rpmMacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
|
1999-04-02 06:26:44 +08:00
|
|
|
|
2013-05-22 19:24:29 +08:00
|
|
|
/*
|
|
|
|
* The macro engine internals do not require recursive mutexes but Lua
|
|
|
|
* macro bindings which can get called from the internals use the external
|
|
|
|
* interfaces which do perform locking. Until that is fixed somehow
|
|
|
|
* we'll just have to settle for recursive mutexes.
|
|
|
|
* Unfortunately POSIX doesn't specify static initializers for recursive
|
|
|
|
* mutexes so we need to have a separate PTHREAD_ONCE initializer just
|
|
|
|
* to initialize the otherwise static macro context mutexes. Pooh.
|
|
|
|
*/
|
|
|
|
static pthread_once_t locksInitialized = PTHREAD_ONCE_INIT;
|
|
|
|
|
|
|
|
static void initLocks(void)
|
|
|
|
{
|
|
|
|
rpmMacroContext mcs[] = { rpmGlobalMacroContext, rpmCLIMacroContext, NULL };
|
|
|
|
|
|
|
|
for (rpmMacroContext *mcp = mcs; *mcp; mcp++) {
|
|
|
|
rpmMacroContext mc = *mcp;
|
|
|
|
pthread_mutexattr_init(&mc->lockattr);
|
|
|
|
pthread_mutexattr_settype(&mc->lockattr, PTHREAD_MUTEX_RECURSIVE);
|
|
|
|
pthread_mutex_init(&mc->lock, &mc->lockattr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-09-02 05:15:40 +08:00
|
|
|
/**
|
2000-09-30 03:50:29 +08:00
|
|
|
* Macro expansion state.
|
2000-09-02 05:15:40 +08:00
|
|
|
*/
|
2007-09-11 22:48:54 +08:00
|
|
|
typedef struct MacroBuf_s {
|
2010-09-27 17:19:13 +08:00
|
|
|
char * buf; /*!< Expansion buffer. */
|
|
|
|
size_t tpos; /*!< Current position in expansion buffer */
|
2001-06-06 03:26:22 +08:00
|
|
|
size_t nb; /*!< No. bytes remaining in expansion buffer. */
|
|
|
|
int depth; /*!< Current expansion depth. */
|
|
|
|
int macro_trace; /*!< Pre-print macro to expand? */
|
|
|
|
int expand_trace; /*!< Post-print macro expansion? */
|
2007-09-20 20:52:03 +08:00
|
|
|
rpmMacroContext mc;
|
2001-06-06 03:26:22 +08:00
|
|
|
} * MacroBuf;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2004-03-02 09:31:01 +08:00
|
|
|
#define _MAX_MACRO_DEPTH 16
|
2008-02-27 04:24:28 +08:00
|
|
|
static int max_macro_depth = _MAX_MACRO_DEPTH;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2004-03-02 09:31:01 +08:00
|
|
|
#define _PRINT_MACRO_TRACE 0
|
2008-02-27 04:24:28 +08:00
|
|
|
static int print_macro_trace = _PRINT_MACRO_TRACE;
|
2004-03-02 09:31:01 +08:00
|
|
|
|
|
|
|
#define _PRINT_EXPAND_TRACE 0
|
2008-02-27 04:24:28 +08:00
|
|
|
static int print_expand_trace = _PRINT_EXPAND_TRACE;
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2001-10-18 00:43:36 +08:00
|
|
|
/* forward ref */
|
2010-09-27 22:15:15 +08:00
|
|
|
static int expandMacro(MacroBuf mb, const char *src, size_t slen);
|
2013-05-22 14:44:34 +08:00
|
|
|
static void pushMacro(rpmMacroContext mc,
|
|
|
|
const char * n, const char * o, const char * b, int level);
|
|
|
|
static void popMacro(rpmMacroContext mc, const char * n);
|
2001-10-18 00:43:36 +08:00
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
/* =============================================================== */
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2013-05-22 15:53:20 +08:00
|
|
|
static rpmMacroContext rpmmctxAcquire(rpmMacroContext mc)
|
|
|
|
{
|
|
|
|
if (mc == NULL)
|
|
|
|
mc = rpmGlobalMacroContext;
|
2013-05-22 19:24:29 +08:00
|
|
|
pthread_once(&locksInitialized, initLocks);
|
|
|
|
pthread_mutex_lock(&mc->lock);
|
2013-05-22 15:53:20 +08:00
|
|
|
return mc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static rpmMacroContext rpmmctxRelease(rpmMacroContext mc)
|
|
|
|
{
|
2013-05-22 19:24:29 +08:00
|
|
|
pthread_mutex_unlock(&mc->lock);
|
2013-05-22 15:53:20 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Find entry in macro table.
|
|
|
|
* @param mc macro context
|
|
|
|
* @param name macro name
|
2004-03-02 09:31:01 +08:00
|
|
|
* @param namelen no. of bytes
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
* @param pos found/insert position
|
2000-09-30 03:50:29 +08:00
|
|
|
* @return address of slot in macro table with name (or NULL)
|
|
|
|
*/
|
2007-09-20 20:52:03 +08:00
|
|
|
static rpmMacroEntry *
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
findEntry(rpmMacroContext mc, const char *name, size_t namelen, size_t *pos)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
/* bsearch */
|
|
|
|
int cmp = 1;
|
|
|
|
size_t l = 0;
|
|
|
|
size_t u = mc->n;
|
|
|
|
size_t i = 0;
|
|
|
|
while (l < u) {
|
|
|
|
i = (l + u) / 2;
|
|
|
|
rpmMacroEntry me = mc->tab[i];
|
|
|
|
if (namelen == 0)
|
|
|
|
cmp = strcmp(me->name, name);
|
|
|
|
else {
|
|
|
|
cmp = strncmp(me->name, name, namelen);
|
|
|
|
/* longer me->name compares greater */
|
|
|
|
if (cmp == 0)
|
|
|
|
cmp = (me->name[namelen] != '\0');
|
|
|
|
}
|
|
|
|
if (cmp < 0)
|
|
|
|
l = i + 1;
|
|
|
|
else if (cmp > 0)
|
|
|
|
u = i;
|
|
|
|
else
|
|
|
|
break;
|
2001-05-04 05:00:18 +08:00
|
|
|
}
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
|
|
|
|
if (pos)
|
|
|
|
*pos = (cmp < 0) ? i + 1 : i;
|
|
|
|
if (cmp == 0)
|
|
|
|
return &mc->tab[i];
|
|
|
|
return NULL;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
1997-03-31 22:13:21 +08:00
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
/* =============================================================== */
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* fgets(3) analogue that reads \ continuations. Last newline always trimmed.
|
2002-06-22 02:00:50 +08:00
|
|
|
* @param buf input buffer
|
|
|
|
* @param size inbut buffer size (bytes)
|
|
|
|
* @param fd file handle
|
|
|
|
* @return buffer, or NULL on end-of-file
|
2000-09-30 03:50:29 +08:00
|
|
|
*/
|
2002-06-22 02:00:50 +08:00
|
|
|
static char *
|
2008-04-18 21:14:15 +08:00
|
|
|
rdcl(char * buf, size_t size, FILE *f)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2002-06-22 02:00:50 +08:00
|
|
|
char *q = buf - 1; /* initialize just before buffer. */
|
2001-05-04 05:00:18 +08:00
|
|
|
size_t nb = 0;
|
|
|
|
size_t nread = 0;
|
2004-04-09 04:27:53 +08:00
|
|
|
int pc = 0, bc = 0;
|
|
|
|
char *p = buf;
|
2001-05-04 05:00:18 +08:00
|
|
|
|
|
|
|
if (f != NULL)
|
|
|
|
do {
|
2002-06-22 02:00:50 +08:00
|
|
|
*(++q) = '\0'; /* terminate and move forward. */
|
|
|
|
if (fgets(q, size, f) == NULL) /* read next line. */
|
2001-05-04 05:00:18 +08:00
|
|
|
break;
|
|
|
|
nb = strlen(q);
|
2002-06-22 02:00:50 +08:00
|
|
|
nread += nb; /* trim trailing \r and \n */
|
2001-05-04 05:00:18 +08:00
|
|
|
for (q += nb - 1; nb > 0 && iseol(*q); q--)
|
|
|
|
nb--;
|
2004-04-09 04:27:53 +08:00
|
|
|
for (; p <= q; p++) {
|
|
|
|
switch (*p) {
|
|
|
|
case '\\':
|
|
|
|
switch (*(p+1)) {
|
2007-09-11 22:48:54 +08:00
|
|
|
case '\0': break;
|
|
|
|
default: p++; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
2004-04-09 04:27:53 +08:00
|
|
|
case '%':
|
|
|
|
switch (*(p+1)) {
|
2007-09-11 22:48:54 +08:00
|
|
|
case '{': p++, bc++; break;
|
|
|
|
case '(': p++, pc++; break;
|
|
|
|
case '%': p++; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
2007-09-11 22:48:54 +08:00
|
|
|
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;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
|
|
|
}
|
2004-04-19 20:12:12 +08:00
|
|
|
if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
|
2001-05-04 05:00:18 +08:00
|
|
|
*(++q) = '\0'; /* trim trailing \r, \n */
|
|
|
|
break;
|
|
|
|
}
|
2004-04-09 04:27:53 +08:00
|
|
|
q++; p++; nb++; /* copy newline too */
|
2001-05-04 05:00:18 +08:00
|
|
|
size -= nb;
|
|
|
|
if (*q == '\r') /* XXX avoid \r madness */
|
|
|
|
*q = '\n';
|
|
|
|
} while (size > 0);
|
2002-06-22 02:00:50 +08:00
|
|
|
return (nread > 0 ? buf : NULL);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
2000-11-01 00:18:34 +08:00
|
|
|
* Return text between pl and matching pr characters.
|
2000-09-30 03:50:29 +08:00
|
|
|
* @param p start of text
|
|
|
|
* @param pl left char, i.e. '[', '(', '{', etc.
|
|
|
|
* @param pr right char, i.e. ']', ')', '}', etc.
|
|
|
|
* @return address of last char before pr (or NULL)
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static const char *
|
2001-06-06 03:26:22 +08:00
|
|
|
matchchar(const char * p, char pl, char pr)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
int lvl = 0;
|
|
|
|
char c;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
while ((c = *p++) != '\0') {
|
|
|
|
if (c == '\\') { /* Ignore escaped chars */
|
|
|
|
p++;
|
|
|
|
continue;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2001-06-06 03:26:22 +08:00
|
|
|
if (c == pr) {
|
|
|
|
if (--lvl <= 0) return --p;
|
|
|
|
} else if (c == pl)
|
|
|
|
lvl++;
|
|
|
|
}
|
|
|
|
return (const char *)NULL;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Pre-print macro expression to be expanded.
|
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param s current expansion string
|
|
|
|
* @param se end of string
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static void
|
2001-06-06 03:26:22 +08:00
|
|
|
printMacro(MacroBuf mb, const char * s, const char * se)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
const char *senl;
|
|
|
|
const char *ellipsis;
|
|
|
|
int choplen;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
if (s >= se) { /* XXX just in case */
|
|
|
|
fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
|
|
|
|
(2 * mb->depth + 1), "");
|
|
|
|
return;
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
if (s[-1] == '{')
|
|
|
|
s--;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Print only to first end-of-line (or end-of-string). */
|
|
|
|
for (senl = se; *senl && !iseol(*senl); senl++)
|
|
|
|
{};
|
|
|
|
|
|
|
|
/* Limit trailing non-trace output */
|
|
|
|
choplen = 61 - (2 * mb->depth);
|
|
|
|
if ((senl - s) > choplen) {
|
|
|
|
senl = s + choplen;
|
|
|
|
ellipsis = "...";
|
|
|
|
} else
|
|
|
|
ellipsis = "";
|
|
|
|
|
|
|
|
/* Substitute caret at end-of-macro position */
|
|
|
|
fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
|
|
|
|
(2 * mb->depth + 1), "", (int)(se - s), s);
|
|
|
|
if (se[1] != '\0' && (senl - (se+1)) > 0)
|
|
|
|
fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
|
|
|
|
fprintf(stderr, "\n");
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Post-print expanded macro expression.
|
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param t current expansion string result
|
|
|
|
* @param te end of string
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static void
|
2001-06-06 03:26:22 +08:00
|
|
|
printExpansion(MacroBuf mb, const char * t, const char * te)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
const char *ellipsis;
|
|
|
|
int choplen;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
if (!(te > t)) {
|
2010-07-13 19:35:22 +08:00
|
|
|
rpmlog(RPMLOG_DEBUG, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
|
2001-06-06 03:26:22 +08:00
|
|
|
return;
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Shorten output which contains newlines */
|
|
|
|
while (te > t && iseol(te[-1]))
|
|
|
|
te--;
|
|
|
|
ellipsis = "";
|
|
|
|
if (mb->depth > 0) {
|
|
|
|
const char *tenl;
|
|
|
|
|
|
|
|
/* Skip to last line of expansion */
|
|
|
|
while ((tenl = strchr(t, '\n')) && tenl < te)
|
|
|
|
t = ++tenl;
|
|
|
|
|
|
|
|
/* Limit expand output */
|
|
|
|
choplen = 61 - (2 * mb->depth);
|
|
|
|
if ((te - t) > choplen) {
|
|
|
|
te = t + choplen;
|
|
|
|
ellipsis = "...";
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2010-07-13 19:35:22 +08:00
|
|
|
rpmlog(RPMLOG_DEBUG,"%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
|
2001-06-06 03:26:22 +08:00
|
|
|
if (te > t)
|
2010-07-13 19:35:22 +08:00
|
|
|
rpmlog(RPMLOG_DEBUG, "%.*s%s", (int)(te - t), t, ellipsis);
|
|
|
|
rpmlog(RPMLOG_DEBUG, "\n");
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define SKIPBLANK(_s, _c) \
|
|
|
|
while (((_c) = *(_s)) && isblank(_c)) \
|
2002-06-22 02:00:50 +08:00
|
|
|
(_s)++; \
|
1998-07-09 01:50:48 +08:00
|
|
|
|
|
|
|
#define SKIPNONBLANK(_s, _c) \
|
1999-08-17 02:57:37 +08:00
|
|
|
while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
|
2002-06-22 02:00:50 +08:00
|
|
|
(_s)++; \
|
1998-07-09 01:50:48 +08:00
|
|
|
|
|
|
|
#define COPYNAME(_ne, _s, _c) \
|
|
|
|
{ SKIPBLANK(_s,_c); \
|
2008-03-18 15:10:13 +08:00
|
|
|
while(((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \
|
1998-07-09 01:50:48 +08:00
|
|
|
*(_ne)++ = *(_s)++; \
|
|
|
|
*(_ne) = '\0'; \
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
#define COPYOPTS(_oe, _s, _c) \
|
2007-09-12 01:23:32 +08:00
|
|
|
{ \
|
2002-06-24 03:47:08 +08:00
|
|
|
while(((_c) = *(_s)) && (_c) != ')') \
|
1998-07-09 01:50:48 +08:00
|
|
|
*(_oe)++ = *(_s)++; \
|
|
|
|
*(_oe) = '\0'; \
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
2010-09-27 21:03:56 +08:00
|
|
|
* Macro-expand string src, return result in dynamically allocated buffer.
|
2000-09-30 03:50:29 +08:00
|
|
|
* @param mb macro expansion state
|
2010-09-27 21:03:56 +08:00
|
|
|
* @param src string to expand
|
|
|
|
* @param slen input string length (or 0 for strlen())
|
|
|
|
* @retval target pointer to expanded string (malloced)
|
2000-09-30 03:50:29 +08:00
|
|
|
* @return result of expansion
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static int
|
2010-09-27 21:03:56 +08:00
|
|
|
expandThis(MacroBuf mb, const char * src, size_t slen, char **target)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2010-09-27 19:22:56 +08:00
|
|
|
struct MacroBuf_s umb;
|
2001-06-06 03:26:22 +08:00
|
|
|
int rc;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2010-09-27 19:22:56 +08:00
|
|
|
/* Copy other state from "parent", but we want a buffer of our own */
|
|
|
|
umb = *mb;
|
|
|
|
umb.buf = NULL;
|
2010-09-27 22:15:15 +08:00
|
|
|
rc = expandMacro(&umb, src, slen);
|
2010-09-27 21:03:56 +08:00
|
|
|
*target = umb.buf;
|
2008-02-01 18:11:03 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
return rc;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2010-09-27 19:05:37 +08:00
|
|
|
static void mbAppend(MacroBuf mb, char c)
|
|
|
|
{
|
|
|
|
if (mb->nb < 1) {
|
2012-05-10 16:28:39 +08:00
|
|
|
mb->buf = xrealloc(mb->buf, mb->tpos + MACROBUFSIZ + 1);
|
2010-09-27 19:05:37 +08:00
|
|
|
mb->nb += MACROBUFSIZ;
|
|
|
|
}
|
|
|
|
mb->buf[mb->tpos++] = c;
|
2012-05-10 16:49:24 +08:00
|
|
|
mb->buf[mb->tpos] = '\0';
|
2010-09-27 19:05:37 +08:00
|
|
|
mb->nb--;
|
|
|
|
}
|
|
|
|
|
2010-09-27 21:32:15 +08:00
|
|
|
static void mbAppendStr(MacroBuf mb, const char *str)
|
|
|
|
{
|
|
|
|
size_t len = strlen(str);
|
|
|
|
if (len > mb->nb) {
|
2012-05-10 16:28:39 +08:00
|
|
|
mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ + len + 1);
|
2010-09-27 21:32:15 +08:00
|
|
|
mb->nb += MACROBUFSIZ + len;
|
|
|
|
}
|
2012-05-10 16:49:24 +08:00
|
|
|
memcpy(mb->buf+mb->tpos, str, len + 1);
|
2010-09-27 21:32:15 +08:00
|
|
|
mb->tpos += len;
|
|
|
|
mb->nb -= len;
|
|
|
|
}
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Expand output of shell command into target buffer.
|
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param cmd shell command
|
|
|
|
* @param clen no. bytes in shell command
|
|
|
|
* @return result of expansion
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static int
|
2001-06-06 03:26:22 +08:00
|
|
|
doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2010-09-27 21:03:56 +08:00
|
|
|
char *buf = NULL;
|
2001-06-06 03:26:22 +08:00
|
|
|
FILE *shf;
|
2008-02-01 19:00:40 +08:00
|
|
|
int rc = 0;
|
2001-06-06 03:26:22 +08:00
|
|
|
int c;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2010-09-27 21:03:56 +08:00
|
|
|
rc = expandThis(mb, cmd, clen, &buf);
|
2001-06-06 03:26:22 +08:00
|
|
|
if (rc)
|
2008-02-01 19:00:40 +08:00
|
|
|
goto exit;
|
2001-06-06 03:26:22 +08:00
|
|
|
|
2008-02-01 19:00:40 +08:00
|
|
|
if ((shf = popen(buf, "r")) == NULL) {
|
|
|
|
rc = 1;
|
|
|
|
goto exit;
|
|
|
|
}
|
2013-02-07 11:44:41 +08:00
|
|
|
|
|
|
|
size_t tpos = mb->tpos;
|
2008-02-01 17:09:13 +08:00
|
|
|
while((c = fgetc(shf)) != EOF) {
|
2010-09-27 19:05:37 +08:00
|
|
|
mbAppend(mb, c);
|
2008-02-01 17:09:13 +08:00
|
|
|
}
|
2001-06-06 03:26:22 +08:00
|
|
|
(void) pclose(shf);
|
|
|
|
|
2013-02-07 11:44:41 +08:00
|
|
|
/* Delete trailing \r \n */
|
|
|
|
while (mb->tpos > tpos && iseol(mb->buf[mb->tpos-1])) {
|
|
|
|
mb->buf[--mb->tpos] = '\0';
|
2001-06-06 03:26:22 +08:00
|
|
|
mb->nb++;
|
|
|
|
}
|
2008-02-01 19:00:40 +08:00
|
|
|
|
|
|
|
exit:
|
|
|
|
_free(buf);
|
|
|
|
return rc;
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2007-09-11 22:48:54 +08:00
|
|
|
static const char *
|
|
|
|
doDefine(MacroBuf mb, const char * se, int level, int expandbody)
|
1997-03-31 22:13:21 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
const char *s = se;
|
2008-02-01 19:00:40 +08:00
|
|
|
size_t blen = MACROBUFSIZ;
|
|
|
|
char *buf = xmalloc(blen);
|
|
|
|
char *n = buf, *ne = n;
|
2001-06-06 03:26:22 +08:00
|
|
|
char *o = NULL, *oe;
|
2010-09-27 21:03:56 +08:00
|
|
|
char *b, *be, *ebody = NULL;
|
2001-06-06 03:26:22 +08:00
|
|
|
int c;
|
|
|
|
int oc = ')';
|
2013-01-19 20:11:11 +08:00
|
|
|
const char *sbody; /* as-is body start */
|
2001-06-06 03:26:22 +08:00
|
|
|
|
|
|
|
/* 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 ) */
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Copy body, skipping over escaped newlines */
|
|
|
|
b = be = oe + 1;
|
2013-01-19 20:11:11 +08:00
|
|
|
sbody = s;
|
2001-06-06 03:26:22 +08:00
|
|
|
SKIPBLANK(s, c);
|
|
|
|
if (c == '{') { /* XXX permit silent {...} grouping */
|
|
|
|
if ((se = matchchar(s, c, '}')) == NULL) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2001-06-06 03:26:22 +08:00
|
|
|
_("Macro %%%s has unterminated body\n"), n);
|
|
|
|
se = s; /* XXX W2DO? */
|
2011-03-17 21:35:42 +08:00
|
|
|
goto exit;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2001-06-06 03:26:22 +08:00
|
|
|
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 */
|
2004-04-09 04:27:53 +08:00
|
|
|
int bc = 0, pc = 0;
|
|
|
|
while (*s && (bc || pc || !iseol(*s))) {
|
|
|
|
switch (*s) {
|
|
|
|
case '\\':
|
|
|
|
switch (*(s+1)) {
|
2007-09-11 22:48:54 +08:00
|
|
|
case '\0': break;
|
|
|
|
default: s++; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
2004-04-09 04:27:53 +08:00
|
|
|
case '%':
|
|
|
|
switch (*(s+1)) {
|
2007-09-11 22:48:54 +08:00
|
|
|
case '{': *be++ = *s++; bc++; break;
|
|
|
|
case '(': *be++ = *s++; pc++; break;
|
|
|
|
case '%': *be++ = *s++; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
2007-09-11 22:48:54 +08:00
|
|
|
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;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
|
|
|
*be++ = *s++;
|
|
|
|
}
|
|
|
|
*be = '\0';
|
|
|
|
|
|
|
|
if (bc || pc) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2004-04-09 04:27:53 +08:00
|
|
|
_("Macro %%%s has unterminated body\n"), n);
|
|
|
|
se = s; /* XXX W2DO? */
|
2011-03-17 21:35:42 +08:00
|
|
|
goto exit;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Trim trailing blanks/newlines */
|
|
|
|
while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
|
|
|
|
{};
|
|
|
|
*(++be) = '\0'; /* one too far */
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Move scan over body */
|
|
|
|
while (iseol(*s))
|
|
|
|
s++;
|
|
|
|
se = s;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Names must start with alphabetic or _ and be at least 3 chars */
|
2008-03-18 15:10:13 +08:00
|
|
|
if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2001-06-06 03:26:22 +08:00
|
|
|
_("Macro %%%s has illegal name (%%define)\n"), n);
|
2011-03-17 21:35:42 +08:00
|
|
|
goto exit;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Options must be terminated with ')' */
|
|
|
|
if (o && oc != ')') {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
|
2008-02-01 19:00:40 +08:00
|
|
|
goto exit;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
if ((be - b) < 1) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
|
2008-02-01 19:00:40 +08:00
|
|
|
goto exit;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2013-01-19 20:11:11 +08:00
|
|
|
if (!isblank(*sbody) && !(*sbody == '\\' && iseol(sbody[1])))
|
|
|
|
rpmlog(RPMLOG_WARNING, _("Macro %%%s needs whitespace before body\n"), n);
|
|
|
|
|
2010-09-27 21:03:56 +08:00
|
|
|
if (expandbody) {
|
|
|
|
if (expandThis(mb, b, 0, &ebody)) {
|
|
|
|
rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
b = ebody;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mb->mc, n, o, b, (level - 1));
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2008-02-01 19:00:40 +08:00
|
|
|
exit:
|
|
|
|
_free(buf);
|
2010-09-27 21:03:56 +08:00
|
|
|
_free(ebody);
|
2002-06-22 02:00:50 +08:00
|
|
|
return se;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Parse (and execute) macro undefinition.
|
|
|
|
* @param mc macro context
|
|
|
|
* @param se macro name to undefine
|
|
|
|
* @return address to continue parsing
|
|
|
|
*/
|
2007-09-11 22:48:54 +08:00
|
|
|
static const char *
|
2007-09-20 20:52:03 +08:00
|
|
|
doUndefine(rpmMacroContext mc, const char * se)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
const char *s = se;
|
2008-02-01 19:00:40 +08:00
|
|
|
char *buf = xmalloc(MACROBUFSIZ);
|
|
|
|
char *n = buf, *ne = n;
|
2001-06-06 03:26:22 +08:00
|
|
|
int c;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
COPYNAME(ne, s, c);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Move scan over body */
|
|
|
|
while (iseol(*s))
|
|
|
|
s++;
|
|
|
|
se = s;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Names must start with alphabetic or _ and be at least 3 chars */
|
2008-03-18 15:10:13 +08:00
|
|
|
if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2001-06-06 03:26:22 +08:00
|
|
|
_("Macro %%%s has illegal name (%%undefine)\n"), n);
|
2008-02-01 19:00:40 +08:00
|
|
|
goto exit;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2013-05-22 14:44:34 +08:00
|
|
|
popMacro(mc, n);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2008-02-01 19:00:40 +08:00
|
|
|
exit:
|
|
|
|
_free(buf);
|
2002-06-22 02:00:50 +08:00
|
|
|
return se;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Free parsed arguments for parameterized macro.
|
|
|
|
* @param mb macro expansion state
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static void
|
2001-06-06 03:26:22 +08:00
|
|
|
freeArgs(MacroBuf mb)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2007-09-20 20:52:03 +08:00
|
|
|
rpmMacroContext mc = mb->mc;
|
1999-08-17 00:18:25 +08:00
|
|
|
|
2001-05-04 05:00:18 +08:00
|
|
|
/* Delete dynamic macro definitions */
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
for (int i = 0; i < mc->n; i++) {
|
2001-05-04 05:00:18 +08:00
|
|
|
int skiptest = 0;
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
rpmMacroEntry me = mc->tab[i];
|
|
|
|
assert(me);
|
2001-05-04 05:00:18 +08:00
|
|
|
if (me->level < mb->depth)
|
|
|
|
continue;
|
|
|
|
if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
|
|
|
|
if (*me->name == '*' && me->used > 0)
|
|
|
|
skiptest = 1; /* XXX skip test for %# %* %0 */
|
|
|
|
} else if (!skiptest && me->used <= 0) {
|
1998-07-09 01:50:48 +08:00
|
|
|
#if NOTYET
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2001-05-04 05:00:18 +08:00
|
|
|
_("Macro %%%s (%s) was not used below level %d\n"),
|
|
|
|
me->name, me->body, me->level);
|
1998-07-09 01:50:48 +08:00
|
|
|
#endif
|
|
|
|
}
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
/* compensate if the slot is to go away */
|
|
|
|
if (me->prev == NULL)
|
|
|
|
i--;
|
2013-05-22 14:44:34 +08:00
|
|
|
popMacro(mc, me->name);
|
2001-05-04 05:00:18 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Parse arguments (to next new line) for parameterized macro.
|
2001-09-15 21:49:11 +08:00
|
|
|
* @todo Use popt rather than getopt to parse args.
|
2000-09-30 03:50:29 +08:00
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param me macro entry slot
|
|
|
|
* @param se arguments to parse
|
|
|
|
* @param lastc stop parsing at lastc
|
|
|
|
* @return address to continue parsing
|
|
|
|
*/
|
2007-09-11 22:48:54 +08:00
|
|
|
static const char *
|
2007-09-20 20:52:03 +08:00
|
|
|
grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
|
2003-12-30 23:12:50 +08:00
|
|
|
const char * lastc)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2009-09-07 22:50:43 +08:00
|
|
|
const char *opts;
|
2008-04-18 19:54:58 +08:00
|
|
|
char *args = NULL;
|
|
|
|
ARGV_t argv = NULL;
|
1998-07-09 01:50:48 +08:00
|
|
|
int argc = 0;
|
|
|
|
int c;
|
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/* Copy macro name as argv[0] */
|
|
|
|
argvAdd(&argv, me->name);
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mb->mc, "0", NULL, me->name, mb->depth);
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/*
|
|
|
|
* Make a copy of se up to lastc string that we can pass to argvSplit().
|
2008-09-11 21:56:50 +08:00
|
|
|
* Append the results to main argv.
|
2008-04-18 19:54:58 +08:00
|
|
|
*/
|
|
|
|
{ ARGV_t av = NULL;
|
|
|
|
char *s = xcalloc((lastc-se)+1, sizeof(*s));
|
2008-07-16 14:40:10 +08:00
|
|
|
memcpy(s, se, (lastc-se));
|
|
|
|
|
2008-10-19 06:53:03 +08:00
|
|
|
argvSplit(&av, s, " \t");
|
2008-04-18 19:54:58 +08:00
|
|
|
argvAppend(&argv, av);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
argvFree(av);
|
|
|
|
free(s);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2001-09-15 21:49:11 +08:00
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/*
|
|
|
|
* The macro %* analoguous to the shell's $* means "Pass all non-macro
|
|
|
|
* parameters." Consequently, there needs to be a macro that means "Pass all
|
|
|
|
* (including macro parameters) options". This is useful for verifying
|
|
|
|
* parameters during expansion and yet transparently passing all parameters
|
|
|
|
* through for higher level processing (e.g. %description and/or %setup).
|
|
|
|
* This is the (potential) justification for %{**} ...
|
|
|
|
*/
|
|
|
|
args = argvJoin(argv + 1, " ");
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mb->mc, "**", NULL, args, mb->depth);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(args);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* POSIX states optind must be 1 before any call but glibc uses 0
|
|
|
|
* to (re)initialize getopt structures, eww.
|
|
|
|
*/
|
2001-09-15 21:49:11 +08:00
|
|
|
#ifdef __GLIBC__
|
2008-04-18 19:54:58 +08:00
|
|
|
optind = 0;
|
2003-12-27 09:37:56 +08:00
|
|
|
#else
|
|
|
|
optind = 1;
|
2001-09-15 21:49:11 +08:00
|
|
|
#endif
|
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
opts = me->opts;
|
2008-04-18 19:54:58 +08:00
|
|
|
argc = argvCount(argv);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2000-06-22 07:28:50 +08:00
|
|
|
/* Define option macros. */
|
2007-12-19 18:49:52 +08:00
|
|
|
while((c = getopt(argc, argv, opts)) != -1)
|
2003-04-02 06:20:45 +08:00
|
|
|
{
|
2008-04-18 19:54:58 +08:00
|
|
|
char *name = NULL, *body = NULL;
|
2009-09-07 22:50:43 +08:00
|
|
|
if (c == '?' || strchr(opts, c) == NULL) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR, _("Unknown option %c in %s(%s)\n"),
|
2012-12-17 20:58:44 +08:00
|
|
|
(char)optopt, me->name, opts);
|
2008-02-01 19:00:40 +08:00
|
|
|
goto exit;
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
2008-04-18 20:15:05 +08:00
|
|
|
|
|
|
|
rasprintf(&name, "-%c", c);
|
2008-04-18 19:54:58 +08:00
|
|
|
if (optarg) {
|
|
|
|
rasprintf(&body, "-%c %s", c, optarg);
|
|
|
|
} else {
|
|
|
|
rasprintf(&body, "-%c", c);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mb->mc, name, NULL, body, mb->depth);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(name);
|
2008-04-18 20:15:05 +08:00
|
|
|
free(body);
|
2008-04-18 19:54:58 +08:00
|
|
|
|
|
|
|
if (optarg) {
|
|
|
|
rasprintf(&name, "-%c*", c);
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mb->mc, name, NULL, optarg, mb->depth);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(name);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
1998-05-21 01:05:26 +08:00
|
|
|
}
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/* Add argument count (remaining non-option items) as macro. */
|
|
|
|
{ char *ac = NULL;
|
|
|
|
rasprintf(&ac, "%d", (argc - optind));
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mb->mc, "#", NULL, ac, mb->depth);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(ac);
|
|
|
|
}
|
2000-06-22 07:28:50 +08:00
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/* Add macro for each argument */
|
|
|
|
if (argc - optind) {
|
2001-05-04 05:00:18 +08:00
|
|
|
for (c = optind; c < argc; c++) {
|
2008-04-18 19:54:58 +08:00
|
|
|
char *name = NULL;
|
|
|
|
rasprintf(&name, "%d", (c - optind + 1));
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mb->mc, name, NULL, argv[c], mb->depth);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(name);
|
2001-05-04 05:00:18 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
1999-04-21 06:45:52 +08:00
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/* Add concatenated unexpanded arguments as yet another macro. */
|
|
|
|
args = argvJoin(argv + optind, " ");
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mb->mc, "*", NULL, args ? args : "", mb->depth);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(args);
|
1999-04-21 06:45:52 +08:00
|
|
|
|
2008-02-01 19:00:40 +08:00
|
|
|
exit:
|
2008-04-18 19:54:58 +08:00
|
|
|
argvFree(argv);
|
2008-09-11 21:56:50 +08:00
|
|
|
return *lastc ? lastc + 1 : lastc;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Perform macro message output
|
|
|
|
* @param mb macro expansion state
|
2007-10-09 19:06:06 +08:00
|
|
|
* @param waserror use rpmlog()?
|
2000-09-30 03:50:29 +08:00
|
|
|
* @param msg message to ouput
|
|
|
|
* @param msglen no. of bytes in message
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static void
|
2001-06-06 03:26:22 +08:00
|
|
|
doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
|
1998-01-13 05:31:29 +08:00
|
|
|
{
|
2010-09-27 21:03:56 +08:00
|
|
|
char *buf = NULL;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2010-09-27 21:03:56 +08:00
|
|
|
(void) expandThis(mb, msg, msglen, &buf);
|
2001-06-06 03:26:22 +08:00
|
|
|
if (waserror)
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR, "%s\n", buf);
|
2001-06-06 03:26:22 +08:00
|
|
|
else
|
|
|
|
fprintf(stderr, "%s", buf);
|
2008-02-01 19:00:40 +08:00
|
|
|
_free(buf);
|
1998-01-13 05:31:29 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Execute macro primitives.
|
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param negate should logic be inverted?
|
|
|
|
* @param f beginning of field f
|
|
|
|
* @param fn length of field f
|
|
|
|
* @param g beginning of field g
|
|
|
|
* @param gn length of field g
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static void
|
2001-06-06 03:26:22 +08:00
|
|
|
doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
|
2007-09-11 22:48:54 +08:00
|
|
|
const char * g, size_t gn)
|
1997-03-31 22:13:21 +08:00
|
|
|
{
|
2010-09-27 21:03:56 +08:00
|
|
|
char *buf = NULL;
|
2008-02-01 19:00:40 +08:00
|
|
|
char *b = NULL, *be;
|
2001-06-06 03:26:22 +08:00
|
|
|
int c;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2003-04-02 06:20:45 +08:00
|
|
|
if (g != NULL) {
|
2010-09-27 21:03:56 +08:00
|
|
|
(void) expandThis(mb, g, gn, &buf);
|
|
|
|
} else {
|
|
|
|
buf = xmalloc(MACROBUFSIZ + fn + gn);
|
|
|
|
buf[0] = '\0';
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
|
|
|
if (STREQ("basename", f, fn)) {
|
|
|
|
if ((b = strrchr(buf, '/')) == NULL)
|
|
|
|
b = buf;
|
2002-08-16 02:50:46 +08:00
|
|
|
else
|
|
|
|
b++;
|
2001-06-06 03:26:22 +08:00
|
|
|
} else if (STREQ("dirname", f, fn)) {
|
|
|
|
if ((b = strrchr(buf, '/')) != NULL)
|
|
|
|
*b = '\0';
|
|
|
|
b = buf;
|
|
|
|
} else if (STREQ("suffix", f, fn)) {
|
|
|
|
if ((b = strrchr(buf, '.')) != NULL)
|
|
|
|
b++;
|
|
|
|
} else if (STREQ("expand", f, fn)) {
|
|
|
|
b = buf;
|
|
|
|
} else if (STREQ("verbose", f, fn)) {
|
|
|
|
if (negate)
|
|
|
|
b = (rpmIsVerbose() ? NULL : buf);
|
|
|
|
else
|
|
|
|
b = (rpmIsVerbose() ? buf : NULL);
|
|
|
|
} else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
|
|
|
|
(void)urlPath(buf, (const char **)&b);
|
|
|
|
if (*b == '\0') b = "/";
|
|
|
|
} else if (STREQ("uncompress", f, fn)) {
|
|
|
|
rpmCompressedMagic compressed = COMPRESSED_OTHER;
|
|
|
|
for (b = buf; (c = *b) && isblank(c);)
|
|
|
|
b++;
|
|
|
|
for (be = b; (c = *be) && !isblank(c);)
|
|
|
|
be++;
|
|
|
|
*be++ = '\0';
|
2008-01-27 22:39:40 +08:00
|
|
|
(void) rpmFileIsCompressed(b, &compressed);
|
2001-06-06 03:26:22 +08:00
|
|
|
switch(compressed) {
|
|
|
|
default:
|
2007-10-09 16:02:49 +08:00
|
|
|
case COMPRESSED_NOT:
|
2008-01-23 13:56:51 +08:00
|
|
|
sprintf(be, "%%__cat %s", b);
|
2001-06-06 03:26:22 +08:00
|
|
|
break;
|
2007-10-09 16:02:49 +08:00
|
|
|
case COMPRESSED_OTHER:
|
2008-01-23 13:56:51 +08:00
|
|
|
sprintf(be, "%%__gzip -dc %s", b);
|
2001-06-06 03:26:22 +08:00
|
|
|
break;
|
2007-10-09 16:02:49 +08:00
|
|
|
case COMPRESSED_BZIP2:
|
2008-04-04 20:52:52 +08:00
|
|
|
sprintf(be, "%%__bzip2 -dc %s", b);
|
2001-06-06 03:26:22 +08:00
|
|
|
break;
|
2007-10-09 16:02:49 +08:00
|
|
|
case COMPRESSED_ZIP:
|
2008-01-23 13:56:51 +08:00
|
|
|
sprintf(be, "%%__unzip %s", b);
|
2001-06-06 03:26:22 +08:00
|
|
|
break;
|
2007-10-10 14:39:01 +08:00
|
|
|
case COMPRESSED_LZMA:
|
2009-03-27 20:11:43 +08:00
|
|
|
case COMPRESSED_XZ:
|
|
|
|
sprintf(be, "%%__xz -dc %s", b);
|
|
|
|
break;
|
2011-04-24 20:56:11 +08:00
|
|
|
case COMPRESSED_LZIP:
|
|
|
|
sprintf(be, "%%__lzip -dc %s", b);
|
|
|
|
break;
|
2011-04-24 20:57:23 +08:00
|
|
|
case COMPRESSED_LRZIP:
|
|
|
|
sprintf(be, "%%__lrzip -dqo- %s", b);
|
|
|
|
break;
|
2012-05-11 05:27:56 +08:00
|
|
|
case COMPRESSED_7ZIP:
|
|
|
|
sprintf(be, "%%__7zip x %s", b);
|
|
|
|
break;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2001-06-06 03:26:22 +08:00
|
|
|
b = be;
|
2009-01-16 15:09:40 +08:00
|
|
|
} else if (STREQ("getenv", f, fn)) {
|
|
|
|
b = getenv(buf);
|
2009-03-27 20:01:47 +08:00
|
|
|
} else if (STREQ("getconfdir", f, fn)) {
|
|
|
|
sprintf(buf, "%s", rpmConfigDir());
|
|
|
|
b = buf;
|
2001-06-06 03:26:22 +08:00
|
|
|
} else if (STREQ("S", f, fn)) {
|
2008-03-18 15:10:13 +08:00
|
|
|
for (b = buf; (c = *b) && risdigit(c);)
|
2001-06-06 03:26:22 +08:00
|
|
|
b++;
|
|
|
|
if (!c) { /* digit index */
|
|
|
|
b++;
|
|
|
|
sprintf(b, "%%SOURCE%s", buf);
|
|
|
|
} else
|
|
|
|
b = buf;
|
|
|
|
} else if (STREQ("P", f, fn)) {
|
2008-03-18 15:10:13 +08:00
|
|
|
for (b = buf; (c = *b) && risdigit(c);)
|
2001-06-06 03:26:22 +08:00
|
|
|
b++;
|
|
|
|
if (!c) { /* digit index */
|
|
|
|
b++;
|
|
|
|
sprintf(b, "%%PATCH%s", buf);
|
|
|
|
} else
|
|
|
|
b = buf;
|
|
|
|
} else if (STREQ("F", f, fn)) {
|
|
|
|
b = buf + strlen(buf) + 1;
|
|
|
|
sprintf(b, "file%s.file", buf);
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
if (b) {
|
2010-09-27 22:15:15 +08:00
|
|
|
(void) expandMacro(mb, b, 0);
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
2008-04-29 22:04:08 +08:00
|
|
|
free(buf);
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
|
|
|
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
/**
|
|
|
|
* Parse for flags before macro name, such as in %{?!name:subst}.
|
|
|
|
* @param s macro flags start
|
|
|
|
* @param negate flipped by each '!' flag
|
|
|
|
* @param chkexist increased by each '?' flag
|
|
|
|
* @return macro flags end
|
|
|
|
*/
|
|
|
|
static const char *
|
|
|
|
parseMacroFlags(const char *s, int *negate, int *chkexist)
|
|
|
|
{
|
|
|
|
while (1) {
|
|
|
|
switch (*s) {
|
|
|
|
case '!':
|
|
|
|
*negate = !*negate;
|
|
|
|
s++;
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
(*chkexist)++;
|
|
|
|
s++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* not reached */
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse for valid macro name (during expansion).
|
|
|
|
* @param s macro name start
|
|
|
|
* @return macro name end, NULL on error
|
|
|
|
*/
|
|
|
|
static const char *
|
|
|
|
parseMacroName(const char *s)
|
|
|
|
{
|
|
|
|
/* alnum identifiers */
|
|
|
|
if (risalpha(*s) || *s == '_') {
|
|
|
|
const char *se = s + 1;
|
|
|
|
while (risalnum(*se) || *se == '_')
|
|
|
|
se++;
|
|
|
|
switch (se - s) {
|
|
|
|
case 1:
|
|
|
|
/* recheck for [SPF] */
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
return NULL;
|
|
|
|
default:
|
|
|
|
return se;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* simple special names */
|
|
|
|
switch (*s) {
|
|
|
|
case '0':
|
|
|
|
case '#':
|
|
|
|
case 'S':
|
|
|
|
case 'P':
|
|
|
|
case 'F':
|
|
|
|
s++;
|
|
|
|
return s;
|
|
|
|
case '*':
|
|
|
|
s++;
|
|
|
|
if (*s == '*')
|
|
|
|
s++;
|
|
|
|
return s;
|
|
|
|
/* option names */
|
|
|
|
case '-':
|
|
|
|
s++;
|
|
|
|
if (risalnum(*s))
|
|
|
|
s++;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
if (*s == '*')
|
|
|
|
s++;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
/* argument names */
|
|
|
|
if (risdigit(*s)) {
|
|
|
|
s++;
|
|
|
|
while (risdigit(*s))
|
|
|
|
s++;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
/* invalid macro name */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2000-09-02 05:15:40 +08:00
|
|
|
/**
|
2000-09-30 03:50:29 +08:00
|
|
|
* The main macro recursion loop.
|
|
|
|
* @param mb macro expansion state
|
2010-09-24 15:42:38 +08:00
|
|
|
* @param src string to expand
|
2000-09-30 03:50:29 +08:00
|
|
|
* @return 0 on success, 1 on failure
|
2000-09-02 05:15:40 +08:00
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static int
|
2010-09-27 22:15:15 +08:00
|
|
|
expandMacro(MacroBuf mb, const char *src, size_t slen)
|
1997-03-31 22:13:21 +08:00
|
|
|
{
|
2007-09-20 20:52:03 +08:00
|
|
|
rpmMacroEntry *mep;
|
|
|
|
rpmMacroEntry me;
|
2010-09-24 15:42:38 +08:00
|
|
|
const char *s = src, *se;
|
1998-07-09 01:50:48 +08:00
|
|
|
const char *f, *fe;
|
|
|
|
const char *g, *ge;
|
2010-09-27 18:05:37 +08:00
|
|
|
size_t fn, gn, tpos;
|
1998-07-09 01:50:48 +08:00
|
|
|
int c;
|
|
|
|
int rc = 0;
|
|
|
|
int negate;
|
2003-12-30 23:12:50 +08:00
|
|
|
const char * lastc;
|
1998-09-06 07:13:35 +08:00
|
|
|
int chkexist;
|
2010-09-27 22:15:15 +08:00
|
|
|
char *source = NULL;
|
|
|
|
|
2013-04-16 15:45:56 +08:00
|
|
|
/*
|
|
|
|
* Always make a (terminated) copy of the source string.
|
|
|
|
* Beware: avoiding the copy when src is known \0-terminated seems like
|
|
|
|
* an obvious opportunity for optimization, but doing that breaks
|
|
|
|
* a special case of macro undefining itself.
|
|
|
|
*/
|
2011-05-18 14:04:40 +08:00
|
|
|
if (!slen)
|
|
|
|
slen = strlen(src);
|
|
|
|
source = xmalloc(slen + 1);
|
|
|
|
strncpy(source, src, slen);
|
|
|
|
source[slen] = '\0';
|
|
|
|
s = source;
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2010-09-27 18:05:37 +08:00
|
|
|
if (mb->buf == NULL) {
|
2013-04-16 15:45:56 +08:00
|
|
|
size_t blen = MACROBUFSIZ + slen;
|
|
|
|
mb->buf = xmalloc(blen + 1);
|
|
|
|
mb->buf[0] = '\0';
|
2010-09-27 18:05:37 +08:00
|
|
|
mb->tpos = 0;
|
|
|
|
mb->nb = blen;
|
|
|
|
}
|
|
|
|
tpos = mb->tpos; /* save expansion pointer for printExpand */
|
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
if (++mb->depth > max_macro_depth) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2010-07-13 19:34:05 +08:00
|
|
|
_("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
|
1998-07-09 01:50:48 +08:00
|
|
|
mb->depth--;
|
|
|
|
mb->expand_trace = 1;
|
2010-09-27 22:15:15 +08:00
|
|
|
_free(source);
|
1998-07-09 01:50:48 +08:00
|
|
|
return 1;
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2010-09-27 19:05:37 +08:00
|
|
|
while (rc == 0 && (c = *s) != '\0') {
|
1998-07-09 01:50:48 +08:00
|
|
|
s++;
|
|
|
|
/* Copy text until next macro */
|
|
|
|
switch(c) {
|
|
|
|
case '%':
|
2005-07-13 17:52:45 +08:00
|
|
|
if (*s) { /* Ensure not end-of-string. */
|
|
|
|
if (*s != '%')
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
2005-07-13 17:52:45 +08:00
|
|
|
s++; /* skip first % in %% */
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
default:
|
2010-09-27 19:05:37 +08:00
|
|
|
mbAppend(mb, c);
|
1998-07-09 01:50:48 +08:00
|
|
|
continue;
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Expand next macro */
|
|
|
|
f = fe = NULL;
|
|
|
|
g = ge = NULL;
|
|
|
|
if (mb->depth > 1) /* XXX full expansion for outermost level */
|
2010-09-27 17:19:13 +08:00
|
|
|
tpos = mb->tpos; /* save expansion pointer for printExpand */
|
1998-07-09 01:50:48 +08:00
|
|
|
negate = 0;
|
2003-12-30 23:12:50 +08:00
|
|
|
lastc = NULL;
|
1998-09-06 07:13:35 +08:00
|
|
|
chkexist = 0;
|
1998-07-09 01:50:48 +08:00
|
|
|
switch ((c = *s)) {
|
|
|
|
default: /* %name substitution */
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
f = parseMacroFlags(s, &negate, &chkexist);
|
|
|
|
fe = parseMacroName(f);
|
|
|
|
/* no valid name? assume as-is substitution */
|
|
|
|
if (fe == NULL) {
|
|
|
|
mbAppend(mb, '%');
|
|
|
|
continue;
|
1999-07-29 20:36:03 +08:00
|
|
|
}
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
se = fe;
|
1998-07-09 01:50:48 +08:00
|
|
|
/* For "%name " macros ... */
|
|
|
|
if ((c = *fe) && isblank(c))
|
2003-12-30 23:12:50 +08:00
|
|
|
if ((lastc = strchr(fe,'\n')) == NULL)
|
|
|
|
lastc = strchr(fe, '\0');
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
1998-07-09 01:50:48 +08:00
|
|
|
case '(': /* %(...) shell escape */
|
|
|
|
if ((se = matchchar(s, c, ')')) == NULL) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2001-05-01 06:32:22 +08:00
|
|
|
_("Unterminated %c: %s\n"), (char)c, s);
|
1998-07-09 01:50:48 +08:00
|
|
|
rc = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (mb->macro_trace)
|
|
|
|
printMacro(mb, s, se+1);
|
|
|
|
|
|
|
|
s++; /* skip ( */
|
|
|
|
rc = doShellEscape(mb, s, (se - s));
|
|
|
|
se++; /* skip ) */
|
|
|
|
|
|
|
|
s = se;
|
|
|
|
continue;
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
1998-07-09 01:50:48 +08:00
|
|
|
case '{': /* %{...}/%{...:...} substitution */
|
|
|
|
if ((se = matchchar(s, c, '}')) == NULL) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2001-05-01 06:32:22 +08:00
|
|
|
_("Unterminated %c: %s\n"), (char)c, s);
|
1998-07-09 01:50:48 +08:00
|
|
|
rc = 1;
|
|
|
|
continue;
|
|
|
|
}
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
f = parseMacroFlags(s + 1 /* skip { */, &negate, &chkexist);
|
|
|
|
fe = parseMacroName(f);
|
1998-07-09 01:50:48 +08:00
|
|
|
se++; /* skip } */
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
/* no valid name? syntax error */
|
|
|
|
if (fe == NULL) {
|
|
|
|
rpmlog(RPMLOG_ERR,
|
|
|
|
_("Invalid macro name: %%%.*s\n"), (int)(se - s), s);
|
|
|
|
rc = 1;
|
|
|
|
continue;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
switch (*fe) {
|
1999-07-29 20:36:03 +08:00
|
|
|
case ':':
|
1998-07-09 01:50:48 +08:00
|
|
|
g = fe + 1;
|
|
|
|
ge = se - 1;
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
1999-07-29 20:36:03 +08:00
|
|
|
case ' ':
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
case '\t':
|
2003-12-30 23:12:50 +08:00
|
|
|
lastc = se-1;
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
case '}':
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
default:
|
|
|
|
rpmlog(RPMLOG_ERR,
|
|
|
|
_("Invalid macro syntax: %%%.*s\n"), (int)(se - s), s);
|
|
|
|
rc = 1;
|
|
|
|
continue;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
Stricter macro substitution syntax
This change introduces a separate routine to parse for valid macro
names. Valid macro names are either regular 3+ character identifiers,
or special names: "S", "P", "0", "#", "*", "**", macro options such as
"-o" and "-o*", and macro arguments such as "1". Other names are not
valid. This fixes a number of bugs seen earlier due to sloppy name
parsing: "%_libdir*" and "%01" were not expanded (these are now expanded
to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes
bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise
"%!!!" was expanded to "%" (and to "%<garbage>" at EOL).
Also, bad names in %name and %{name...} substitutions are now handled
differently. In %name form, the name is parsed tentatively; a silent
fall-back to as-is substitution is provisioned when no valid name can
be obtain. In %{name...} form, a failure to obtain a valid name is now
a syntax error. Furthermore, only 3 variants are syntactically valid:
%{name} proper, %{name:...}, and %{name ...}. This renders invalid
ambiguous macro substitutions such as the one found in FC18 lvm2.spec:
Requires: util-linux >= %{util-linux_version}
error: Invalid macro syntax: %{util-linux_version}
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-21 04:45:30 +08:00
|
|
|
assert(fe > f);
|
1998-07-09 01:50:48 +08:00
|
|
|
fn = (fe - f);
|
|
|
|
gn = (ge - g);
|
|
|
|
|
|
|
|
if (mb->macro_trace)
|
|
|
|
printMacro(mb, s, se);
|
|
|
|
|
|
|
|
/* Expand builtin macros */
|
|
|
|
if (STREQ("global", f, fn)) {
|
1998-08-02 23:14:38 +08:00
|
|
|
s = doDefine(mb, se, RMIL_GLOBAL, 1);
|
1998-07-09 01:50:48 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (STREQ("define", f, fn)) {
|
|
|
|
s = doDefine(mb, se, mb->depth, 0);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (STREQ("undefine", f, fn)) {
|
|
|
|
s = doUndefine(mb->mc, se);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ("echo", f, fn) ||
|
|
|
|
STREQ("warn", f, fn) ||
|
|
|
|
STREQ("error", f, fn)) {
|
|
|
|
int waserror = 0;
|
|
|
|
if (STREQ("error", f, fn))
|
|
|
|
waserror = 1;
|
2003-04-02 06:20:45 +08:00
|
|
|
if (g != NULL && g < ge)
|
1998-07-09 01:50:48 +08:00
|
|
|
doOutput(mb, waserror, g, gn);
|
|
|
|
else
|
|
|
|
doOutput(mb, waserror, f, fn);
|
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ("trace", f, fn)) {
|
|
|
|
/* XXX TODO restore expand_trace/macro_trace to 0 on return */
|
|
|
|
mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
|
|
|
|
if (mb->depth == 1) {
|
|
|
|
print_macro_trace = mb->macro_trace;
|
|
|
|
print_expand_trace = mb->expand_trace;
|
|
|
|
}
|
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ("dump", f, fn)) {
|
1999-12-13 05:14:05 +08:00
|
|
|
rpmDumpMacroTable(mb->mc, NULL);
|
1999-08-17 02:57:37 +08:00
|
|
|
while (iseol(*se))
|
1998-07-09 01:50:48 +08:00
|
|
|
se++;
|
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2004-10-10 01:29:22 +08:00
|
|
|
#ifdef WITH_LUA
|
2013-06-10 19:44:55 +08:00
|
|
|
if (STREQ("lua", f, fn) && g != NULL) {
|
2004-03-25 03:47:11 +08:00
|
|
|
rpmlua lua = NULL; /* Global state. */
|
2004-03-20 04:08:20 +08:00
|
|
|
const char *ls = s+sizeof("{lua:")-1;
|
|
|
|
const char *lse = se-sizeof("}")+1;
|
|
|
|
char *scriptbuf = (char *)xmalloc((lse-ls)+1);
|
2011-05-25 01:28:16 +08:00
|
|
|
char *printbuf;
|
2004-03-20 04:08:20 +08:00
|
|
|
memcpy(scriptbuf, ls, lse-ls);
|
|
|
|
scriptbuf[lse-ls] = '\0';
|
2011-05-25 01:28:16 +08:00
|
|
|
rpmluaPushPrintBuffer(lua);
|
2004-03-20 04:08:20 +08:00
|
|
|
if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
|
|
|
|
rc = 1;
|
2011-05-25 01:28:16 +08:00
|
|
|
printbuf = rpmluaPopPrintBuffer(lua);
|
2004-03-20 04:08:20 +08:00
|
|
|
if (printbuf) {
|
2010-09-27 21:32:15 +08:00
|
|
|
mbAppendStr(mb, printbuf);
|
2011-05-25 01:28:16 +08:00
|
|
|
free(printbuf);
|
2004-03-20 04:08:20 +08:00
|
|
|
}
|
|
|
|
free(scriptbuf);
|
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
2004-10-10 01:29:22 +08:00
|
|
|
#endif
|
2004-03-20 04:08:20 +08:00
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
/* XXX necessary but clunky */
|
|
|
|
if (STREQ("basename", f, fn) ||
|
2012-11-01 18:09:30 +08:00
|
|
|
STREQ("dirname", f, fn) ||
|
1998-07-09 01:50:48 +08:00
|
|
|
STREQ("suffix", f, fn) ||
|
|
|
|
STREQ("expand", f, fn) ||
|
1999-11-20 02:19:41 +08:00
|
|
|
STREQ("verbose", f, fn) ||
|
1998-07-09 01:50:48 +08:00
|
|
|
STREQ("uncompress", f, fn) ||
|
1999-11-20 02:19:41 +08:00
|
|
|
STREQ("url2path", f, fn) ||
|
|
|
|
STREQ("u2p", f, fn) ||
|
2009-01-16 15:09:40 +08:00
|
|
|
STREQ("getenv", f, fn) ||
|
2009-03-27 20:01:47 +08:00
|
|
|
STREQ("getconfdir", f, fn) ||
|
1998-07-09 01:50:48 +08:00
|
|
|
STREQ("S", f, fn) ||
|
|
|
|
STREQ("P", f, fn) ||
|
|
|
|
STREQ("F", f, fn)) {
|
2007-09-11 22:48:54 +08:00
|
|
|
/* FIX: verbose may be set */
|
1999-11-20 02:19:41 +08:00
|
|
|
doFoo(mb, negate, f, fn, g, gn);
|
1998-07-09 01:50:48 +08:00
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Expand defined macros */
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
mep = findEntry(mb->mc, f, fn, NULL);
|
1998-07-09 01:50:48 +08:00
|
|
|
me = (mep ? *mep : NULL);
|
|
|
|
|
|
|
|
/* XXX Special processing for flags */
|
|
|
|
if (*f == '-') {
|
|
|
|
if (me)
|
|
|
|
me->used++; /* Mark macro as used */
|
|
|
|
if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
|
|
|
|
(me != NULL && negate)) { /* With -f, skip %{!-f...} */
|
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g && g < ge) { /* Expand X in %{-f:X} */
|
2010-09-27 22:15:15 +08:00
|
|
|
rc = expandMacro(mb, g, gn);
|
1998-07-09 01:50:48 +08:00
|
|
|
} else
|
2001-05-04 05:00:18 +08:00
|
|
|
if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
|
2010-09-27 22:15:15 +08:00
|
|
|
rc = expandMacro(mb, me->body, 0);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
1998-09-06 07:13:35 +08:00
|
|
|
/* XXX Special processing for macro existence */
|
|
|
|
if (chkexist) {
|
|
|
|
if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
|
|
|
|
(me != NULL && negate)) { /* With -f, skip %{!?f...} */
|
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (g && g < ge) { /* Expand X in %{?f:X} */
|
2010-09-27 22:15:15 +08:00
|
|
|
rc = expandMacro(mb, g, gn);
|
1998-09-06 07:13:35 +08:00
|
|
|
} else
|
2000-02-21 18:32:01 +08:00
|
|
|
if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
|
2010-09-27 22:15:15 +08:00
|
|
|
rc = expandMacro(mb, me->body, 0);
|
1998-09-06 07:13:35 +08:00
|
|
|
}
|
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
if (me == NULL) { /* leave unknown %... as is */
|
|
|
|
/* XXX hack to permit non-overloaded %foo to be passed */
|
|
|
|
c = '%'; /* XXX only need to save % */
|
2010-09-27 19:05:37 +08:00
|
|
|
mbAppend(mb, c);
|
1998-07-09 01:50:48 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup args for "%name " macros with opts */
|
|
|
|
if (me && me->opts != NULL) {
|
2003-12-30 23:12:50 +08:00
|
|
|
if (lastc != NULL) {
|
|
|
|
se = grabArgs(mb, me, fe, lastc);
|
1999-07-29 20:36:03 +08:00
|
|
|
} else {
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mb->mc, "**", NULL, "", mb->depth);
|
|
|
|
pushMacro(mb->mc, "*", NULL, "", mb->depth);
|
|
|
|
pushMacro(mb->mc, "#", NULL, "0", mb->depth);
|
|
|
|
pushMacro(mb->mc, "0", NULL, me->name, mb->depth);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Recursively expand body of macro */
|
1999-03-28 08:47:40 +08:00
|
|
|
if (me->body && *me->body) {
|
2010-09-27 22:15:15 +08:00
|
|
|
rc = expandMacro(mb, me->body, 0);
|
1999-03-28 08:47:40 +08:00
|
|
|
if (rc == 0)
|
|
|
|
me->used++; /* Mark macro as used */
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2010-01-07 18:04:53 +08:00
|
|
|
/* Free args for "%name " macros with opts */
|
|
|
|
if (me->opts != NULL)
|
|
|
|
freeArgs(mb);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
|
|
|
s = se;
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
|
|
|
|
2010-09-27 17:19:13 +08:00
|
|
|
mb->buf[mb->tpos] = '\0';
|
1998-07-09 01:50:48 +08:00
|
|
|
mb->depth--;
|
|
|
|
if (rc != 0 || mb->expand_trace)
|
2010-09-27 17:19:13 +08:00
|
|
|
printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos);
|
2010-09-27 22:15:15 +08:00
|
|
|
_free(source);
|
1998-07-09 01:50:48 +08:00
|
|
|
return rc;
|
|
|
|
}
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2004-03-02 09:31:01 +08:00
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
/* =============================================================== */
|
1999-03-28 08:47:40 +08:00
|
|
|
|
2010-09-24 17:48:15 +08:00
|
|
|
static int doExpandMacros(rpmMacroContext mc, const char *src, char **target)
|
1997-03-31 22:13:21 +08:00
|
|
|
{
|
2008-02-01 18:11:03 +08:00
|
|
|
MacroBuf mb = xcalloc(1, sizeof(*mb));
|
|
|
|
int rc = 0;
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2010-09-27 18:05:37 +08:00
|
|
|
mb->buf = NULL;
|
2001-06-06 03:26:22 +08:00
|
|
|
mb->depth = 0;
|
|
|
|
mb->macro_trace = print_macro_trace;
|
|
|
|
mb->expand_trace = print_expand_trace;
|
|
|
|
mb->mc = mc;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2010-09-27 22:15:15 +08:00
|
|
|
rc = expandMacro(mb, src, 0);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2010-09-27 18:05:37 +08:00
|
|
|
mb->buf[mb->tpos] = '\0'; /* XXX just in case */
|
2010-09-24 17:48:15 +08:00
|
|
|
/* expanded output is usually much less than alloced buffer, downsize */
|
2010-09-27 18:05:37 +08:00
|
|
|
*target = xrealloc(mb->buf, mb->tpos + 1);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2008-02-01 18:11:03 +08:00
|
|
|
_free(mb);
|
2010-09-24 17:48:15 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-05-22 14:44:34 +08:00
|
|
|
static void pushMacro(rpmMacroContext mc,
|
2001-06-06 03:26:22 +08:00
|
|
|
const char * n, const char * o, const char * b, int level)
|
1997-03-31 22:13:21 +08:00
|
|
|
{
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
/* new entry */
|
|
|
|
rpmMacroEntry me;
|
|
|
|
/* pointer into me */
|
|
|
|
char *p;
|
|
|
|
/* calculate sizes */
|
|
|
|
size_t olen = o ? strlen(o) : 0;
|
|
|
|
size_t blen = b ? strlen(b) : 0;
|
|
|
|
size_t mesize = sizeof(*me) + blen + 1 + (olen ? olen + 1 : 0);
|
|
|
|
|
|
|
|
size_t pos;
|
|
|
|
rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
|
|
|
|
if (mep) {
|
|
|
|
/* entry with shared name */
|
|
|
|
me = xmalloc(mesize);
|
|
|
|
/* copy body */
|
2013-04-04 16:22:22 +08:00
|
|
|
me->body = p = me->arena;
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
if (blen)
|
|
|
|
memcpy(p, b, blen + 1);
|
|
|
|
else
|
|
|
|
*p = '\0';
|
|
|
|
p += blen + 1;
|
|
|
|
/* set name */
|
|
|
|
me->name = (*mep)->name;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
else {
|
|
|
|
/* extend macro table */
|
|
|
|
const int delta = 256;
|
|
|
|
if (mc->n % delta == 0)
|
|
|
|
mc->tab = xrealloc(mc->tab, sizeof(me) * (mc->n + delta));
|
|
|
|
/* shift pos+ entries to the right */
|
|
|
|
memmove(mc->tab + pos + 1, mc->tab + pos, sizeof(me) * (mc->n - pos));
|
|
|
|
mc->n++;
|
|
|
|
/* make slot */
|
|
|
|
mc->tab[pos] = NULL;
|
|
|
|
mep = &mc->tab[pos];
|
|
|
|
/* entry with new name */
|
|
|
|
size_t nlen = strlen(n);
|
|
|
|
me = xmalloc(mesize + nlen + 1);
|
|
|
|
/* copy body */
|
2013-04-04 16:22:22 +08:00
|
|
|
me->body = p = me->arena;
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
if (blen)
|
|
|
|
memcpy(p, b, blen + 1);
|
|
|
|
else
|
|
|
|
*p = '\0';
|
|
|
|
p += blen + 1;
|
|
|
|
/* copy name */
|
|
|
|
me->name = memcpy(p, n, nlen + 1);
|
|
|
|
p += nlen + 1;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
|
|
|
|
/* copy options */
|
|
|
|
if (olen)
|
|
|
|
me->opts = memcpy(p, o, olen + 1);
|
|
|
|
else
|
|
|
|
me->opts = o ? "" : NULL;
|
|
|
|
/* initialize */
|
|
|
|
me->used = 0;
|
|
|
|
me->level = level;
|
|
|
|
/* push over previous definition */
|
|
|
|
me->prev = *mep;
|
|
|
|
*mep = me;
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
|
|
|
|
2013-05-22 14:44:34 +08:00
|
|
|
static void popMacro(rpmMacroContext mc, const char * n)
|
1997-03-31 22:13:21 +08:00
|
|
|
{
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
size_t pos;
|
|
|
|
rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
|
|
|
|
if (mep == NULL)
|
|
|
|
return;
|
|
|
|
/* parting entry */
|
|
|
|
rpmMacroEntry me = *mep;
|
|
|
|
assert(me);
|
|
|
|
/* detach/pop definition */
|
|
|
|
mc->tab[pos] = me->prev;
|
|
|
|
/* shrink macro table */
|
|
|
|
if (me->prev == NULL) {
|
|
|
|
mc->n--;
|
|
|
|
/* move pos+ elements to the left */
|
|
|
|
memmove(mc->tab + pos, mc->tab + pos + 1, sizeof(me) * (mc->n - pos));
|
|
|
|
/* deallocate */
|
|
|
|
if (mc->n == 0)
|
|
|
|
mc->tab = _free(mc->tab);
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
/* comes in a single chunk */
|
|
|
|
free(me);
|
1998-01-13 05:31:29 +08:00
|
|
|
}
|
|
|
|
|
2013-05-22 16:15:20 +08:00
|
|
|
static int defineMacro(rpmMacroContext mc, const char * macro, int level)
|
|
|
|
{
|
|
|
|
MacroBuf mb = xcalloc(1, sizeof(*mb));
|
|
|
|
|
|
|
|
/* XXX just enough to get by */
|
|
|
|
mb->mc = mc;
|
|
|
|
(void) doDefine(mb, macro, level, 0);
|
|
|
|
_free(mb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-22 16:27:29 +08:00
|
|
|
static int loadMacroFile(rpmMacroContext mc, const char * fn)
|
|
|
|
{
|
|
|
|
FILE *fd = fopen(fn, "r");
|
|
|
|
size_t blen = MACROBUFSIZ;
|
|
|
|
char *buf = xmalloc(blen);
|
|
|
|
int rc = -1;
|
|
|
|
|
|
|
|
if (fd == NULL)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
/* XXX Assume new fangled macro expansion */
|
|
|
|
max_macro_depth = 16;
|
|
|
|
|
|
|
|
buf[0] = '\0';
|
|
|
|
while(rdcl(buf, blen, fd) != NULL) {
|
|
|
|
char c, *n;
|
|
|
|
|
|
|
|
n = buf;
|
|
|
|
SKIPBLANK(n, c);
|
|
|
|
|
|
|
|
if (c != '%')
|
|
|
|
continue;
|
|
|
|
n++; /* skip % */
|
|
|
|
rc = defineMacro(mc, n, RMIL_MACROFILES);
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = fclose(fd);
|
|
|
|
|
|
|
|
exit:
|
|
|
|
_free(buf);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-05-22 16:43:40 +08:00
|
|
|
static void copyMacros(rpmMacroContext src, rpmMacroContext dst, int level)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < src->n; i++) {
|
|
|
|
rpmMacroEntry me = src->tab[i];
|
|
|
|
assert(me);
|
|
|
|
pushMacro(dst, me->name, me->opts, me->body, (level - 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-22 15:21:04 +08:00
|
|
|
/* External interfaces */
|
|
|
|
|
|
|
|
int expandMacros(void * spec, rpmMacroContext mc, char * sbuf, size_t slen)
|
|
|
|
{
|
|
|
|
char *target = NULL;
|
2013-05-22 15:53:20 +08:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
mc = rpmmctxAcquire(mc);
|
|
|
|
rc = doExpandMacros(mc, sbuf, &target);
|
|
|
|
rpmmctxRelease(mc);
|
|
|
|
|
2013-05-22 15:21:04 +08:00
|
|
|
rstrlcpy(sbuf, target, slen);
|
|
|
|
free(target);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
|
|
|
|
{
|
2013-05-22 15:53:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
2013-05-22 15:21:04 +08:00
|
|
|
if (fp == NULL) fp = stderr;
|
|
|
|
|
|
|
|
fprintf(fp, "========================\n");
|
|
|
|
for (int i = 0; i < mc->n; i++) {
|
|
|
|
rpmMacroEntry me = mc->tab[i];
|
|
|
|
assert(me);
|
|
|
|
fprintf(fp, "%3d%c %s", me->level,
|
|
|
|
(me->used > 0 ? '=' : ':'), me->name);
|
|
|
|
if (me->opts && *me->opts)
|
|
|
|
fprintf(fp, "(%s)", me->opts);
|
|
|
|
if (me->body && *me->body)
|
|
|
|
fprintf(fp, "\t%s", me->body);
|
|
|
|
fprintf(fp, "\n");
|
|
|
|
}
|
|
|
|
fprintf(fp, _("======================== active %d empty %d\n"),
|
|
|
|
mc->n, 0);
|
2013-05-22 15:53:20 +08:00
|
|
|
rpmmctxRelease(mc);
|
2013-05-22 15:21:04 +08:00
|
|
|
}
|
|
|
|
|
2013-05-22 14:44:34 +08:00
|
|
|
void addMacro(rpmMacroContext mc,
|
|
|
|
const char * n, const char * o, const char * b, int level)
|
|
|
|
{
|
2013-05-22 15:53:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
2013-05-22 14:44:34 +08:00
|
|
|
pushMacro(mc, n, o, b, level);
|
2013-05-22 15:53:20 +08:00
|
|
|
rpmmctxRelease(mc);
|
2013-05-22 14:44:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void delMacro(rpmMacroContext mc, const char * n)
|
|
|
|
{
|
2013-05-22 15:53:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
2013-05-22 14:44:34 +08:00
|
|
|
popMacro(mc, n);
|
2013-05-22 15:53:20 +08:00
|
|
|
rpmmctxRelease(mc);
|
2013-05-22 14:44:34 +08:00
|
|
|
}
|
|
|
|
|
1999-03-28 08:47:40 +08:00
|
|
|
int
|
2007-09-20 20:52:03 +08:00
|
|
|
rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
|
1999-03-28 08:47:40 +08:00
|
|
|
{
|
2013-05-22 16:15:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
|
|
|
(void) defineMacro(mc, macro, level);
|
|
|
|
rpmmctxRelease(mc);
|
2001-06-06 03:26:22 +08:00
|
|
|
return 0;
|
1999-03-28 08:47:40 +08:00
|
|
|
}
|
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
void
|
2007-09-20 20:52:03 +08:00
|
|
|
rpmLoadMacros(rpmMacroContext mc, int level)
|
1999-12-13 05:14:05 +08:00
|
|
|
{
|
2013-05-22 15:53:20 +08:00
|
|
|
rpmMacroContext gmc;
|
2001-10-15 11:22:10 +08:00
|
|
|
if (mc == NULL || mc == rpmGlobalMacroContext)
|
2001-05-04 05:00:18 +08:00
|
|
|
return;
|
1999-12-13 05:14:05 +08:00
|
|
|
|
2013-05-22 15:53:20 +08:00
|
|
|
gmc = rpmmctxAcquire(NULL);
|
|
|
|
mc = rpmmctxAcquire(mc);
|
2013-05-22 16:43:40 +08:00
|
|
|
|
|
|
|
copyMacros(mc, gmc, level);
|
|
|
|
|
2013-05-22 15:53:20 +08:00
|
|
|
rpmmctxRelease(mc);
|
|
|
|
rpmmctxRelease(gmc);
|
1999-12-13 05:14:05 +08:00
|
|
|
}
|
|
|
|
|
2004-03-02 09:31:01 +08:00
|
|
|
int
|
2007-09-20 20:52:03 +08:00
|
|
|
rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
|
2004-03-02 09:31:01 +08:00
|
|
|
{
|
2013-05-22 16:27:29 +08:00
|
|
|
int rc;
|
2004-03-02 09:31:01 +08:00
|
|
|
|
2013-05-22 15:53:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
2013-05-22 16:27:29 +08:00
|
|
|
rc = loadMacroFile(mc, fn);
|
2013-05-22 15:53:20 +08:00
|
|
|
rpmmctxRelease(mc);
|
|
|
|
|
2004-03-02 09:31:01 +08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
1999-12-13 05:14:05 +08:00
|
|
|
void
|
2007-09-20 20:52:03 +08:00
|
|
|
rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2008-04-19 21:42:56 +08:00
|
|
|
ARGV_t pattern, globs = NULL;
|
2013-05-22 16:43:40 +08:00
|
|
|
rpmMacroContext climc;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-05-04 05:00:18 +08:00
|
|
|
if (macrofiles == NULL)
|
|
|
|
return;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2008-04-19 21:42:56 +08:00
|
|
|
argvSplit(&globs, macrofiles, ":");
|
2013-05-22 15:53:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
2008-04-19 21:42:56 +08:00
|
|
|
for (pattern = globs; *pattern; pattern++) {
|
|
|
|
ARGV_t path, files = NULL;
|
|
|
|
|
2004-03-02 09:31:01 +08:00
|
|
|
/* Glob expand the macro file path element, expanding ~ to $HOME. */
|
2008-04-19 21:42:56 +08:00
|
|
|
if (rpmGlob(*pattern, NULL, &files) != 0) {
|
2001-05-04 05:00:18 +08:00
|
|
|
continue;
|
2008-04-19 21:42:56 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2004-03-02 09:31:01 +08:00
|
|
|
/* Read macros from each file. */
|
2008-04-19 21:42:56 +08:00
|
|
|
for (path = files; *path; path++) {
|
|
|
|
if (rpmFileHasSuffix(*path, ".rpmnew") ||
|
|
|
|
rpmFileHasSuffix(*path, ".rpmsave") ||
|
|
|
|
rpmFileHasSuffix(*path, ".rpmorig")) {
|
2007-08-30 18:02:12 +08:00
|
|
|
continue;
|
|
|
|
}
|
2013-05-22 16:27:29 +08:00
|
|
|
(void) loadMacroFile(mc, *path);
|
2007-07-02 20:02:15 +08:00
|
|
|
}
|
2008-04-19 21:42:56 +08:00
|
|
|
argvFree(files);
|
2001-05-04 05:00:18 +08:00
|
|
|
}
|
2008-04-19 21:42:56 +08:00
|
|
|
argvFree(globs);
|
1999-12-13 05:14:05 +08:00
|
|
|
|
2001-05-04 05:00:18 +08:00
|
|
|
/* Reload cmdline macros */
|
2013-05-22 16:43:40 +08:00
|
|
|
climc = rpmmctxAcquire(rpmCLIMacroContext);
|
|
|
|
copyMacros(climc, mc, RMIL_CMDLINE);
|
|
|
|
rpmmctxRelease(climc);
|
|
|
|
|
|
|
|
rpmmctxRelease(mc);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
1998-01-13 05:31:29 +08:00
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
void
|
2007-09-20 20:52:03 +08:00
|
|
|
rpmFreeMacros(rpmMacroContext mc)
|
1998-01-13 05:31:29 +08:00
|
|
|
{
|
2013-05-22 15:53:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
2013-01-28 04:20:10 +08:00
|
|
|
while (mc->n > 0) {
|
|
|
|
/* remove from the end to avoid memmove */
|
|
|
|
rpmMacroEntry me = mc->tab[mc->n - 1];
|
2013-05-22 14:44:34 +08:00
|
|
|
popMacro(mc, me->name);
|
2001-05-04 05:00:18 +08:00
|
|
|
}
|
2013-05-22 15:53:20 +08:00
|
|
|
rpmmctxRelease(mc);
|
1998-01-13 05:31:29 +08:00
|
|
|
}
|
|
|
|
|
1999-03-28 08:47:40 +08:00
|
|
|
char *
|
|
|
|
rpmExpand(const char *arg, ...)
|
|
|
|
{
|
2010-09-24 17:48:15 +08:00
|
|
|
size_t blen = 0;
|
|
|
|
char *buf = NULL, *ret = NULL;
|
2008-07-16 15:33:57 +08:00
|
|
|
char *pe;
|
1999-03-28 08:47:40 +08:00
|
|
|
const char *s;
|
|
|
|
va_list ap;
|
2013-05-22 15:53:20 +08:00
|
|
|
rpmMacroContext mc;
|
1999-03-28 08:47:40 +08:00
|
|
|
|
2008-02-01 19:00:40 +08:00
|
|
|
if (arg == NULL) {
|
2010-09-24 17:48:15 +08:00
|
|
|
ret = xstrdup("");
|
2008-02-01 19:00:40 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
1999-03-28 08:47:40 +08:00
|
|
|
|
2010-09-24 17:48:15 +08:00
|
|
|
/* precalculate unexpanded size */
|
2008-07-16 15:52:33 +08:00
|
|
|
va_start(ap, arg);
|
|
|
|
for (s = arg; s != NULL; s = va_arg(ap, const char *))
|
|
|
|
blen += strlen(s);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
buf = xmalloc(blen + 1);
|
2000-11-01 00:18:34 +08:00
|
|
|
buf[0] = '\0';
|
1999-03-28 08:47:40 +08:00
|
|
|
|
|
|
|
va_start(ap, arg);
|
2008-07-16 15:33:57 +08:00
|
|
|
for (pe = buf, s = arg; s != NULL; s = va_arg(ap, const char *))
|
2000-06-22 07:28:50 +08:00
|
|
|
pe = stpcpy(pe, s);
|
1999-03-28 08:47:40 +08:00
|
|
|
va_end(ap);
|
2008-07-16 15:52:33 +08:00
|
|
|
|
2013-05-22 15:53:20 +08:00
|
|
|
mc = rpmmctxAcquire(NULL);
|
|
|
|
(void) doExpandMacros(mc, buf, &ret);
|
|
|
|
rpmmctxRelease(mc);
|
2008-07-16 15:52:33 +08:00
|
|
|
|
2010-09-24 17:48:15 +08:00
|
|
|
free(buf);
|
2008-02-01 19:00:40 +08:00
|
|
|
exit:
|
2010-09-24 17:48:15 +08:00
|
|
|
return ret;
|
1999-03-28 08:47:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rpmExpandNumeric(const char *arg)
|
|
|
|
{
|
2007-12-14 21:50:17 +08:00
|
|
|
char *val;
|
1999-03-28 08:47:40 +08:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (arg == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
val = rpmExpand(arg, NULL);
|
|
|
|
if (!(val && *val != '%'))
|
|
|
|
rc = 0;
|
|
|
|
else if (*val == 'Y' || *val == 'y')
|
|
|
|
rc = 1;
|
|
|
|
else if (*val == 'N' || *val == 'n')
|
|
|
|
rc = 0;
|
|
|
|
else {
|
|
|
|
char *end;
|
|
|
|
rc = strtol(val, &end, 0);
|
|
|
|
if (!(end && *end == '\0'))
|
|
|
|
rc = 0;
|
|
|
|
}
|
2011-05-29 00:43:52 +08:00
|
|
|
free(val);
|
1999-03-28 08:47:40 +08:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|