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>
|
2009-12-23 20:02:19 +08:00
|
|
|
#ifdef HAVE_GETOPT_H
|
|
|
|
#include <getopt.h>
|
|
|
|
#else
|
|
|
|
extern char *optarg;
|
|
|
|
extern int optind;
|
|
|
|
#endif
|
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
|
|
|
|
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),
|
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 18:43:29 +08:00
|
|
|
int escape; /*!< Preserve '%%' during expansion? */
|
2017-05-18 16:19:13 +08:00
|
|
|
int flags; /*!< Flags to control behavior */
|
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-01-15 22:28:10 +08:00
|
|
|
typedef const char *(*parseFunc)(MacroBuf mb, const char * se, size_t slen);
|
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);
|
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);
|
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);
|
2001-10-18 00:43:36 +08:00
|
|
|
|
2019-01-15 22:28:10 +08:00
|
|
|
static const char * doDef(MacroBuf mb, const char * se, size_t slen);
|
|
|
|
static const char * doGlobal(MacroBuf mb, const char * se, size_t slen);
|
|
|
|
static const char * doDump(MacroBuf mb, const char * se, size_t slen);
|
|
|
|
static const char * doUndefine(MacroBuf mb, const char * se, size_t slen);
|
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
|
2002-06-22 02:00:50 +08:00
|
|
|
* @return buffer, or NULL on end-of-file
|
2000-09-30 03:50:29 +08:00
|
|
|
*/
|
2002-06-22 02:00:50 +08:00
|
|
|
static char *
|
2008-04-18 21:14:15 +08:00
|
|
|
rdcl(char * buf, size_t size, FILE *f)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2002-06-22 02:00:50 +08:00
|
|
|
char *q = buf - 1; /* initialize just before buffer. */
|
2001-05-04 05:00:18 +08:00
|
|
|
size_t nb = 0;
|
|
|
|
size_t nread = 0;
|
2004-04-09 04:27:53 +08:00
|
|
|
int pc = 0, bc = 0;
|
|
|
|
char *p = buf;
|
2001-05-04 05:00:18 +08:00
|
|
|
|
|
|
|
if (f != NULL)
|
|
|
|
do {
|
2002-06-22 02:00:50 +08:00
|
|
|
*(++q) = '\0'; /* terminate and move forward. */
|
|
|
|
if (fgets(q, size, f) == NULL) /* read next line. */
|
2001-05-04 05:00:18 +08:00
|
|
|
break;
|
|
|
|
nb = strlen(q);
|
2002-06-22 02:00:50 +08:00
|
|
|
nread += nb; /* trim trailing \r and \n */
|
2001-05-04 05:00:18 +08:00
|
|
|
for (q += nb - 1; nb > 0 && iseol(*q); q--)
|
|
|
|
nb--;
|
2004-04-09 04:27:53 +08:00
|
|
|
for (; p <= q; p++) {
|
|
|
|
switch (*p) {
|
|
|
|
case '\\':
|
|
|
|
switch (*(p+1)) {
|
2007-09-11 22:48:54 +08:00
|
|
|
case '\0': break;
|
|
|
|
default: p++; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
2004-04-09 04:27:53 +08:00
|
|
|
case '%':
|
|
|
|
switch (*(p+1)) {
|
2007-09-11 22:48:54 +08:00
|
|
|
case '{': p++, bc++; break;
|
|
|
|
case '(': p++, pc++; break;
|
|
|
|
case '%': p++; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
|
|
|
case '{': if (bc > 0) bc++; break;
|
|
|
|
case '}': if (bc > 0) bc--; break;
|
|
|
|
case '(': if (pc > 0) pc++; break;
|
|
|
|
case ')': if (pc > 0) pc--; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
|
|
|
}
|
2004-04-19 20:12:12 +08:00
|
|
|
if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
|
2001-05-04 05:00:18 +08:00
|
|
|
*(++q) = '\0'; /* trim trailing \r, \n */
|
|
|
|
break;
|
|
|
|
}
|
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);
|
2002-06-22 02:00:50 +08:00
|
|
|
return (nread > 0 ? buf : NULL);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
2000-11-01 00:18:34 +08:00
|
|
|
* Return text between pl and matching pr characters.
|
2000-09-30 03:50:29 +08:00
|
|
|
* @param p start of text
|
|
|
|
* @param pl left char, i.e. '[', '(', '{', etc.
|
|
|
|
* @param pr right char, i.e. ']', ')', '}', etc.
|
|
|
|
* @return address of last char before pr (or NULL)
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static const char *
|
2001-06-06 03:26:22 +08:00
|
|
|
matchchar(const char * p, char pl, char pr)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
int lvl = 0;
|
|
|
|
char c;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
while ((c = *p++) != '\0') {
|
|
|
|
if (c == '\\') { /* Ignore escaped chars */
|
|
|
|
p++;
|
|
|
|
continue;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2001-06-06 03:26:22 +08:00
|
|
|
if (c == pr) {
|
|
|
|
if (--lvl <= 0) return --p;
|
|
|
|
} else if (c == pl)
|
|
|
|
lvl++;
|
|
|
|
}
|
|
|
|
return (const char *)NULL;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Pre-print macro expression to be expanded.
|
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param s current expansion string
|
|
|
|
* @param se end of string
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static void
|
2001-06-06 03:26:22 +08:00
|
|
|
printMacro(MacroBuf mb, const char * s, const char * se)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
const char *senl;
|
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())
|
|
|
|
* @retval target pointer to expanded string (malloced)
|
2000-09-30 03:50:29 +08:00
|
|
|
* @return result of expansion
|
|
|
|
*/
|
1998-07-09 01:50:48 +08:00
|
|
|
static int
|
2010-09-27 21:03:56 +08:00
|
|
|
expandThis(MacroBuf mb, const char * src, size_t slen, char **target)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2010-09-27 19:22:56 +08:00
|
|
|
struct MacroBuf_s umb;
|
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
|
|
|
}
|
|
|
|
|
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--;
|
|
|
|
}
|
|
|
|
|
2017-10-06 04:50:54 +08:00
|
|
|
#ifdef WITH_LUA
|
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
|
|
|
#endif
|
|
|
|
|
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) {
|
2017-01-19 19:52:30 +08:00
|
|
|
mb->error = 1;
|
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
|
|
|
}
|
|
|
|
|
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;
|
2018-01-06 04:16:41 +08:00
|
|
|
} const builtinmacros[] = {
|
2019-01-15 22:28:10 +08:00
|
|
|
{ STR_AND_LEN("F"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("P"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("S"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("basename"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("define"), NULL, doDef },
|
|
|
|
{ STR_AND_LEN("dirname"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("dump"), NULL, doDump },
|
|
|
|
{ STR_AND_LEN("echo"), doOutput, NULL },
|
|
|
|
{ STR_AND_LEN("error"), doOutput, NULL },
|
|
|
|
{ STR_AND_LEN("expand"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("getconfdir"),doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("getenv"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("getncpus"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("global"), NULL, doGlobal },
|
|
|
|
{ STR_AND_LEN("load"), doLoad, NULL },
|
|
|
|
{ STR_AND_LEN("lua"), doLua, NULL },
|
|
|
|
{ STR_AND_LEN("quote"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("shrink"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("suffix"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("trace"), doTrace, NULL },
|
|
|
|
{ STR_AND_LEN("u2p"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("uncompress"),doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("undefine"), NULL, doUndefine },
|
|
|
|
{ STR_AND_LEN("url2path"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("verbose"), doFoo, NULL },
|
|
|
|
{ STR_AND_LEN("warn"), doOutput, NULL },
|
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);
|
|
|
|
}
|
|
|
|
|
2017-12-12 18:57:16 +08:00
|
|
|
static const int
|
|
|
|
validName(const char *name, size_t namelen, const char *action) {
|
|
|
|
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)) {
|
|
|
|
rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%s)\n"),
|
|
|
|
name, action);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2019-01-15 19:36:46 +08:00
|
|
|
if (lookupBuiltin(name, namelen)) {
|
2018-01-06 04:16:41 +08:00
|
|
|
rpmlog(RPMLOG_ERR, _("Macro %%%s is a built-in (%s)\n"), name, action);
|
|
|
|
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
|
2014-06-09 19:28:20 +08:00
|
|
|
* @param slen length of se argument
|
2000-09-30 03:50:29 +08:00
|
|
|
* @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 *
|
2014-06-09 19:28:20 +08:00
|
|
|
doDefine(MacroBuf mb, const char * se, size_t slen, int level, int expandbody)
|
1997-03-31 22:13:21 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
const char *s = se;
|
2014-06-09 19:28:20 +08:00
|
|
|
char *buf = xmalloc(slen + 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 {
|
|
|
|
rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
|
|
|
|
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) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2001-06-06 03:26:22 +08:00
|
|
|
_("Macro %%%s has unterminated body\n"), n);
|
|
|
|
se = s; /* XXX W2DO? */
|
2011-03-17 21:35:42 +08:00
|
|
|
goto exit;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2001-06-06 03:26:22 +08:00
|
|
|
s++; /* XXX skip { */
|
|
|
|
strncpy(b, s, (se - s));
|
|
|
|
b[se - s] = '\0';
|
|
|
|
be += strlen(b);
|
|
|
|
se++; /* XXX skip } */
|
|
|
|
s = se; /* move scan forward */
|
|
|
|
} else { /* otherwise free-field */
|
2004-04-09 04:27:53 +08:00
|
|
|
int bc = 0, pc = 0;
|
|
|
|
while (*s && (bc || pc || !iseol(*s))) {
|
|
|
|
switch (*s) {
|
|
|
|
case '\\':
|
|
|
|
switch (*(s+1)) {
|
2007-09-11 22:48:54 +08:00
|
|
|
case '\0': break;
|
|
|
|
default: s++; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
2004-04-09 04:27:53 +08:00
|
|
|
case '%':
|
|
|
|
switch (*(s+1)) {
|
2007-09-11 22:48:54 +08:00
|
|
|
case '{': *be++ = *s++; bc++; break;
|
|
|
|
case '(': *be++ = *s++; pc++; break;
|
|
|
|
case '%': *be++ = *s++; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
|
|
|
case '{': if (bc > 0) bc++; break;
|
|
|
|
case '}': if (bc > 0) bc--; break;
|
|
|
|
case '(': if (pc > 0) pc++; break;
|
|
|
|
case ')': if (pc > 0) pc--; break;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
|
|
|
*be++ = *s++;
|
|
|
|
}
|
|
|
|
*be = '\0';
|
|
|
|
|
|
|
|
if (bc || pc) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2004-04-09 04:27:53 +08:00
|
|
|
_("Macro %%%s has unterminated body\n"), n);
|
|
|
|
se = s; /* XXX W2DO? */
|
2011-03-17 21:35:42 +08:00
|
|
|
goto exit;
|
2004-04-09 04:27:53 +08:00
|
|
|
}
|
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Trim trailing blanks/newlines */
|
|
|
|
while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
|
|
|
|
{};
|
|
|
|
*(++be) = '\0'; /* one too far */
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
/* Move scan over body */
|
|
|
|
while (iseol(*s))
|
|
|
|
s++;
|
|
|
|
se = s;
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2017-12-12 18:57:16 +08:00
|
|
|
if (!validName(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) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
|
2008-02-01 19:00:40 +08:00
|
|
|
goto exit;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2013-01-19 20:11:11 +08:00
|
|
|
if (!isblank(*sbody) && !(*sbody == '\\' && iseol(sbody[1])))
|
|
|
|
rpmlog(RPMLOG_WARNING, _("Macro %%%s needs whitespace before body\n"), n);
|
|
|
|
|
2010-09-27 21:03:56 +08:00
|
|
|
if (expandbody) {
|
|
|
|
if (expandThis(mb, b, 0, &ebody)) {
|
|
|
|
rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
b = ebody;
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
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
|
2014-06-09 19:28:20 +08:00
|
|
|
* @param slen length of se argument
|
2000-09-30 03:50:29 +08:00
|
|
|
* @return address to continue parsing
|
|
|
|
*/
|
2007-09-11 22:48:54 +08:00
|
|
|
static const char *
|
2017-01-19 20:13:15 +08:00
|
|
|
doUndefine(MacroBuf mb, const char * se, size_t slen)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2001-06-06 03:26:22 +08:00
|
|
|
const char *s = se;
|
2014-06-09 19:28:20 +08:00
|
|
|
char *buf = xmalloc(slen + 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
|
|
|
|
2017-12-12 18:57:16 +08:00
|
|
|
if (!validName(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-01-15 22:28:10 +08:00
|
|
|
static const char * doDef(MacroBuf mb, const char * se, size_t slen)
|
|
|
|
{
|
|
|
|
return doDefine(mb, se, slen, mb->level, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * doGlobal(MacroBuf mb, const char * se, size_t slen)
|
|
|
|
{
|
|
|
|
return doDefine(mb, se, slen, RMIL_GLOBAL, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * doDump(MacroBuf mb, const char * se, size_t slen)
|
|
|
|
{
|
|
|
|
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))) {
|
2014-02-28 22:45:33 +08:00
|
|
|
rpmlog(RPMLOG_WARNING,
|
|
|
|
_("Macro %%%s defined but not used within scope\n"),
|
|
|
|
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--;
|
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) {
|
|
|
|
char *d, arg[slen + 1];
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-09-30 03:50:29 +08:00
|
|
|
/**
|
|
|
|
* Parse arguments (to next new line) for parameterized macro.
|
2001-09-15 21:49:11 +08:00
|
|
|
* @todo Use popt rather than getopt to parse args.
|
2000-09-30 03:50:29 +08:00
|
|
|
* @param mb macro expansion state
|
|
|
|
* @param me macro entry slot
|
|
|
|
* @param se arguments to parse
|
|
|
|
* @param lastc stop parsing at lastc
|
|
|
|
* @return address to continue parsing
|
|
|
|
*/
|
2007-09-11 22:48:54 +08:00
|
|
|
static const char *
|
2007-09-20 20:52:03 +08:00
|
|
|
grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
|
2003-12-30 23:12:50 +08:00
|
|
|
const char * lastc)
|
1998-07-09 01:50:48 +08:00
|
|
|
{
|
2017-03-07 18:54:09 +08:00
|
|
|
const char *cont = NULL;
|
2009-09-07 22:50:43 +08:00
|
|
|
const char *opts;
|
2008-04-18 19:54:58 +08:00
|
|
|
char *args = NULL;
|
|
|
|
ARGV_t argv = NULL;
|
1998-07-09 01:50:48 +08:00
|
|
|
int argc = 0;
|
|
|
|
int c;
|
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/*
|
2017-03-07 18:54:09 +08:00
|
|
|
* Prepare list of call arguments, starting with macro name as argv[0].
|
2008-04-18 19:54:58 +08:00
|
|
|
* Make a copy of se up to lastc string that we can pass to argvSplit().
|
2008-09-11 21:56:50 +08:00
|
|
|
* Append the results to main argv.
|
2008-04-18 19:54:58 +08:00
|
|
|
*/
|
2017-03-07 18:54:09 +08:00
|
|
|
argvAdd(&argv, me->name);
|
|
|
|
if (lastc) {
|
2017-05-18 18:43:29 +08:00
|
|
|
int oescape = mb->escape;
|
2017-05-23 15:10:38 +08:00
|
|
|
char *s = NULL;
|
2008-07-16 14:40:10 +08:00
|
|
|
|
2017-03-07 20:08:27 +08:00
|
|
|
/* Expand possible macros in arguments */
|
2017-05-18 18:43:29 +08:00
|
|
|
mb->escape = 1;
|
2017-05-23 15:10:38 +08:00
|
|
|
expandThis(mb, se, lastc-se, &s);
|
2017-05-18 18:43:29 +08:00
|
|
|
mb->escape = oescape;
|
2017-05-23 15:10:38 +08:00
|
|
|
|
2017-08-31 17:19:58 +08:00
|
|
|
splitQuoted(&argv, s, " \t");
|
2017-05-23 15:10:38 +08:00
|
|
|
free(s);
|
2017-03-07 18:54:09 +08:00
|
|
|
|
|
|
|
cont = ((*lastc == '\0' || *lastc == '\n') && *(lastc-1) != '\\') ?
|
|
|
|
lastc : lastc + 1;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2001-09-15 21:49:11 +08:00
|
|
|
|
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 */
|
2017-03-07 19:02:26 +08:00
|
|
|
pushMacro(mb->mc, "0", NULL, me->name, mb->level, ME_AUTO);
|
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, " ");
|
2017-03-07 19:02:26 +08:00
|
|
|
pushMacro(mb->mc, "**", NULL, args, mb->level, ME_AUTO);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(args);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* POSIX states optind must be 1 before any call but glibc uses 0
|
|
|
|
* to (re)initialize getopt structures, eww.
|
|
|
|
*/
|
2001-09-15 21:49:11 +08:00
|
|
|
#ifdef __GLIBC__
|
2008-04-18 19:54:58 +08:00
|
|
|
optind = 0;
|
2003-12-27 09:37:56 +08:00
|
|
|
#else
|
|
|
|
optind = 1;
|
2001-09-15 21:49:11 +08:00
|
|
|
#endif
|
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
opts = me->opts;
|
2008-04-18 19:54:58 +08:00
|
|
|
argc = argvCount(argv);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2000-06-22 07:28:50 +08:00
|
|
|
/* Define option macros. */
|
2017-02-27 23:37:18 +08:00
|
|
|
while ((c = getopt(argc, argv, opts)) != -1)
|
2003-04-02 06:20:45 +08:00
|
|
|
{
|
2008-04-18 19:54:58 +08:00
|
|
|
char *name = NULL, *body = NULL;
|
2009-09-07 22:50:43 +08:00
|
|
|
if (c == '?' || strchr(opts, c) == NULL) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR, _("Unknown option %c in %s(%s)\n"),
|
2012-12-17 20:58:44 +08:00
|
|
|
(char)optopt, me->name, opts);
|
2017-01-19 20:18:29 +08:00
|
|
|
mb->error = 1;
|
2008-02-01 19:00:40 +08:00
|
|
|
goto exit;
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
2008-04-18 20:15:05 +08:00
|
|
|
|
|
|
|
rasprintf(&name, "-%c", c);
|
2008-04-18 19:54:58 +08:00
|
|
|
if (optarg) {
|
|
|
|
rasprintf(&body, "-%c %s", c, optarg);
|
|
|
|
} else {
|
|
|
|
rasprintf(&body, "-%c", c);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2017-03-07 19:02:26 +08:00
|
|
|
pushMacro(mb->mc, name, NULL, body, mb->level, ME_AUTO);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(name);
|
2008-04-18 20:15:05 +08:00
|
|
|
free(body);
|
2008-04-18 19:54:58 +08:00
|
|
|
|
|
|
|
if (optarg) {
|
|
|
|
rasprintf(&name, "-%c*", c);
|
2017-03-07 19:02:26 +08:00
|
|
|
pushMacro(mb->mc, name, NULL, optarg, mb->level, ME_AUTO);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(name);
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
1998-05-21 01:05:26 +08:00
|
|
|
}
|
1997-03-31 22:13:21 +08:00
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/* Add argument count (remaining non-option items) as macro. */
|
|
|
|
{ char *ac = NULL;
|
|
|
|
rasprintf(&ac, "%d", (argc - optind));
|
2017-03-07 19:02:26 +08:00
|
|
|
pushMacro(mb->mc, "#", NULL, ac, mb->level, ME_AUTO);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(ac);
|
|
|
|
}
|
2000-06-22 07:28:50 +08:00
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/* Add macro for each argument */
|
|
|
|
if (argc - optind) {
|
2001-05-04 05:00:18 +08:00
|
|
|
for (c = optind; c < argc; c++) {
|
2008-04-18 19:54:58 +08:00
|
|
|
char *name = NULL;
|
|
|
|
rasprintf(&name, "%d", (c - optind + 1));
|
2017-03-07 19:02:26 +08:00
|
|
|
pushMacro(mb->mc, name, NULL, argv[c], mb->level, ME_AUTO);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(name);
|
2001-05-04 05:00:18 +08:00
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
1999-04-21 06:45:52 +08:00
|
|
|
|
2008-04-18 19:54:58 +08:00
|
|
|
/* Add concatenated unexpanded arguments as yet another macro. */
|
|
|
|
args = argvJoin(argv + optind, " ");
|
2017-03-07 19:02:26 +08:00
|
|
|
pushMacro(mb->mc, "*", NULL, args ? args : "", mb->level, ME_AUTO);
|
2008-04-18 19:54:58 +08:00
|
|
|
free(args);
|
1999-04-21 06:45:52 +08:00
|
|
|
|
2008-02-01 19:00:40 +08:00
|
|
|
exit:
|
2008-04-18 19:54:58 +08:00
|
|
|
argvFree(argv);
|
2017-03-07 18:54:09 +08:00
|
|
|
return cont;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
1997-03-31 22:13:21 +08:00
|
|
|
|
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;
|
|
|
|
int odepth = mc->depth;
|
2017-03-07 19:02:26 +08:00
|
|
|
int olevel = mc->level;
|
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;
|
2017-01-19 18:09:10 +08:00
|
|
|
if (rpmluaRunScript(lua, scriptbuf, NULL) == -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
|
|
|
|
rpmlog(RPMLOG_ERR, _("<lua> scriptlet support not built in\n"));
|
2017-01-19 19:52:30 +08:00
|
|
|
mb->error = 1;
|
2017-01-19 18:09:10 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
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;
|
2008-02-01 19:00:40 +08:00
|
|
|
char *b = NULL, *be;
|
2001-06-06 03:26:22 +08:00
|
|
|
int c;
|
2017-10-18 15:25:08 +08:00
|
|
|
int verbose = (rpmIsVerbose() != 0);
|
|
|
|
int expand = (g != NULL && gn > 0);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2017-10-18 15:25:08 +08:00
|
|
|
/* Don't expand %{verbose:...} argument on false condition */
|
|
|
|
if (STREQ("verbose", f, fn) && (verbose == negate))
|
|
|
|
expand = 0;
|
|
|
|
|
|
|
|
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++;
|
2017-10-18 15:25:08 +08:00
|
|
|
} else if (STREQ("expand", f, fn) || STREQ("verbose", f, fn)) {
|
2001-06-06 03:26:22 +08:00
|
|
|
b = buf;
|
|
|
|
} else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
|
|
|
|
(void)urlPath(buf, (const char **)&b);
|
|
|
|
if (*b == '\0') b = "/";
|
|
|
|
} else if (STREQ("uncompress", f, fn)) {
|
|
|
|
rpmCompressedMagic compressed = COMPRESSED_OTHER;
|
|
|
|
for (b = buf; (c = *b) && isblank(c);)
|
|
|
|
b++;
|
|
|
|
for (be = b; (c = *be) && !isblank(c);)
|
|
|
|
be++;
|
|
|
|
*be++ = '\0';
|
2019-02-25 22:29:06 +08:00
|
|
|
if (rpmFileIsCompressed(b, &compressed))
|
|
|
|
mb->error = 1;
|
2017-02-27 23:37:18 +08:00
|
|
|
switch (compressed) {
|
2001-06-06 03:26:22 +08:00
|
|
|
default:
|
2007-10-09 16:02:49 +08:00
|
|
|
case COMPRESSED_NOT:
|
2008-01-23 13:56:51 +08:00
|
|
|
sprintf(be, "%%__cat %s", b);
|
2001-06-06 03:26:22 +08:00
|
|
|
break;
|
2007-10-09 16:02:49 +08:00
|
|
|
case COMPRESSED_OTHER:
|
2008-01-23 13:56:51 +08:00
|
|
|
sprintf(be, "%%__gzip -dc %s", b);
|
2001-06-06 03:26:22 +08:00
|
|
|
break;
|
2007-10-09 16:02:49 +08:00
|
|
|
case COMPRESSED_BZIP2:
|
2008-04-04 20:52:52 +08:00
|
|
|
sprintf(be, "%%__bzip2 -dc %s", b);
|
2001-06-06 03:26:22 +08:00
|
|
|
break;
|
2007-10-09 16:02:49 +08:00
|
|
|
case COMPRESSED_ZIP:
|
2008-01-23 13:56:51 +08:00
|
|
|
sprintf(be, "%%__unzip %s", b);
|
2001-06-06 03:26:22 +08:00
|
|
|
break;
|
2007-10-10 14:39:01 +08:00
|
|
|
case COMPRESSED_LZMA:
|
2009-03-27 20:11:43 +08:00
|
|
|
case COMPRESSED_XZ:
|
|
|
|
sprintf(be, "%%__xz -dc %s", b);
|
|
|
|
break;
|
2011-04-24 20:56:11 +08:00
|
|
|
case COMPRESSED_LZIP:
|
|
|
|
sprintf(be, "%%__lzip -dc %s", b);
|
|
|
|
break;
|
2011-04-24 20:57:23 +08:00
|
|
|
case COMPRESSED_LRZIP:
|
|
|
|
sprintf(be, "%%__lrzip -dqo- %s", b);
|
|
|
|
break;
|
2012-05-11 05:27:56 +08:00
|
|
|
case COMPRESSED_7ZIP:
|
|
|
|
sprintf(be, "%%__7zip x %s", b);
|
|
|
|
break;
|
2017-08-09 22:42:56 +08:00
|
|
|
case COMPRESSED_ZSTD:
|
|
|
|
sprintf(be, "%%__zstd -dc %s", b);
|
|
|
|
break;
|
1998-07-09 01:50:48 +08:00
|
|
|
}
|
2001-06-06 03:26:22 +08:00
|
|
|
b = be;
|
2009-01-16 15:09:40 +08:00
|
|
|
} else if (STREQ("getenv", f, fn)) {
|
|
|
|
b = getenv(buf);
|
2009-03-27 20:01:47 +08:00
|
|
|
} else if (STREQ("getconfdir", f, fn)) {
|
|
|
|
sprintf(buf, "%s", rpmConfigDir());
|
|
|
|
b = buf;
|
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
|
|
|
} else if (STREQ("S", f, fn)) {
|
2008-03-18 15:10:13 +08:00
|
|
|
for (b = buf; (c = *b) && risdigit(c);)
|
2001-06-06 03:26:22 +08:00
|
|
|
b++;
|
|
|
|
if (!c) { /* digit index */
|
|
|
|
b++;
|
|
|
|
sprintf(b, "%%SOURCE%s", buf);
|
|
|
|
} else
|
|
|
|
b = buf;
|
|
|
|
} else if (STREQ("P", f, fn)) {
|
2008-03-18 15:10:13 +08:00
|
|
|
for (b = buf; (c = *b) && risdigit(c);)
|
2001-06-06 03:26:22 +08:00
|
|
|
b++;
|
|
|
|
if (!c) { /* digit index */
|
|
|
|
b++;
|
|
|
|
sprintf(b, "%%PATCH%s", buf);
|
|
|
|
} else
|
|
|
|
b = buf;
|
|
|
|
} else if (STREQ("F", f, fn)) {
|
|
|
|
b = buf + strlen(buf) + 1;
|
|
|
|
sprintf(b, "file%s.file", buf);
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2001-06-06 03:26:22 +08:00
|
|
|
if (b) {
|
2010-09-27 22:15:15 +08:00
|
|
|
(void) expandMacro(mb, b, 0);
|
2001-06-06 03:26:22 +08:00
|
|
|
}
|
2008-04-29 22:04:08 +08:00
|
|
|
free(buf);
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
rpmlog(RPMLOG_ERR, _("failed to load macro file %s"), arg);
|
|
|
|
mb->error = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2010-09-27 18:05:37 +08:00
|
|
|
if (mb->buf == NULL) {
|
2013-04-16 15:45:56 +08:00
|
|
|
size_t blen = MACROBUFSIZ + slen;
|
|
|
|
mb->buf = xmalloc(blen + 1);
|
|
|
|
mb->buf[0] = '\0';
|
2010-09-27 18:05:37 +08:00
|
|
|
mb->tpos = 0;
|
|
|
|
mb->nb = blen;
|
|
|
|
}
|
|
|
|
tpos = mb->tpos; /* save expansion pointer for printExpand */
|
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) {
|
2007-11-19 22:25:24 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
2010-07-13 19:34:05 +08:00
|
|
|
_("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
|
1998-07-09 01:50:48 +08:00
|
|
|
mb->depth--;
|
|
|
|
mb->expand_trace = 1;
|
2017-01-19 19:52:30 +08:00
|
|
|
mb->error = 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 %% */
|
2017-05-18 18:43:29 +08:00
|
|
|
if (mb->escape)
|
|
|
|
mbAppend(mb, c);
|
2017-01-19 17:26:52 +08:00
|
|
|
}
|
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;
|
1998-07-09 01:50:48 +08:00
|
|
|
switch ((c = *s)) {
|
|
|
|
default: /* %name substitution */
|
2019-05-07 18:21:51 +08:00
|
|
|
s = setNegateAndCheck(s, &negate, &chkexist);
|
2017-01-19 17:26:52 +08:00
|
|
|
f = se = s;
|
|
|
|
if (*se == '-')
|
|
|
|
se++;
|
2017-02-27 23:37:18 +08:00
|
|
|
while ((c = *se) && (risalnum(c) || c == '_'))
|
2017-01-19 17:26:52 +08:00
|
|
|
se++;
|
|
|
|
/* Recognize non-alnum macros too */
|
|
|
|
switch (*se) {
|
|
|
|
case '*':
|
|
|
|
se++;
|
|
|
|
if (*se == '*') se++;
|
2007-09-11 22:48:54 +08:00
|
|
|
break;
|
2017-01-19 17:26:52 +08:00
|
|
|
case '#':
|
|
|
|
se++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
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 ((se = matchchar(s, c, ')')) == NULL) {
|
|
|
|
rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
|
2017-01-19 19:52:30 +08:00
|
|
|
mb->error = 1;
|
2017-01-19 17:26:52 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (mb->macro_trace)
|
|
|
|
printMacro(mb, s, se+1);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2017-01-19 17:26:52 +08:00
|
|
|
s++; /* skip ( */
|
2017-01-19 19:52:30 +08:00
|
|
|
doShellEscape(mb, s, (se - s));
|
2017-01-19 17:26:52 +08:00
|
|
|
se++; /* skip ) */
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2017-01-19 17:26:52 +08:00
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
break;
|
1998-07-09 01:50:48 +08:00
|
|
|
case '{': /* %{...}/%{...:...} substitution */
|
2017-01-19 17:26:52 +08:00
|
|
|
if ((se = matchchar(s, c, '}')) == NULL) {
|
|
|
|
rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
|
2017-01-19 19:52:30 +08:00
|
|
|
mb->error = 1;
|
2017-01-19 17:26:52 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
f = s+1;/* skip { */
|
|
|
|
se++; /* 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
|
2017-01-19 17:26:52 +08:00
|
|
|
rpmlog(RPMLOG_ERR,
|
|
|
|
_("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
|
|
|
s = se;
|
|
|
|
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))) {
|
|
|
|
if (builtin->parse) {
|
|
|
|
s = builtin->parse(mb, se, slen - (se - s));
|
|
|
|
} 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
|
|
|
|
1998-07-09 01:50:48 +08:00
|
|
|
/* XXX Special processing for flags */
|
|
|
|
if (*f == '-') {
|
2017-01-19 17:26:52 +08:00
|
|
|
if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
|
1998-07-09 01:50:48 +08:00
|
|
|
(me != NULL && negate)) { /* With -f, skip %{!-f...} */
|
2017-01-19 17:26:52 +08:00
|
|
|
s = se;
|
|
|
|
continue;
|
|
|
|
}
|
1998-07-09 01:50:48 +08:00
|
|
|
|
2017-01-19 17:26:52 +08:00
|
|
|
if (g && g < ge) { /* Expand X in %{-f:X} */
|
2017-01-19 19:52:30 +08:00
|
|
|
expandMacro(mb, g, gn);
|
2017-01-19 17:26:52 +08:00
|
|
|
} else
|
2001-05-04 05:00:18 +08:00
|
|
|
if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
|
2017-01-19 19:52:30 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
1998-09-06 07:13:35 +08:00
|
|
|
/* XXX Special processing for macro existence */
|
|
|
|
if (chkexist) {
|
2017-01-19 17:26:52 +08:00
|
|
|
if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
|
1998-09-06 07:13:35 +08:00
|
|
|
(me != NULL && negate)) { /* With -f, skip %{!?f...} */
|
|
|
|
s = se;
|
|
|
|
continue;
|
2017-01-19 17:26:52 +08:00
|
|
|
}
|
|
|
|
if (g && g < ge) { /* Expand X in %{?f:X} */
|
2017-01-19 19:52:30 +08:00
|
|
|
expandMacro(mb, g, gn);
|
2017-01-19 17:26:52 +08:00
|
|
|
} else
|
|
|
|
if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
|
2017-01-19 19:52:30 +08:00
|
|
|
expandMacro(mb, me->body, 0);
|
2017-01-19 17:26:52 +08:00
|
|
|
}
|
|
|
|
s = se;
|
|
|
|
continue;
|
1998-09-06 07:13:35 +08:00
|
|
|
}
|
|
|
|
|
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 */
|
|
|
|
if (me && me->opts != NULL) {
|
2017-03-07 18:54:09 +08:00
|
|
|
const char *xe = grabArgs(mb, me, fe, lastc);
|
|
|
|
if (xe != NULL)
|
|
|
|
se = xe;
|
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) {
|
2017-01-19 19:52:30 +08:00
|
|
|
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 */
|
|
|
|
if (me->opts != NULL)
|
2017-08-22 15:24:21 +08:00
|
|
|
freeArgs(mb);
|
1998-07-09 01:50:48 +08:00
|
|
|
|
|
|
|
s = se;
|
1997-03-31 22:13:21 +08:00
|
|
|
}
|
|
|
|
|
2010-09-27 17:19:13 +08:00
|
|
|
mb->buf[mb->tpos] = '\0';
|
1998-07-09 01:50:48 +08:00
|
|
|
mb->depth--;
|
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;
|
2014-06-09 19:28:20 +08:00
|
|
|
(void) doDefine(mb, macro, strlen(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 ndefs = 0;
|
|
|
|
int nfailed = 0;
|
2013-05-22 16:27:29 +08:00
|
|
|
|
|
|
|
if (fd == NULL)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
buf[0] = '\0';
|
2017-02-27 23:37:18 +08:00
|
|
|
while (rdcl(buf, blen, fd) != NULL) {
|
2013-05-22 16:27:29 +08:00
|
|
|
char c, *n;
|
|
|
|
|
|
|
|
n = buf;
|
|
|
|
SKIPBLANK(n, c);
|
|
|
|
|
|
|
|
if (c != '%')
|
|
|
|
continue;
|
|
|
|
n++; /* skip % */
|
2018-01-08 18:30:37 +08:00
|
|
|
ndefs++;
|
|
|
|
if (defineMacro(mc, n, RMIL_MACROFILES))
|
|
|
|
nfailed++;
|
2013-05-22 16:27:29 +08:00
|
|
|
}
|
2018-01-08 18:30:37 +08:00
|
|
|
fclose(fd);
|
2013-05-22 16:27:29 +08:00
|
|
|
|
2018-01-08 18:30:37 +08:00
|
|
|
/* if all definitions fail then return an error, otherwise just warn */
|
|
|
|
rc = (nfailed && ndefs == nfailed);
|
|
|
|
|
|
|
|
if (nfailed) {
|
|
|
|
rpmlog(rc ? RPMLOG_ERR : RPMLOG_WARNING,
|
|
|
|
_("file %s: %d invalid macro definitions\n"), fn, nfailed);
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2017-01-19 22:02:00 +08:00
|
|
|
int rpmPushMacro(rpmMacroContext mc,
|
2013-05-22 14:44:34 +08:00
|
|
|
const char * n, const char * o, const char * b, int level)
|
|
|
|
{
|
2013-05-22 15:53:20 +08:00
|
|
|
mc = rpmmctxAcquire(mc);
|
2014-02-28 21:57:09 +08:00
|
|
|
pushMacro(mc, n, o, b, level, 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
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|