perf subcmd: Create subcmd library
Move the subcommand-related files from perf to a new library named libsubcmd.a. Since we're moving files anyway, go ahead and rename 'exec_cmd.*' to 'exec-cmd.*' to be consistent with the naming of all the other files. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/c0a838d4c878ab17fee50998811612b2281355c1.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:

committed by
Arnaldo Carvalho de Melo

parent
2f4ce5ec1d
commit
4b6ab94eab
7
tools/lib/subcmd/Build
Normal file
7
tools/lib/subcmd/Build
Normal file
@@ -0,0 +1,7 @@
|
||||
libsubcmd-y += exec-cmd.o
|
||||
libsubcmd-y += help.o
|
||||
libsubcmd-y += pager.o
|
||||
libsubcmd-y += parse-options.o
|
||||
libsubcmd-y += run-command.o
|
||||
libsubcmd-y += sigchain.o
|
||||
libsubcmd-y += subcmd-config.o
|
48
tools/lib/subcmd/Makefile
Normal file
48
tools/lib/subcmd/Makefile
Normal file
@@ -0,0 +1,48 @@
|
||||
include ../../scripts/Makefile.include
|
||||
include ../../perf/config/utilities.mak # QUIET_CLEAN
|
||||
|
||||
ifeq ($(srctree),)
|
||||
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
#$(info Determined 'srctree' to be $(srctree))
|
||||
endif
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
AR = $(CROSS_COMPILE)ar
|
||||
RM = rm -f
|
||||
|
||||
MAKEFLAGS += --no-print-directory
|
||||
|
||||
LIBFILE = $(OUTPUT)libsubcmd.a
|
||||
|
||||
CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
|
||||
CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
|
||||
CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
|
||||
|
||||
CFLAGS += -I$(srctree)/tools/include/
|
||||
CFLAGS += -I$(srctree)/include/uapi
|
||||
CFLAGS += -I$(srctree)/include
|
||||
|
||||
SUBCMD_IN := $(OUTPUT)libsubcmd-in.o
|
||||
|
||||
all:
|
||||
|
||||
export srctree OUTPUT CC LD CFLAGS V
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
all: fixdep $(LIBFILE)
|
||||
|
||||
$(SUBCMD_IN): FORCE
|
||||
@$(MAKE) $(build)=libsubcmd
|
||||
|
||||
$(LIBFILE): $(SUBCMD_IN)
|
||||
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(SUBCMD_IN)
|
||||
|
||||
clean:
|
||||
$(call QUIET_CLEAN, libsubcmd) $(RM) $(LIBFILE); \
|
||||
find $(if $(OUTPUT),$(OUTPUT),.) -name \*.o -or -name \*.o.cmd -or -name \*.o.d | xargs $(RM)
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: clean FORCE
|
209
tools/lib/subcmd/exec-cmd.c
Normal file
209
tools/lib/subcmd/exec-cmd.c
Normal file
@@ -0,0 +1,209 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "subcmd-util.h"
|
||||
#include "exec-cmd.h"
|
||||
#include "subcmd-config.h"
|
||||
|
||||
#define MAX_ARGS 32
|
||||
#define PATH_MAX 4096
|
||||
|
||||
static const char *argv_exec_path;
|
||||
static const char *argv0_path;
|
||||
|
||||
void exec_cmd_init(const char *exec_name, const char *prefix,
|
||||
const char *exec_path, const char *exec_path_env)
|
||||
{
|
||||
subcmd_config.exec_name = exec_name;
|
||||
subcmd_config.prefix = prefix;
|
||||
subcmd_config.exec_path = exec_path;
|
||||
subcmd_config.exec_path_env = exec_path_env;
|
||||
}
|
||||
|
||||
#define is_dir_sep(c) ((c) == '/')
|
||||
|
||||
static int is_absolute_path(const char *path)
|
||||
{
|
||||
return path[0] == '/';
|
||||
}
|
||||
|
||||
static const char *get_pwd_cwd(void)
|
||||
{
|
||||
static char cwd[PATH_MAX + 1];
|
||||
char *pwd;
|
||||
struct stat cwd_stat, pwd_stat;
|
||||
if (getcwd(cwd, PATH_MAX) == NULL)
|
||||
return NULL;
|
||||
pwd = getenv("PWD");
|
||||
if (pwd && strcmp(pwd, cwd)) {
|
||||
stat(cwd, &cwd_stat);
|
||||
if (!stat(pwd, &pwd_stat) &&
|
||||
pwd_stat.st_dev == cwd_stat.st_dev &&
|
||||
pwd_stat.st_ino == cwd_stat.st_ino) {
|
||||
strlcpy(cwd, pwd, PATH_MAX);
|
||||
}
|
||||
}
|
||||
return cwd;
|
||||
}
|
||||
|
||||
static const char *make_nonrelative_path(const char *path)
|
||||
{
|
||||
static char buf[PATH_MAX + 1];
|
||||
|
||||
if (is_absolute_path(path)) {
|
||||
if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
|
||||
die("Too long path: %.*s", 60, path);
|
||||
} else {
|
||||
const char *cwd = get_pwd_cwd();
|
||||
if (!cwd)
|
||||
die("Cannot determine the current working directory");
|
||||
if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
|
||||
die("Too long path: %.*s", 60, path);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *system_path(const char *path)
|
||||
{
|
||||
char *buf = NULL;
|
||||
|
||||
if (is_absolute_path(path))
|
||||
return strdup(path);
|
||||
|
||||
astrcatf(&buf, "%s/%s", subcmd_config.prefix, path);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *extract_argv0_path(const char *argv0)
|
||||
{
|
||||
const char *slash;
|
||||
|
||||
if (!argv0 || !*argv0)
|
||||
return NULL;
|
||||
slash = argv0 + strlen(argv0);
|
||||
|
||||
while (argv0 <= slash && !is_dir_sep(*slash))
|
||||
slash--;
|
||||
|
||||
if (slash >= argv0) {
|
||||
argv0_path = strndup(argv0, slash - argv0);
|
||||
return argv0_path ? slash + 1 : NULL;
|
||||
}
|
||||
|
||||
return argv0;
|
||||
}
|
||||
|
||||
void set_argv_exec_path(const char *exec_path)
|
||||
{
|
||||
argv_exec_path = exec_path;
|
||||
/*
|
||||
* Propagate this setting to external programs.
|
||||
*/
|
||||
setenv(subcmd_config.exec_path_env, exec_path, 1);
|
||||
}
|
||||
|
||||
|
||||
/* Returns the highest-priority location to look for subprograms. */
|
||||
char *get_argv_exec_path(void)
|
||||
{
|
||||
char *env;
|
||||
|
||||
if (argv_exec_path)
|
||||
return strdup(argv_exec_path);
|
||||
|
||||
env = getenv(subcmd_config.exec_path_env);
|
||||
if (env && *env)
|
||||
return strdup(env);
|
||||
|
||||
return system_path(subcmd_config.exec_path);
|
||||
}
|
||||
|
||||
static void add_path(char **out, const char *path)
|
||||
{
|
||||
if (path && *path) {
|
||||
if (is_absolute_path(path))
|
||||
astrcat(out, path);
|
||||
else
|
||||
astrcat(out, make_nonrelative_path(path));
|
||||
|
||||
astrcat(out, ":");
|
||||
}
|
||||
}
|
||||
|
||||
void setup_path(void)
|
||||
{
|
||||
const char *old_path = getenv("PATH");
|
||||
char *new_path = NULL;
|
||||
char *tmp = get_argv_exec_path();
|
||||
|
||||
add_path(&new_path, tmp);
|
||||
add_path(&new_path, argv0_path);
|
||||
free(tmp);
|
||||
|
||||
if (old_path)
|
||||
astrcat(&new_path, old_path);
|
||||
else
|
||||
astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin");
|
||||
|
||||
setenv("PATH", new_path, 1);
|
||||
|
||||
free(new_path);
|
||||
}
|
||||
|
||||
static const char **prepare_exec_cmd(const char **argv)
|
||||
{
|
||||
int argc;
|
||||
const char **nargv;
|
||||
|
||||
for (argc = 0; argv[argc]; argc++)
|
||||
; /* just counting */
|
||||
nargv = malloc(sizeof(*nargv) * (argc + 2));
|
||||
|
||||
nargv[0] = subcmd_config.exec_name;
|
||||
for (argc = 0; argv[argc]; argc++)
|
||||
nargv[argc + 1] = argv[argc];
|
||||
nargv[argc + 1] = NULL;
|
||||
return nargv;
|
||||
}
|
||||
|
||||
int execv_cmd(const char **argv) {
|
||||
const char **nargv = prepare_exec_cmd(argv);
|
||||
|
||||
/* execvp() can only ever return if it fails */
|
||||
execvp(subcmd_config.exec_name, (char **)nargv);
|
||||
|
||||
free(nargv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int execl_cmd(const char *cmd,...)
|
||||
{
|
||||
int argc;
|
||||
const char *argv[MAX_ARGS + 1];
|
||||
const char *arg;
|
||||
va_list param;
|
||||
|
||||
va_start(param, cmd);
|
||||
argv[0] = cmd;
|
||||
argc = 1;
|
||||
while (argc < MAX_ARGS) {
|
||||
arg = argv[argc++] = va_arg(param, char *);
|
||||
if (!arg)
|
||||
break;
|
||||
}
|
||||
va_end(param);
|
||||
if (MAX_ARGS <= argc) {
|
||||
fprintf(stderr, " Error: too many args to run %s\n", cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
argv[argc] = NULL;
|
||||
return execv_cmd(argv);
|
||||
}
|
16
tools/lib/subcmd/exec-cmd.h
Normal file
16
tools/lib/subcmd/exec-cmd.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef __PERF_EXEC_CMD_H
|
||||
#define __PERF_EXEC_CMD_H
|
||||
|
||||
extern void exec_cmd_init(const char *exec_name, const char *prefix,
|
||||
const char *exec_path, const char *exec_path_env);
|
||||
|
||||
extern void set_argv_exec_path(const char *exec_path);
|
||||
extern const char *extract_argv0_path(const char *path);
|
||||
extern void setup_path(void);
|
||||
extern int execv_cmd(const char **argv); /* NULL terminated */
|
||||
extern int execl_cmd(const char *cmd, ...);
|
||||
/* get_argv_exec_path and system_path return malloc'd string, caller must free it */
|
||||
extern char *get_argv_exec_path(void);
|
||||
extern char *system_path(const char *path);
|
||||
|
||||
#endif /* __PERF_EXEC_CMD_H */
|
268
tools/lib/subcmd/help.c
Normal file
268
tools/lib/subcmd/help.c
Normal file
@@ -0,0 +1,268 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include "subcmd-util.h"
|
||||
#include "help.h"
|
||||
#include "exec-cmd.h"
|
||||
|
||||
void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
|
||||
{
|
||||
struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
|
||||
|
||||
ent->len = len;
|
||||
memcpy(ent->name, name, len);
|
||||
ent->name[len] = 0;
|
||||
|
||||
ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
|
||||
cmds->names[cmds->cnt++] = ent;
|
||||
}
|
||||
|
||||
void clean_cmdnames(struct cmdnames *cmds)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < cmds->cnt; ++i)
|
||||
zfree(&cmds->names[i]);
|
||||
zfree(&cmds->names);
|
||||
cmds->cnt = 0;
|
||||
cmds->alloc = 0;
|
||||
}
|
||||
|
||||
int cmdname_compare(const void *a_, const void *b_)
|
||||
{
|
||||
struct cmdname *a = *(struct cmdname **)a_;
|
||||
struct cmdname *b = *(struct cmdname **)b_;
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
|
||||
void uniq(struct cmdnames *cmds)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
if (!cmds->cnt)
|
||||
return;
|
||||
|
||||
for (i = j = 1; i < cmds->cnt; i++)
|
||||
if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name))
|
||||
cmds->names[j++] = cmds->names[i];
|
||||
|
||||
cmds->cnt = j;
|
||||
}
|
||||
|
||||
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
|
||||
{
|
||||
size_t ci, cj, ei;
|
||||
int cmp;
|
||||
|
||||
ci = cj = ei = 0;
|
||||
while (ci < cmds->cnt && ei < excludes->cnt) {
|
||||
cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
|
||||
if (cmp < 0)
|
||||
cmds->names[cj++] = cmds->names[ci++];
|
||||
else if (cmp == 0)
|
||||
ci++, ei++;
|
||||
else if (cmp > 0)
|
||||
ei++;
|
||||
}
|
||||
|
||||
while (ci < cmds->cnt)
|
||||
cmds->names[cj++] = cmds->names[ci++];
|
||||
|
||||
cmds->cnt = cj;
|
||||
}
|
||||
|
||||
static void get_term_dimensions(struct winsize *ws)
|
||||
{
|
||||
char *s = getenv("LINES");
|
||||
|
||||
if (s != NULL) {
|
||||
ws->ws_row = atoi(s);
|
||||
s = getenv("COLUMNS");
|
||||
if (s != NULL) {
|
||||
ws->ws_col = atoi(s);
|
||||
if (ws->ws_row && ws->ws_col)
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifdef TIOCGWINSZ
|
||||
if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
|
||||
ws->ws_row && ws->ws_col)
|
||||
return;
|
||||
#endif
|
||||
ws->ws_row = 25;
|
||||
ws->ws_col = 80;
|
||||
}
|
||||
|
||||
static void pretty_print_string_list(struct cmdnames *cmds, int longest)
|
||||
{
|
||||
int cols = 1, rows;
|
||||
int space = longest + 1; /* min 1 SP between words */
|
||||
struct winsize win;
|
||||
int max_cols;
|
||||
int i, j;
|
||||
|
||||
get_term_dimensions(&win);
|
||||
max_cols = win.ws_col - 1; /* don't print *on* the edge */
|
||||
|
||||
if (space < max_cols)
|
||||
cols = max_cols / space;
|
||||
rows = (cmds->cnt + cols - 1) / cols;
|
||||
|
||||
for (i = 0; i < rows; i++) {
|
||||
printf(" ");
|
||||
|
||||
for (j = 0; j < cols; j++) {
|
||||
unsigned int n = j * rows + i;
|
||||
unsigned int size = space;
|
||||
|
||||
if (n >= cmds->cnt)
|
||||
break;
|
||||
if (j == cols-1 || n + rows >= cmds->cnt)
|
||||
size = 1;
|
||||
printf("%-*s", size, cmds->names[n]->name);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
static int is_executable(const char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(name, &st) || /* stat, not lstat */
|
||||
!S_ISREG(st.st_mode))
|
||||
return 0;
|
||||
|
||||
return st.st_mode & S_IXUSR;
|
||||
}
|
||||
|
||||
static int has_extension(const char *filename, const char *ext)
|
||||
{
|
||||
size_t len = strlen(filename);
|
||||
size_t extlen = strlen(ext);
|
||||
|
||||
return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
|
||||
}
|
||||
|
||||
static void list_commands_in_dir(struct cmdnames *cmds,
|
||||
const char *path,
|
||||
const char *prefix)
|
||||
{
|
||||
int prefix_len;
|
||||
DIR *dir = opendir(path);
|
||||
struct dirent *de;
|
||||
char *buf = NULL;
|
||||
|
||||
if (!dir)
|
||||
return;
|
||||
if (!prefix)
|
||||
prefix = "perf-";
|
||||
prefix_len = strlen(prefix);
|
||||
|
||||
astrcatf(&buf, "%s/", path);
|
||||
|
||||
while ((de = readdir(dir)) != NULL) {
|
||||
int entlen;
|
||||
|
||||
if (prefixcmp(de->d_name, prefix))
|
||||
continue;
|
||||
|
||||
astrcat(&buf, de->d_name);
|
||||
if (!is_executable(buf))
|
||||
continue;
|
||||
|
||||
entlen = strlen(de->d_name) - prefix_len;
|
||||
if (has_extension(de->d_name, ".exe"))
|
||||
entlen -= 4;
|
||||
|
||||
add_cmdname(cmds, de->d_name + prefix_len, entlen);
|
||||
}
|
||||
closedir(dir);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void load_command_list(const char *prefix,
|
||||
struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds)
|
||||
{
|
||||
const char *env_path = getenv("PATH");
|
||||
char *exec_path = get_argv_exec_path();
|
||||
|
||||
if (exec_path) {
|
||||
list_commands_in_dir(main_cmds, exec_path, prefix);
|
||||
qsort(main_cmds->names, main_cmds->cnt,
|
||||
sizeof(*main_cmds->names), cmdname_compare);
|
||||
uniq(main_cmds);
|
||||
}
|
||||
|
||||
if (env_path) {
|
||||
char *paths, *path, *colon;
|
||||
path = paths = strdup(env_path);
|
||||
while (1) {
|
||||
if ((colon = strchr(path, ':')))
|
||||
*colon = 0;
|
||||
if (!exec_path || strcmp(path, exec_path))
|
||||
list_commands_in_dir(other_cmds, path, prefix);
|
||||
|
||||
if (!colon)
|
||||
break;
|
||||
path = colon + 1;
|
||||
}
|
||||
free(paths);
|
||||
|
||||
qsort(other_cmds->names, other_cmds->cnt,
|
||||
sizeof(*other_cmds->names), cmdname_compare);
|
||||
uniq(other_cmds);
|
||||
}
|
||||
free(exec_path);
|
||||
exclude_cmds(other_cmds, main_cmds);
|
||||
}
|
||||
|
||||
void list_commands(const char *title, struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds)
|
||||
{
|
||||
unsigned int i, longest = 0;
|
||||
|
||||
for (i = 0; i < main_cmds->cnt; i++)
|
||||
if (longest < main_cmds->names[i]->len)
|
||||
longest = main_cmds->names[i]->len;
|
||||
for (i = 0; i < other_cmds->cnt; i++)
|
||||
if (longest < other_cmds->names[i]->len)
|
||||
longest = other_cmds->names[i]->len;
|
||||
|
||||
if (main_cmds->cnt) {
|
||||
char *exec_path = get_argv_exec_path();
|
||||
printf("available %s in '%s'\n", title, exec_path);
|
||||
printf("----------------");
|
||||
mput_char('-', strlen(title) + strlen(exec_path));
|
||||
putchar('\n');
|
||||
pretty_print_string_list(main_cmds, longest);
|
||||
putchar('\n');
|
||||
free(exec_path);
|
||||
}
|
||||
|
||||
if (other_cmds->cnt) {
|
||||
printf("%s available from elsewhere on your $PATH\n", title);
|
||||
printf("---------------------------------------");
|
||||
mput_char('-', strlen(title));
|
||||
putchar('\n');
|
||||
pretty_print_string_list(other_cmds, longest);
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
int is_in_cmdlist(struct cmdnames *c, const char *s)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < c->cnt; i++)
|
||||
if (!strcmp(s, c->names[i]->name))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
34
tools/lib/subcmd/help.h
Normal file
34
tools/lib/subcmd/help.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef __PERF_HELP_H
|
||||
#define __PERF_HELP_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
struct cmdnames {
|
||||
size_t alloc;
|
||||
size_t cnt;
|
||||
struct cmdname {
|
||||
size_t len; /* also used for similarity index in help.c */
|
||||
char name[];
|
||||
} **names;
|
||||
};
|
||||
|
||||
static inline void mput_char(char c, unsigned int num)
|
||||
{
|
||||
while(num--)
|
||||
putchar(c);
|
||||
}
|
||||
|
||||
void load_command_list(const char *prefix,
|
||||
struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds);
|
||||
void add_cmdname(struct cmdnames *cmds, const char *name, size_t len);
|
||||
void clean_cmdnames(struct cmdnames *cmds);
|
||||
int cmdname_compare(const void *a, const void *b);
|
||||
void uniq(struct cmdnames *cmds);
|
||||
/* Here we require that excludes is a sorted list. */
|
||||
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
|
||||
int is_in_cmdlist(struct cmdnames *c, const char *s);
|
||||
void list_commands(const char *title, struct cmdnames *main_cmds,
|
||||
struct cmdnames *other_cmds);
|
||||
|
||||
#endif /* __PERF_HELP_H */
|
100
tools/lib/subcmd/pager.c
Normal file
100
tools/lib/subcmd/pager.c
Normal file
@@ -0,0 +1,100 @@
|
||||
#include <sys/select.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include "pager.h"
|
||||
#include "run-command.h"
|
||||
#include "sigchain.h"
|
||||
#include "subcmd-config.h"
|
||||
|
||||
/*
|
||||
* This is split up from the rest of git so that we can do
|
||||
* something different on Windows.
|
||||
*/
|
||||
|
||||
static int spawned_pager;
|
||||
|
||||
void pager_init(const char *pager_env)
|
||||
{
|
||||
subcmd_config.pager_env = pager_env;
|
||||
}
|
||||
|
||||
static void pager_preexec(void)
|
||||
{
|
||||
/*
|
||||
* Work around bug in "less" by not starting it until we
|
||||
* have real input
|
||||
*/
|
||||
fd_set in;
|
||||
|
||||
FD_ZERO(&in);
|
||||
FD_SET(0, &in);
|
||||
select(1, &in, NULL, &in, NULL);
|
||||
|
||||
setenv("LESS", "FRSX", 0);
|
||||
}
|
||||
|
||||
static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
|
||||
static struct child_process pager_process;
|
||||
|
||||
static void wait_for_pager(void)
|
||||
{
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
/* signal EOF to pager */
|
||||
close(1);
|
||||
close(2);
|
||||
finish_command(&pager_process);
|
||||
}
|
||||
|
||||
static void wait_for_pager_signal(int signo)
|
||||
{
|
||||
wait_for_pager();
|
||||
sigchain_pop(signo);
|
||||
raise(signo);
|
||||
}
|
||||
|
||||
void setup_pager(void)
|
||||
{
|
||||
const char *pager = getenv(subcmd_config.pager_env);
|
||||
|
||||
if (!isatty(1))
|
||||
return;
|
||||
if (!pager)
|
||||
pager = getenv("PAGER");
|
||||
if (!(pager || access("/usr/bin/pager", X_OK)))
|
||||
pager = "/usr/bin/pager";
|
||||
if (!(pager || access("/usr/bin/less", X_OK)))
|
||||
pager = "/usr/bin/less";
|
||||
if (!pager)
|
||||
pager = "cat";
|
||||
if (!*pager || !strcmp(pager, "cat"))
|
||||
return;
|
||||
|
||||
spawned_pager = 1; /* means we are emitting to terminal */
|
||||
|
||||
/* spawn the pager */
|
||||
pager_argv[2] = pager;
|
||||
pager_process.argv = pager_argv;
|
||||
pager_process.in = -1;
|
||||
pager_process.preexec_cb = pager_preexec;
|
||||
|
||||
if (start_command(&pager_process))
|
||||
return;
|
||||
|
||||
/* original process continues, but writes to the pipe */
|
||||
dup2(pager_process.in, 1);
|
||||
if (isatty(2))
|
||||
dup2(pager_process.in, 2);
|
||||
close(pager_process.in);
|
||||
|
||||
/* this makes sure that the parent terminates after the pager */
|
||||
sigchain_push_common(wait_for_pager_signal);
|
||||
atexit(wait_for_pager);
|
||||
}
|
||||
|
||||
int pager_in_use(void)
|
||||
{
|
||||
return spawned_pager;
|
||||
}
|
9
tools/lib/subcmd/pager.h
Normal file
9
tools/lib/subcmd/pager.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef __PERF_PAGER_H
|
||||
#define __PERF_PAGER_H
|
||||
|
||||
extern void pager_init(const char *pager_env);
|
||||
|
||||
extern void setup_pager(void);
|
||||
extern int pager_in_use(void);
|
||||
|
||||
#endif /* __PERF_PAGER_H */
|
983
tools/lib/subcmd/parse-options.c
Normal file
983
tools/lib/subcmd/parse-options.c
Normal file
@@ -0,0 +1,983 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "subcmd-util.h"
|
||||
#include "parse-options.h"
|
||||
#include "subcmd-config.h"
|
||||
#include "pager.h"
|
||||
|
||||
#define OPT_SHORT 1
|
||||
#define OPT_UNSET 2
|
||||
|
||||
char *error_buf;
|
||||
|
||||
static int opterror(const struct option *opt, const char *reason, int flags)
|
||||
{
|
||||
if (flags & OPT_SHORT)
|
||||
fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason);
|
||||
else if (flags & OPT_UNSET)
|
||||
fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason);
|
||||
else
|
||||
fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *skip_prefix(const char *str, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
return strncmp(str, prefix, len) ? NULL : str + len;
|
||||
}
|
||||
|
||||
static void optwarning(const struct option *opt, const char *reason, int flags)
|
||||
{
|
||||
if (flags & OPT_SHORT)
|
||||
fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason);
|
||||
else if (flags & OPT_UNSET)
|
||||
fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason);
|
||||
else
|
||||
fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason);
|
||||
}
|
||||
|
||||
static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
|
||||
int flags, const char **arg)
|
||||
{
|
||||
const char *res;
|
||||
|
||||
if (p->opt) {
|
||||
res = p->opt;
|
||||
p->opt = NULL;
|
||||
} else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
|
||||
**(p->argv + 1) == '-')) {
|
||||
res = (const char *)opt->defval;
|
||||
} else if (p->argc > 1) {
|
||||
p->argc--;
|
||||
res = *++p->argv;
|
||||
} else
|
||||
return opterror(opt, "requires a value", flags);
|
||||
if (arg)
|
||||
*arg = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_value(struct parse_opt_ctx_t *p,
|
||||
const struct option *opt, int flags)
|
||||
{
|
||||
const char *s, *arg = NULL;
|
||||
const int unset = flags & OPT_UNSET;
|
||||
int err;
|
||||
|
||||
if (unset && p->opt)
|
||||
return opterror(opt, "takes no value", flags);
|
||||
if (unset && (opt->flags & PARSE_OPT_NONEG))
|
||||
return opterror(opt, "isn't available", flags);
|
||||
if (opt->flags & PARSE_OPT_DISABLED)
|
||||
return opterror(opt, "is not usable", flags);
|
||||
|
||||
if (opt->flags & PARSE_OPT_EXCLUSIVE) {
|
||||
if (p->excl_opt && p->excl_opt != opt) {
|
||||
char msg[128];
|
||||
|
||||
if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
|
||||
p->excl_opt->long_name == NULL) {
|
||||
snprintf(msg, sizeof(msg), "cannot be used with switch `%c'",
|
||||
p->excl_opt->short_name);
|
||||
} else {
|
||||
snprintf(msg, sizeof(msg), "cannot be used with %s",
|
||||
p->excl_opt->long_name);
|
||||
}
|
||||
opterror(opt, msg, flags);
|
||||
return -3;
|
||||
}
|
||||
p->excl_opt = opt;
|
||||
}
|
||||
if (!(flags & OPT_SHORT) && p->opt) {
|
||||
switch (opt->type) {
|
||||
case OPTION_CALLBACK:
|
||||
if (!(opt->flags & PARSE_OPT_NOARG))
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
case OPTION_BOOLEAN:
|
||||
case OPTION_INCR:
|
||||
case OPTION_BIT:
|
||||
case OPTION_SET_UINT:
|
||||
case OPTION_SET_PTR:
|
||||
return opterror(opt, "takes no value", flags);
|
||||
case OPTION_END:
|
||||
case OPTION_ARGUMENT:
|
||||
case OPTION_GROUP:
|
||||
case OPTION_STRING:
|
||||
case OPTION_INTEGER:
|
||||
case OPTION_UINTEGER:
|
||||
case OPTION_LONG:
|
||||
case OPTION_U64:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt->flags & PARSE_OPT_NOBUILD) {
|
||||
char reason[128];
|
||||
bool noarg = false;
|
||||
|
||||
err = snprintf(reason, sizeof(reason),
|
||||
opt->flags & PARSE_OPT_CANSKIP ?
|
||||
"is being ignored because %s " :
|
||||
"is not available because %s",
|
||||
opt->build_opt);
|
||||
reason[sizeof(reason) - 1] = '\0';
|
||||
|
||||
if (err < 0)
|
||||
strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ?
|
||||
"is being ignored" :
|
||||
"is not available",
|
||||
sizeof(reason));
|
||||
|
||||
if (!(opt->flags & PARSE_OPT_CANSKIP))
|
||||
return opterror(opt, reason, flags);
|
||||
|
||||
err = 0;
|
||||
if (unset)
|
||||
noarg = true;
|
||||
if (opt->flags & PARSE_OPT_NOARG)
|
||||
noarg = true;
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
||||
noarg = true;
|
||||
|
||||
switch (opt->type) {
|
||||
case OPTION_BOOLEAN:
|
||||
case OPTION_INCR:
|
||||
case OPTION_BIT:
|
||||
case OPTION_SET_UINT:
|
||||
case OPTION_SET_PTR:
|
||||
case OPTION_END:
|
||||
case OPTION_ARGUMENT:
|
||||
case OPTION_GROUP:
|
||||
noarg = true;
|
||||
break;
|
||||
case OPTION_CALLBACK:
|
||||
case OPTION_STRING:
|
||||
case OPTION_INTEGER:
|
||||
case OPTION_UINTEGER:
|
||||
case OPTION_LONG:
|
||||
case OPTION_U64:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!noarg)
|
||||
err = get_arg(p, opt, flags, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
optwarning(opt, reason, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (opt->type) {
|
||||
case OPTION_BIT:
|
||||
if (unset)
|
||||
*(int *)opt->value &= ~opt->defval;
|
||||
else
|
||||
*(int *)opt->value |= opt->defval;
|
||||
return 0;
|
||||
|
||||
case OPTION_BOOLEAN:
|
||||
*(bool *)opt->value = unset ? false : true;
|
||||
if (opt->set)
|
||||
*(bool *)opt->set = true;
|
||||
return 0;
|
||||
|
||||
case OPTION_INCR:
|
||||
*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
|
||||
return 0;
|
||||
|
||||
case OPTION_SET_UINT:
|
||||
*(unsigned int *)opt->value = unset ? 0 : opt->defval;
|
||||
return 0;
|
||||
|
||||
case OPTION_SET_PTR:
|
||||
*(void **)opt->value = unset ? NULL : (void *)opt->defval;
|
||||
return 0;
|
||||
|
||||
case OPTION_STRING:
|
||||
err = 0;
|
||||
if (unset)
|
||||
*(const char **)opt->value = NULL;
|
||||
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
||||
*(const char **)opt->value = (const char *)opt->defval;
|
||||
else
|
||||
err = get_arg(p, opt, flags, (const char **)opt->value);
|
||||
|
||||
/* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */
|
||||
if (opt->flags & PARSE_OPT_NOEMPTY) {
|
||||
const char *val = *(const char **)opt->value;
|
||||
|
||||
if (!val)
|
||||
return err;
|
||||
|
||||
/* Similar to unset if we are given an empty string. */
|
||||
if (val[0] == '\0') {
|
||||
*(const char **)opt->value = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
case OPTION_CALLBACK:
|
||||
if (unset)
|
||||
return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
|
||||
if (opt->flags & PARSE_OPT_NOARG)
|
||||
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
||||
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
|
||||
if (get_arg(p, opt, flags, &arg))
|
||||
return -1;
|
||||
return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
|
||||
|
||||
case OPTION_INTEGER:
|
||||
if (unset) {
|
||||
*(int *)opt->value = 0;
|
||||
return 0;
|
||||
}
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
|
||||
*(int *)opt->value = opt->defval;
|
||||
return 0;
|
||||
}
|
||||
if (get_arg(p, opt, flags, &arg))
|
||||
return -1;
|
||||
*(int *)opt->value = strtol(arg, (char **)&s, 10);
|
||||
if (*s)
|
||||
return opterror(opt, "expects a numerical value", flags);
|
||||
return 0;
|
||||
|
||||
case OPTION_UINTEGER:
|
||||
if (unset) {
|
||||
*(unsigned int *)opt->value = 0;
|
||||
return 0;
|
||||
}
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
|
||||
*(unsigned int *)opt->value = opt->defval;
|
||||
return 0;
|
||||
}
|
||||
if (get_arg(p, opt, flags, &arg))
|
||||
return -1;
|
||||
*(unsigned int *)opt->value = strtol(arg, (char **)&s, 10);
|
||||
if (*s)
|
||||
return opterror(opt, "expects a numerical value", flags);
|
||||
return 0;
|
||||
|
||||
case OPTION_LONG:
|
||||
if (unset) {
|
||||
*(long *)opt->value = 0;
|
||||
return 0;
|
||||
}
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
|
||||
*(long *)opt->value = opt->defval;
|
||||
return 0;
|
||||
}
|
||||
if (get_arg(p, opt, flags, &arg))
|
||||
return -1;
|
||||
*(long *)opt->value = strtol(arg, (char **)&s, 10);
|
||||
if (*s)
|
||||
return opterror(opt, "expects a numerical value", flags);
|
||||
return 0;
|
||||
|
||||
case OPTION_U64:
|
||||
if (unset) {
|
||||
*(u64 *)opt->value = 0;
|
||||
return 0;
|
||||
}
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
|
||||
*(u64 *)opt->value = opt->defval;
|
||||
return 0;
|
||||
}
|
||||
if (get_arg(p, opt, flags, &arg))
|
||||
return -1;
|
||||
*(u64 *)opt->value = strtoull(arg, (char **)&s, 10);
|
||||
if (*s)
|
||||
return opterror(opt, "expects a numerical value", flags);
|
||||
return 0;
|
||||
|
||||
case OPTION_END:
|
||||
case OPTION_ARGUMENT:
|
||||
case OPTION_GROUP:
|
||||
default:
|
||||
die("should not happen, someone must be hit on the forehead");
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
|
||||
{
|
||||
for (; options->type != OPTION_END; options++) {
|
||||
if (options->short_name == *p->opt) {
|
||||
p->opt = p->opt[1] ? p->opt + 1 : NULL;
|
||||
return get_value(p, options, OPT_SHORT);
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
|
||||
const struct option *options)
|
||||
{
|
||||
const char *arg_end = strchr(arg, '=');
|
||||
const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
|
||||
int abbrev_flags = 0, ambiguous_flags = 0;
|
||||
|
||||
if (!arg_end)
|
||||
arg_end = arg + strlen(arg);
|
||||
|
||||
for (; options->type != OPTION_END; options++) {
|
||||
const char *rest;
|
||||
int flags = 0;
|
||||
|
||||
if (!options->long_name)
|
||||
continue;
|
||||
|
||||
rest = skip_prefix(arg, options->long_name);
|
||||
if (options->type == OPTION_ARGUMENT) {
|
||||
if (!rest)
|
||||
continue;
|
||||
if (*rest == '=')
|
||||
return opterror(options, "takes no value", flags);
|
||||
if (*rest)
|
||||
continue;
|
||||
p->out[p->cpidx++] = arg - 2;
|
||||
return 0;
|
||||
}
|
||||
if (!rest) {
|
||||
if (!prefixcmp(options->long_name, "no-")) {
|
||||
/*
|
||||
* The long name itself starts with "no-", so
|
||||
* accept the option without "no-" so that users
|
||||
* do not have to enter "no-no-" to get the
|
||||
* negation.
|
||||
*/
|
||||
rest = skip_prefix(arg, options->long_name + 3);
|
||||
if (rest) {
|
||||
flags |= OPT_UNSET;
|
||||
goto match;
|
||||
}
|
||||
/* Abbreviated case */
|
||||
if (!prefixcmp(options->long_name + 3, arg)) {
|
||||
flags |= OPT_UNSET;
|
||||
goto is_abbreviated;
|
||||
}
|
||||
}
|
||||
/* abbreviated? */
|
||||
if (!strncmp(options->long_name, arg, arg_end - arg)) {
|
||||
is_abbreviated:
|
||||
if (abbrev_option) {
|
||||
/*
|
||||
* If this is abbreviated, it is
|
||||
* ambiguous. So when there is no
|
||||
* exact match later, we need to
|
||||
* error out.
|
||||
*/
|
||||
ambiguous_option = abbrev_option;
|
||||
ambiguous_flags = abbrev_flags;
|
||||
}
|
||||
if (!(flags & OPT_UNSET) && *arg_end)
|
||||
p->opt = arg_end + 1;
|
||||
abbrev_option = options;
|
||||
abbrev_flags = flags;
|
||||
continue;
|
||||
}
|
||||
/* negated and abbreviated very much? */
|
||||
if (!prefixcmp("no-", arg)) {
|
||||
flags |= OPT_UNSET;
|
||||
goto is_abbreviated;
|
||||
}
|
||||
/* negated? */
|
||||
if (strncmp(arg, "no-", 3))
|
||||
continue;
|
||||
flags |= OPT_UNSET;
|
||||
rest = skip_prefix(arg + 3, options->long_name);
|
||||
/* abbreviated and negated? */
|
||||
if (!rest && !prefixcmp(options->long_name, arg + 3))
|
||||
goto is_abbreviated;
|
||||
if (!rest)
|
||||
continue;
|
||||
}
|
||||
match:
|
||||
if (*rest) {
|
||||
if (*rest != '=')
|
||||
continue;
|
||||
p->opt = rest + 1;
|
||||
}
|
||||
return get_value(p, options, flags);
|
||||
}
|
||||
|
||||
if (ambiguous_option) {
|
||||
fprintf(stderr,
|
||||
" Error: Ambiguous option: %s (could be --%s%s or --%s%s)",
|
||||
arg,
|
||||
(ambiguous_flags & OPT_UNSET) ? "no-" : "",
|
||||
ambiguous_option->long_name,
|
||||
(abbrev_flags & OPT_UNSET) ? "no-" : "",
|
||||
abbrev_option->long_name);
|
||||
return -1;
|
||||
}
|
||||
if (abbrev_option)
|
||||
return get_value(p, abbrev_option, abbrev_flags);
|
||||
return -2;
|
||||
}
|
||||
|
||||
static void check_typos(const char *arg, const struct option *options)
|
||||
{
|
||||
if (strlen(arg) < 3)
|
||||
return;
|
||||
|
||||
if (!prefixcmp(arg, "no-")) {
|
||||
fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg);
|
||||
exit(129);
|
||||
}
|
||||
|
||||
for (; options->type != OPTION_END; options++) {
|
||||
if (!options->long_name)
|
||||
continue;
|
||||
if (!prefixcmp(options->long_name, arg)) {
|
||||
fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg);
|
||||
exit(129);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_options_start(struct parse_opt_ctx_t *ctx,
|
||||
int argc, const char **argv, int flags)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->argc = argc - 1;
|
||||
ctx->argv = argv + 1;
|
||||
ctx->out = argv;
|
||||
ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
|
||||
ctx->flags = flags;
|
||||
if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
|
||||
(flags & PARSE_OPT_STOP_AT_NON_OPTION))
|
||||
die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
|
||||
}
|
||||
|
||||
static int usage_with_options_internal(const char * const *,
|
||||
const struct option *, int,
|
||||
struct parse_opt_ctx_t *);
|
||||
|
||||
static int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[])
|
||||
{
|
||||
int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
|
||||
int excl_short_opt = 1;
|
||||
const char *arg;
|
||||
|
||||
/* we must reset ->opt, unknown short option leave it dangling */
|
||||
ctx->opt = NULL;
|
||||
|
||||
for (; ctx->argc; ctx->argc--, ctx->argv++) {
|
||||
arg = ctx->argv[0];
|
||||
if (*arg != '-' || !arg[1]) {
|
||||
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
|
||||
break;
|
||||
ctx->out[ctx->cpidx++] = ctx->argv[0];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg[1] != '-') {
|
||||
ctx->opt = ++arg;
|
||||
if (internal_help && *ctx->opt == 'h') {
|
||||
return usage_with_options_internal(usagestr, options, 0, ctx);
|
||||
}
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options, arg, 1);
|
||||
case -2:
|
||||
goto unknown;
|
||||
case -3:
|
||||
goto exclusive;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ctx->opt)
|
||||
check_typos(arg, options);
|
||||
while (ctx->opt) {
|
||||
if (internal_help && *ctx->opt == 'h')
|
||||
return usage_with_options_internal(usagestr, options, 0, ctx);
|
||||
arg = ctx->opt;
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options, arg, 1);
|
||||
case -2:
|
||||
/* fake a short option thing to hide the fact that we may have
|
||||
* started to parse aggregated stuff
|
||||
*
|
||||
* This is leaky, too bad.
|
||||
*/
|
||||
ctx->argv[0] = strdup(ctx->opt - 1);
|
||||
*(char *)ctx->argv[0] = '-';
|
||||
goto unknown;
|
||||
case -3:
|
||||
goto exclusive;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!arg[2]) { /* "--" */
|
||||
if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
|
||||
ctx->argc--;
|
||||
ctx->argv++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
arg += 2;
|
||||
if (internal_help && !strcmp(arg, "help-all"))
|
||||
return usage_with_options_internal(usagestr, options, 1, ctx);
|
||||
if (internal_help && !strcmp(arg, "help"))
|
||||
return usage_with_options_internal(usagestr, options, 0, ctx);
|
||||
if (!strcmp(arg, "list-opts"))
|
||||
return PARSE_OPT_LIST_OPTS;
|
||||
if (!strcmp(arg, "list-cmds"))
|
||||
return PARSE_OPT_LIST_SUBCMDS;
|
||||
switch (parse_long_opt(ctx, arg, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options, arg, 0);
|
||||
case -2:
|
||||
goto unknown;
|
||||
case -3:
|
||||
excl_short_opt = 0;
|
||||
goto exclusive;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
unknown:
|
||||
if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
ctx->out[ctx->cpidx++] = ctx->argv[0];
|
||||
ctx->opt = NULL;
|
||||
}
|
||||
return PARSE_OPT_DONE;
|
||||
|
||||
exclusive:
|
||||
parse_options_usage(usagestr, options, arg, excl_short_opt);
|
||||
if ((excl_short_opt && ctx->excl_opt->short_name) ||
|
||||
ctx->excl_opt->long_name == NULL) {
|
||||
char opt = ctx->excl_opt->short_name;
|
||||
parse_options_usage(NULL, options, &opt, 1);
|
||||
} else {
|
||||
parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0);
|
||||
}
|
||||
return PARSE_OPT_HELP;
|
||||
}
|
||||
|
||||
static int parse_options_end(struct parse_opt_ctx_t *ctx)
|
||||
{
|
||||
memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
|
||||
ctx->out[ctx->cpidx + ctx->argc] = NULL;
|
||||
return ctx->cpidx + ctx->argc;
|
||||
}
|
||||
|
||||
int parse_options_subcommand(int argc, const char **argv, const struct option *options,
|
||||
const char *const subcommands[], const char *usagestr[], int flags)
|
||||
{
|
||||
struct parse_opt_ctx_t ctx;
|
||||
|
||||
/* build usage string if it's not provided */
|
||||
if (subcommands && !usagestr[0]) {
|
||||
char *buf = NULL;
|
||||
|
||||
astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]);
|
||||
|
||||
for (int i = 0; subcommands[i]; i++) {
|
||||
if (i)
|
||||
astrcat(&buf, "|");
|
||||
astrcat(&buf, subcommands[i]);
|
||||
}
|
||||
astrcat(&buf, "}");
|
||||
|
||||
usagestr[0] = buf;
|
||||
}
|
||||
|
||||
parse_options_start(&ctx, argc, argv, flags);
|
||||
switch (parse_options_step(&ctx, options, usagestr)) {
|
||||
case PARSE_OPT_HELP:
|
||||
exit(129);
|
||||
case PARSE_OPT_DONE:
|
||||
break;
|
||||
case PARSE_OPT_LIST_OPTS:
|
||||
while (options->type != OPTION_END) {
|
||||
if (options->long_name)
|
||||
printf("--%s ", options->long_name);
|
||||
options++;
|
||||
}
|
||||
putchar('\n');
|
||||
exit(130);
|
||||
case PARSE_OPT_LIST_SUBCMDS:
|
||||
if (subcommands) {
|
||||
for (int i = 0; subcommands[i]; i++)
|
||||
printf("%s ", subcommands[i]);
|
||||
}
|
||||
putchar('\n');
|
||||
exit(130);
|
||||
default: /* PARSE_OPT_UNKNOWN */
|
||||
if (ctx.argv[0][1] == '-')
|
||||
astrcatf(&error_buf, "unknown option `%s'",
|
||||
ctx.argv[0] + 2);
|
||||
else
|
||||
astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt);
|
||||
usage_with_options(usagestr, options);
|
||||
}
|
||||
|
||||
return parse_options_end(&ctx);
|
||||
}
|
||||
|
||||
int parse_options(int argc, const char **argv, const struct option *options,
|
||||
const char * const usagestr[], int flags)
|
||||
{
|
||||
return parse_options_subcommand(argc, argv, options, NULL,
|
||||
(const char **) usagestr, flags);
|
||||
}
|
||||
|
||||
#define USAGE_OPTS_WIDTH 24
|
||||
#define USAGE_GAP 2
|
||||
|
||||
static void print_option_help(const struct option *opts, int full)
|
||||
{
|
||||
size_t pos;
|
||||
int pad;
|
||||
|
||||
if (opts->type == OPTION_GROUP) {
|
||||
fputc('\n', stderr);
|
||||
if (*opts->help)
|
||||
fprintf(stderr, "%s\n", opts->help);
|
||||
return;
|
||||
}
|
||||
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
|
||||
return;
|
||||
if (opts->flags & PARSE_OPT_DISABLED)
|
||||
return;
|
||||
|
||||
pos = fprintf(stderr, " ");
|
||||
if (opts->short_name)
|
||||
pos += fprintf(stderr, "-%c", opts->short_name);
|
||||
else
|
||||
pos += fprintf(stderr, " ");
|
||||
|
||||
if (opts->long_name && opts->short_name)
|
||||
pos += fprintf(stderr, ", ");
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "--%s", opts->long_name);
|
||||
|
||||
switch (opts->type) {
|
||||
case OPTION_ARGUMENT:
|
||||
break;
|
||||
case OPTION_LONG:
|
||||
case OPTION_U64:
|
||||
case OPTION_INTEGER:
|
||||
case OPTION_UINTEGER:
|
||||
if (opts->flags & PARSE_OPT_OPTARG)
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "[=<n>]");
|
||||
else
|
||||
pos += fprintf(stderr, "[<n>]");
|
||||
else
|
||||
pos += fprintf(stderr, " <n>");
|
||||
break;
|
||||
case OPTION_CALLBACK:
|
||||
if (opts->flags & PARSE_OPT_NOARG)
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
case OPTION_STRING:
|
||||
if (opts->argh) {
|
||||
if (opts->flags & PARSE_OPT_OPTARG)
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "[=<%s>]", opts->argh);
|
||||
else
|
||||
pos += fprintf(stderr, "[<%s>]", opts->argh);
|
||||
else
|
||||
pos += fprintf(stderr, " <%s>", opts->argh);
|
||||
} else {
|
||||
if (opts->flags & PARSE_OPT_OPTARG)
|
||||
if (opts->long_name)
|
||||
pos += fprintf(stderr, "[=...]");
|
||||
else
|
||||
pos += fprintf(stderr, "[...]");
|
||||
else
|
||||
pos += fprintf(stderr, " ...");
|
||||
}
|
||||
break;
|
||||
default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */
|
||||
case OPTION_END:
|
||||
case OPTION_GROUP:
|
||||
case OPTION_BIT:
|
||||
case OPTION_BOOLEAN:
|
||||
case OPTION_INCR:
|
||||
case OPTION_SET_UINT:
|
||||
case OPTION_SET_PTR:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos <= USAGE_OPTS_WIDTH)
|
||||
pad = USAGE_OPTS_WIDTH - pos;
|
||||
else {
|
||||
fputc('\n', stderr);
|
||||
pad = USAGE_OPTS_WIDTH;
|
||||
}
|
||||
fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
|
||||
if (opts->flags & PARSE_OPT_NOBUILD)
|
||||
fprintf(stderr, "%*s(not built-in because %s)\n",
|
||||
USAGE_OPTS_WIDTH + USAGE_GAP, "",
|
||||
opts->build_opt);
|
||||
}
|
||||
|
||||
static int option__cmp(const void *va, const void *vb)
|
||||
{
|
||||
const struct option *a = va, *b = vb;
|
||||
int sa = tolower(a->short_name), sb = tolower(b->short_name), ret;
|
||||
|
||||
if (sa == 0)
|
||||
sa = 'z' + 1;
|
||||
if (sb == 0)
|
||||
sb = 'z' + 1;
|
||||
|
||||
ret = sa - sb;
|
||||
|
||||
if (ret == 0) {
|
||||
const char *la = a->long_name ?: "",
|
||||
*lb = b->long_name ?: "";
|
||||
ret = strcmp(la, lb);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct option *options__order(const struct option *opts)
|
||||
{
|
||||
int nr_opts = 0, len;
|
||||
const struct option *o = opts;
|
||||
struct option *ordered;
|
||||
|
||||
for (o = opts; o->type != OPTION_END; o++)
|
||||
++nr_opts;
|
||||
|
||||
len = sizeof(*o) * (nr_opts + 1);
|
||||
ordered = malloc(len);
|
||||
if (!ordered)
|
||||
goto out;
|
||||
memcpy(ordered, opts, len);
|
||||
|
||||
qsort(ordered, nr_opts, sizeof(*o), option__cmp);
|
||||
out:
|
||||
return ordered;
|
||||
}
|
||||
|
||||
static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < ctx->argc; ++i) {
|
||||
const char *arg = ctx->argv[i];
|
||||
|
||||
if (arg[0] != '-') {
|
||||
if (arg[1] == '\0') {
|
||||
if (arg[0] == opt->short_name)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt->long_name && strcmp(opt->long_name, arg) == 0)
|
||||
return true;
|
||||
|
||||
if (opt->help && strcasestr(opt->help, arg) != NULL)
|
||||
return true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg[1] == opt->short_name ||
|
||||
(arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int usage_with_options_internal(const char * const *usagestr,
|
||||
const struct option *opts, int full,
|
||||
struct parse_opt_ctx_t *ctx)
|
||||
{
|
||||
struct option *ordered;
|
||||
|
||||
if (!usagestr)
|
||||
return PARSE_OPT_HELP;
|
||||
|
||||
setup_pager();
|
||||
|
||||
if (error_buf) {
|
||||
fprintf(stderr, " Error: %s\n", error_buf);
|
||||
zfree(&error_buf);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n Usage: %s\n", *usagestr++);
|
||||
while (*usagestr && **usagestr)
|
||||
fprintf(stderr, " or: %s\n", *usagestr++);
|
||||
while (*usagestr) {
|
||||
fprintf(stderr, "%s%s\n",
|
||||
**usagestr ? " " : "",
|
||||
*usagestr);
|
||||
usagestr++;
|
||||
}
|
||||
|
||||
if (opts->type != OPTION_GROUP)
|
||||
fputc('\n', stderr);
|
||||
|
||||
ordered = options__order(opts);
|
||||
if (ordered)
|
||||
opts = ordered;
|
||||
|
||||
for ( ; opts->type != OPTION_END; opts++) {
|
||||
if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx))
|
||||
continue;
|
||||
print_option_help(opts, full);
|
||||
}
|
||||
|
||||
fputc('\n', stderr);
|
||||
|
||||
free(ordered);
|
||||
|
||||
return PARSE_OPT_HELP;
|
||||
}
|
||||
|
||||
void usage_with_options(const char * const *usagestr,
|
||||
const struct option *opts)
|
||||
{
|
||||
usage_with_options_internal(usagestr, opts, 0, NULL);
|
||||
exit(129);
|
||||
}
|
||||
|
||||
void usage_with_options_msg(const char * const *usagestr,
|
||||
const struct option *opts, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *tmp = error_buf;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (vasprintf(&error_buf, fmt, ap) == -1)
|
||||
die("vasprintf failed");
|
||||
va_end(ap);
|
||||
|
||||
free(tmp);
|
||||
|
||||
usage_with_options_internal(usagestr, opts, 0, NULL);
|
||||
exit(129);
|
||||
}
|
||||
|
||||
int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts,
|
||||
const char *optstr, bool short_opt)
|
||||
{
|
||||
if (!usagestr)
|
||||
goto opt;
|
||||
|
||||
fprintf(stderr, "\n Usage: %s\n", *usagestr++);
|
||||
while (*usagestr && **usagestr)
|
||||
fprintf(stderr, " or: %s\n", *usagestr++);
|
||||
while (*usagestr) {
|
||||
fprintf(stderr, "%s%s\n",
|
||||
**usagestr ? " " : "",
|
||||
*usagestr);
|
||||
usagestr++;
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
|
||||
opt:
|
||||
for ( ; opts->type != OPTION_END; opts++) {
|
||||
if (short_opt) {
|
||||
if (opts->short_name == *optstr) {
|
||||
print_option_help(opts, 0);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opts->long_name == NULL)
|
||||
continue;
|
||||
|
||||
if (!prefixcmp(opts->long_name, optstr))
|
||||
print_option_help(opts, 0);
|
||||
if (!prefixcmp("no-", optstr) &&
|
||||
!prefixcmp(opts->long_name, optstr + 3))
|
||||
print_option_help(opts, 0);
|
||||
}
|
||||
|
||||
return PARSE_OPT_HELP;
|
||||
}
|
||||
|
||||
|
||||
int parse_opt_verbosity_cb(const struct option *opt,
|
||||
const char *arg __maybe_unused,
|
||||
int unset)
|
||||
{
|
||||
int *target = opt->value;
|
||||
|
||||
if (unset)
|
||||
/* --no-quiet, --no-verbose */
|
||||
*target = 0;
|
||||
else if (opt->short_name == 'v') {
|
||||
if (*target >= 0)
|
||||
(*target)++;
|
||||
else
|
||||
*target = 1;
|
||||
} else {
|
||||
if (*target <= 0)
|
||||
(*target)--;
|
||||
else
|
||||
*target = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct option *
|
||||
find_option(struct option *opts, int shortopt, const char *longopt)
|
||||
{
|
||||
for (; opts->type != OPTION_END; opts++) {
|
||||
if ((shortopt && opts->short_name == shortopt) ||
|
||||
(opts->long_name && longopt &&
|
||||
!strcmp(opts->long_name, longopt)))
|
||||
return opts;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void set_option_flag(struct option *opts, int shortopt, const char *longopt,
|
||||
int flag)
|
||||
{
|
||||
struct option *opt = find_option(opts, shortopt, longopt);
|
||||
|
||||
if (opt)
|
||||
opt->flags |= flag;
|
||||
return;
|
||||
}
|
||||
|
||||
void set_option_nobuild(struct option *opts, int shortopt,
|
||||
const char *longopt,
|
||||
const char *build_opt,
|
||||
bool can_skip)
|
||||
{
|
||||
struct option *opt = find_option(opts, shortopt, longopt);
|
||||
|
||||
if (!opt)
|
||||
return;
|
||||
|
||||
opt->flags |= PARSE_OPT_NOBUILD;
|
||||
opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0;
|
||||
opt->build_opt = build_opt;
|
||||
}
|
228
tools/lib/subcmd/parse-options.h
Normal file
228
tools/lib/subcmd/parse-options.h
Normal file
@@ -0,0 +1,228 @@
|
||||
#ifndef __PERF_PARSE_OPTIONS_H
|
||||
#define __PERF_PARSE_OPTIONS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum parse_opt_type {
|
||||
/* special types */
|
||||
OPTION_END,
|
||||
OPTION_ARGUMENT,
|
||||
OPTION_GROUP,
|
||||
/* options with no arguments */
|
||||
OPTION_BIT,
|
||||
OPTION_BOOLEAN,
|
||||
OPTION_INCR,
|
||||
OPTION_SET_UINT,
|
||||
OPTION_SET_PTR,
|
||||
/* options with arguments (usually) */
|
||||
OPTION_STRING,
|
||||
OPTION_INTEGER,
|
||||
OPTION_LONG,
|
||||
OPTION_CALLBACK,
|
||||
OPTION_U64,
|
||||
OPTION_UINTEGER,
|
||||
};
|
||||
|
||||
enum parse_opt_flags {
|
||||
PARSE_OPT_KEEP_DASHDASH = 1,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION = 2,
|
||||
PARSE_OPT_KEEP_ARGV0 = 4,
|
||||
PARSE_OPT_KEEP_UNKNOWN = 8,
|
||||
PARSE_OPT_NO_INTERNAL_HELP = 16,
|
||||
};
|
||||
|
||||
enum parse_opt_option_flags {
|
||||
PARSE_OPT_OPTARG = 1,
|
||||
PARSE_OPT_NOARG = 2,
|
||||
PARSE_OPT_NONEG = 4,
|
||||
PARSE_OPT_HIDDEN = 8,
|
||||
PARSE_OPT_LASTARG_DEFAULT = 16,
|
||||
PARSE_OPT_DISABLED = 32,
|
||||
PARSE_OPT_EXCLUSIVE = 64,
|
||||
PARSE_OPT_NOEMPTY = 128,
|
||||
PARSE_OPT_NOBUILD = 256,
|
||||
PARSE_OPT_CANSKIP = 512,
|
||||
};
|
||||
|
||||
struct option;
|
||||
typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
|
||||
|
||||
/*
|
||||
* `type`::
|
||||
* holds the type of the option, you must have an OPTION_END last in your
|
||||
* array.
|
||||
*
|
||||
* `short_name`::
|
||||
* the character to use as a short option name, '\0' if none.
|
||||
*
|
||||
* `long_name`::
|
||||
* the long option name, without the leading dashes, NULL if none.
|
||||
*
|
||||
* `value`::
|
||||
* stores pointers to the values to be filled.
|
||||
*
|
||||
* `argh`::
|
||||
* token to explain the kind of argument this option wants. Keep it
|
||||
* homogenous across the repository.
|
||||
*
|
||||
* `help`::
|
||||
* the short help associated to what the option does.
|
||||
* Must never be NULL (except for OPTION_END).
|
||||
* OPTION_GROUP uses this pointer to store the group header.
|
||||
*
|
||||
* `flags`::
|
||||
* mask of parse_opt_option_flags.
|
||||
* PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs)
|
||||
* PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
|
||||
* PARSE_OPT_NONEG: says that this option cannot be negated
|
||||
* PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in
|
||||
* the long one.
|
||||
*
|
||||
* `callback`::
|
||||
* pointer to the callback to use for OPTION_CALLBACK.
|
||||
*
|
||||
* `defval`::
|
||||
* default value to fill (*->value) with for PARSE_OPT_OPTARG.
|
||||
* OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in
|
||||
* the value when met.
|
||||
* CALLBACKS can use it like they want.
|
||||
*
|
||||
* `set`::
|
||||
* whether an option was set by the user
|
||||
*/
|
||||
struct option {
|
||||
enum parse_opt_type type;
|
||||
int short_name;
|
||||
const char *long_name;
|
||||
void *value;
|
||||
const char *argh;
|
||||
const char *help;
|
||||
const char *build_opt;
|
||||
|
||||
int flags;
|
||||
parse_opt_cb *callback;
|
||||
intptr_t defval;
|
||||
bool *set;
|
||||
void *data;
|
||||
};
|
||||
|
||||
#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v )
|
||||
|
||||
#define OPT_END() { .type = OPTION_END }
|
||||
#define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) }
|
||||
#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) }
|
||||
#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) }
|
||||
#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) }
|
||||
#define OPT_BOOLEAN_FLAG(s, l, v, h, f) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h), .flags = (f) }
|
||||
#define OPT_BOOLEAN_SET(s, l, v, os, h) \
|
||||
{ .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \
|
||||
.value = check_vtype(v, bool *), .help = (h), \
|
||||
.set = check_vtype(os, bool *)}
|
||||
#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
|
||||
#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) }
|
||||
#define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) }
|
||||
#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) }
|
||||
#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) }
|
||||
#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) }
|
||||
#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) }
|
||||
#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) }
|
||||
#define OPT_STRING_OPTARG(s, l, v, a, h, d) \
|
||||
{ .type = OPTION_STRING, .short_name = (s), .long_name = (l), \
|
||||
.value = check_vtype(v, const char **), (a), .help = (h), \
|
||||
.flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) }
|
||||
#define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h), .flags = PARSE_OPT_NOEMPTY}
|
||||
#define OPT_DATE(s, l, v, h) \
|
||||
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
|
||||
#define OPT_CALLBACK(s, l, v, a, h, f) \
|
||||
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) }
|
||||
#define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \
|
||||
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG }
|
||||
#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \
|
||||
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT }
|
||||
#define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \
|
||||
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\
|
||||
.value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\
|
||||
.flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG}
|
||||
#define OPT_CALLBACK_OPTARG(s, l, v, d, a, h, f) \
|
||||
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \
|
||||
.value = (v), (a), .help = (h), .callback = (f), \
|
||||
.flags = PARSE_OPT_OPTARG, .data = (d) }
|
||||
|
||||
/* parse_options() will filter out the processed options and leave the
|
||||
* non-option argments in argv[].
|
||||
* Returns the number of arguments left in argv[].
|
||||
*
|
||||
* NOTE: parse_options() and parse_options_subcommand() may call exit() in the
|
||||
* case of an error (or for 'special' options like --list-cmds or --list-opts).
|
||||
*/
|
||||
extern int parse_options(int argc, const char **argv,
|
||||
const struct option *options,
|
||||
const char * const usagestr[], int flags);
|
||||
|
||||
extern int parse_options_subcommand(int argc, const char **argv,
|
||||
const struct option *options,
|
||||
const char *const subcommands[],
|
||||
const char *usagestr[], int flags);
|
||||
|
||||
extern NORETURN void usage_with_options(const char * const *usagestr,
|
||||
const struct option *options);
|
||||
extern NORETURN __attribute__((format(printf,3,4)))
|
||||
void usage_with_options_msg(const char * const *usagestr,
|
||||
const struct option *options,
|
||||
const char *fmt, ...);
|
||||
|
||||
/*----- incremantal advanced APIs -----*/
|
||||
|
||||
enum {
|
||||
PARSE_OPT_HELP = -1,
|
||||
PARSE_OPT_DONE,
|
||||
PARSE_OPT_LIST_OPTS,
|
||||
PARSE_OPT_LIST_SUBCMDS,
|
||||
PARSE_OPT_UNKNOWN,
|
||||
};
|
||||
|
||||
/*
|
||||
* It's okay for the caller to consume argv/argc in the usual way.
|
||||
* Other fields of that structure are private to parse-options and should not
|
||||
* be modified in any way.
|
||||
*/
|
||||
struct parse_opt_ctx_t {
|
||||
const char **argv;
|
||||
const char **out;
|
||||
int argc, cpidx;
|
||||
const char *opt;
|
||||
const struct option *excl_opt;
|
||||
int flags;
|
||||
};
|
||||
|
||||
extern int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts,
|
||||
const char *optstr,
|
||||
bool short_opt);
|
||||
|
||||
|
||||
/*----- some often used options -----*/
|
||||
extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
|
||||
|
||||
#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose")
|
||||
#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet")
|
||||
#define OPT__VERBOSITY(var) \
|
||||
{ OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
|
||||
PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
|
||||
{ OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
|
||||
PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
|
||||
#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run")
|
||||
#define OPT__ABBREV(var) \
|
||||
{ OPTION_CALLBACK, 0, "abbrev", (var), "n", \
|
||||
"use <n> digits to display SHA-1s", \
|
||||
PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
|
||||
|
||||
extern const char *parse_options_fix_filename(const char *prefix, const char *file);
|
||||
|
||||
void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag);
|
||||
void set_option_nobuild(struct option *opts, int shortopt, const char *longopt,
|
||||
const char *build_opt, bool can_skip);
|
||||
#endif /* __PERF_PARSE_OPTIONS_H */
|
227
tools/lib/subcmd/run-command.c
Normal file
227
tools/lib/subcmd/run-command.c
Normal file
@@ -0,0 +1,227 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include "subcmd-util.h"
|
||||
#include "run-command.h"
|
||||
#include "exec-cmd.h"
|
||||
|
||||
#define STRERR_BUFSIZE 128
|
||||
|
||||
static inline void close_pair(int fd[2])
|
||||
{
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
}
|
||||
|
||||
static inline void dup_devnull(int to)
|
||||
{
|
||||
int fd = open("/dev/null", O_RDWR);
|
||||
dup2(fd, to);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int start_command(struct child_process *cmd)
|
||||
{
|
||||
int need_in, need_out, need_err;
|
||||
int fdin[2], fdout[2], fderr[2];
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
/*
|
||||
* In case of errors we must keep the promise to close FDs
|
||||
* that have been passed in via ->in and ->out.
|
||||
*/
|
||||
|
||||
need_in = !cmd->no_stdin && cmd->in < 0;
|
||||
if (need_in) {
|
||||
if (pipe(fdin) < 0) {
|
||||
if (cmd->out > 0)
|
||||
close(cmd->out);
|
||||
return -ERR_RUN_COMMAND_PIPE;
|
||||
}
|
||||
cmd->in = fdin[1];
|
||||
}
|
||||
|
||||
need_out = !cmd->no_stdout
|
||||
&& !cmd->stdout_to_stderr
|
||||
&& cmd->out < 0;
|
||||
if (need_out) {
|
||||
if (pipe(fdout) < 0) {
|
||||
if (need_in)
|
||||
close_pair(fdin);
|
||||
else if (cmd->in)
|
||||
close(cmd->in);
|
||||
return -ERR_RUN_COMMAND_PIPE;
|
||||
}
|
||||
cmd->out = fdout[0];
|
||||
}
|
||||
|
||||
need_err = !cmd->no_stderr && cmd->err < 0;
|
||||
if (need_err) {
|
||||
if (pipe(fderr) < 0) {
|
||||
if (need_in)
|
||||
close_pair(fdin);
|
||||
else if (cmd->in)
|
||||
close(cmd->in);
|
||||
if (need_out)
|
||||
close_pair(fdout);
|
||||
else if (cmd->out)
|
||||
close(cmd->out);
|
||||
return -ERR_RUN_COMMAND_PIPE;
|
||||
}
|
||||
cmd->err = fderr[0];
|
||||
}
|
||||
|
||||
fflush(NULL);
|
||||
cmd->pid = fork();
|
||||
if (!cmd->pid) {
|
||||
if (cmd->no_stdin)
|
||||
dup_devnull(0);
|
||||
else if (need_in) {
|
||||
dup2(fdin[0], 0);
|
||||
close_pair(fdin);
|
||||
} else if (cmd->in) {
|
||||
dup2(cmd->in, 0);
|
||||
close(cmd->in);
|
||||
}
|
||||
|
||||
if (cmd->no_stderr)
|
||||
dup_devnull(2);
|
||||
else if (need_err) {
|
||||
dup2(fderr[1], 2);
|
||||
close_pair(fderr);
|
||||
}
|
||||
|
||||
if (cmd->no_stdout)
|
||||
dup_devnull(1);
|
||||
else if (cmd->stdout_to_stderr)
|
||||
dup2(2, 1);
|
||||
else if (need_out) {
|
||||
dup2(fdout[1], 1);
|
||||
close_pair(fdout);
|
||||
} else if (cmd->out > 1) {
|
||||
dup2(cmd->out, 1);
|
||||
close(cmd->out);
|
||||
}
|
||||
|
||||
if (cmd->dir && chdir(cmd->dir))
|
||||
die("exec %s: cd to %s failed (%s)", cmd->argv[0],
|
||||
cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
if (cmd->env) {
|
||||
for (; *cmd->env; cmd->env++) {
|
||||
if (strchr(*cmd->env, '='))
|
||||
putenv((char*)*cmd->env);
|
||||
else
|
||||
unsetenv(*cmd->env);
|
||||
}
|
||||
}
|
||||
if (cmd->preexec_cb)
|
||||
cmd->preexec_cb();
|
||||
if (cmd->exec_cmd) {
|
||||
execv_cmd(cmd->argv);
|
||||
} else {
|
||||
execvp(cmd->argv[0], (char *const*) cmd->argv);
|
||||
}
|
||||
exit(127);
|
||||
}
|
||||
|
||||
if (cmd->pid < 0) {
|
||||
int err = errno;
|
||||
if (need_in)
|
||||
close_pair(fdin);
|
||||
else if (cmd->in)
|
||||
close(cmd->in);
|
||||
if (need_out)
|
||||
close_pair(fdout);
|
||||
else if (cmd->out)
|
||||
close(cmd->out);
|
||||
if (need_err)
|
||||
close_pair(fderr);
|
||||
return err == ENOENT ?
|
||||
-ERR_RUN_COMMAND_EXEC :
|
||||
-ERR_RUN_COMMAND_FORK;
|
||||
}
|
||||
|
||||
if (need_in)
|
||||
close(fdin[0]);
|
||||
else if (cmd->in)
|
||||
close(cmd->in);
|
||||
|
||||
if (need_out)
|
||||
close(fdout[1]);
|
||||
else if (cmd->out)
|
||||
close(cmd->out);
|
||||
|
||||
if (need_err)
|
||||
close(fderr[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_or_whine(pid_t pid)
|
||||
{
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
for (;;) {
|
||||
int status, code;
|
||||
pid_t waiting = waitpid(pid, &status, 0);
|
||||
|
||||
if (waiting < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
fprintf(stderr, " Error: waitpid failed (%s)",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
return -ERR_RUN_COMMAND_WAITPID;
|
||||
}
|
||||
if (waiting != pid)
|
||||
return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
|
||||
if (WIFSIGNALED(status))
|
||||
return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
|
||||
|
||||
if (!WIFEXITED(status))
|
||||
return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
|
||||
code = WEXITSTATUS(status);
|
||||
switch (code) {
|
||||
case 127:
|
||||
return -ERR_RUN_COMMAND_EXEC;
|
||||
case 0:
|
||||
return 0;
|
||||
default:
|
||||
return -code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int finish_command(struct child_process *cmd)
|
||||
{
|
||||
return wait_or_whine(cmd->pid);
|
||||
}
|
||||
|
||||
int run_command(struct child_process *cmd)
|
||||
{
|
||||
int code = start_command(cmd);
|
||||
if (code)
|
||||
return code;
|
||||
return finish_command(cmd);
|
||||
}
|
||||
|
||||
static void prepare_run_command_v_opt(struct child_process *cmd,
|
||||
const char **argv,
|
||||
int opt)
|
||||
{
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
cmd->argv = argv;
|
||||
cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
|
||||
cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
|
||||
cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
|
||||
}
|
||||
|
||||
int run_command_v_opt(const char **argv, int opt)
|
||||
{
|
||||
struct child_process cmd;
|
||||
prepare_run_command_v_opt(&cmd, argv, opt);
|
||||
return run_command(&cmd);
|
||||
}
|
60
tools/lib/subcmd/run-command.h
Normal file
60
tools/lib/subcmd/run-command.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef __PERF_RUN_COMMAND_H
|
||||
#define __PERF_RUN_COMMAND_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
enum {
|
||||
ERR_RUN_COMMAND_FORK = 10000,
|
||||
ERR_RUN_COMMAND_EXEC,
|
||||
ERR_RUN_COMMAND_PIPE,
|
||||
ERR_RUN_COMMAND_WAITPID,
|
||||
ERR_RUN_COMMAND_WAITPID_WRONG_PID,
|
||||
ERR_RUN_COMMAND_WAITPID_SIGNAL,
|
||||
ERR_RUN_COMMAND_WAITPID_NOEXIT,
|
||||
};
|
||||
#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK)
|
||||
|
||||
struct child_process {
|
||||
const char **argv;
|
||||
pid_t pid;
|
||||
/*
|
||||
* Using .in, .out, .err:
|
||||
* - Specify 0 for no redirections (child inherits stdin, stdout,
|
||||
* stderr from parent).
|
||||
* - Specify -1 to have a pipe allocated as follows:
|
||||
* .in: returns the writable pipe end; parent writes to it,
|
||||
* the readable pipe end becomes child's stdin
|
||||
* .out, .err: returns the readable pipe end; parent reads from
|
||||
* it, the writable pipe end becomes child's stdout/stderr
|
||||
* The caller of start_command() must close the returned FDs
|
||||
* after it has completed reading from/writing to it!
|
||||
* - Specify > 0 to set a channel to a particular FD as follows:
|
||||
* .in: a readable FD, becomes child's stdin
|
||||
* .out: a writable FD, becomes child's stdout/stderr
|
||||
* .err > 0 not supported
|
||||
* The specified FD is closed by start_command(), even in case
|
||||
* of errors!
|
||||
*/
|
||||
int in;
|
||||
int out;
|
||||
int err;
|
||||
const char *dir;
|
||||
const char *const *env;
|
||||
unsigned no_stdin:1;
|
||||
unsigned no_stdout:1;
|
||||
unsigned no_stderr:1;
|
||||
unsigned exec_cmd:1; /* if this is to be external sub-command */
|
||||
unsigned stdout_to_stderr:1;
|
||||
void (*preexec_cb)(void);
|
||||
};
|
||||
|
||||
int start_command(struct child_process *);
|
||||
int finish_command(struct child_process *);
|
||||
int run_command(struct child_process *);
|
||||
|
||||
#define RUN_COMMAND_NO_STDIN 1
|
||||
#define RUN_EXEC_CMD 2 /*If this is to be external sub-command */
|
||||
#define RUN_COMMAND_STDOUT_TO_STDERR 4
|
||||
int run_command_v_opt(const char **argv, int opt);
|
||||
|
||||
#endif /* __PERF_RUN_COMMAND_H */
|
53
tools/lib/subcmd/sigchain.c
Normal file
53
tools/lib/subcmd/sigchain.c
Normal file
@@ -0,0 +1,53 @@
|
||||
#include <signal.h>
|
||||
#include "subcmd-util.h"
|
||||
#include "sigchain.h"
|
||||
|
||||
#define SIGCHAIN_MAX_SIGNALS 32
|
||||
|
||||
struct sigchain_signal {
|
||||
sigchain_fun *old;
|
||||
int n;
|
||||
int alloc;
|
||||
};
|
||||
static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS];
|
||||
|
||||
static void check_signum(int sig)
|
||||
{
|
||||
if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS)
|
||||
die("BUG: signal out of range: %d", sig);
|
||||
}
|
||||
|
||||
static int sigchain_push(int sig, sigchain_fun f)
|
||||
{
|
||||
struct sigchain_signal *s = signals + sig;
|
||||
check_signum(sig);
|
||||
|
||||
ALLOC_GROW(s->old, s->n + 1, s->alloc);
|
||||
s->old[s->n] = signal(sig, f);
|
||||
if (s->old[s->n] == SIG_ERR)
|
||||
return -1;
|
||||
s->n++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sigchain_pop(int sig)
|
||||
{
|
||||
struct sigchain_signal *s = signals + sig;
|
||||
check_signum(sig);
|
||||
if (s->n < 1)
|
||||
return 0;
|
||||
|
||||
if (signal(sig, s->old[s->n - 1]) == SIG_ERR)
|
||||
return -1;
|
||||
s->n--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sigchain_push_common(sigchain_fun f)
|
||||
{
|
||||
sigchain_push(SIGINT, f);
|
||||
sigchain_push(SIGHUP, f);
|
||||
sigchain_push(SIGTERM, f);
|
||||
sigchain_push(SIGQUIT, f);
|
||||
sigchain_push(SIGPIPE, f);
|
||||
}
|
10
tools/lib/subcmd/sigchain.h
Normal file
10
tools/lib/subcmd/sigchain.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __PERF_SIGCHAIN_H
|
||||
#define __PERF_SIGCHAIN_H
|
||||
|
||||
typedef void (*sigchain_fun)(int);
|
||||
|
||||
int sigchain_pop(int sig);
|
||||
|
||||
void sigchain_push_common(sigchain_fun f);
|
||||
|
||||
#endif /* __PERF_SIGCHAIN_H */
|
11
tools/lib/subcmd/subcmd-config.c
Normal file
11
tools/lib/subcmd/subcmd-config.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "subcmd-config.h"
|
||||
|
||||
#define UNDEFINED "SUBCMD_HAS_NOT_BEEN_INITIALIZED"
|
||||
|
||||
struct subcmd_config subcmd_config = {
|
||||
.exec_name = UNDEFINED,
|
||||
.prefix = UNDEFINED,
|
||||
.exec_path = UNDEFINED,
|
||||
.exec_path_env = UNDEFINED,
|
||||
.pager_env = UNDEFINED,
|
||||
};
|
14
tools/lib/subcmd/subcmd-config.h
Normal file
14
tools/lib/subcmd/subcmd-config.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __PERF_SUBCMD_CONFIG_H
|
||||
#define __PERF_SUBCMD_CONFIG_H
|
||||
|
||||
struct subcmd_config {
|
||||
const char *exec_name;
|
||||
const char *prefix;
|
||||
const char *exec_path;
|
||||
const char *exec_path_env;
|
||||
const char *pager_env;
|
||||
};
|
||||
|
||||
extern struct subcmd_config subcmd_config;
|
||||
|
||||
#endif /* __PERF_SUBCMD_CONFIG_H */
|
91
tools/lib/subcmd/subcmd-util.h
Normal file
91
tools/lib/subcmd/subcmd-util.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#ifndef __PERF_SUBCMD_UTIL_H
|
||||
#define __PERF_SUBCMD_UTIL_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
|
||||
static inline void report(const char *prefix, const char *err, va_list params)
|
||||
{
|
||||
char msg[1024];
|
||||
vsnprintf(msg, sizeof(msg), err, params);
|
||||
fprintf(stderr, " %s%s\n", prefix, msg);
|
||||
}
|
||||
|
||||
static NORETURN inline void die(const char *err, ...)
|
||||
{
|
||||
va_list params;
|
||||
|
||||
va_start(params, err);
|
||||
report(" Fatal: ", err, params);
|
||||
exit(128);
|
||||
va_end(params);
|
||||
}
|
||||
|
||||
#define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
|
||||
|
||||
#define alloc_nr(x) (((x)+16)*3/2)
|
||||
|
||||
/*
|
||||
* Realloc the buffer pointed at by variable 'x' so that it can hold
|
||||
* at least 'nr' entries; the number of entries currently allocated
|
||||
* is 'alloc', using the standard growing factor alloc_nr() macro.
|
||||
*
|
||||
* DO NOT USE any expression with side-effect for 'x' or 'alloc'.
|
||||
*/
|
||||
#define ALLOC_GROW(x, nr, alloc) \
|
||||
do { \
|
||||
if ((nr) > alloc) { \
|
||||
if (alloc_nr(alloc) < (nr)) \
|
||||
alloc = (nr); \
|
||||
else \
|
||||
alloc = alloc_nr(alloc); \
|
||||
x = xrealloc((x), alloc * sizeof(*(x))); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static inline void *xrealloc(void *ptr, size_t size)
|
||||
{
|
||||
void *ret = realloc(ptr, size);
|
||||
if (!ret && !size)
|
||||
ret = realloc(ptr, 1);
|
||||
if (!ret) {
|
||||
ret = realloc(ptr, size);
|
||||
if (!ret && !size)
|
||||
ret = realloc(ptr, 1);
|
||||
if (!ret)
|
||||
die("Out of memory, realloc failed");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define astrcatf(out, fmt, ...) \
|
||||
({ \
|
||||
char *tmp = *(out); \
|
||||
if (asprintf((out), "%s" fmt, tmp ?: "", ## __VA_ARGS__) == -1) \
|
||||
die("asprintf failed"); \
|
||||
free(tmp); \
|
||||
})
|
||||
|
||||
static inline void astrcat(char **out, const char *add)
|
||||
{
|
||||
char *tmp = *out;
|
||||
|
||||
if (asprintf(out, "%s%s", tmp ?: "", add) == -1)
|
||||
die("asprintf failed");
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
static inline int prefixcmp(const char *str, const char *prefix)
|
||||
{
|
||||
for (; ; str++, prefix++)
|
||||
if (!*prefix)
|
||||
return 0;
|
||||
else if (*str != *prefix)
|
||||
return (unsigned char)*prefix - (unsigned char)*str;
|
||||
}
|
||||
|
||||
#endif /* __PERF_SUBCMD_UTIL_H */
|
Reference in New Issue
Block a user