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>
|
2014-01-30 20:24:05 +08:00
|
|
|
#include <errno.h>
|
2019-01-15 17:16:04 +08:00
|
|
|
#if HAVE_SCHED_GETAFFINITY
|
|
|
|
#include <sched.h>
|
|
|
|
#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
|
|
|
|
2019-09-18 21:31:08 +08:00
|
|
|
#include "rpmio/rpmmacro_internal.h"
|
2000-12-13 04:03:45 +08:00
|
|
|
#include "debug.h"
|
|
|
|
|
2014-02-28 21:57:09 +08:00
|
|
|
enum macroFlags_e {
|
|
|
|
ME_NONE = 0,
|
|
|
|
ME_AUTO = (1 << 0),
|
2014-02-28 22:10:14 +08:00
|
|
|
ME_USED = (1 << 1),
|
2020-02-07 18:14:52 +08:00
|
|
|
ME_LITERAL = (1 << 2),
|
2014-02-28 21:57:09 +08:00
|
|
|
};
|
|
|
|
|
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. */
|
2014-02-28 21:57:09 +08:00
|
|
|
int flags; /*!< Macro state bits. */
|
2007-11-02 16:40:45 +08:00
|
|
|
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. */
|
2017-01-20 20:55:59 +08:00
|
|
|
int depth; /*!< Depth tracking when recursing from Lua */
|
2017-03-07 19:02:26 +08:00
|
|
|
int level; /*!< Scope level tracking when recursing from Lua */
|
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. */
|
2017-03-07 19:02:26 +08:00
|
|
|
int level; /*!< Current scoping level */
|
2017-01-19 19:52:30 +08:00
|
|
|
int error; /*!< Errors encountered during expansion? */
|
2001-06-06 03:26:22 +08:00
|
|
|
int macro_trace; /*!< Pre-print macro to expand? */
|
|
|
|
int expand_trace; /*!< Post-print macro expansion? */
|
2017-05-18 16:19:13 +08:00
|
|
|
int flags; /*!< Flags to control behavior */
|
2020-10-08 16:10:40 +08:00
|
|
|
rpmMacroEntry me; /*!< Current macro (or NULL if anonymous) */
|
|
|
|
ARGV_t args; /*!< Current macro arguments (or NULL) */
|
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
|
|
|
|
2017-08-21 21:29:56 +08:00
|
|
|
#define _MAX_MACRO_DEPTH 64
|
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
|
|
|
|
2019-01-15 20:19:06 +08:00
|
|
|
typedef void (*macroFunc)(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2019-09-17 17:24:22 +08:00
|
|
|
typedef const char *(*parseFunc)(MacroBuf mb, const char * se);
|
2019-01-15 20:19:06 +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,
|
2014-02-28 21:57:09 +08:00
|
|
|
const char * n, const char * o, const char * b, int level, int flags);
|
2013-05-22 14:44:34 +08:00
|
|
|
static void popMacro(rpmMacroContext mc, const char * n);
|
2014-01-30 20:24:05 +08:00
|
|
|
static int loadMacroFile(rpmMacroContext mc, const char * fn);
|
2020-02-11 18:58:25 +08:00
|
|
|
static void doBody(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2020-02-10 19:28:03 +08:00
|
|
|
static void doExpand(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2019-01-15 20:19:06 +08:00
|
|
|
static void doFoo(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2019-01-15 21:30:47 +08:00
|
|
|
static void doLoad(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2019-01-15 20:41:02 +08:00
|
|
|
static void doLua(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2019-01-15 20:58:08 +08:00
|
|
|
static void doOutput(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2020-02-10 19:15:06 +08:00
|
|
|
static void doSP(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2019-01-15 21:39:18 +08:00
|
|
|
static void doTrace(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2020-02-10 18:31:21 +08:00
|
|
|
static void doUncompress(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2020-02-10 19:47:27 +08:00
|
|
|
static void doVerbose(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn);
|
2001-10-18 00:43:36 +08:00
|
|
|
|
2019-09-17 17:24:22 +08:00
|
|
|
static const char * doDef(MacroBuf mb, const char * se);
|
|
|
|
static const char * doGlobal(MacroBuf mb, const char * se);
|
|
|
|
static const char * doDump(MacroBuf mb, const char * se);
|
|
|
|
static const char * doUndefine(MacroBuf mb, const char * se);
|
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)
|
2015-02-17 22:29:59 +08:00
|
|
|
* @param f file handle
|
2019-05-17 17:13:41 +08:00
|
|
|
* @return number of lines read, or 0 on end-of-file
|
2000-09-30 03:50:29 +08:00
|
|
|
*/
|
2019-05-17 17:13:41 +08:00
|
|
|
static int
|
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;
|
2019-09-18 21:31:08 +08:00
|
|
|
int pc = 0, bc = 0, xc = 0;
|
2019-05-17 17:13:41 +08:00
|
|
|
int nlines = 0;
|
2004-04-09 04:27:53 +08:00
|
|
|
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;
|
2019-05-17 17:13:41 +08:00
|
|
|
nlines++;
|
2001-05-04 05:00:18 +08:00
|
|
|
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;
|
2019-09-18 21:31:08 +08:00
|
|
|
case '[': p++, xc++; break;
|
2007-09-11 22:48:54 +08:00
|
|
|
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;
|
2019-09-18 21:31:08 +08:00
|
|
|
case '[': if (xc > 0) xc++; break;
|
|
|
|
case ']': if (xc > 0) xc--; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
|
|
|
}
|
2019-09-18 21:31:08 +08:00
|
|
|
if (nb == 0 || (*q != '\\' && !bc && !pc && !xc) || *(q+1) == '\0') {
|
2001-05-04 05:00:18 +08:00
|
|
|
*(++q) = '\0'; /* trim trailing \r, \n */
|
|
|
|
break;
|
|
|
|
}
|
2018-03-27 18:29:56 +08:00
|
|
|
q++; 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);
|
2019-05-17 17:13:41 +08:00
|
|
|
return nlines;
|
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.
|
2019-09-18 19:49:55 +08:00
|
|
|
* @return address of char after pr (or NULL)
|
2000-09-30 03:50:29 +08:00
|
|
|
*/
|
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) {
|
2019-09-18 19:49:55 +08:00
|
|
|
if (--lvl <= 0) return p;
|
2001-06-06 03:26:22 +08:00
|
|
|
} else if (c == pl)
|
|
|
|
lvl++;
|
|
|
|
}
|
|
|
|
return (const char *)NULL;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2019-05-17 14:02:58 +08:00
|
|
|
static void mbErr(MacroBuf mb, int error, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
char *emsg = NULL;
|
|
|
|
int n;
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
n = rvasprintf(&emsg, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
if (n >= -1) {
|
2019-05-17 17:26:58 +08:00
|
|
|
/* XXX should have an non-locking version for this */
|
|
|
|
char *pfx = rpmExpand("%{?__file_name:%{__file_name}: }",
|
|
|
|
"%{?__file_lineno:line %{__file_lineno}: }",
|
|
|
|
NULL);
|
|
|
|
rpmlog(error ? RPMLOG_ERR : RPMLOG_WARNING, "%s%s", pfx, emsg);
|
|
|
|
free(pfx);
|
2019-05-17 14:02:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
mb->error = error;
|
|
|
|
|
|
|
|
free(emsg);
|
|
|
|
}
|
|
|
|
|
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;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
if (s >= se) { /* XXX just in case */
|
2017-08-29 16:03:53 +08:00
|
|
|
fprintf(stderr, _("%3d>%*s(empty)\n"), mb->depth,
|
2001-06-06 03:26:22 +08:00
|
|
|
(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++)
|
|
|
|
{};
|
|
|
|
|
|
|
|
/* Substitute caret at end-of-macro position */
|
|
|
|
fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
|
|
|
|
(2 * mb->depth + 1), "", (int)(se - s), s);
|
Bounds check strings to print correctly in %trace mode.
In %trace mode, evaluating a macro which is undefined causes an invalid
read of 1 byte when searching for the end of the string:
trillian:~$ valgrind rpmspec --eval '%trace' --eval '%{?myUndefinedMacro}'
==21534== Memcheck, a memory error detector
==21534== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==21534== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==21534== Command: rpmspec --trace --eval %{?myUndefinedMacro}
==21534==
1> %{?myUndefinedMacro}^==21534== Invalid read of size 1
==21534== at 0x55018D4: printMacro (macro.c:296)
==21534== by 0x5502DFC: expandMacro (macro.c:1077)
==21534== by 0x5503710: doExpandMacros (macro.c:1280)
==21534== by 0x5504AB6: rpmExpand (macro.c:1629)
==21534== by 0x508F59A: rpmcliAllArgCallback (poptALL.c:120)
==21534== by 0x6DAF71D: invokeCallbacksOPTION (popt.c:156)
==21534== by 0x6DAF75B: invokeCallbacksOPTION (popt.c:139)
==21534== by 0x6DB1428: poptGetNextOpt (popt.c:1515)
==21534== by 0x508F912: rpmcliInit (poptALL.c:302)
==21534== by 0x1095B2: main (rpmspec.c:63)
==21534== Address 0x8a010f3 is 0 bytes after a block of size 19 alloc'd
==21534== at 0x4C2DB9D: malloc (vg_replace_malloc.c:299)
==21534== by 0x5507C17: rmalloc (rpmmalloc.c:44)
==21534== by 0x5502788: expandMacro (macro.c:927)
==21534== by 0x5503710: doExpandMacros (macro.c:1280)
==21534== by 0x5504AB6: rpmExpand (macro.c:1629)
==21534== by 0x508F59A: rpmcliAllArgCallback (poptALL.c:120)
==21534== by 0x6DAF71D: invokeCallbacksOPTION (popt.c:156)
==21534== by 0x6DAF75B: invokeCallbacksOPTION (popt.c:139)
==21534== by 0x6DB1428: poptGetNextOpt (popt.c:1515)
==21534== by 0x508F912: rpmcliInit (poptALL.c:302)
==21534== by 0x1095B2: main (rpmspec.c:63)
==21534==
1> %{?_transaction_color}^
1> %{?_prefer_color}^
1> %{_netsharedpath}^
1> %{_install_langs}^
==21534==
==21534== HEAP SUMMARY:
==21534== in use at exit: 7,183 bytes in 71 blocks
==21534== total heap usage: 7,811 allocs, 7,740 frees, 3,500,361 bytes allocated
==21534==
==21534== LEAK SUMMARY:
==21534== definitely lost: 19 bytes in 1 blocks
==21534== indirectly lost: 0 bytes in 0 blocks
==21534== possibly lost: 0 bytes in 0 blocks
==21534== still reachable: 7,164 bytes in 70 blocks
==21534== suppressed: 0 bytes in 0 blocks
==21534== Rerun with --leak-check=full to see details of leaked memory
==21534==
==21534== For counts of detected and suppressed errors, rerun with: -v
==21534== ERROR SUMMARY: 5 errors from 1 contexts (suppressed: 0 from 0)
trillian:~$
This can easily be avoided by checking the first byte as well as the
second for our sentinal value (NUL).
Signed-off-by: Peter Jones <pjones@redhat.com>
2017-02-21 00:08:23 +08:00
|
|
|
if (se[0] != '\0' && se[1] != '\0' && (senl - (se+1)) > 0)
|
2017-02-18 01:12:37 +08:00
|
|
|
fprintf(stderr, "%-.*s", (int)(senl - (se+1)), se+1);
|
2001-06-06 03:26:22 +08:00
|
|
|
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
|
|
|
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--;
|
|
|
|
if (mb->depth > 0) {
|
|
|
|
const char *tenl;
|
|
|
|
|
|
|
|
/* Skip to last line of expansion */
|
|
|
|
while ((tenl = strchr(t, '\n')) && tenl < te)
|
|
|
|
t = ++tenl;
|
|
|
|
|
|
|
|
}
|
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)
|
2017-02-18 01:12:37 +08:00
|
|
|
rpmlog(RPMLOG_DEBUG, "%.*s", (int)(te - t), t);
|
2010-07-13 19:35:22 +08:00
|
|
|
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); \
|
2017-02-27 23:37:18 +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
|
|
|
{ \
|
2017-02-27 23:37:18 +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())
|
2020-10-28 15:50:45 +08:00
|
|
|
* @param[out] 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;
|
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;
|
2017-01-19 19:52:30 +08:00
|
|
|
umb.error = 0;
|
|
|
|
/* In case of error, flag it in the "parent"... */
|
|
|
|
if (expandMacro(&umb, src, slen))
|
|
|
|
mb->error = 1;
|
2010-09-27 21:03:56 +08:00
|
|
|
*target = umb.buf;
|
2008-02-01 18:11:03 +08:00
|
|
|
|
2017-01-19 19:52:30 +08:00
|
|
|
/* ...but return code for this operation specifically */
|
|
|
|
return umb.error;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2020-10-23 20:31:56 +08:00
|
|
|
static void mbAllocBuf(MacroBuf mb, size_t slen)
|
|
|
|
{
|
|
|
|
size_t blen = MACROBUFSIZ + slen;
|
|
|
|
mb->buf = xmalloc(blen + 1);
|
|
|
|
mb->buf[0] = '\0';
|
|
|
|
mb->tpos = 0;
|
|
|
|
mb->nb = blen;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
2017-10-06 04:50:54 +08:00
|
|
|
|
2019-09-17 17:24:22 +08:00
|
|
|
static const char * doDnl(MacroBuf mb, const char * se)
|
2019-06-18 18:10:55 +08:00
|
|
|
{
|
|
|
|
const char *s = se;
|
|
|
|
while (*s && !iseol(*s))
|
|
|
|
s++;
|
|
|
|
return (*s != '\0') ? s + 1 : s;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
*/
|
2017-01-19 19:52:30 +08:00
|
|
|
static void
|
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;
|
|
|
|
int c;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2017-01-19 19:52:30 +08:00
|
|
|
if (expandThis(mb, cmd, clen, &buf))
|
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) {
|
2019-09-25 16:39:11 +08:00
|
|
|
mbErr(mb, 1, _("Failed to open shell expansion pipe for command: "
|
|
|
|
"%s: %m \n"), buf);
|
2008-02-01 19:00:40 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
2013-02-07 11:44:41 +08:00
|
|
|
|
|
|
|
size_t tpos = mb->tpos;
|
2017-02-27 23:37:18 +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);
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
|
|
|
|
2019-09-18 21:31:08 +08:00
|
|
|
/**
|
|
|
|
* Expand an expression into target buffer.
|
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param expr expression
|
|
|
|
* @param len no. bytes in expression
|
|
|
|
*/
|
|
|
|
static void doExpressionExpansion(MacroBuf mb, const char * expr, size_t len)
|
|
|
|
{
|
|
|
|
char *buf = xmalloc(len + 1);
|
|
|
|
char *result;
|
|
|
|
strncpy(buf, expr, len);
|
|
|
|
buf[len] = 0;
|
2020-03-23 16:54:58 +08:00
|
|
|
result = rpmExprStrFlags(buf, RPMEXPR_EXPAND);
|
2019-09-18 21:31:08 +08:00
|
|
|
if (!result) {
|
|
|
|
mb->error = 1;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
mbAppendStr(mb, result);
|
|
|
|
free(result);
|
|
|
|
exit:
|
|
|
|
_free(buf);
|
|
|
|
}
|
2018-01-06 04:16:41 +08:00
|
|
|
|
2019-01-15 17:16:04 +08:00
|
|
|
static unsigned int getncpus(void)
|
|
|
|
{
|
|
|
|
unsigned int ncpus = 0;
|
|
|
|
#if HAVE_SCHED_GETAFFINITY
|
|
|
|
cpu_set_t set;
|
|
|
|
if (sched_getaffinity (0, sizeof(set), &set) == 0)
|
|
|
|
ncpus = CPU_COUNT(&set);
|
|
|
|
#endif
|
|
|
|
/* Fallback to sysconf() if the above isn't supported or didn't work */
|
|
|
|
if (ncpus < 1)
|
|
|
|
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
/* If all else fails, there's always the one we're running on... */
|
|
|
|
if (ncpus < 1)
|
|
|
|
ncpus = 1;
|
|
|
|
return ncpus;
|
|
|
|
}
|
|
|
|
|
2019-01-15 20:19:06 +08:00
|
|
|
#define STR_AND_LEN(_str) (_str), sizeof((_str))-1
|
2019-01-15 19:36:46 +08:00
|
|
|
|
2018-01-06 04:16:41 +08:00
|
|
|
/* Names in the table must be in ASCII-code order */
|
|
|
|
static struct builtins_s {
|
|
|
|
const char * name;
|
2019-01-15 19:36:46 +08:00
|
|
|
size_t len;
|
2019-01-15 20:19:06 +08:00
|
|
|
macroFunc func;
|
2019-01-15 22:28:10 +08:00
|
|
|
parseFunc parse;
|
2019-09-23 21:41:19 +08:00
|
|
|
int havearg;
|
2018-01-06 04:16:41 +08:00
|
|
|
} const builtinmacros[] = {
|
2020-02-10 19:15:06 +08:00
|
|
|
{ STR_AND_LEN("P"), doSP, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("S"), doSP, NULL, 1 },
|
2019-09-23 21:41:19 +08:00
|
|
|
{ STR_AND_LEN("basename"), doFoo, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("define"), NULL, doDef, 0 },
|
|
|
|
{ STR_AND_LEN("dirname"), doFoo, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("dnl"), NULL, doDnl, 0 },
|
|
|
|
{ STR_AND_LEN("dump"), NULL, doDump, 0 },
|
|
|
|
{ STR_AND_LEN("echo"), doOutput, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("error"), doOutput, NULL, 1 },
|
2020-02-10 19:28:03 +08:00
|
|
|
{ STR_AND_LEN("expand"), doExpand, NULL, 1 },
|
2019-09-23 21:41:19 +08:00
|
|
|
{ STR_AND_LEN("expr"), doFoo, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("getconfdir"),doFoo, NULL, 0 },
|
|
|
|
{ STR_AND_LEN("getenv"), doFoo, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("getncpus"), doFoo, NULL, 0 },
|
|
|
|
{ STR_AND_LEN("global"), NULL, doGlobal, 0 },
|
|
|
|
{ STR_AND_LEN("load"), doLoad, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("lua"), doLua, NULL, 1 },
|
2020-02-11 18:58:25 +08:00
|
|
|
{ STR_AND_LEN("macrobody"), doBody, NULL, 1 },
|
2019-09-23 21:41:19 +08:00
|
|
|
{ STR_AND_LEN("quote"), doFoo, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("shrink"), doFoo, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("suffix"), doFoo, NULL, 1 },
|
|
|
|
{ STR_AND_LEN("trace"), doTrace, NULL, 0 },
|
|
|
|
{ STR_AND_LEN("u2p"), doFoo, NULL, 1 },
|
2020-02-10 18:31:21 +08:00
|
|
|
{ STR_AND_LEN("uncompress"),doUncompress, NULL, 1 },
|
2019-09-23 21:41:19 +08:00
|
|
|
{ STR_AND_LEN("undefine"), NULL, doUndefine, 0 },
|
|
|
|
{ STR_AND_LEN("url2path"), doFoo, NULL, 1 },
|
2020-02-10 19:47:27 +08:00
|
|
|
{ STR_AND_LEN("verbose"), doVerbose, NULL, 1 },
|
2019-09-23 21:41:19 +08:00
|
|
|
{ STR_AND_LEN("warn"), doOutput, NULL, 1 },
|
2018-01-06 04:16:41 +08:00
|
|
|
};
|
|
|
|
static const size_t numbuiltins = sizeof(builtinmacros)/sizeof(*builtinmacros);
|
|
|
|
|
|
|
|
static int namecmp(const void *name1, const void *name2)
|
|
|
|
{
|
|
|
|
struct builtins_s *n1 = (struct builtins_s *)name1;
|
|
|
|
struct builtins_s *n2 = (struct builtins_s *)name2;
|
|
|
|
|
2019-01-15 19:36:46 +08:00
|
|
|
int rc = strncmp(n1->name, n2->name, n1->len);
|
|
|
|
if (rc == 0)
|
|
|
|
rc = n1->len - n2->len;
|
|
|
|
return rc;
|
2018-01-06 04:16:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a pointer to the built-in macro with the given name
|
|
|
|
* @param name macro name
|
2019-01-15 19:36:46 +08:00
|
|
|
* @param nlen name length
|
2018-01-06 04:16:41 +08:00
|
|
|
* @return pointer to the built-in macro or NULL if not found
|
|
|
|
*/
|
2019-01-15 19:36:46 +08:00
|
|
|
static const struct builtins_s* lookupBuiltin(const char *name, size_t nlen)
|
2018-01-06 04:16:41 +08:00
|
|
|
{
|
2019-01-15 19:36:46 +08:00
|
|
|
struct builtins_s macro = {
|
|
|
|
.name = name,
|
|
|
|
.len = nlen,
|
|
|
|
};
|
2018-01-06 04:16:41 +08:00
|
|
|
|
|
|
|
return bsearch(¯o, builtinmacros, numbuiltins, sizeof(*builtinmacros),
|
|
|
|
namecmp);
|
|
|
|
}
|
|
|
|
|
2019-10-30 21:28:26 +08:00
|
|
|
static int
|
2019-05-17 14:02:58 +08:00
|
|
|
validName(MacroBuf mb, const char *name, size_t namelen, const char *action)
|
|
|
|
{
|
2017-12-12 18:57:16 +08:00
|
|
|
int rc = 0;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
/* Names must start with alphabetic or _ and be at least 3 chars */
|
|
|
|
if (!((c = *name) && (risalpha(c) || c == '_') && (namelen) > 2)) {
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1, _("Macro %%%s has illegal name (%s)\n"), name, action);
|
2017-12-12 18:57:16 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2019-01-15 19:36:46 +08:00
|
|
|
if (lookupBuiltin(name, namelen)) {
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1, _("Macro %%%s is a built-in (%s)\n"), name, action);
|
2018-01-06 04:16:41 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2017-12-12 18:57:16 +08:00
|
|
|
rc = 1;
|
|
|
|
|
|
|
|
exit:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
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 *
|
2019-09-17 17:24:22 +08:00
|
|
|
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;
|
2019-09-17 17:24:22 +08:00
|
|
|
char *buf = xmalloc(strlen(s) + 3); /* Some leeway for termination issues... */
|
2008-02-01 19:00:40 +08:00
|
|
|
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 */
|
2017-01-19 20:15:38 +08:00
|
|
|
int rc = 1; /* assume failure */
|
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 ( */
|
2014-06-11 20:28:36 +08:00
|
|
|
/* Options must be terminated with ')' */
|
|
|
|
if (strchr(s, ')')) {
|
|
|
|
o = oe;
|
|
|
|
COPYOPTS(oe, s, oc);
|
|
|
|
s++; /* skip ) */
|
|
|
|
} else {
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1, _("Macro %%%s has unterminated opts\n"), n);
|
2014-06-11 20:28:36 +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
|
|
|
/* 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) {
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1, _("Macro %%%s has unterminated body\n"), n);
|
2001-06-06 03:26:22 +08:00
|
|
|
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 { */
|
2019-09-18 19:49:55 +08:00
|
|
|
strncpy(b, s, (se - 1 - s));
|
|
|
|
b[se - 1 - s] = '\0';
|
2001-06-06 03:26:22 +08:00
|
|
|
be += strlen(b);
|
|
|
|
s = se; /* move scan forward */
|
|
|
|
} else { /* otherwise free-field */
|
2019-09-18 21:31:08 +08:00
|
|
|
int bc = 0, pc = 0, xc = 0;
|
2004-04-09 04:27:53 +08:00
|
|
|
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;
|
2019-09-18 21:31:08 +08:00
|
|
|
case '[': *be++ = *s++; xc++; break;
|
2007-09-11 22:48:54 +08:00
|
|
|
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;
|
2019-09-18 21:31:08 +08:00
|
|
|
case '[': if (xc > 0) xc++; break;
|
|
|
|
case ']': if (xc > 0) xc--; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
|
|
|
*be++ = *s++;
|
|
|
|
}
|
|
|
|
*be = '\0';
|
|
|
|
|
2019-09-18 21:31:08 +08:00
|
|
|
if (bc || pc || xc) {
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1, _("Macro %%%s has unterminated body\n"), n);
|
2004-04-09 04:27:53 +08:00
|
|
|
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
|
|
|
|
2019-05-17 14:02:58 +08:00
|
|
|
if (!validName(mb, n, ne - n, expandbody ? "%global": "%define"))
|
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
|
|
|
if ((be - b) < 1) {
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1, _("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])))
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 0, _("Macro %%%s needs whitespace before body\n"), n);
|
2013-01-19 20:11:11 +08:00
|
|
|
|
2010-09-27 21:03:56 +08:00
|
|
|
if (expandbody) {
|
|
|
|
if (expandThis(mb, b, 0, &ebody)) {
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1, _("Macro %%%s failed to expand\n"), n);
|
2010-09-27 21:03:56 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
b = ebody;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
Shift macro scoping level values as they are defined
Some of scoping level values are defined in rpmmacros.h:
#define RMIL_DEFAULT -15
#define RMIL_MACROFILES -13
...
#define RMIL_GLOBAL 0
Before this patch RPM set these values decreased of 1 (for all macros).
Because of the (level - 1) in
pushMacro(dst, me->name, me->opts, me->body, (level - 1), me->flags);
in function copyMacros scoping level for some CMDLINE macros was decreased of 1.
Because of the (level - 1) in
pushMacro(mb->mc, n, o, b, (level - 1), ME_NONE);
in function doDefine scoping level values of all other macro types and
some CMDLINE macros was decreased of 1.
The patch shifts scoping level values back to the numbers defined
in rpmmacros.h.
This was spotted when investigating a regression introduced in
commit 237f35f16f978bb20e4cafd78abfab1f2e5db480 where locally scoped
scoped macros did not get deleted on scope closing.
This also fixes the regression in question.
The first test in this patch tests automatic deleting of %defined macros.
The second test checks whether the behavior changed by
237f35f16f978bb20e4cafd78abfab1f2e5db480 is still working correctly.
Thanks to Panu Matilainen for investigation and proposal of the main part
of the patch!
2017-08-09 19:05:11 +08:00
|
|
|
pushMacro(mb->mc, n, o, b, level, ME_NONE);
|
2017-01-19 20:15:38 +08:00
|
|
|
rc = 0;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2008-02-01 19:00:40 +08:00
|
|
|
exit:
|
2017-01-19 20:15:38 +08:00
|
|
|
if (rc)
|
|
|
|
mb->error = 1;
|
2008-02-01 19:00:40 +08:00
|
|
|
_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.
|
2017-01-19 20:13:15 +08:00
|
|
|
* @param mb macro expansion state
|
2000-09-30 03:50:29 +08:00
|
|
|
* @param se macro name to undefine
|
|
|
|
* @return address to continue parsing
|
|
|
|
*/
|
2007-09-11 22:48:54 +08:00
|
|
|
static const char *
|
2019-09-17 17:24:22 +08:00
|
|
|
doUndefine(MacroBuf mb, const char * se)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
const char *s = se;
|
2019-09-17 17:24:22 +08:00
|
|
|
char *buf = xmalloc(strlen(s) + 1);
|
2008-02-01 19:00:40 +08:00
|
|
|
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
|
|
|
|
2019-05-17 14:02:58 +08:00
|
|
|
if (!validName(mb, n, ne - n, "%undefine")) {
|
2017-01-19 20:15:38 +08:00
|
|
|
mb->error = 1;
|
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
|
|
|
|
2017-01-19 20:13:15 +08:00
|
|
|
popMacro(mb->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
|
|
|
}
|
|
|
|
|
2019-09-17 17:24:22 +08:00
|
|
|
static const char * doDef(MacroBuf mb, const char * se)
|
2019-01-15 22:28:10 +08:00
|
|
|
{
|
2019-09-17 17:24:22 +08:00
|
|
|
return doDefine(mb, se, mb->level, 0);
|
2019-01-15 22:28:10 +08:00
|
|
|
}
|
|
|
|
|
2019-09-17 17:24:22 +08:00
|
|
|
static const char * doGlobal(MacroBuf mb, const char * se)
|
2019-01-15 22:28:10 +08:00
|
|
|
{
|
2019-09-17 17:24:22 +08:00
|
|
|
return doDefine(mb, se, RMIL_GLOBAL, 1);
|
2019-01-15 22:28:10 +08:00
|
|
|
}
|
|
|
|
|
2019-09-17 17:24:22 +08:00
|
|
|
static const char * doDump(MacroBuf mb, const char * se)
|
2019-01-15 22:28:10 +08:00
|
|
|
{
|
|
|
|
rpmDumpMacroTable(mb->mc, NULL);
|
|
|
|
while (iseol(*se))
|
|
|
|
se++;
|
|
|
|
return se;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
2017-08-22 15:24:21 +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++) {
|
|
|
|
rpmMacroEntry me = mc->tab[i];
|
|
|
|
assert(me);
|
2017-03-07 19:02:26 +08:00
|
|
|
if (me->level < mb->level)
|
2001-05-04 05:00:18 +08:00
|
|
|
continue;
|
2014-02-28 22:45:33 +08:00
|
|
|
/* Warn on defined but unused non-automatic, scoped macros */
|
2014-02-28 22:10:14 +08:00
|
|
|
if (!(me->flags & (ME_AUTO|ME_USED))) {
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 0, _("Macro %%%s defined but not used within scope\n"),
|
2014-02-28 22:45:33 +08:00
|
|
|
me->name);
|
|
|
|
/* Only whine once */
|
|
|
|
me->flags |= ME_USED;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2017-03-07 19:02:26 +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
|
|
|
/* 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
|
|
|
}
|
2017-03-07 19:02:26 +08:00
|
|
|
mb->level--;
|
2020-10-08 16:10:40 +08:00
|
|
|
mb->args = argvFree(mb->args);
|
|
|
|
mb->me = NULL;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2017-08-31 17:19:58 +08:00
|
|
|
static void splitQuoted(ARGV_t *av, const char * str, const char * seps)
|
|
|
|
{
|
|
|
|
const int qchar = 0x1f; /* ASCII unit separator */
|
|
|
|
const char *s = str;
|
|
|
|
const char *start = str;
|
|
|
|
int quoted = 0;
|
|
|
|
|
|
|
|
while (start != NULL) {
|
2017-09-01 16:17:48 +08:00
|
|
|
if (!quoted && strchr(seps, *s)) {
|
2017-09-01 15:39:56 +08:00
|
|
|
size_t slen = s - start;
|
|
|
|
/* quoted arguments are always kept, otherwise skip empty args */
|
|
|
|
if (slen > 0) {
|
2020-02-07 18:14:52 +08:00
|
|
|
char *d, arg[slen + 1];
|
2017-09-01 15:39:56 +08:00
|
|
|
const char *t;
|
|
|
|
for (d = arg, t = start; t - start < slen; t++) {
|
|
|
|
if (*t == qchar)
|
|
|
|
continue;
|
|
|
|
*d++ = *t;
|
|
|
|
}
|
|
|
|
arg[d - arg] = '\0';
|
2017-08-31 17:19:58 +08:00
|
|
|
argvAdd(av, arg);
|
|
|
|
}
|
|
|
|
start = s + 1;
|
|
|
|
}
|
|
|
|
if (*s == qchar)
|
|
|
|
quoted = !quoted;
|
|
|
|
else if (*s == '\0')
|
|
|
|
start = NULL;
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-09 19:02:20 +08:00
|
|
|
static int mbopt(int c, const char *oarg, int oint, void *data)
|
|
|
|
{
|
|
|
|
MacroBuf mb = data;
|
|
|
|
char *name = NULL, *body = NULL;
|
|
|
|
|
|
|
|
/* Define option macros. */
|
|
|
|
rasprintf(&name, "-%c", c);
|
|
|
|
if (oarg) {
|
|
|
|
rasprintf(&body, "-%c %s", c, oarg);
|
|
|
|
} else {
|
|
|
|
rasprintf(&body, "-%c", c);
|
|
|
|
}
|
|
|
|
pushMacro(mb->mc, name, NULL, body, mb->level, ME_AUTO | ME_LITERAL);
|
|
|
|
free(name);
|
|
|
|
free(body);
|
|
|
|
|
|
|
|
if (oarg) {
|
|
|
|
rasprintf(&name, "-%c*", c);
|
|
|
|
pushMacro(mb->mc, name, NULL, oarg, mb->level, ME_AUTO | ME_LITERAL);
|
|
|
|
free(name);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
2020-10-23 20:51:04 +08:00
|
|
|
* Setup arguments for parameterized macro.
|
2000-09-30 03:50:29 +08:00
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param me macro entry slot
|
|
|
|
* @param se arguments to parse
|
2020-10-23 20:51:04 +08:00
|
|
|
* @param argv parsed arguments for the macro
|
|
|
|
* Note that the call takes ownership of the argv
|
2000-09-30 03:50:29 +08:00
|
|
|
*/
|
2020-10-23 20:51:04 +08:00
|
|
|
static void
|
|
|
|
setupArgs(MacroBuf mb, const rpmMacroEntry me, ARGV_t argv)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2008-04-18 19:54:58 +08:00
|
|
|
char *args = NULL;
|
2020-10-23 20:51:04 +08:00
|
|
|
int argc;
|
|
|
|
int ind;
|
1998-07-09 01:50:48 +08:00
|
|
|
int c;
|
|
|
|
|
2017-03-07 19:02:26 +08:00
|
|
|
/* Bump call depth on entry before first macro define */
|
|
|
|
mb->level++;
|
|
|
|
|
2017-03-07 18:54:09 +08:00
|
|
|
/* Setup macro name as %0 */
|
2020-02-07 18:14:52 +08:00
|
|
|
pushMacro(mb->mc, "0", NULL, me->name, mb->level, ME_AUTO | ME_LITERAL);
|
2017-03-07 18:54:09 +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, " ");
|
2020-02-07 18:14:52 +08:00
|
|
|
pushMacro(mb->mc, "**", NULL, args, mb->level, ME_AUTO | ME_LITERAL);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(args);
|
|
|
|
|
|
|
|
argc = argvCount(argv);
|
2020-10-09 19:02:20 +08:00
|
|
|
ind = rgetopt(argc, argv, me->opts, mbopt, mb);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2020-10-09 19:02:20 +08:00
|
|
|
if (ind < 0) {
|
|
|
|
mbErr(mb, 1, _("Unknown option %c in %s(%s)\n"), -ind,
|
|
|
|
me->name, me->opts);
|
|
|
|
goto exit;
|
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;
|
2020-10-09 19:02:20 +08:00
|
|
|
rasprintf(&ac, "%d", (argc - ind));
|
2020-02-07 18:14:52 +08:00
|
|
|
pushMacro(mb->mc, "#", NULL, ac, mb->level, ME_AUTO | ME_LITERAL);
|
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 */
|
2020-10-09 19:02:20 +08:00
|
|
|
if (argc - ind) {
|
|
|
|
for (c = ind; c < argc; c++) {
|
2008-04-18 19:54:58 +08:00
|
|
|
char *name = NULL;
|
2020-10-09 19:02:20 +08:00
|
|
|
rasprintf(&name, "%d", (c - ind + 1));
|
2020-02-07 18:14:52 +08:00
|
|
|
pushMacro(mb->mc, name, NULL, argv[c], mb->level, ME_AUTO | ME_LITERAL);
|
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. */
|
2020-10-09 19:02:20 +08:00
|
|
|
args = argvJoin(argv + ind, " ");
|
2020-02-07 18:14:52 +08:00
|
|
|
pushMacro(mb->mc, "*", NULL, args ? args : "", mb->level, ME_AUTO | ME_LITERAL);
|
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:
|
2020-10-08 16:10:40 +08:00
|
|
|
mb->me = me;
|
|
|
|
mb->args = argv;
|
2020-10-23 20:51:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse arguments (to next new line) for parameterized macro.
|
|
|
|
* @todo Use popt rather than getopt to parse args.
|
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param me macro entry slot
|
2020-10-23 21:12:03 +08:00
|
|
|
* @param argvp pointer to argv array to store the result
|
2020-10-23 20:51:04 +08:00
|
|
|
* @param se arguments to parse
|
|
|
|
* @param lastc stop parsing at lastc
|
|
|
|
* @return address to continue parsing
|
|
|
|
*/
|
|
|
|
static const char *
|
2020-10-23 21:12:03 +08:00
|
|
|
grabArgs(MacroBuf mb, ARGV_t *argvp, const char * se,
|
2020-10-23 20:51:04 +08:00
|
|
|
const char * lastc)
|
|
|
|
{
|
2020-10-23 21:12:03 +08:00
|
|
|
char *s = NULL;
|
|
|
|
/* Expand possible macros in arguments */
|
|
|
|
expandThis(mb, se, lastc-se, &s);
|
|
|
|
splitQuoted(argvp, s, " \t");
|
|
|
|
free(s);
|
|
|
|
return (*lastc == '\0') || (*lastc == '\n' && *(lastc-1) != '\\') ?
|
|
|
|
lastc : lastc + 1;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2020-02-11 18:58:25 +08:00
|
|
|
static void doBody(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn)
|
|
|
|
{
|
|
|
|
if (gn > 0) {
|
|
|
|
char *buf = NULL;
|
|
|
|
if (expandThis(mb, g, gn, &buf) == 0) {
|
|
|
|
rpmMacroEntry *mep = findEntry(mb->mc, buf, 0, NULL);
|
|
|
|
if (mep) {
|
|
|
|
mbAppendStr(mb, (*mep)->body);
|
|
|
|
} else {
|
|
|
|
mbErr(mb, 1, _("no such macro: '%s'\n"), buf);
|
|
|
|
}
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 20:58:08 +08:00
|
|
|
static void doOutput(MacroBuf mb, int chkexist, int negate, const char * f, size_t fn, const char * g, size_t gn)
|
1998-01-13 05:31:29 +08:00
|
|
|
{
|
2010-09-27 21:03:56 +08:00
|
|
|
char *buf = NULL;
|
2019-01-15 20:58:08 +08:00
|
|
|
int loglevel = RPMLOG_NOTICE; /* assume echo */
|
|
|
|
if (STREQ("error", f, fn)) {
|
|
|
|
loglevel = RPMLOG_ERR;
|
|
|
|
mb->error = 1;
|
|
|
|
} else if (STREQ("warn", f, fn)) {
|
|
|
|
loglevel = RPMLOG_WARNING;
|
|
|
|
}
|
|
|
|
if (gn == 0)
|
|
|
|
g = "";
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2019-01-15 20:58:08 +08:00
|
|
|
(void) expandThis(mb, g, gn, &buf);
|
2017-08-17 18:00:23 +08:00
|
|
|
rpmlog(loglevel, "%s\n", buf);
|
2008-02-01 19:00:40 +08:00
|
|
|
_free(buf);
|
1998-01-13 05:31:29 +08:00
|
|
|
}
|
|
|
|
|
2019-01-15 20:41:02 +08:00
|
|
|
static void doLua(MacroBuf mb, int chkexist, int negate, const char * f, size_t fn, const char * g, size_t gn)
|
2017-01-19 18:09:10 +08:00
|
|
|
{
|
|
|
|
#ifdef WITH_LUA
|
|
|
|
rpmlua lua = NULL; /* Global state. */
|
|
|
|
char *scriptbuf = xmalloc(gn + 1);
|
|
|
|
char *printbuf;
|
2017-01-20 20:55:59 +08:00
|
|
|
rpmMacroContext mc = mb->mc;
|
2020-10-08 16:10:40 +08:00
|
|
|
rpmMacroEntry me = mb->me;
|
2017-01-20 20:55:59 +08:00
|
|
|
int odepth = mc->depth;
|
2017-03-07 19:02:26 +08:00
|
|
|
int olevel = mc->level;
|
2020-10-08 16:10:40 +08:00
|
|
|
const char *opts = NULL;
|
|
|
|
const char *name = NULL;
|
|
|
|
ARGV_t args = NULL;
|
|
|
|
|
|
|
|
if (me) {
|
|
|
|
opts = me->opts;
|
|
|
|
name = me->name;
|
|
|
|
if (mb->args)
|
|
|
|
args = mb->args;
|
|
|
|
}
|
2017-01-19 18:09:10 +08:00
|
|
|
|
|
|
|
if (g != NULL && gn > 0)
|
|
|
|
memcpy(scriptbuf, g, gn);
|
|
|
|
scriptbuf[gn] = '\0';
|
|
|
|
rpmluaPushPrintBuffer(lua);
|
2017-01-20 20:55:59 +08:00
|
|
|
mc->depth = mb->depth;
|
2017-03-07 19:02:26 +08:00
|
|
|
mc->level = mb->level;
|
2020-10-08 16:10:40 +08:00
|
|
|
if (rpmluaRunScript(lua, scriptbuf, name, opts, args) == -1)
|
2017-01-19 19:52:30 +08:00
|
|
|
mb->error = 1;
|
2017-01-20 20:55:59 +08:00
|
|
|
mc->depth = odepth;
|
2017-03-07 19:02:26 +08:00
|
|
|
mc->level = olevel;
|
2017-01-19 18:09:10 +08:00
|
|
|
printbuf = rpmluaPopPrintBuffer(lua);
|
|
|
|
if (printbuf) {
|
|
|
|
mbAppendStr(mb, printbuf);
|
|
|
|
free(printbuf);
|
|
|
|
}
|
|
|
|
free(scriptbuf);
|
|
|
|
#else
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1, _("<lua> scriptlet support not built in\n"));
|
2017-01-19 18:09:10 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:15:06 +08:00
|
|
|
static void
|
|
|
|
doSP(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn)
|
|
|
|
{
|
|
|
|
const char *b = "";
|
|
|
|
char *buf = NULL;
|
|
|
|
char *s = NULL;
|
|
|
|
|
|
|
|
if (gn > 0) {
|
|
|
|
expandThis(mb, g, gn, &buf);
|
|
|
|
b = buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
s = rstrscat(NULL, (*f == 'S') ? "%SOURCE" : "%PATCH", b, NULL);
|
|
|
|
expandMacro(mb, s, 0);
|
|
|
|
free(s);
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
|
2020-02-10 18:31:21 +08:00
|
|
|
static void doUncompress(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn)
|
|
|
|
{
|
|
|
|
rpmCompressedMagic compressed = COMPRESSED_OTHER;
|
|
|
|
char *b, *be, *buf = NULL;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
if (gn) {
|
|
|
|
expandThis(mb, g, gn, &buf);
|
|
|
|
for (b = buf; (c = *b) && isblank(c);)
|
|
|
|
b++;
|
|
|
|
for (be = b; (c = *be) && !isblank(c);)
|
|
|
|
be++;
|
|
|
|
*be++ = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gn == 0 || *b == '\0')
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
if (rpmFileIsCompressed(b, &compressed))
|
|
|
|
mb->error = 1;
|
|
|
|
|
|
|
|
switch (compressed) {
|
|
|
|
default:
|
|
|
|
case COMPRESSED_NOT:
|
|
|
|
expandMacro(mb, "%__cat ", 0);
|
|
|
|
break;
|
|
|
|
case COMPRESSED_OTHER:
|
|
|
|
expandMacro(mb, "%__gzip -dc ", 0);
|
|
|
|
break;
|
|
|
|
case COMPRESSED_BZIP2:
|
|
|
|
expandMacro(mb, "%__bzip2 -dc ", 0);
|
|
|
|
break;
|
|
|
|
case COMPRESSED_ZIP:
|
|
|
|
expandMacro(mb, "%__unzip ", 0);
|
|
|
|
break;
|
|
|
|
case COMPRESSED_LZMA:
|
|
|
|
case COMPRESSED_XZ:
|
|
|
|
expandMacro(mb, "%__xz -dc ", 0);
|
|
|
|
break;
|
|
|
|
case COMPRESSED_LZIP:
|
|
|
|
expandMacro(mb, "%__lzip -dc ", 0);
|
|
|
|
break;
|
|
|
|
case COMPRESSED_LRZIP:
|
|
|
|
expandMacro(mb, "%__lrzip -dqo- ", 0);
|
|
|
|
break;
|
|
|
|
case COMPRESSED_7ZIP:
|
|
|
|
expandMacro(mb, "%__7zip x ", 0);
|
|
|
|
break;
|
|
|
|
case COMPRESSED_ZSTD:
|
|
|
|
expandMacro(mb, "%__zstd -dc ", 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
mbAppendStr(mb, buf);
|
|
|
|
|
|
|
|
exit:
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:28:03 +08:00
|
|
|
static void doExpand(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn)
|
|
|
|
{
|
|
|
|
if (gn > 0) {
|
|
|
|
char *buf;
|
|
|
|
expandThis(mb, g, gn, &buf);
|
|
|
|
expandMacro(mb, buf, 0);
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-10 19:47:27 +08:00
|
|
|
static void doVerbose(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn)
|
|
|
|
{
|
|
|
|
int verbose = (rpmIsVerbose() != 0);
|
|
|
|
/* Don't expand %{verbose:...} argument on false condition */
|
|
|
|
if (verbose != negate) {
|
|
|
|
char *buf = NULL;
|
|
|
|
expandThis(mb, g, gn, &buf);
|
|
|
|
mbAppendStr(mb, buf);
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Execute macro primitives.
|
|
|
|
* @param mb macro expansion state
|
2019-01-15 20:19:06 +08:00
|
|
|
* @param chkexist unused
|
2000-09-30 03:50:29 +08:00
|
|
|
* @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
|
2019-01-15 20:19:06 +08:00
|
|
|
doFoo(MacroBuf mb, int chkexist, 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;
|
2020-02-10 18:31:21 +08:00
|
|
|
char *b = NULL;
|
2017-10-18 15:25:08 +08:00
|
|
|
int expand = (g != NULL && gn > 0);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2017-10-18 15:25:08 +08:00
|
|
|
if (expand) {
|
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;
|
2017-06-11 12:07:17 +08:00
|
|
|
} else if (STREQ("shrink", f, fn)) {
|
|
|
|
/*
|
|
|
|
* shrink body by removing all leading and trailing whitespaces and
|
|
|
|
* reducing intermediate whitespaces to a single space character.
|
|
|
|
*/
|
|
|
|
size_t i = 0, j = 0;
|
|
|
|
size_t buflen = strlen(buf);
|
|
|
|
int was_space = 0;
|
|
|
|
while (i < buflen) {
|
|
|
|
if (risspace(buf[i])) {
|
|
|
|
was_space = 1;
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
} else if (was_space) {
|
|
|
|
was_space = 0;
|
|
|
|
if (j > 0) /* remove leading blanks at all */
|
|
|
|
buf[j++] = ' ';
|
|
|
|
}
|
|
|
|
buf[j++] = buf[i++];
|
|
|
|
}
|
|
|
|
buf[j] = '\0';
|
|
|
|
b = buf;
|
2017-08-31 17:19:58 +08:00
|
|
|
} else if (STREQ("quote", f, fn)) {
|
|
|
|
char *quoted = NULL;
|
|
|
|
rasprintf("ed, "%c%s%c", 0x1f, buf, 0x1f);
|
|
|
|
free(buf);
|
|
|
|
b = buf = quoted;
|
2001-06-06 03:26:22 +08:00
|
|
|
} else if (STREQ("suffix", f, fn)) {
|
|
|
|
if ((b = strrchr(buf, '.')) != NULL)
|
|
|
|
b++;
|
2019-08-21 18:59:04 +08:00
|
|
|
} else if (STREQ("expr", f, fn)) {
|
2020-03-23 16:54:58 +08:00
|
|
|
char *expr = rpmExprStrFlags(buf, 0);
|
2019-08-21 18:59:04 +08:00
|
|
|
if (expr) {
|
|
|
|
free(buf);
|
|
|
|
b = buf = expr;
|
|
|
|
} else {
|
|
|
|
mb->error = 1;
|
|
|
|
}
|
2001-06-06 03:26:22 +08:00
|
|
|
} else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
|
|
|
|
(void)urlPath(buf, (const char **)&b);
|
|
|
|
if (*b == '\0') b = "/";
|
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;
|
2019-01-15 17:16:04 +08:00
|
|
|
} else if (STREQ("getncpus", f, fn)) {
|
|
|
|
sprintf(buf, "%u", getncpus());
|
|
|
|
b = buf;
|
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 (b) {
|
2020-02-10 19:42:19 +08:00
|
|
|
mbAppendStr(mb, b);
|
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
|
|
|
}
|
|
|
|
|
2019-01-15 21:30:47 +08:00
|
|
|
static void doLoad(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn)
|
|
|
|
{
|
|
|
|
char *arg = NULL;
|
|
|
|
if (g && gn > 0 && expandThis(mb, g, gn, &arg) == 0) {
|
|
|
|
/* Print failure iff %{load:...} or %{!?load:...} */
|
|
|
|
if (loadMacroFile(mb->mc, arg) && chkexist == negate) {
|
2019-07-04 21:15:41 +08:00
|
|
|
mbErr(mb, 1, _("failed to load macro file %s\n"), arg);
|
2019-01-15 21:30:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
free(arg);
|
|
|
|
}
|
|
|
|
|
2019-01-15 21:39:18 +08:00
|
|
|
static void doTrace(MacroBuf mb, int chkexist, int negate,
|
|
|
|
const char * f, size_t fn, const char * g, size_t gn)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-07 18:21:51 +08:00
|
|
|
static const char *setNegateAndCheck(const char *str, int *pnegate, int *pchkexist) {
|
|
|
|
|
|
|
|
*pnegate = 0;
|
|
|
|
*pchkexist = 0;
|
|
|
|
while ((*str == '?') || (*str == '!')) {
|
|
|
|
if (*str == '!')
|
|
|
|
*pnegate = !*pnegate;
|
|
|
|
else
|
|
|
|
(*pchkexist)++;
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2019-09-18 21:31:08 +08:00
|
|
|
/**
|
|
|
|
* Find the end of a macro call
|
|
|
|
* @param str pointer to the character after the initial '%'
|
|
|
|
* @return pointer to the next character after the macro
|
|
|
|
*/
|
|
|
|
RPM_GNUC_INTERNAL
|
|
|
|
const char *findMacroEnd(const char *str)
|
2019-09-18 19:49:55 +08:00
|
|
|
{
|
|
|
|
int c;
|
|
|
|
if (*str == '(')
|
|
|
|
return matchchar(str, *str, ')');
|
|
|
|
if (*str == '{')
|
|
|
|
return matchchar(str, *str, '}');
|
2019-09-18 21:31:08 +08:00
|
|
|
if (*str == '[')
|
|
|
|
return matchchar(str, *str, ']');
|
2019-09-18 19:49:55 +08:00
|
|
|
while (*str == '?' || *str == '!')
|
|
|
|
str++;
|
|
|
|
if (*str == '-') /* %-f */
|
|
|
|
str++;
|
|
|
|
while ((c = *str) && (risalnum(c) || c == '_'))
|
|
|
|
str++;
|
|
|
|
if (*str == '*' && str[1] == '*') /* %** */
|
|
|
|
str += 2;
|
|
|
|
else if (*str == '*' || *str == '#') /* %* and %# */
|
|
|
|
str++;
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
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
|
2015-02-17 22:29:59 +08:00
|
|
|
* @param slen length of string buffer
|
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 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;
|
2017-08-25 17:17:03 +08:00
|
|
|
int store_macro_trace;
|
|
|
|
int store_expand_trace;
|
2010-09-27 22:15:15 +08:00
|
|
|
|
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
|
|
|
|
2020-10-23 20:31:56 +08:00
|
|
|
if (mb->buf == NULL)
|
|
|
|
mbAllocBuf(mb, slen);
|
2010-09-27 18:05:37 +08:00
|
|
|
tpos = mb->tpos; /* save expansion pointer for printExpand */
|
2017-08-25 17:17:03 +08:00
|
|
|
store_macro_trace = mb->macro_trace;
|
|
|
|
store_expand_trace = mb->expand_trace;
|
2010-09-27 18:05:37 +08:00
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
if (++mb->depth > max_macro_depth) {
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1,
|
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;
|
2017-01-19 19:41:42 +08:00
|
|
|
goto exit;
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2017-01-19 19:52:30 +08:00
|
|
|
while (mb->error == 0 && (c = *s) != '\0') {
|
2019-01-15 20:19:06 +08:00
|
|
|
const struct builtins_s* builtin = NULL;
|
1998-07-09 01:50:48 +08:00
|
|
|
s++;
|
|
|
|
/* Copy text until next macro */
|
2017-02-27 23:37:18 +08:00
|
|
|
switch (c) {
|
1998-07-09 01:50:48 +08:00
|
|
|
case '%':
|
2017-01-19 17:26:52 +08:00
|
|
|
if (*s) { /* Ensure not end-of-string. */
|
|
|
|
if (*s != '%')
|
|
|
|
break;
|
|
|
|
s++; /* skip first % in %% */
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
default:
|
2017-01-19 17:26:52 +08:00
|
|
|
mbAppend(mb, c);
|
|
|
|
continue;
|
|
|
|
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 */
|
2003-12-30 23:12:50 +08:00
|
|
|
lastc = NULL;
|
2019-09-18 19:49:55 +08:00
|
|
|
if ((se = findMacroEnd(s)) == NULL) {
|
|
|
|
mbErr(mb, 1, _("Unterminated %c: %s\n"), (char)*s, s);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (*s) {
|
1998-07-09 01:50:48 +08:00
|
|
|
default: /* %name substitution */
|
2019-09-18 19:49:55 +08:00
|
|
|
f = s = setNegateAndCheck(s, &negate, &chkexist);
|
2017-01-19 17:26:52 +08:00
|
|
|
fe = se;
|
|
|
|
/* For "%name " macros ... */
|
|
|
|
if ((c = *fe) && isblank(c))
|
|
|
|
if ((lastc = strchr(fe,'\n')) == NULL)
|
|
|
|
lastc = strchr(fe, '\0');
|
|
|
|
break;
|
1998-07-09 01:50:48 +08:00
|
|
|
case '(': /* %(...) shell escape */
|
2017-01-19 17:26:52 +08:00
|
|
|
if (mb->macro_trace)
|
2019-09-18 19:49:55 +08:00
|
|
|
printMacro(mb, s, se);
|
2017-01-19 17:26:52 +08:00
|
|
|
s++; /* skip ( */
|
2019-09-18 19:49:55 +08:00
|
|
|
doShellEscape(mb, s, (se - 1 - s));
|
2017-01-19 17:26:52 +08:00
|
|
|
s = se;
|
|
|
|
continue;
|
2019-09-18 21:31:08 +08:00
|
|
|
case '[': /* %[...] expression expansion */
|
|
|
|
if (mb->macro_trace)
|
|
|
|
printMacro(mb, s, se);
|
|
|
|
s++; /* skip [ */
|
|
|
|
doExpressionExpansion(mb, s, (se - 1 - s));
|
|
|
|
s = se;
|
|
|
|
continue;
|
1998-07-09 01:50:48 +08:00
|
|
|
case '{': /* %{...}/%{...:...} substitution */
|
2019-09-18 19:49:55 +08:00
|
|
|
f = s+1; /* skip { */
|
2019-05-07 18:21:51 +08:00
|
|
|
f = setNegateAndCheck(f, &negate, &chkexist);
|
2017-01-19 17:26:52 +08:00
|
|
|
for (fe = f; (c = *fe) && !strchr(" :}", c);)
|
|
|
|
fe++;
|
|
|
|
switch (c) {
|
|
|
|
case ':':
|
|
|
|
g = fe + 1;
|
|
|
|
ge = se - 1;
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
2017-01-19 17:26:52 +08:00
|
|
|
case ' ':
|
|
|
|
lastc = se-1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2014-02-25 22:07:28 +08:00
|
|
|
/* XXX Everything below expects fe > f */
|
1998-07-09 01:50:48 +08:00
|
|
|
fn = (fe - f);
|
|
|
|
gn = (ge - g);
|
2014-02-25 22:07:28 +08:00
|
|
|
if ((fe - f) <= 0) {
|
2017-01-19 17:26:52 +08:00
|
|
|
/* XXX Process % in unknown context */
|
|
|
|
c = '%'; /* XXX only need to save % */
|
|
|
|
mbAppend(mb, c);
|
2014-02-25 22:07:28 +08:00
|
|
|
#if 0
|
2019-05-17 14:02:58 +08:00
|
|
|
mbErr(mb, 1,
|
2017-01-19 17:26:52 +08:00
|
|
|
_("A %% is followed by an unparseable macro\n"));
|
2014-02-25 22:07:28 +08:00
|
|
|
#endif
|
2017-01-19 17:26:52 +08:00
|
|
|
continue;
|
2014-02-25 22:07:28 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
|
|
|
if (mb->macro_trace)
|
2017-01-19 17:26:52 +08:00
|
|
|
printMacro(mb, s, se);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
|
|
|
/* Expand builtin macros */
|
2019-01-15 22:28:10 +08:00
|
|
|
if ((builtin = lookupBuiltin(f, fn))) {
|
2019-09-23 21:41:19 +08:00
|
|
|
if (builtin->havearg != (g != NULL)) {
|
|
|
|
mbErr(mb, 1, "%%%s: %s\n", builtin->name, builtin->havearg ?
|
|
|
|
_("argument expected") : _("unexpected argument"));
|
|
|
|
continue;
|
|
|
|
}
|
2019-01-15 22:28:10 +08:00
|
|
|
if (builtin->parse) {
|
2019-09-17 17:24:22 +08:00
|
|
|
s = builtin->parse(mb, se);
|
2019-01-15 22:28:10 +08:00
|
|
|
} else {
|
|
|
|
builtin->func(mb, chkexist, negate, f, fn, g, gn);
|
|
|
|
s = se;
|
|
|
|
}
|
2017-01-19 17:26:52 +08:00
|
|
|
continue;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2017-03-07 20:06:55 +08:00
|
|
|
if (me) {
|
|
|
|
if ((me->flags & ME_AUTO) && mb->level > me->level) {
|
|
|
|
/* Ignore out-of-scope automatic macros */
|
|
|
|
me = NULL;
|
|
|
|
} else {
|
|
|
|
/* If we looked up a macro, consider it used */
|
|
|
|
me->flags |= ME_USED;
|
|
|
|
}
|
|
|
|
}
|
2014-02-28 22:33:54 +08:00
|
|
|
|
2020-02-07 04:17:08 +08:00
|
|
|
/* XXX Special processing for flags and existance test */
|
|
|
|
if (*f == '-' || chkexist) {
|
|
|
|
if ((me == NULL && !negate) || /* Without existance, skip %{?...} */
|
|
|
|
(me != NULL && negate)) { /* With existance, skip %{!?...} */
|
2017-01-19 17:26:52 +08:00
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2020-02-07 04:17:08 +08:00
|
|
|
if (g && g < ge) { /* Expand X in %{...:X} */
|
2017-01-19 19:52:30 +08:00
|
|
|
expandMacro(mb, g, gn);
|
2017-01-19 17:26:52 +08:00
|
|
|
} else
|
2020-02-07 04:17:08 +08:00
|
|
|
if (me && me->body && *me->body) {/* Expand macro body */
|
2020-02-07 18:14:52 +08:00
|
|
|
if ((me->flags & ME_LITERAL) != 0)
|
|
|
|
mbAppendStr(mb, me->body);
|
|
|
|
else
|
|
|
|
expandMacro(mb, me->body, 0);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2017-01-19 17:26:52 +08:00
|
|
|
s = se;
|
|
|
|
continue;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (me == NULL) { /* leave unknown %... as is */
|
2017-01-19 17:26:52 +08:00
|
|
|
/* XXX hack to permit non-overloaded %foo to be passed */
|
|
|
|
c = '%'; /* XXX only need to save % */
|
|
|
|
mbAppend(mb, c);
|
|
|
|
continue;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup args for "%name " macros with opts */
|
2020-10-26 05:24:06 +08:00
|
|
|
ARGV_t args = NULL;
|
2020-10-23 21:12:03 +08:00
|
|
|
if (me->opts != NULL) {
|
|
|
|
argvAdd(&args, me->name);
|
|
|
|
if (lastc)
|
|
|
|
se = grabArgs(mb, &args, fe, lastc);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2020-10-26 05:24:06 +08:00
|
|
|
rpmMacroEntry prevme = mb->me;
|
|
|
|
ARGV_t prevarg = mb->args;
|
|
|
|
if (args != NULL)
|
|
|
|
setupArgs(mb, me, args);
|
|
|
|
|
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) {
|
2020-02-07 18:14:52 +08:00
|
|
|
if ((me->flags & ME_LITERAL) != 0)
|
|
|
|
mbAppendStr(mb, me->body);
|
|
|
|
else
|
|
|
|
expandMacro(mb, me->body, 0);
|
1999-03-28 08:47:40 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2010-01-07 18:04:53 +08:00
|
|
|
/* Free args for "%name " macros with opts */
|
2020-10-26 05:24:06 +08:00
|
|
|
if (args != NULL)
|
2017-08-22 15:24:21 +08:00
|
|
|
freeArgs(mb);
|
2020-10-08 16:10:40 +08:00
|
|
|
mb->args = prevarg;
|
|
|
|
mb->me = prevme;
|
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--;
|
2017-01-19 19:52:30 +08:00
|
|
|
if (mb->error != 0 || mb->expand_trace)
|
2010-09-27 17:19:13 +08:00
|
|
|
printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos);
|
2017-08-25 17:17:03 +08:00
|
|
|
mb->macro_trace = store_macro_trace;
|
|
|
|
mb->expand_trace = store_expand_trace;
|
2017-01-19 19:41:42 +08:00
|
|
|
exit:
|
2010-09-27 22:15:15 +08:00
|
|
|
_free(source);
|
2017-01-19 19:52:30 +08:00
|
|
|
return mb->error;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
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
|
|
|
|
2017-05-18 16:19:13 +08:00
|
|
|
static int doExpandMacros(rpmMacroContext mc, const char *src, int flags,
|
|
|
|
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;
|
2017-01-20 20:55:59 +08:00
|
|
|
mb->depth = mc->depth;
|
2017-03-07 19:02:26 +08:00
|
|
|
mb->level = mc->level;
|
2001-06-06 03:26:22 +08:00
|
|
|
mb->macro_trace = print_macro_trace;
|
|
|
|
mb->expand_trace = print_expand_trace;
|
|
|
|
mb->mc = mc;
|
2017-05-18 16:19:13 +08:00
|
|
|
mb->flags = flags;
|
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,
|
2014-02-28 21:57:09 +08:00
|
|
|
const char * n, const char * o, const char * b, int level, int flags)
|
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 */
|
2014-02-28 21:57:09 +08:00
|
|
|
me->flags = flags;
|
2014-02-28 22:10:14 +08:00
|
|
|
me->flags &= ~(ME_USED);
|
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
|
|
|
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));
|
2017-01-19 20:24:26 +08:00
|
|
|
int rc;
|
2013-05-22 16:15:20 +08:00
|
|
|
|
|
|
|
/* XXX just enough to get by */
|
|
|
|
mb->mc = mc;
|
2019-09-17 17:24:22 +08:00
|
|
|
(void) doDefine(mb, macro, level, 0);
|
2017-01-19 20:24:26 +08:00
|
|
|
rc = mb->error;
|
2013-05-22 16:15:20 +08:00
|
|
|
_free(mb);
|
2017-01-19 20:24:26 +08:00
|
|
|
return rc;
|
2013-05-22 16:15:20 +08:00
|
|
|
}
|
|
|
|
|
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;
|
2018-01-08 18:30:37 +08:00
|
|
|
int nfailed = 0;
|
2019-05-17 17:26:58 +08:00
|
|
|
int lineno = 0;
|
|
|
|
int nlines = 0;
|
2013-05-22 16:27:29 +08:00
|
|
|
|
|
|
|
if (fd == NULL)
|
|
|
|
goto exit;
|
|
|
|
|
2020-02-07 18:22:35 +08:00
|
|
|
pushMacro(mc, "__file_name", NULL, fn, RMIL_MACROFILES, ME_LITERAL);
|
2019-05-17 17:26:58 +08:00
|
|
|
|
2013-05-22 16:27:29 +08:00
|
|
|
buf[0] = '\0';
|
2019-05-17 17:26:58 +08:00
|
|
|
while ((nlines = rdcl(buf, blen, fd)) > 0) {
|
2013-05-22 16:27:29 +08:00
|
|
|
char c, *n;
|
2019-05-17 17:26:58 +08:00
|
|
|
char lnobuf[16];
|
2013-05-22 16:27:29 +08:00
|
|
|
|
2019-05-17 17:26:58 +08:00
|
|
|
lineno += nlines;
|
2013-05-22 16:27:29 +08:00
|
|
|
n = buf;
|
|
|
|
SKIPBLANK(n, c);
|
|
|
|
|
|
|
|
if (c != '%')
|
|
|
|
continue;
|
|
|
|
n++; /* skip % */
|
2019-05-17 17:26:58 +08:00
|
|
|
|
|
|
|
snprintf(lnobuf, sizeof(lnobuf), "%d", lineno);
|
2020-02-07 18:22:35 +08:00
|
|
|
pushMacro(mc, "__file_lineno", NULL, lnobuf, RMIL_MACROFILES, ME_LITERAL);
|
2018-01-08 18:30:37 +08:00
|
|
|
if (defineMacro(mc, n, RMIL_MACROFILES))
|
|
|
|
nfailed++;
|
2019-05-17 17:26:58 +08:00
|
|
|
popMacro(mc, "__file_lineno");
|
2013-05-22 16:27:29 +08:00
|
|
|
}
|
2018-01-08 18:30:37 +08:00
|
|
|
fclose(fd);
|
2019-05-17 17:26:58 +08:00
|
|
|
popMacro(mc, "__file_name");
|
2013-05-22 16:27:29 +08:00
|
|
|
|
2019-05-17 14:38:29 +08:00
|
|
|
rc = (nfailed > 0);
|
2018-01-08 18:30:37 +08:00
|
|
|
|
2013-05-22 16:27:29 +08:00
|
|
|
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);
|
Shift macro scoping level values as they are defined
Some of scoping level values are defined in rpmmacros.h:
#define RMIL_DEFAULT -15
#define RMIL_MACROFILES -13
...
#define RMIL_GLOBAL 0
Before this patch RPM set these values decreased of 1 (for all macros).
Because of the (level - 1) in
pushMacro(dst, me->name, me->opts, me->body, (level - 1), me->flags);
in function copyMacros scoping level for some CMDLINE macros was decreased of 1.
Because of the (level - 1) in
pushMacro(mb->mc, n, o, b, (level - 1), ME_NONE);
in function doDefine scoping level values of all other macro types and
some CMDLINE macros was decreased of 1.
The patch shifts scoping level values back to the numbers defined
in rpmmacros.h.
This was spotted when investigating a regression introduced in
commit 237f35f16f978bb20e4cafd78abfab1f2e5db480 where locally scoped
scoped macros did not get deleted on scope closing.
This also fixes the regression in question.
The first test in this patch tests automatic deleting of %defined macros.
The second test checks whether the behavior changed by
237f35f16f978bb20e4cafd78abfab1f2e5db480 is still working correctly.
Thanks to Panu Matilainen for investigation and proposal of the main part
of the patch!
2017-08-09 19:05:11 +08:00
|
|
|
pushMacro(dst, me->name, me->opts, me->body, level, me->flags);
|
2013-05-22 16:43:40 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-22 15:21:04 +08:00
|
|
|
/* External interfaces */
|
|
|
|
|
2015-11-26 22:20:05 +08:00
|
|
|
int rpmExpandMacros(rpmMacroContext mc, const char * sbuf, char ** obuf, int flags)
|
2015-11-18 01:36:16 +08:00
|
|
|
{
|
|
|
|
char *target = NULL;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
mc = rpmmctxAcquire(mc);
|
2017-05-18 16:19:13 +08:00
|
|
|
rc = doExpandMacros(mc, sbuf, flags, &target);
|
2015-11-18 01:36:16 +08:00
|
|
|
rpmmctxRelease(mc);
|
|
|
|
|
|
|
|
if (rc) {
|
|
|
|
free(target);
|
2015-11-26 22:20:05 +08:00
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
*obuf = target;
|
|
|
|
return 1;
|
2015-11-18 01:36:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-22 15:21:04 +08:00
|
|
|
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,
|
2014-02-28 22:10:14 +08:00
|
|
|
((me->flags & ME_USED) ? '=' : ':'), me->name);
|
2013-05-22 15:21:04 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-02-07 20:18:08 +08:00
|
|
|
int rpmPushMacroFlags(rpmMacroContext mc,
|
|
|
|
const char * n, const char * o, const char * b,
|
|
|
|
int level, rpmMacroFlags flags)
|
2013-05-22 14:44:34 +08:00
|
|
|
{
|
2013-05-22 15:53:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
2020-02-07 20:18:08 +08:00
|
|
|
pushMacro(mc, n, o, b, level, flags & RPMMACRO_LITERAL ? ME_LITERAL : ME_NONE);
|
2013-05-22 15:53:20 +08:00
|
|
|
rpmmctxRelease(mc);
|
2017-01-19 22:02:00 +08:00
|
|
|
return 0;
|
2013-05-22 14:44:34 +08:00
|
|
|
}
|
|
|
|
|
2020-02-07 20:18:08 +08:00
|
|
|
int rpmPushMacro(rpmMacroContext mc,
|
|
|
|
const char * n, const char * o, const char * b, int level)
|
|
|
|
{
|
|
|
|
return rpmPushMacroFlags(mc, n, o, b, level, RPMMACRO_DEFAULT);
|
|
|
|
}
|
|
|
|
|
2017-01-19 22:02:00 +08:00
|
|
|
int rpmPopMacro(rpmMacroContext mc, const char * n)
|
2013-05-22 14:44:34 +08:00
|
|
|
{
|
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);
|
2017-01-19 22:02:00 +08:00
|
|
|
return 0;
|
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
|
|
|
{
|
2017-01-19 20:24:26 +08:00
|
|
|
int rc;
|
2013-05-22 16:15:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
2017-01-19 20:24:26 +08:00
|
|
|
rc = defineMacro(mc, macro, level);
|
2013-05-22 16:15:20 +08:00
|
|
|
rpmmctxRelease(mc);
|
2017-01-19 20:24:26 +08:00
|
|
|
return rc;
|
1999-03-28 08:47:40 +08:00
|
|
|
}
|
|
|
|
|
2020-02-06 16:13:31 +08:00
|
|
|
int rpmMacroIsDefined(rpmMacroContext mc, const char *n)
|
|
|
|
{
|
|
|
|
int defined = 0;
|
|
|
|
if ((mc = rpmmctxAcquire(mc)) != NULL) {
|
|
|
|
if (findEntry(mc, n, 0, NULL))
|
|
|
|
defined = 1;
|
|
|
|
rpmmctxRelease(mc);
|
|
|
|
}
|
|
|
|
return defined;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rpmMacroIsParametric(rpmMacroContext mc, const char *n)
|
|
|
|
{
|
|
|
|
int parametric = 0;
|
|
|
|
if ((mc = rpmmctxAcquire(mc)) != NULL) {
|
|
|
|
rpmMacroEntry *mep = findEntry(mc, n, 0, NULL);
|
|
|
|
if (mep && (*mep)->opts)
|
|
|
|
parametric = 1;
|
|
|
|
rpmmctxRelease(mc);
|
|
|
|
}
|
|
|
|
return parametric;
|
|
|
|
}
|
|
|
|
|
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);
|
2017-05-18 16:19:13 +08:00
|
|
|
(void) doExpandMacros(mc, buf, 0, &ret);
|
2013-05-22 15:53:20 +08:00
|
|
|
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;
|
|
|
|
}
|