diff options
author | Josh Poimboeuf <jpoimboe@redhat.com> | 2015-12-15 10:39:39 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-12-17 12:27:14 -0500 |
commit | 4b6ab94eabe4f55371cff4569750bb3996c55db6 (patch) | |
tree | db5b95ed4647e3455bb1b1d4bb24137708a97829 /tools/lib | |
parent | 2f4ce5ec1d447beb42143a9653716a2ab025161e (diff) |
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>
Diffstat (limited to 'tools/lib')
-rw-r--r-- | tools/lib/subcmd/Build | 7 | ||||
-rw-r--r-- | tools/lib/subcmd/Makefile | 48 | ||||
-rw-r--r-- | tools/lib/subcmd/exec-cmd.c | 209 | ||||
-rw-r--r-- | tools/lib/subcmd/exec-cmd.h | 16 | ||||
-rw-r--r-- | tools/lib/subcmd/help.c | 268 | ||||
-rw-r--r-- | tools/lib/subcmd/help.h | 34 | ||||
-rw-r--r-- | tools/lib/subcmd/pager.c | 100 | ||||
-rw-r--r-- | tools/lib/subcmd/pager.h | 9 | ||||
-rw-r--r-- | tools/lib/subcmd/parse-options.c | 983 | ||||
-rw-r--r-- | tools/lib/subcmd/parse-options.h | 228 | ||||
-rw-r--r-- | tools/lib/subcmd/run-command.c | 227 | ||||
-rw-r--r-- | tools/lib/subcmd/run-command.h | 60 | ||||
-rw-r--r-- | tools/lib/subcmd/sigchain.c | 53 | ||||
-rw-r--r-- | tools/lib/subcmd/sigchain.h | 10 | ||||
-rw-r--r-- | tools/lib/subcmd/subcmd-config.c | 11 | ||||
-rw-r--r-- | tools/lib/subcmd/subcmd-config.h | 14 | ||||
-rw-r--r-- | tools/lib/subcmd/subcmd-util.h | 91 |
17 files changed, 2368 insertions, 0 deletions
diff --git a/tools/lib/subcmd/Build b/tools/lib/subcmd/Build new file mode 100644 index 000000000000..ee31288788c1 --- /dev/null +++ b/tools/lib/subcmd/Build | |||
@@ -0,0 +1,7 @@ | |||
1 | libsubcmd-y += exec-cmd.o | ||
2 | libsubcmd-y += help.o | ||
3 | libsubcmd-y += pager.o | ||
4 | libsubcmd-y += parse-options.o | ||
5 | libsubcmd-y += run-command.o | ||
6 | libsubcmd-y += sigchain.o | ||
7 | libsubcmd-y += subcmd-config.o | ||
diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile new file mode 100644 index 000000000000..629cf8c14e68 --- /dev/null +++ b/tools/lib/subcmd/Makefile | |||
@@ -0,0 +1,48 @@ | |||
1 | include ../../scripts/Makefile.include | ||
2 | include ../../perf/config/utilities.mak # QUIET_CLEAN | ||
3 | |||
4 | ifeq ($(srctree),) | ||
5 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) | ||
6 | srctree := $(patsubst %/,%,$(dir $(srctree))) | ||
7 | srctree := $(patsubst %/,%,$(dir $(srctree))) | ||
8 | #$(info Determined 'srctree' to be $(srctree)) | ||
9 | endif | ||
10 | |||
11 | CC = $(CROSS_COMPILE)gcc | ||
12 | AR = $(CROSS_COMPILE)ar | ||
13 | RM = rm -f | ||
14 | |||
15 | MAKEFLAGS += --no-print-directory | ||
16 | |||
17 | LIBFILE = $(OUTPUT)libsubcmd.a | ||
18 | |||
19 | CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) | ||
20 | CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC | ||
21 | CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | ||
22 | |||
23 | CFLAGS += -I$(srctree)/tools/include/ | ||
24 | CFLAGS += -I$(srctree)/include/uapi | ||
25 | CFLAGS += -I$(srctree)/include | ||
26 | |||
27 | SUBCMD_IN := $(OUTPUT)libsubcmd-in.o | ||
28 | |||
29 | all: | ||
30 | |||
31 | export srctree OUTPUT CC LD CFLAGS V | ||
32 | include $(srctree)/tools/build/Makefile.include | ||
33 | |||
34 | all: fixdep $(LIBFILE) | ||
35 | |||
36 | $(SUBCMD_IN): FORCE | ||
37 | @$(MAKE) $(build)=libsubcmd | ||
38 | |||
39 | $(LIBFILE): $(SUBCMD_IN) | ||
40 | $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(SUBCMD_IN) | ||
41 | |||
42 | clean: | ||
43 | $(call QUIET_CLEAN, libsubcmd) $(RM) $(LIBFILE); \ | ||
44 | find $(if $(OUTPUT),$(OUTPUT),.) -name \*.o -or -name \*.o.cmd -or -name \*.o.d | xargs $(RM) | ||
45 | |||
46 | FORCE: | ||
47 | |||
48 | .PHONY: clean FORCE | ||
diff --git a/tools/lib/subcmd/exec-cmd.c b/tools/lib/subcmd/exec-cmd.c new file mode 100644 index 000000000000..1ae833af1a4a --- /dev/null +++ b/tools/lib/subcmd/exec-cmd.c | |||
@@ -0,0 +1,209 @@ | |||
1 | #include <linux/compiler.h> | ||
2 | #include <linux/string.h> | ||
3 | #include <sys/types.h> | ||
4 | #include <sys/stat.h> | ||
5 | #include <unistd.h> | ||
6 | #include <string.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <stdio.h> | ||
9 | #include "subcmd-util.h" | ||
10 | #include "exec-cmd.h" | ||
11 | #include "subcmd-config.h" | ||
12 | |||
13 | #define MAX_ARGS 32 | ||
14 | #define PATH_MAX 4096 | ||
15 | |||
16 | static const char *argv_exec_path; | ||
17 | static const char *argv0_path; | ||
18 | |||
19 | void exec_cmd_init(const char *exec_name, const char *prefix, | ||
20 | const char *exec_path, const char *exec_path_env) | ||
21 | { | ||
22 | subcmd_config.exec_name = exec_name; | ||
23 | subcmd_config.prefix = prefix; | ||
24 | subcmd_config.exec_path = exec_path; | ||
25 | subcmd_config.exec_path_env = exec_path_env; | ||
26 | } | ||
27 | |||
28 | #define is_dir_sep(c) ((c) == '/') | ||
29 | |||
30 | static int is_absolute_path(const char *path) | ||
31 | { | ||
32 | return path[0] == '/'; | ||
33 | } | ||
34 | |||
35 | static const char *get_pwd_cwd(void) | ||
36 | { | ||
37 | static char cwd[PATH_MAX + 1]; | ||
38 | char *pwd; | ||
39 | struct stat cwd_stat, pwd_stat; | ||
40 | if (getcwd(cwd, PATH_MAX) == NULL) | ||
41 | return NULL; | ||
42 | pwd = getenv("PWD"); | ||
43 | if (pwd && strcmp(pwd, cwd)) { | ||
44 | stat(cwd, &cwd_stat); | ||
45 | if (!stat(pwd, &pwd_stat) && | ||
46 | pwd_stat.st_dev == cwd_stat.st_dev && | ||
47 | pwd_stat.st_ino == cwd_stat.st_ino) { | ||
48 | strlcpy(cwd, pwd, PATH_MAX); | ||
49 | } | ||
50 | } | ||
51 | return cwd; | ||
52 | } | ||
53 | |||
54 | static const char *make_nonrelative_path(const char *path) | ||
55 | { | ||
56 | static char buf[PATH_MAX + 1]; | ||
57 | |||
58 | if (is_absolute_path(path)) { | ||
59 | if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) | ||
60 | die("Too long path: %.*s", 60, path); | ||
61 | } else { | ||
62 | const char *cwd = get_pwd_cwd(); | ||
63 | if (!cwd) | ||
64 | die("Cannot determine the current working directory"); | ||
65 | if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) | ||
66 | die("Too long path: %.*s", 60, path); | ||
67 | } | ||
68 | return buf; | ||
69 | } | ||
70 | |||
71 | char *system_path(const char *path) | ||
72 | { | ||
73 | char *buf = NULL; | ||
74 | |||
75 | if (is_absolute_path(path)) | ||
76 | return strdup(path); | ||
77 | |||
78 | astrcatf(&buf, "%s/%s", subcmd_config.prefix, path); | ||
79 | |||
80 | return buf; | ||
81 | } | ||
82 | |||
83 | const char *extract_argv0_path(const char *argv0) | ||
84 | { | ||
85 | const char *slash; | ||
86 | |||
87 | if (!argv0 || !*argv0) | ||
88 | return NULL; | ||
89 | slash = argv0 + strlen(argv0); | ||
90 | |||
91 | while (argv0 <= slash && !is_dir_sep(*slash)) | ||
92 | slash--; | ||
93 | |||
94 | if (slash >= argv0) { | ||
95 | argv0_path = strndup(argv0, slash - argv0); | ||
96 | return argv0_path ? slash + 1 : NULL; | ||
97 | } | ||
98 | |||
99 | return argv0; | ||
100 | } | ||
101 | |||
102 | void set_argv_exec_path(const char *exec_path) | ||
103 | { | ||
104 | argv_exec_path = exec_path; | ||
105 | /* | ||
106 | * Propagate this setting to external programs. | ||
107 | */ | ||
108 | setenv(subcmd_config.exec_path_env, exec_path, 1); | ||
109 | } | ||
110 | |||
111 | |||
112 | /* Returns the highest-priority location to look for subprograms. */ | ||
113 | char *get_argv_exec_path(void) | ||
114 | { | ||
115 | char *env; | ||
116 | |||
117 | if (argv_exec_path) | ||
118 | return strdup(argv_exec_path); | ||
119 | |||
120 | env = getenv(subcmd_config.exec_path_env); | ||
121 | if (env && *env) | ||
122 | return strdup(env); | ||
123 | |||
124 | return system_path(subcmd_config.exec_path); | ||
125 | } | ||
126 | |||
127 | static void add_path(char **out, const char *path) | ||
128 | { | ||
129 | if (path && *path) { | ||
130 | if (is_absolute_path(path)) | ||
131 | astrcat(out, path); | ||
132 | else | ||
133 | astrcat(out, make_nonrelative_path(path)); | ||
134 | |||
135 | astrcat(out, ":"); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | void setup_path(void) | ||
140 | { | ||
141 | const char *old_path = getenv("PATH"); | ||
142 | char *new_path = NULL; | ||
143 | char *tmp = get_argv_exec_path(); | ||
144 | |||
145 | add_path(&new_path, tmp); | ||
146 | add_path(&new_path, argv0_path); | ||
147 | free(tmp); | ||
148 | |||
149 | if (old_path) | ||
150 | astrcat(&new_path, old_path); | ||
151 | else | ||
152 | astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin"); | ||
153 | |||
154 | setenv("PATH", new_path, 1); | ||
155 | |||
156 | free(new_path); | ||
157 | } | ||
158 | |||
159 | static const char **prepare_exec_cmd(const char **argv) | ||
160 | { | ||
161 | int argc; | ||
162 | const char **nargv; | ||
163 | |||
164 | for (argc = 0; argv[argc]; argc++) | ||
165 | ; /* just counting */ | ||
166 | nargv = malloc(sizeof(*nargv) * (argc + 2)); | ||
167 | |||
168 | nargv[0] = subcmd_config.exec_name; | ||
169 | for (argc = 0; argv[argc]; argc++) | ||
170 | nargv[argc + 1] = argv[argc]; | ||
171 | nargv[argc + 1] = NULL; | ||
172 | return nargv; | ||
173 | } | ||
174 | |||
175 | int execv_cmd(const char **argv) { | ||
176 | const char **nargv = prepare_exec_cmd(argv); | ||
177 | |||
178 | /* execvp() can only ever return if it fails */ | ||
179 | execvp(subcmd_config.exec_name, (char **)nargv); | ||
180 | |||
181 | free(nargv); | ||
182 | return -1; | ||
183 | } | ||
184 | |||
185 | |||
186 | int execl_cmd(const char *cmd,...) | ||
187 | { | ||
188 | int argc; | ||
189 | const char *argv[MAX_ARGS + 1]; | ||
190 | const char *arg; | ||
191 | va_list param; | ||
192 | |||
193 | va_start(param, cmd); | ||
194 | argv[0] = cmd; | ||
195 | argc = 1; | ||
196 | while (argc < MAX_ARGS) { | ||
197 | arg = argv[argc++] = va_arg(param, char *); | ||
198 | if (!arg) | ||
199 | break; | ||
200 | } | ||
201 | va_end(param); | ||
202 | if (MAX_ARGS <= argc) { | ||
203 | fprintf(stderr, " Error: too many args to run %s\n", cmd); | ||
204 | return -1; | ||
205 | } | ||
206 | |||
207 | argv[argc] = NULL; | ||
208 | return execv_cmd(argv); | ||
209 | } | ||
diff --git a/tools/lib/subcmd/exec-cmd.h b/tools/lib/subcmd/exec-cmd.h new file mode 100644 index 000000000000..f1bd3436ad5f --- /dev/null +++ b/tools/lib/subcmd/exec-cmd.h | |||
@@ -0,0 +1,16 @@ | |||
1 | #ifndef __PERF_EXEC_CMD_H | ||
2 | #define __PERF_EXEC_CMD_H | ||
3 | |||
4 | extern void exec_cmd_init(const char *exec_name, const char *prefix, | ||
5 | const char *exec_path, const char *exec_path_env); | ||
6 | |||
7 | extern void set_argv_exec_path(const char *exec_path); | ||
8 | extern const char *extract_argv0_path(const char *path); | ||
9 | extern void setup_path(void); | ||
10 | extern int execv_cmd(const char **argv); /* NULL terminated */ | ||
11 | extern int execl_cmd(const char *cmd, ...); | ||
12 | /* get_argv_exec_path and system_path return malloc'd string, caller must free it */ | ||
13 | extern char *get_argv_exec_path(void); | ||
14 | extern char *system_path(const char *path); | ||
15 | |||
16 | #endif /* __PERF_EXEC_CMD_H */ | ||
diff --git a/tools/lib/subcmd/help.c b/tools/lib/subcmd/help.c new file mode 100644 index 000000000000..e228c3cb3716 --- /dev/null +++ b/tools/lib/subcmd/help.c | |||
@@ -0,0 +1,268 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include <termios.h> | ||
5 | #include <sys/ioctl.h> | ||
6 | #include <sys/types.h> | ||
7 | #include <sys/stat.h> | ||
8 | #include <unistd.h> | ||
9 | #include <dirent.h> | ||
10 | #include "subcmd-util.h" | ||
11 | #include "help.h" | ||
12 | #include "exec-cmd.h" | ||
13 | |||
14 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) | ||
15 | { | ||
16 | struct cmdname *ent = malloc(sizeof(*ent) + len + 1); | ||
17 | |||
18 | ent->len = len; | ||
19 | memcpy(ent->name, name, len); | ||
20 | ent->name[len] = 0; | ||
21 | |||
22 | ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc); | ||
23 | cmds->names[cmds->cnt++] = ent; | ||
24 | } | ||
25 | |||
26 | void clean_cmdnames(struct cmdnames *cmds) | ||
27 | { | ||
28 | unsigned int i; | ||
29 | |||
30 | for (i = 0; i < cmds->cnt; ++i) | ||
31 | zfree(&cmds->names[i]); | ||
32 | zfree(&cmds->names); | ||
33 | cmds->cnt = 0; | ||
34 | cmds->alloc = 0; | ||
35 | } | ||
36 | |||
37 | int cmdname_compare(const void *a_, const void *b_) | ||
38 | { | ||
39 | struct cmdname *a = *(struct cmdname **)a_; | ||
40 | struct cmdname *b = *(struct cmdname **)b_; | ||
41 | return strcmp(a->name, b->name); | ||
42 | } | ||
43 | |||
44 | void uniq(struct cmdnames *cmds) | ||
45 | { | ||
46 | unsigned int i, j; | ||
47 | |||
48 | if (!cmds->cnt) | ||
49 | return; | ||
50 | |||
51 | for (i = j = 1; i < cmds->cnt; i++) | ||
52 | if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name)) | ||
53 | cmds->names[j++] = cmds->names[i]; | ||
54 | |||
55 | cmds->cnt = j; | ||
56 | } | ||
57 | |||
58 | void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) | ||
59 | { | ||
60 | size_t ci, cj, ei; | ||
61 | int cmp; | ||
62 | |||
63 | ci = cj = ei = 0; | ||
64 | while (ci < cmds->cnt && ei < excludes->cnt) { | ||
65 | cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name); | ||
66 | if (cmp < 0) | ||
67 | cmds->names[cj++] = cmds->names[ci++]; | ||
68 | else if (cmp == 0) | ||
69 | ci++, ei++; | ||
70 | else if (cmp > 0) | ||
71 | ei++; | ||
72 | } | ||
73 | |||
74 | while (ci < cmds->cnt) | ||
75 | cmds->names[cj++] = cmds->names[ci++]; | ||
76 | |||
77 | cmds->cnt = cj; | ||
78 | } | ||
79 | |||
80 | static void get_term_dimensions(struct winsize *ws) | ||
81 | { | ||
82 | char *s = getenv("LINES"); | ||
83 | |||
84 | if (s != NULL) { | ||
85 | ws->ws_row = atoi(s); | ||
86 | s = getenv("COLUMNS"); | ||
87 | if (s != NULL) { | ||
88 | ws->ws_col = atoi(s); | ||
89 | if (ws->ws_row && ws->ws_col) | ||
90 | return; | ||
91 | } | ||
92 | } | ||
93 | #ifdef TIOCGWINSZ | ||
94 | if (ioctl(1, TIOCGWINSZ, ws) == 0 && | ||
95 | ws->ws_row && ws->ws_col) | ||
96 | return; | ||
97 | #endif | ||
98 | ws->ws_row = 25; | ||
99 | ws->ws_col = 80; | ||
100 | } | ||
101 | |||
102 | static void pretty_print_string_list(struct cmdnames *cmds, int longest) | ||
103 | { | ||
104 | int cols = 1, rows; | ||
105 | int space = longest + 1; /* min 1 SP between words */ | ||
106 | struct winsize win; | ||
107 | int max_cols; | ||
108 | int i, j; | ||
109 | |||
110 | get_term_dimensions(&win); | ||
111 | max_cols = win.ws_col - 1; /* don't print *on* the edge */ | ||
112 | |||
113 | if (space < max_cols) | ||
114 | cols = max_cols / space; | ||
115 | rows = (cmds->cnt + cols - 1) / cols; | ||
116 | |||
117 | for (i = 0; i < rows; i++) { | ||
118 | printf(" "); | ||
119 | |||
120 | for (j = 0; j < cols; j++) { | ||
121 | unsigned int n = j * rows + i; | ||
122 | unsigned int size = space; | ||
123 | |||
124 | if (n >= cmds->cnt) | ||
125 | break; | ||
126 | if (j == cols-1 || n + rows >= cmds->cnt) | ||
127 | size = 1; | ||
128 | printf("%-*s", size, cmds->names[n]->name); | ||
129 | } | ||
130 | putchar('\n'); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | static int is_executable(const char *name) | ||
135 | { | ||
136 | struct stat st; | ||
137 | |||
138 | if (stat(name, &st) || /* stat, not lstat */ | ||
139 | !S_ISREG(st.st_mode)) | ||
140 | return 0; | ||
141 | |||
142 | return st.st_mode & S_IXUSR; | ||
143 | } | ||
144 | |||
145 | static int has_extension(const char *filename, const char *ext) | ||
146 | { | ||
147 | size_t len = strlen(filename); | ||
148 | size_t extlen = strlen(ext); | ||
149 | |||
150 | return len > extlen && !memcmp(filename + len - extlen, ext, extlen); | ||
151 | } | ||
152 | |||
153 | static void list_commands_in_dir(struct cmdnames *cmds, | ||
154 | const char *path, | ||
155 | const char *prefix) | ||
156 | { | ||
157 | int prefix_len; | ||
158 | DIR *dir = opendir(path); | ||
159 | struct dirent *de; | ||
160 | char *buf = NULL; | ||
161 | |||
162 | if (!dir) | ||
163 | return; | ||
164 | if (!prefix) | ||
165 | prefix = "perf-"; | ||
166 | prefix_len = strlen(prefix); | ||
167 | |||
168 | astrcatf(&buf, "%s/", path); | ||
169 | |||
170 | while ((de = readdir(dir)) != NULL) { | ||
171 | int entlen; | ||
172 | |||
173 | if (prefixcmp(de->d_name, prefix)) | ||
174 | continue; | ||
175 | |||
176 | astrcat(&buf, de->d_name); | ||
177 | if (!is_executable(buf)) | ||
178 | continue; | ||
179 | |||
180 | entlen = strlen(de->d_name) - prefix_len; | ||
181 | if (has_extension(de->d_name, ".exe")) | ||
182 | entlen -= 4; | ||
183 | |||
184 | add_cmdname(cmds, de->d_name + prefix_len, entlen); | ||
185 | } | ||
186 | closedir(dir); | ||
187 | free(buf); | ||
188 | } | ||
189 | |||
190 | void load_command_list(const char *prefix, | ||
191 | struct cmdnames *main_cmds, | ||
192 | struct cmdnames *other_cmds) | ||
193 | { | ||
194 | const char *env_path = getenv("PATH"); | ||
195 | char *exec_path = get_argv_exec_path(); | ||
196 | |||
197 | if (exec_path) { | ||
198 | list_commands_in_dir(main_cmds, exec_path, prefix); | ||
199 | qsort(main_cmds->names, main_cmds->cnt, | ||
200 | sizeof(*main_cmds->names), cmdname_compare); | ||
201 | uniq(main_cmds); | ||
202 | } | ||
203 | |||
204 | if (env_path) { | ||
205 | char *paths, *path, *colon; | ||
206 | path = paths = strdup(env_path); | ||
207 | while (1) { | ||
208 | if ((colon = strchr(path, ':'))) | ||
209 | *colon = 0; | ||
210 | if (!exec_path || strcmp(path, exec_path)) | ||
211 | list_commands_in_dir(other_cmds, path, prefix); | ||
212 | |||
213 | if (!colon) | ||
214 | break; | ||
215 | path = colon + 1; | ||
216 | } | ||
217 | free(paths); | ||
218 | |||
219 | qsort(other_cmds->names, other_cmds->cnt, | ||
220 | sizeof(*other_cmds->names), cmdname_compare); | ||
221 | uniq(other_cmds); | ||
222 | } | ||
223 | free(exec_path); | ||
224 | exclude_cmds(other_cmds, main_cmds); | ||
225 | } | ||
226 | |||
227 | void list_commands(const char *title, struct cmdnames *main_cmds, | ||
228 | struct cmdnames *other_cmds) | ||
229 | { | ||
230 | unsigned int i, longest = 0; | ||
231 | |||
232 | for (i = 0; i < main_cmds->cnt; i++) | ||
233 | if (longest < main_cmds->names[i]->len) | ||
234 | longest = main_cmds->names[i]->len; | ||
235 | for (i = 0; i < other_cmds->cnt; i++) | ||
236 | if (longest < other_cmds->names[i]->len) | ||
237 | longest = other_cmds->names[i]->len; | ||
238 | |||
239 | if (main_cmds->cnt) { | ||
240 | char *exec_path = get_argv_exec_path(); | ||
241 | printf("available %s in '%s'\n", title, exec_path); | ||
242 | printf("----------------"); | ||
243 | mput_char('-', strlen(title) + strlen(exec_path)); | ||
244 | putchar('\n'); | ||
245 | pretty_print_string_list(main_cmds, longest); | ||
246 | putchar('\n'); | ||
247 | free(exec_path); | ||
248 | } | ||
249 | |||
250 | if (other_cmds->cnt) { | ||
251 | printf("%s available from elsewhere on your $PATH\n", title); | ||
252 | printf("---------------------------------------"); | ||
253 | mput_char('-', strlen(title)); | ||
254 | putchar('\n'); | ||
255 | pretty_print_string_list(other_cmds, longest); | ||
256 | putchar('\n'); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | int is_in_cmdlist(struct cmdnames *c, const char *s) | ||
261 | { | ||
262 | unsigned int i; | ||
263 | |||
264 | for (i = 0; i < c->cnt; i++) | ||
265 | if (!strcmp(s, c->names[i]->name)) | ||
266 | return 1; | ||
267 | return 0; | ||
268 | } | ||
diff --git a/tools/lib/subcmd/help.h b/tools/lib/subcmd/help.h new file mode 100644 index 000000000000..096c8bc45cd7 --- /dev/null +++ b/tools/lib/subcmd/help.h | |||
@@ -0,0 +1,34 @@ | |||
1 | #ifndef __PERF_HELP_H | ||
2 | #define __PERF_HELP_H | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | |||
6 | struct cmdnames { | ||
7 | size_t alloc; | ||
8 | size_t cnt; | ||
9 | struct cmdname { | ||
10 | size_t len; /* also used for similarity index in help.c */ | ||
11 | char name[]; | ||
12 | } **names; | ||
13 | }; | ||
14 | |||
15 | static inline void mput_char(char c, unsigned int num) | ||
16 | { | ||
17 | while(num--) | ||
18 | putchar(c); | ||
19 | } | ||
20 | |||
21 | void load_command_list(const char *prefix, | ||
22 | struct cmdnames *main_cmds, | ||
23 | struct cmdnames *other_cmds); | ||
24 | void add_cmdname(struct cmdnames *cmds, const char *name, size_t len); | ||
25 | void clean_cmdnames(struct cmdnames *cmds); | ||
26 | int cmdname_compare(const void *a, const void *b); | ||
27 | void uniq(struct cmdnames *cmds); | ||
28 | /* Here we require that excludes is a sorted list. */ | ||
29 | void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); | ||
30 | int is_in_cmdlist(struct cmdnames *c, const char *s); | ||
31 | void list_commands(const char *title, struct cmdnames *main_cmds, | ||
32 | struct cmdnames *other_cmds); | ||
33 | |||
34 | #endif /* __PERF_HELP_H */ | ||
diff --git a/tools/lib/subcmd/pager.c b/tools/lib/subcmd/pager.c new file mode 100644 index 000000000000..d50f3b58606b --- /dev/null +++ b/tools/lib/subcmd/pager.c | |||
@@ -0,0 +1,100 @@ | |||
1 | #include <sys/select.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <stdio.h> | ||
4 | #include <string.h> | ||
5 | #include <signal.h> | ||
6 | #include "pager.h" | ||
7 | #include "run-command.h" | ||
8 | #include "sigchain.h" | ||
9 | #include "subcmd-config.h" | ||
10 | |||
11 | /* | ||
12 | * This is split up from the rest of git so that we can do | ||
13 | * something different on Windows. | ||
14 | */ | ||
15 | |||
16 | static int spawned_pager; | ||
17 | |||
18 | void pager_init(const char *pager_env) | ||
19 | { | ||
20 | subcmd_config.pager_env = pager_env; | ||
21 | } | ||
22 | |||
23 | static void pager_preexec(void) | ||
24 | { | ||
25 | /* | ||
26 | * Work around bug in "less" by not starting it until we | ||
27 | * have real input | ||
28 | */ | ||
29 | fd_set in; | ||
30 | |||
31 | FD_ZERO(&in); | ||
32 | FD_SET(0, &in); | ||
33 | select(1, &in, NULL, &in, NULL); | ||
34 | |||
35 | setenv("LESS", "FRSX", 0); | ||
36 | } | ||
37 | |||
38 | static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; | ||
39 | static struct child_process pager_process; | ||
40 | |||
41 | static void wait_for_pager(void) | ||
42 | { | ||
43 | fflush(stdout); | ||
44 | fflush(stderr); | ||
45 | /* signal EOF to pager */ | ||
46 | close(1); | ||
47 | close(2); | ||
48 | finish_command(&pager_process); | ||
49 | } | ||
50 | |||
51 | static void wait_for_pager_signal(int signo) | ||
52 | { | ||
53 | wait_for_pager(); | ||
54 | sigchain_pop(signo); | ||
55 | raise(signo); | ||
56 | } | ||
57 | |||
58 | void setup_pager(void) | ||
59 | { | ||
60 | const char *pager = getenv(subcmd_config.pager_env); | ||
61 | |||
62 | if (!isatty(1)) | ||
63 | return; | ||
64 | if (!pager) | ||
65 | pager = getenv("PAGER"); | ||
66 | if (!(pager || access("/usr/bin/pager", X_OK))) | ||
67 | pager = "/usr/bin/pager"; | ||
68 | if (!(pager || access("/usr/bin/less", X_OK))) | ||
69 | pager = "/usr/bin/less"; | ||
70 | if (!pager) | ||
71 | pager = "cat"; | ||
72 | if (!*pager || !strcmp(pager, "cat")) | ||
73 | return; | ||
74 | |||
75 | spawned_pager = 1; /* means we are emitting to terminal */ | ||
76 | |||
77 | /* spawn the pager */ | ||
78 | pager_argv[2] = pager; | ||
79 | pager_process.argv = pager_argv; | ||
80 | pager_process.in = -1; | ||
81 | pager_process.preexec_cb = pager_preexec; | ||
82 | |||
83 | if (start_command(&pager_process)) | ||
84 | return; | ||
85 | |||
86 | /* original process continues, but writes to the pipe */ | ||
87 | dup2(pager_process.in, 1); | ||
88 | if (isatty(2)) | ||
89 | dup2(pager_process.in, 2); | ||
90 | close(pager_process.in); | ||
91 | |||
92 | /* this makes sure that the parent terminates after the pager */ | ||
93 | sigchain_push_common(wait_for_pager_signal); | ||
94 | atexit(wait_for_pager); | ||
95 | } | ||
96 | |||
97 | int pager_in_use(void) | ||
98 | { | ||
99 | return spawned_pager; | ||
100 | } | ||
diff --git a/tools/lib/subcmd/pager.h b/tools/lib/subcmd/pager.h new file mode 100644 index 000000000000..d6a591a4c017 --- /dev/null +++ b/tools/lib/subcmd/pager.h | |||
@@ -0,0 +1,9 @@ | |||
1 | #ifndef __PERF_PAGER_H | ||
2 | #define __PERF_PAGER_H | ||
3 | |||
4 | extern void pager_init(const char *pager_env); | ||
5 | |||
6 | extern void setup_pager(void); | ||
7 | extern int pager_in_use(void); | ||
8 | |||
9 | #endif /* __PERF_PAGER_H */ | ||
diff --git a/tools/lib/subcmd/parse-options.c b/tools/lib/subcmd/parse-options.c new file mode 100644 index 000000000000..981bb4481fd5 --- /dev/null +++ b/tools/lib/subcmd/parse-options.c | |||
@@ -0,0 +1,983 @@ | |||
1 | #include <linux/compiler.h> | ||
2 | #include <linux/types.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <stdint.h> | ||
6 | #include <string.h> | ||
7 | #include <ctype.h> | ||
8 | #include "subcmd-util.h" | ||
9 | #include "parse-options.h" | ||
10 | #include "subcmd-config.h" | ||
11 | #include "pager.h" | ||
12 | |||
13 | #define OPT_SHORT 1 | ||
14 | #define OPT_UNSET 2 | ||
15 | |||
16 | char *error_buf; | ||
17 | |||
18 | static int opterror(const struct option *opt, const char *reason, int flags) | ||
19 | { | ||
20 | if (flags & OPT_SHORT) | ||
21 | fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason); | ||
22 | else if (flags & OPT_UNSET) | ||
23 | fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason); | ||
24 | else | ||
25 | fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason); | ||
26 | |||
27 | return -1; | ||
28 | } | ||
29 | |||
30 | static const char *skip_prefix(const char *str, const char *prefix) | ||
31 | { | ||
32 | size_t len = strlen(prefix); | ||
33 | return strncmp(str, prefix, len) ? NULL : str + len; | ||
34 | } | ||
35 | |||
36 | static void optwarning(const struct option *opt, const char *reason, int flags) | ||
37 | { | ||
38 | if (flags & OPT_SHORT) | ||
39 | fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason); | ||
40 | else if (flags & OPT_UNSET) | ||
41 | fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason); | ||
42 | else | ||
43 | fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason); | ||
44 | } | ||
45 | |||
46 | static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, | ||
47 | int flags, const char **arg) | ||
48 | { | ||
49 | const char *res; | ||
50 | |||
51 | if (p->opt) { | ||
52 | res = p->opt; | ||
53 | p->opt = NULL; | ||
54 | } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || | ||
55 | **(p->argv + 1) == '-')) { | ||
56 | res = (const char *)opt->defval; | ||
57 | } else if (p->argc > 1) { | ||
58 | p->argc--; | ||
59 | res = *++p->argv; | ||
60 | } else | ||
61 | return opterror(opt, "requires a value", flags); | ||
62 | if (arg) | ||
63 | *arg = res; | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static int get_value(struct parse_opt_ctx_t *p, | ||
68 | const struct option *opt, int flags) | ||
69 | { | ||
70 | const char *s, *arg = NULL; | ||
71 | const int unset = flags & OPT_UNSET; | ||
72 | int err; | ||
73 | |||
74 | if (unset && p->opt) | ||
75 | return opterror(opt, "takes no value", flags); | ||
76 | if (unset && (opt->flags & PARSE_OPT_NONEG)) | ||
77 | return opterror(opt, "isn't available", flags); | ||
78 | if (opt->flags & PARSE_OPT_DISABLED) | ||
79 | return opterror(opt, "is not usable", flags); | ||
80 | |||
81 | if (opt->flags & PARSE_OPT_EXCLUSIVE) { | ||
82 | if (p->excl_opt && p->excl_opt != opt) { | ||
83 | char msg[128]; | ||
84 | |||
85 | if (((flags & OPT_SHORT) && p->excl_opt->short_name) || | ||
86 | p->excl_opt->long_name == NULL) { | ||
87 | snprintf(msg, sizeof(msg), "cannot be used with switch `%c'", | ||
88 | p->excl_opt->short_name); | ||
89 | } else { | ||
90 | snprintf(msg, sizeof(msg), "cannot be used with %s", | ||
91 | p->excl_opt->long_name); | ||
92 | } | ||
93 | opterror(opt, msg, flags); | ||
94 | return -3; | ||
95 | } | ||
96 | p->excl_opt = opt; | ||
97 | } | ||
98 | if (!(flags & OPT_SHORT) && p->opt) { | ||
99 | switch (opt->type) { | ||
100 | case OPTION_CALLBACK: | ||
101 | if (!(opt->flags & PARSE_OPT_NOARG)) | ||
102 | break; | ||
103 | /* FALLTHROUGH */ | ||
104 | case OPTION_BOOLEAN: | ||
105 | case OPTION_INCR: | ||
106 | case OPTION_BIT: | ||
107 | case OPTION_SET_UINT: | ||
108 | case OPTION_SET_PTR: | ||
109 | return opterror(opt, "takes no value", flags); | ||
110 | case OPTION_END: | ||
111 | case OPTION_ARGUMENT: | ||
112 | case OPTION_GROUP: | ||
113 | case OPTION_STRING: | ||
114 | case OPTION_INTEGER: | ||
115 | case OPTION_UINTEGER: | ||
116 | case OPTION_LONG: | ||
117 | case OPTION_U64: | ||
118 | default: | ||
119 | break; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | if (opt->flags & PARSE_OPT_NOBUILD) { | ||
124 | char reason[128]; | ||
125 | bool noarg = false; | ||
126 | |||
127 | err = snprintf(reason, sizeof(reason), | ||
128 | opt->flags & PARSE_OPT_CANSKIP ? | ||
129 | "is being ignored because %s " : | ||
130 | "is not available because %s", | ||
131 | opt->build_opt); | ||
132 | reason[sizeof(reason) - 1] = '\0'; | ||
133 | |||
134 | if (err < 0) | ||
135 | strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ? | ||
136 | "is being ignored" : | ||
137 | "is not available", | ||
138 | sizeof(reason)); | ||
139 | |||
140 | if (!(opt->flags & PARSE_OPT_CANSKIP)) | ||
141 | return opterror(opt, reason, flags); | ||
142 | |||
143 | err = 0; | ||
144 | if (unset) | ||
145 | noarg = true; | ||
146 | if (opt->flags & PARSE_OPT_NOARG) | ||
147 | noarg = true; | ||
148 | if (opt->flags & PARSE_OPT_OPTARG && !p->opt) | ||
149 | noarg = true; | ||
150 | |||
151 | switch (opt->type) { | ||
152 | case OPTION_BOOLEAN: | ||
153 | case OPTION_INCR: | ||
154 | case OPTION_BIT: | ||
155 | case OPTION_SET_UINT: | ||
156 | case OPTION_SET_PTR: | ||
157 | case OPTION_END: | ||
158 | case OPTION_ARGUMENT: | ||
159 | case OPTION_GROUP: | ||
160 | noarg = true; | ||
161 | break; | ||
162 | case OPTION_CALLBACK: | ||
163 | case OPTION_STRING: | ||
164 | case OPTION_INTEGER: | ||
165 | case OPTION_UINTEGER: | ||
166 | case OPTION_LONG: | ||
167 | case OPTION_U64: | ||
168 | default: | ||
169 | break; | ||
170 | } | ||
171 | |||
172 | if (!noarg) | ||
173 | err = get_arg(p, opt, flags, NULL); | ||
174 | if (err) | ||
175 | return err; | ||
176 | |||
177 | optwarning(opt, reason, flags); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | switch (opt->type) { | ||
182 | case OPTION_BIT: | ||
183 | if (unset) | ||
184 | *(int *)opt->value &= ~opt->defval; | ||
185 | else | ||
186 | *(int *)opt->value |= opt->defval; | ||
187 | return 0; | ||
188 | |||
189 | case OPTION_BOOLEAN: | ||
190 | *(bool *)opt->value = unset ? false : true; | ||
191 | if (opt->set) | ||
192 | *(bool *)opt->set = true; | ||
193 | return 0; | ||
194 | |||
195 | case OPTION_INCR: | ||
196 | *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; | ||
197 | return 0; | ||
198 | |||
199 | case OPTION_SET_UINT: | ||
200 | *(unsigned int *)opt->value = unset ? 0 : opt->defval; | ||
201 | return 0; | ||
202 | |||
203 | case OPTION_SET_PTR: | ||
204 | *(void **)opt->value = unset ? NULL : (void *)opt->defval; | ||
205 | return 0; | ||
206 | |||
207 | case OPTION_STRING: | ||
208 | err = 0; | ||
209 | if (unset) | ||
210 | *(const char **)opt->value = NULL; | ||
211 | else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) | ||
212 | *(const char **)opt->value = (const char *)opt->defval; | ||
213 | else | ||
214 | err = get_arg(p, opt, flags, (const char **)opt->value); | ||
215 | |||
216 | /* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */ | ||
217 | if (opt->flags & PARSE_OPT_NOEMPTY) { | ||
218 | const char *val = *(const char **)opt->value; | ||
219 | |||
220 | if (!val) | ||
221 | return err; | ||
222 | |||
223 | /* Similar to unset if we are given an empty string. */ | ||
224 | if (val[0] == '\0') { | ||
225 | *(const char **)opt->value = NULL; | ||
226 | return 0; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | return err; | ||
231 | |||
232 | case OPTION_CALLBACK: | ||
233 | if (unset) | ||
234 | return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; | ||
235 | if (opt->flags & PARSE_OPT_NOARG) | ||
236 | return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; | ||
237 | if (opt->flags & PARSE_OPT_OPTARG && !p->opt) | ||
238 | return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; | ||
239 | if (get_arg(p, opt, flags, &arg)) | ||
240 | return -1; | ||
241 | return (*opt->callback)(opt, arg, 0) ? (-1) : 0; | ||
242 | |||
243 | case OPTION_INTEGER: | ||
244 | if (unset) { | ||
245 | *(int *)opt->value = 0; | ||
246 | return 0; | ||
247 | } | ||
248 | if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { | ||
249 | *(int *)opt->value = opt->defval; | ||
250 | return 0; | ||
251 | } | ||
252 | if (get_arg(p, opt, flags, &arg)) | ||
253 | return -1; | ||
254 | *(int *)opt->value = strtol(arg, (char **)&s, 10); | ||
255 | if (*s) | ||
256 | return opterror(opt, "expects a numerical value", flags); | ||
257 | return 0; | ||
258 | |||
259 | case OPTION_UINTEGER: | ||
260 | if (unset) { | ||
261 | *(unsigned int *)opt->value = 0; | ||
262 | return 0; | ||
263 | } | ||
264 | if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { | ||
265 | *(unsigned int *)opt->value = opt->defval; | ||
266 | return 0; | ||
267 | } | ||
268 | if (get_arg(p, opt, flags, &arg)) | ||
269 | return -1; | ||
270 | *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10); | ||
271 | if (*s) | ||
272 | return opterror(opt, "expects a numerical value", flags); | ||
273 | return 0; | ||
274 | |||
275 | case OPTION_LONG: | ||
276 | if (unset) { | ||
277 | *(long *)opt->value = 0; | ||
278 | return 0; | ||
279 | } | ||
280 | if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { | ||
281 | *(long *)opt->value = opt->defval; | ||
282 | return 0; | ||
283 | } | ||
284 | if (get_arg(p, opt, flags, &arg)) | ||
285 | return -1; | ||
286 | *(long *)opt->value = strtol(arg, (char **)&s, 10); | ||
287 | if (*s) | ||
288 | return opterror(opt, "expects a numerical value", flags); | ||
289 | return 0; | ||
290 | |||
291 | case OPTION_U64: | ||
292 | if (unset) { | ||
293 | *(u64 *)opt->value = 0; | ||
294 | return 0; | ||
295 | } | ||
296 | if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { | ||
297 | *(u64 *)opt->value = opt->defval; | ||
298 | return 0; | ||
299 | } | ||
300 | if (get_arg(p, opt, flags, &arg)) | ||
301 | return -1; | ||
302 | *(u64 *)opt->value = strtoull(arg, (char **)&s, 10); | ||
303 | if (*s) | ||
304 | return opterror(opt, "expects a numerical value", flags); | ||
305 | return 0; | ||
306 | |||
307 | case OPTION_END: | ||
308 | case OPTION_ARGUMENT: | ||
309 | case OPTION_GROUP: | ||
310 | default: | ||
311 | die("should not happen, someone must be hit on the forehead"); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) | ||
316 | { | ||
317 | for (; options->type != OPTION_END; options++) { | ||
318 | if (options->short_name == *p->opt) { | ||
319 | p->opt = p->opt[1] ? p->opt + 1 : NULL; | ||
320 | return get_value(p, options, OPT_SHORT); | ||
321 | } | ||
322 | } | ||
323 | return -2; | ||
324 | } | ||
325 | |||
326 | static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, | ||
327 | const struct option *options) | ||
328 | { | ||
329 | const char *arg_end = strchr(arg, '='); | ||
330 | const struct option *abbrev_option = NULL, *ambiguous_option = NULL; | ||
331 | int abbrev_flags = 0, ambiguous_flags = 0; | ||
332 | |||
333 | if (!arg_end) | ||
334 | arg_end = arg + strlen(arg); | ||
335 | |||
336 | for (; options->type != OPTION_END; options++) { | ||
337 | const char *rest; | ||
338 | int flags = 0; | ||
339 | |||
340 | if (!options->long_name) | ||
341 | continue; | ||
342 | |||
343 | rest = skip_prefix(arg, options->long_name); | ||
344 | if (options->type == OPTION_ARGUMENT) { | ||
345 | if (!rest) | ||
346 | continue; | ||
347 | if (*rest == '=') | ||
348 | return opterror(options, "takes no value", flags); | ||
349 | if (*rest) | ||
350 | continue; | ||
351 | p->out[p->cpidx++] = arg - 2; | ||
352 | return 0; | ||
353 | } | ||
354 | if (!rest) { | ||
355 | if (!prefixcmp(options->long_name, "no-")) { | ||
356 | /* | ||
357 | * The long name itself starts with "no-", so | ||
358 | * accept the option without "no-" so that users | ||
359 | * do not have to enter "no-no-" to get the | ||
360 | * negation. | ||
361 | */ | ||
362 | rest = skip_prefix(arg, options->long_name + 3); | ||
363 | if (rest) { | ||
364 | flags |= OPT_UNSET; | ||
365 | goto match; | ||
366 | } | ||
367 | /* Abbreviated case */ | ||
368 | if (!prefixcmp(options->long_name + 3, arg)) { | ||
369 | flags |= OPT_UNSET; | ||
370 | goto is_abbreviated; | ||
371 | } | ||
372 | } | ||
373 | /* abbreviated? */ | ||
374 | if (!strncmp(options->long_name, arg, arg_end - arg)) { | ||
375 | is_abbreviated: | ||
376 | if (abbrev_option) { | ||
377 | /* | ||
378 | * If this is abbreviated, it is | ||
379 | * ambiguous. So when there is no | ||
380 | * exact match later, we need to | ||
381 | * error out. | ||
382 | */ | ||
383 | ambiguous_option = abbrev_option; | ||
384 | ambiguous_flags = abbrev_flags; | ||
385 | } | ||
386 | if (!(flags & OPT_UNSET) && *arg_end) | ||
387 | p->opt = arg_end + 1; | ||
388 | abbrev_option = options; | ||
389 | abbrev_flags = flags; | ||
390 | continue; | ||
391 | } | ||
392 | /* negated and abbreviated very much? */ | ||
393 | if (!prefixcmp("no-", arg)) { | ||
394 | flags |= OPT_UNSET; | ||
395 | goto is_abbreviated; | ||
396 | } | ||
397 | /* negated? */ | ||
398 | if (strncmp(arg, "no-", 3)) | ||
399 | continue; | ||
400 | flags |= OPT_UNSET; | ||
401 | rest = skip_prefix(arg + 3, options->long_name); | ||
402 | /* abbreviated and negated? */ | ||
403 | if (!rest && !prefixcmp(options->long_name, arg + 3)) | ||
404 | goto is_abbreviated; | ||
405 | if (!rest) | ||
406 | continue; | ||
407 | } | ||
408 | match: | ||
409 | if (*rest) { | ||
410 | if (*rest != '=') | ||
411 | continue; | ||
412 | p->opt = rest + 1; | ||
413 | } | ||
414 | return get_value(p, options, flags); | ||
415 | } | ||
416 | |||
417 | if (ambiguous_option) { | ||
418 | fprintf(stderr, | ||
419 | " Error: Ambiguous option: %s (could be --%s%s or --%s%s)", | ||
420 | arg, | ||
421 | (ambiguous_flags & OPT_UNSET) ? "no-" : "", | ||
422 | ambiguous_option->long_name, | ||
423 | (abbrev_flags & OPT_UNSET) ? "no-" : "", | ||
424 | abbrev_option->long_name); | ||
425 | return -1; | ||
426 | } | ||
427 | if (abbrev_option) | ||
428 | return get_value(p, abbrev_option, abbrev_flags); | ||
429 | return -2; | ||
430 | } | ||
431 | |||
432 | static void check_typos(const char *arg, const struct option *options) | ||
433 | { | ||
434 | if (strlen(arg) < 3) | ||
435 | return; | ||
436 | |||
437 | if (!prefixcmp(arg, "no-")) { | ||
438 | fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); | ||
439 | exit(129); | ||
440 | } | ||
441 | |||
442 | for (; options->type != OPTION_END; options++) { | ||
443 | if (!options->long_name) | ||
444 | continue; | ||
445 | if (!prefixcmp(options->long_name, arg)) { | ||
446 | fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); | ||
447 | exit(129); | ||
448 | } | ||
449 | } | ||
450 | } | ||
451 | |||
452 | static void parse_options_start(struct parse_opt_ctx_t *ctx, | ||
453 | int argc, const char **argv, int flags) | ||
454 | { | ||
455 | memset(ctx, 0, sizeof(*ctx)); | ||
456 | ctx->argc = argc - 1; | ||
457 | ctx->argv = argv + 1; | ||
458 | ctx->out = argv; | ||
459 | ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); | ||
460 | ctx->flags = flags; | ||
461 | if ((flags & PARSE_OPT_KEEP_UNKNOWN) && | ||
462 | (flags & PARSE_OPT_STOP_AT_NON_OPTION)) | ||
463 | die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); | ||
464 | } | ||
465 | |||
466 | static int usage_with_options_internal(const char * const *, | ||
467 | const struct option *, int, | ||
468 | struct parse_opt_ctx_t *); | ||
469 | |||
470 | static int parse_options_step(struct parse_opt_ctx_t *ctx, | ||
471 | const struct option *options, | ||
472 | const char * const usagestr[]) | ||
473 | { | ||
474 | int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); | ||
475 | int excl_short_opt = 1; | ||
476 | const char *arg; | ||
477 | |||
478 | /* we must reset ->opt, unknown short option leave it dangling */ | ||
479 | ctx->opt = NULL; | ||
480 | |||
481 | for (; ctx->argc; ctx->argc--, ctx->argv++) { | ||
482 | arg = ctx->argv[0]; | ||
483 | if (*arg != '-' || !arg[1]) { | ||
484 | if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) | ||
485 | break; | ||
486 | ctx->out[ctx->cpidx++] = ctx->argv[0]; | ||
487 | continue; | ||
488 | } | ||
489 | |||
490 | if (arg[1] != '-') { | ||
491 | ctx->opt = ++arg; | ||
492 | if (internal_help && *ctx->opt == 'h') { | ||
493 | return usage_with_options_internal(usagestr, options, 0, ctx); | ||
494 | } | ||
495 | switch (parse_short_opt(ctx, options)) { | ||
496 | case -1: | ||
497 | return parse_options_usage(usagestr, options, arg, 1); | ||
498 | case -2: | ||
499 | goto unknown; | ||
500 | case -3: | ||
501 | goto exclusive; | ||
502 | default: | ||
503 | break; | ||
504 | } | ||
505 | if (ctx->opt) | ||
506 | check_typos(arg, options); | ||
507 | while (ctx->opt) { | ||
508 | if (internal_help && *ctx->opt == 'h') | ||
509 | return usage_with_options_internal(usagestr, options, 0, ctx); | ||
510 | arg = ctx->opt; | ||
511 | switch (parse_short_opt(ctx, options)) { | ||
512 | case -1: | ||
513 | return parse_options_usage(usagestr, options, arg, 1); | ||
514 | case -2: | ||
515 | /* fake a short option thing to hide the fact that we may have | ||
516 | * started to parse aggregated stuff | ||
517 | * | ||
518 | * This is leaky, too bad. | ||
519 | */ | ||
520 | ctx->argv[0] = strdup(ctx->opt - 1); | ||
521 | *(char *)ctx->argv[0] = '-'; | ||
522 | goto unknown; | ||
523 | case -3: | ||
524 | goto exclusive; | ||
525 | default: | ||
526 | break; | ||
527 | } | ||
528 | } | ||
529 | continue; | ||
530 | } | ||
531 | |||
532 | if (!arg[2]) { /* "--" */ | ||
533 | if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { | ||
534 | ctx->argc--; | ||
535 | ctx->argv++; | ||
536 | } | ||
537 | break; | ||
538 | } | ||
539 | |||
540 | arg += 2; | ||
541 | if (internal_help && !strcmp(arg, "help-all")) | ||
542 | return usage_with_options_internal(usagestr, options, 1, ctx); | ||
543 | if (internal_help && !strcmp(arg, "help")) | ||
544 | return usage_with_options_internal(usagestr, options, 0, ctx); | ||
545 | if (!strcmp(arg, "list-opts")) | ||
546 | return PARSE_OPT_LIST_OPTS; | ||
547 | if (!strcmp(arg, "list-cmds")) | ||
548 | return PARSE_OPT_LIST_SUBCMDS; | ||
549 | switch (parse_long_opt(ctx, arg, options)) { | ||
550 | case -1: | ||
551 | return parse_options_usage(usagestr, options, arg, 0); | ||
552 | case -2: | ||
553 | goto unknown; | ||
554 | case -3: | ||
555 | excl_short_opt = 0; | ||
556 | goto exclusive; | ||
557 | default: | ||
558 | break; | ||
559 | } | ||
560 | continue; | ||
561 | unknown: | ||
562 | if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) | ||
563 | return PARSE_OPT_UNKNOWN; | ||
564 | ctx->out[ctx->cpidx++] = ctx->argv[0]; | ||
565 | ctx->opt = NULL; | ||
566 | } | ||
567 | return PARSE_OPT_DONE; | ||
568 | |||
569 | exclusive: | ||
570 | parse_options_usage(usagestr, options, arg, excl_short_opt); | ||
571 | if ((excl_short_opt && ctx->excl_opt->short_name) || | ||
572 | ctx->excl_opt->long_name == NULL) { | ||
573 | char opt = ctx->excl_opt->short_name; | ||
574 | parse_options_usage(NULL, options, &opt, 1); | ||
575 | } else { | ||
576 | parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0); | ||
577 | } | ||
578 | return PARSE_OPT_HELP; | ||
579 | } | ||
580 | |||
581 | static int parse_options_end(struct parse_opt_ctx_t *ctx) | ||
582 | { | ||
583 | memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out)); | ||
584 | ctx->out[ctx->cpidx + ctx->argc] = NULL; | ||
585 | return ctx->cpidx + ctx->argc; | ||
586 | } | ||
587 | |||
588 | int parse_options_subcommand(int argc, const char **argv, const struct option *options, | ||
589 | const char *const subcommands[], const char *usagestr[], int flags) | ||
590 | { | ||
591 | struct parse_opt_ctx_t ctx; | ||
592 | |||
593 | /* build usage string if it's not provided */ | ||
594 | if (subcommands && !usagestr[0]) { | ||
595 | char *buf = NULL; | ||
596 | |||
597 | astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]); | ||
598 | |||
599 | for (int i = 0; subcommands[i]; i++) { | ||
600 | if (i) | ||
601 | astrcat(&buf, "|"); | ||
602 | astrcat(&buf, subcommands[i]); | ||
603 | } | ||
604 | astrcat(&buf, "}"); | ||
605 | |||
606 | usagestr[0] = buf; | ||
607 | } | ||
608 | |||
609 | parse_options_start(&ctx, argc, argv, flags); | ||
610 | switch (parse_options_step(&ctx, options, usagestr)) { | ||
611 | case PARSE_OPT_HELP: | ||
612 | exit(129); | ||
613 | case PARSE_OPT_DONE: | ||
614 | break; | ||
615 | case PARSE_OPT_LIST_OPTS: | ||
616 | while (options->type != OPTION_END) { | ||
617 | if (options->long_name) | ||
618 | printf("--%s ", options->long_name); | ||
619 | options++; | ||
620 | } | ||
621 | putchar('\n'); | ||
622 | exit(130); | ||
623 | case PARSE_OPT_LIST_SUBCMDS: | ||
624 | if (subcommands) { | ||
625 | for (int i = 0; subcommands[i]; i++) | ||
626 | printf("%s ", subcommands[i]); | ||
627 | } | ||
628 | putchar('\n'); | ||
629 | exit(130); | ||
630 | default: /* PARSE_OPT_UNKNOWN */ | ||
631 | if (ctx.argv[0][1] == '-') | ||
632 | astrcatf(&error_buf, "unknown option `%s'", | ||
633 | ctx.argv[0] + 2); | ||
634 | else | ||
635 | astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt); | ||
636 | usage_with_options(usagestr, options); | ||
637 | } | ||
638 | |||
639 | return parse_options_end(&ctx); | ||
640 | } | ||
641 | |||
642 | int parse_options(int argc, const char **argv, const struct option *options, | ||
643 | const char * const usagestr[], int flags) | ||
644 | { | ||
645 | return parse_options_subcommand(argc, argv, options, NULL, | ||
646 | (const char **) usagestr, flags); | ||
647 | } | ||
648 | |||
649 | #define USAGE_OPTS_WIDTH 24 | ||
650 | #define USAGE_GAP 2 | ||
651 | |||
652 | static void print_option_help(const struct option *opts, int full) | ||
653 | { | ||
654 | size_t pos; | ||
655 | int pad; | ||
656 | |||
657 | if (opts->type == OPTION_GROUP) { | ||
658 | fputc('\n', stderr); | ||
659 | if (*opts->help) | ||
660 | fprintf(stderr, "%s\n", opts->help); | ||
661 | return; | ||
662 | } | ||
663 | if (!full && (opts->flags & PARSE_OPT_HIDDEN)) | ||
664 | return; | ||
665 | if (opts->flags & PARSE_OPT_DISABLED) | ||
666 | return; | ||
667 | |||
668 | pos = fprintf(stderr, " "); | ||
669 | if (opts->short_name) | ||
670 | pos += fprintf(stderr, "-%c", opts->short_name); | ||
671 | else | ||
672 | pos += fprintf(stderr, " "); | ||
673 | |||
674 | if (opts->long_name && opts->short_name) | ||
675 | pos += fprintf(stderr, ", "); | ||
676 | if (opts->long_name) | ||
677 | pos += fprintf(stderr, "--%s", opts->long_name); | ||
678 | |||
679 | switch (opts->type) { | ||
680 | case OPTION_ARGUMENT: | ||
681 | break; | ||
682 | case OPTION_LONG: | ||
683 | case OPTION_U64: | ||
684 | case OPTION_INTEGER: | ||
685 | case OPTION_UINTEGER: | ||
686 | if (opts->flags & PARSE_OPT_OPTARG) | ||
687 | if (opts->long_name) | ||
688 | pos += fprintf(stderr, "[=<n>]"); | ||
689 | else | ||
690 | pos += fprintf(stderr, "[<n>]"); | ||
691 | else | ||
692 | pos += fprintf(stderr, " <n>"); | ||
693 | break; | ||
694 | case OPTION_CALLBACK: | ||
695 | if (opts->flags & PARSE_OPT_NOARG) | ||
696 | break; | ||
697 | /* FALLTHROUGH */ | ||
698 | case OPTION_STRING: | ||
699 | if (opts->argh) { | ||
700 | if (opts->flags & PARSE_OPT_OPTARG) | ||
701 | if (opts->long_name) | ||
702 | pos += fprintf(stderr, "[=<%s>]", opts->argh); | ||
703 | else | ||
704 | pos += fprintf(stderr, "[<%s>]", opts->argh); | ||
705 | else | ||
706 | pos += fprintf(stderr, " <%s>", opts->argh); | ||
707 | } else { | ||
708 | if (opts->flags & PARSE_OPT_OPTARG) | ||
709 | if (opts->long_name) | ||
710 | pos += fprintf(stderr, "[=...]"); | ||
711 | else | ||
712 | pos += fprintf(stderr, "[...]"); | ||
713 | else | ||
714 | pos += fprintf(stderr, " ..."); | ||
715 | } | ||
716 | break; | ||
717 | default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ | ||
718 | case OPTION_END: | ||
719 | case OPTION_GROUP: | ||
720 | case OPTION_BIT: | ||
721 | case OPTION_BOOLEAN: | ||
722 | case OPTION_INCR: | ||
723 | case OPTION_SET_UINT: | ||
724 | case OPTION_SET_PTR: | ||
725 | break; | ||
726 | } | ||
727 | |||
728 | if (pos <= USAGE_OPTS_WIDTH) | ||
729 | pad = USAGE_OPTS_WIDTH - pos; | ||
730 | else { | ||
731 | fputc('\n', stderr); | ||
732 | pad = USAGE_OPTS_WIDTH; | ||
733 | } | ||
734 | fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); | ||
735 | if (opts->flags & PARSE_OPT_NOBUILD) | ||
736 | fprintf(stderr, "%*s(not built-in because %s)\n", | ||
737 | USAGE_OPTS_WIDTH + USAGE_GAP, "", | ||
738 | opts->build_opt); | ||
739 | } | ||
740 | |||
741 | static int option__cmp(const void *va, const void *vb) | ||
742 | { | ||
743 | const struct option *a = va, *b = vb; | ||
744 | int sa = tolower(a->short_name), sb = tolower(b->short_name), ret; | ||
745 | |||
746 | if (sa == 0) | ||
747 | sa = 'z' + 1; | ||
748 | if (sb == 0) | ||
749 | sb = 'z' + 1; | ||
750 | |||
751 | ret = sa - sb; | ||
752 | |||
753 | if (ret == 0) { | ||
754 | const char *la = a->long_name ?: "", | ||
755 | *lb = b->long_name ?: ""; | ||
756 | ret = strcmp(la, lb); | ||
757 | } | ||
758 | |||
759 | return ret; | ||
760 | } | ||
761 | |||
762 | static struct option *options__order(const struct option *opts) | ||
763 | { | ||
764 | int nr_opts = 0, len; | ||
765 | const struct option *o = opts; | ||
766 | struct option *ordered; | ||
767 | |||
768 | for (o = opts; o->type != OPTION_END; o++) | ||
769 | ++nr_opts; | ||
770 | |||
771 | len = sizeof(*o) * (nr_opts + 1); | ||
772 | ordered = malloc(len); | ||
773 | if (!ordered) | ||
774 | goto out; | ||
775 | memcpy(ordered, opts, len); | ||
776 | |||
777 | qsort(ordered, nr_opts, sizeof(*o), option__cmp); | ||
778 | out: | ||
779 | return ordered; | ||
780 | } | ||
781 | |||
782 | static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx) | ||
783 | { | ||
784 | int i; | ||
785 | |||
786 | for (i = 1; i < ctx->argc; ++i) { | ||
787 | const char *arg = ctx->argv[i]; | ||
788 | |||
789 | if (arg[0] != '-') { | ||
790 | if (arg[1] == '\0') { | ||
791 | if (arg[0] == opt->short_name) | ||
792 | return true; | ||
793 | continue; | ||
794 | } | ||
795 | |||
796 | if (opt->long_name && strcmp(opt->long_name, arg) == 0) | ||
797 | return true; | ||
798 | |||
799 | if (opt->help && strcasestr(opt->help, arg) != NULL) | ||
800 | return true; | ||
801 | |||
802 | continue; | ||
803 | } | ||
804 | |||
805 | if (arg[1] == opt->short_name || | ||
806 | (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0)) | ||
807 | return true; | ||
808 | } | ||
809 | |||
810 | return false; | ||
811 | } | ||
812 | |||
813 | static int usage_with_options_internal(const char * const *usagestr, | ||
814 | const struct option *opts, int full, | ||
815 | struct parse_opt_ctx_t *ctx) | ||
816 | { | ||
817 | struct option *ordered; | ||
818 | |||
819 | if (!usagestr) | ||
820 | return PARSE_OPT_HELP; | ||
821 | |||
822 | setup_pager(); | ||
823 | |||
824 | if (error_buf) { | ||
825 | fprintf(stderr, " Error: %s\n", error_buf); | ||
826 | zfree(&error_buf); | ||
827 | } | ||
828 | |||
829 | fprintf(stderr, "\n Usage: %s\n", *usagestr++); | ||
830 | while (*usagestr && **usagestr) | ||
831 | fprintf(stderr, " or: %s\n", *usagestr++); | ||
832 | while (*usagestr) { | ||
833 | fprintf(stderr, "%s%s\n", | ||
834 | **usagestr ? " " : "", | ||
835 | *usagestr); | ||
836 | usagestr++; | ||
837 | } | ||
838 | |||
839 | if (opts->type != OPTION_GROUP) | ||
840 | fputc('\n', stderr); | ||
841 | |||
842 | ordered = options__order(opts); | ||
843 | if (ordered) | ||
844 | opts = ordered; | ||
845 | |||
846 | for ( ; opts->type != OPTION_END; opts++) { | ||
847 | if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx)) | ||
848 | continue; | ||
849 | print_option_help(opts, full); | ||
850 | } | ||
851 | |||
852 | fputc('\n', stderr); | ||
853 | |||
854 | free(ordered); | ||
855 | |||
856 | return PARSE_OPT_HELP; | ||
857 | } | ||
858 | |||
859 | void usage_with_options(const char * const *usagestr, | ||
860 | const struct option *opts) | ||
861 | { | ||
862 | usage_with_options_internal(usagestr, opts, 0, NULL); | ||
863 | exit(129); | ||
864 | } | ||
865 | |||
866 | void usage_with_options_msg(const char * const *usagestr, | ||
867 | const struct option *opts, const char *fmt, ...) | ||
868 | { | ||
869 | va_list ap; | ||
870 | char *tmp = error_buf; | ||
871 | |||
872 | va_start(ap, fmt); | ||
873 | if (vasprintf(&error_buf, fmt, ap) == -1) | ||
874 | die("vasprintf failed"); | ||
875 | va_end(ap); | ||
876 | |||
877 | free(tmp); | ||
878 | |||
879 | usage_with_options_internal(usagestr, opts, 0, NULL); | ||
880 | exit(129); | ||
881 | } | ||
882 | |||
883 | int parse_options_usage(const char * const *usagestr, | ||
884 | const struct option *opts, | ||
885 | const char *optstr, bool short_opt) | ||
886 | { | ||
887 | if (!usagestr) | ||
888 | goto opt; | ||
889 | |||
890 | fprintf(stderr, "\n Usage: %s\n", *usagestr++); | ||
891 | while (*usagestr && **usagestr) | ||
892 | fprintf(stderr, " or: %s\n", *usagestr++); | ||
893 | while (*usagestr) { | ||
894 | fprintf(stderr, "%s%s\n", | ||
895 | **usagestr ? " " : "", | ||
896 | *usagestr); | ||
897 | usagestr++; | ||
898 | } | ||
899 | fputc('\n', stderr); | ||
900 | |||
901 | opt: | ||
902 | for ( ; opts->type != OPTION_END; opts++) { | ||
903 | if (short_opt) { | ||
904 | if (opts->short_name == *optstr) { | ||
905 | print_option_help(opts, 0); | ||
906 | break; | ||
907 | } | ||
908 | continue; | ||
909 | } | ||
910 | |||
911 | if (opts->long_name == NULL) | ||
912 | continue; | ||
913 | |||
914 | if (!prefixcmp(opts->long_name, optstr)) | ||
915 | print_option_help(opts, 0); | ||
916 | if (!prefixcmp("no-", optstr) && | ||
917 | !prefixcmp(opts->long_name, optstr + 3)) | ||
918 | print_option_help(opts, 0); | ||
919 | } | ||
920 | |||
921 | return PARSE_OPT_HELP; | ||
922 | } | ||
923 | |||
924 | |||
925 | int parse_opt_verbosity_cb(const struct option *opt, | ||
926 | const char *arg __maybe_unused, | ||
927 | int unset) | ||
928 | { | ||
929 | int *target = opt->value; | ||
930 | |||
931 | if (unset) | ||
932 | /* --no-quiet, --no-verbose */ | ||
933 | *target = 0; | ||
934 | else if (opt->short_name == 'v') { | ||
935 | if (*target >= 0) | ||
936 | (*target)++; | ||
937 | else | ||
938 | *target = 1; | ||
939 | } else { | ||
940 | if (*target <= 0) | ||
941 | (*target)--; | ||
942 | else | ||
943 | *target = -1; | ||
944 | } | ||
945 | return 0; | ||
946 | } | ||
947 | |||
948 | static struct option * | ||
949 | find_option(struct option *opts, int shortopt, const char *longopt) | ||
950 | { | ||
951 | for (; opts->type != OPTION_END; opts++) { | ||
952 | if ((shortopt && opts->short_name == shortopt) || | ||
953 | (opts->long_name && longopt && | ||
954 | !strcmp(opts->long_name, longopt))) | ||
955 | return opts; | ||
956 | } | ||
957 | return NULL; | ||
958 | } | ||
959 | |||
960 | void set_option_flag(struct option *opts, int shortopt, const char *longopt, | ||
961 | int flag) | ||
962 | { | ||
963 | struct option *opt = find_option(opts, shortopt, longopt); | ||
964 | |||
965 | if (opt) | ||
966 | opt->flags |= flag; | ||
967 | return; | ||
968 | } | ||
969 | |||
970 | void set_option_nobuild(struct option *opts, int shortopt, | ||
971 | const char *longopt, | ||
972 | const char *build_opt, | ||
973 | bool can_skip) | ||
974 | { | ||
975 | struct option *opt = find_option(opts, shortopt, longopt); | ||
976 | |||
977 | if (!opt) | ||
978 | return; | ||
979 | |||
980 | opt->flags |= PARSE_OPT_NOBUILD; | ||
981 | opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0; | ||
982 | opt->build_opt = build_opt; | ||
983 | } | ||
diff --git a/tools/lib/subcmd/parse-options.h b/tools/lib/subcmd/parse-options.h new file mode 100644 index 000000000000..dec893f10477 --- /dev/null +++ b/tools/lib/subcmd/parse-options.h | |||
@@ -0,0 +1,228 @@ | |||
1 | #ifndef __PERF_PARSE_OPTIONS_H | ||
2 | #define __PERF_PARSE_OPTIONS_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include <stdint.h> | ||
6 | |||
7 | enum parse_opt_type { | ||
8 | /* special types */ | ||
9 | OPTION_END, | ||
10 | OPTION_ARGUMENT, | ||
11 | OPTION_GROUP, | ||
12 | /* options with no arguments */ | ||
13 | OPTION_BIT, | ||
14 | OPTION_BOOLEAN, | ||
15 | OPTION_INCR, | ||
16 | OPTION_SET_UINT, | ||
17 | OPTION_SET_PTR, | ||
18 | /* options with arguments (usually) */ | ||
19 | OPTION_STRING, | ||
20 | OPTION_INTEGER, | ||
21 | OPTION_LONG, | ||
22 | OPTION_CALLBACK, | ||
23 | OPTION_U64, | ||
24 | OPTION_UINTEGER, | ||
25 | }; | ||
26 | |||
27 | enum parse_opt_flags { | ||
28 | PARSE_OPT_KEEP_DASHDASH = 1, | ||
29 | PARSE_OPT_STOP_AT_NON_OPTION = 2, | ||
30 | PARSE_OPT_KEEP_ARGV0 = 4, | ||
31 | PARSE_OPT_KEEP_UNKNOWN = 8, | ||
32 | PARSE_OPT_NO_INTERNAL_HELP = 16, | ||
33 | }; | ||
34 | |||
35 | enum parse_opt_option_flags { | ||
36 | PARSE_OPT_OPTARG = 1, | ||
37 | PARSE_OPT_NOARG = 2, | ||
38 | PARSE_OPT_NONEG = 4, | ||
39 | PARSE_OPT_HIDDEN = 8, | ||
40 | PARSE_OPT_LASTARG_DEFAULT = 16, | ||
41 | PARSE_OPT_DISABLED = 32, | ||
42 | PARSE_OPT_EXCLUSIVE = 64, | ||
43 | PARSE_OPT_NOEMPTY = 128, | ||
44 | PARSE_OPT_NOBUILD = 256, | ||
45 | PARSE_OPT_CANSKIP = 512, | ||
46 | }; | ||
47 | |||
48 | struct option; | ||
49 | typedef int parse_opt_cb(const struct option *, const char *arg, int unset); | ||
50 | |||
51 | /* | ||
52 | * `type`:: | ||
53 | * holds the type of the option, you must have an OPTION_END last in your | ||
54 | * array. | ||
55 | * | ||
56 | * `short_name`:: | ||
57 | * the character to use as a short option name, '\0' if none. | ||
58 | * | ||
59 | * `long_name`:: | ||
60 | * the long option name, without the leading dashes, NULL if none. | ||
61 | * | ||
62 | * `value`:: | ||
63 | * stores pointers to the values to be filled. | ||
64 | * | ||
65 | * `argh`:: | ||
66 | * token to explain the kind of argument this option wants. Keep it | ||
67 | * homogenous across the repository. | ||
68 | * | ||
69 | * `help`:: | ||
70 | * the short help associated to what the option does. | ||
71 | * Must never be NULL (except for OPTION_END). | ||
72 | * OPTION_GROUP uses this pointer to store the group header. | ||
73 | * | ||
74 | * `flags`:: | ||
75 | * mask of parse_opt_option_flags. | ||
76 | * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs) | ||
77 | * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs | ||
78 | * PARSE_OPT_NONEG: says that this option cannot be negated | ||
79 | * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in | ||
80 | * the long one. | ||
81 | * | ||
82 | * `callback`:: | ||
83 | * pointer to the callback to use for OPTION_CALLBACK. | ||
84 | * | ||
85 | * `defval`:: | ||
86 | * default value to fill (*->value) with for PARSE_OPT_OPTARG. | ||
87 | * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in | ||
88 | * the value when met. | ||
89 | * CALLBACKS can use it like they want. | ||
90 | * | ||
91 | * `set`:: | ||
92 | * whether an option was set by the user | ||
93 | */ | ||
94 | struct option { | ||
95 | enum parse_opt_type type; | ||
96 | int short_name; | ||
97 | const char *long_name; | ||
98 | void *value; | ||
99 | const char *argh; | ||
100 | const char *help; | ||
101 | const char *build_opt; | ||
102 | |||
103 | int flags; | ||
104 | parse_opt_cb *callback; | ||
105 | intptr_t defval; | ||
106 | bool *set; | ||
107 | void *data; | ||
108 | }; | ||
109 | |||
110 | #define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) | ||
111 | |||
112 | #define OPT_END() { .type = OPTION_END } | ||
113 | #define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } | ||
114 | #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } | ||
115 | #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) } | ||
116 | #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } | ||
117 | #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) } | ||
118 | #define OPT_BOOLEAN_SET(s, l, v, os, h) \ | ||
119 | { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ | ||
120 | .value = check_vtype(v, bool *), .help = (h), \ | ||
121 | .set = check_vtype(os, bool *)} | ||
122 | #define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } | ||
123 | #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) } | ||
124 | #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) } | ||
125 | #define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } | ||
126 | #define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } | ||
127 | #define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } | ||
128 | #define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } | ||
129 | #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) } | ||
130 | #define OPT_STRING_OPTARG(s, l, v, a, h, d) \ | ||
131 | { .type = OPTION_STRING, .short_name = (s), .long_name = (l), \ | ||
132 | .value = check_vtype(v, const char **), (a), .help = (h), \ | ||
133 | .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) } | ||
134 | #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} | ||
135 | #define OPT_DATE(s, l, v, h) \ | ||
136 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } | ||
137 | #define OPT_CALLBACK(s, l, v, a, h, f) \ | ||
138 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) } | ||
139 | #define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \ | ||
140 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG } | ||
141 | #define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ | ||
142 | { .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 } | ||
143 | #define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \ | ||
144 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\ | ||
145 | .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\ | ||
146 | .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG} | ||
147 | #define OPT_CALLBACK_OPTARG(s, l, v, d, a, h, f) \ | ||
148 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \ | ||
149 | .value = (v), (a), .help = (h), .callback = (f), \ | ||
150 | .flags = PARSE_OPT_OPTARG, .data = (d) } | ||
151 | |||
152 | /* parse_options() will filter out the processed options and leave the | ||
153 | * non-option argments in argv[]. | ||
154 | * Returns the number of arguments left in argv[]. | ||
155 | * | ||
156 | * NOTE: parse_options() and parse_options_subcommand() may call exit() in the | ||
157 | * case of an error (or for 'special' options like --list-cmds or --list-opts). | ||
158 | */ | ||
159 | extern int parse_options(int argc, const char **argv, | ||
160 | const struct option *options, | ||
161 | const char * const usagestr[], int flags); | ||
162 | |||
163 | extern int parse_options_subcommand(int argc, const char **argv, | ||
164 | const struct option *options, | ||
165 | const char *const subcommands[], | ||
166 | const char *usagestr[], int flags); | ||
167 | |||
168 | extern NORETURN void usage_with_options(const char * const *usagestr, | ||
169 | const struct option *options); | ||
170 | extern NORETURN __attribute__((format(printf,3,4))) | ||
171 | void usage_with_options_msg(const char * const *usagestr, | ||
172 | const struct option *options, | ||
173 | const char *fmt, ...); | ||
174 | |||
175 | /*----- incremantal advanced APIs -----*/ | ||
176 | |||
177 | enum { | ||
178 | PARSE_OPT_HELP = -1, | ||
179 | PARSE_OPT_DONE, | ||
180 | PARSE_OPT_LIST_OPTS, | ||
181 | PARSE_OPT_LIST_SUBCMDS, | ||
182 | PARSE_OPT_UNKNOWN, | ||
183 | }; | ||
184 | |||
185 | /* | ||
186 | * It's okay for the caller to consume argv/argc in the usual way. | ||
187 | * Other fields of that structure are private to parse-options and should not | ||
188 | * be modified in any way. | ||
189 | */ | ||
190 | struct parse_opt_ctx_t { | ||
191 | const char **argv; | ||
192 | const char **out; | ||
193 | int argc, cpidx; | ||
194 | const char *opt; | ||
195 | const struct option *excl_opt; | ||
196 | int flags; | ||
197 | }; | ||
198 | |||
199 | extern int parse_options_usage(const char * const *usagestr, | ||
200 | const struct option *opts, | ||
201 | const char *optstr, | ||
202 | bool short_opt); | ||
203 | |||
204 | |||
205 | /*----- some often used options -----*/ | ||
206 | extern int parse_opt_abbrev_cb(const struct option *, const char *, int); | ||
207 | extern int parse_opt_approxidate_cb(const struct option *, const char *, int); | ||
208 | extern int parse_opt_verbosity_cb(const struct option *, const char *, int); | ||
209 | |||
210 | #define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") | ||
211 | #define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") | ||
212 | #define OPT__VERBOSITY(var) \ | ||
213 | { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \ | ||
214 | PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \ | ||
215 | { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \ | ||
216 | PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 } | ||
217 | #define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") | ||
218 | #define OPT__ABBREV(var) \ | ||
219 | { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ | ||
220 | "use <n> digits to display SHA-1s", \ | ||
221 | PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } | ||
222 | |||
223 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); | ||
224 | |||
225 | void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag); | ||
226 | void set_option_nobuild(struct option *opts, int shortopt, const char *longopt, | ||
227 | const char *build_opt, bool can_skip); | ||
228 | #endif /* __PERF_PARSE_OPTIONS_H */ | ||
diff --git a/tools/lib/subcmd/run-command.c b/tools/lib/subcmd/run-command.c new file mode 100644 index 000000000000..f4f6c9eb8e59 --- /dev/null +++ b/tools/lib/subcmd/run-command.c | |||
@@ -0,0 +1,227 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <sys/types.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <fcntl.h> | ||
5 | #include <string.h> | ||
6 | #include <errno.h> | ||
7 | #include <sys/wait.h> | ||
8 | #include "subcmd-util.h" | ||
9 | #include "run-command.h" | ||
10 | #include "exec-cmd.h" | ||
11 | |||
12 | #define STRERR_BUFSIZE 128 | ||
13 | |||
14 | static inline void close_pair(int fd[2]) | ||
15 | { | ||
16 | close(fd[0]); | ||
17 | close(fd[1]); | ||
18 | } | ||
19 | |||
20 | static inline void dup_devnull(int to) | ||
21 | { | ||
22 | int fd = open("/dev/null", O_RDWR); | ||
23 | dup2(fd, to); | ||
24 | close(fd); | ||
25 | } | ||
26 | |||
27 | int start_command(struct child_process *cmd) | ||
28 | { | ||
29 | int need_in, need_out, need_err; | ||
30 | int fdin[2], fdout[2], fderr[2]; | ||
31 | char sbuf[STRERR_BUFSIZE]; | ||
32 | |||
33 | /* | ||
34 | * In case of errors we must keep the promise to close FDs | ||
35 | * that have been passed in via ->in and ->out. | ||
36 | */ | ||
37 | |||
38 | need_in = !cmd->no_stdin && cmd->in < 0; | ||
39 | if (need_in) { | ||
40 | if (pipe(fdin) < 0) { | ||
41 | if (cmd->out > 0) | ||
42 | close(cmd->out); | ||
43 | return -ERR_RUN_COMMAND_PIPE; | ||
44 | } | ||
45 | cmd->in = fdin[1]; | ||
46 | } | ||
47 | |||
48 | need_out = !cmd->no_stdout | ||
49 | && !cmd->stdout_to_stderr | ||
50 | && cmd->out < 0; | ||
51 | if (need_out) { | ||
52 | if (pipe(fdout) < 0) { | ||
53 | if (need_in) | ||
54 | close_pair(fdin); | ||
55 | else if (cmd->in) | ||
56 | close(cmd->in); | ||
57 | return -ERR_RUN_COMMAND_PIPE; | ||
58 | } | ||
59 | cmd->out = fdout[0]; | ||
60 | } | ||
61 | |||
62 | need_err = !cmd->no_stderr && cmd->err < 0; | ||
63 | if (need_err) { | ||
64 | if (pipe(fderr) < 0) { | ||
65 | if (need_in) | ||
66 | close_pair(fdin); | ||
67 | else if (cmd->in) | ||
68 | close(cmd->in); | ||
69 | if (need_out) | ||
70 | close_pair(fdout); | ||
71 | else if (cmd->out) | ||
72 | close(cmd->out); | ||
73 | return -ERR_RUN_COMMAND_PIPE; | ||
74 | } | ||
75 | cmd->err = fderr[0]; | ||
76 | } | ||
77 | |||
78 | fflush(NULL); | ||
79 | cmd->pid = fork(); | ||
80 | if (!cmd->pid) { | ||
81 | if (cmd->no_stdin) | ||
82 | dup_devnull(0); | ||
83 | else if (need_in) { | ||
84 | dup2(fdin[0], 0); | ||
85 | close_pair(fdin); | ||
86 | } else if (cmd->in) { | ||
87 | dup2(cmd->in, 0); | ||
88 | close(cmd->in); | ||
89 | } | ||
90 | |||
91 | if (cmd->no_stderr) | ||
92 | dup_devnull(2); | ||
93 | else if (need_err) { | ||
94 | dup2(fderr[1], 2); | ||
95 | close_pair(fderr); | ||
96 | } | ||
97 | |||
98 | if (cmd->no_stdout) | ||
99 | dup_devnull(1); | ||
100 | else if (cmd->stdout_to_stderr) | ||
101 | dup2(2, 1); | ||
102 | else if (need_out) { | ||
103 | dup2(fdout[1], 1); | ||
104 | close_pair(fdout); | ||
105 | } else if (cmd->out > 1) { | ||
106 | dup2(cmd->out, 1); | ||
107 | close(cmd->out); | ||
108 | } | ||
109 | |||
110 | if (cmd->dir && chdir(cmd->dir)) | ||
111 | die("exec %s: cd to %s failed (%s)", cmd->argv[0], | ||
112 | cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf))); | ||
113 | if (cmd->env) { | ||
114 | for (; *cmd->env; cmd->env++) { | ||
115 | if (strchr(*cmd->env, '=')) | ||
116 | putenv((char*)*cmd->env); | ||
117 | else | ||
118 | unsetenv(*cmd->env); | ||
119 | } | ||
120 | } | ||
121 | if (cmd->preexec_cb) | ||
122 | cmd->preexec_cb(); | ||
123 | if (cmd->exec_cmd) { | ||
124 | execv_cmd(cmd->argv); | ||
125 | } else { | ||
126 | execvp(cmd->argv[0], (char *const*) cmd->argv); | ||
127 | } | ||
128 | exit(127); | ||
129 | } | ||
130 | |||
131 | if (cmd->pid < 0) { | ||
132 | int err = errno; | ||
133 | if (need_in) | ||
134 | close_pair(fdin); | ||
135 | else if (cmd->in) | ||
136 | close(cmd->in); | ||
137 | if (need_out) | ||
138 | close_pair(fdout); | ||
139 | else if (cmd->out) | ||
140 | close(cmd->out); | ||
141 | if (need_err) | ||
142 | close_pair(fderr); | ||
143 | return err == ENOENT ? | ||
144 | -ERR_RUN_COMMAND_EXEC : | ||
145 | -ERR_RUN_COMMAND_FORK; | ||
146 | } | ||
147 | |||
148 | if (need_in) | ||
149 | close(fdin[0]); | ||
150 | else if (cmd->in) | ||
151 | close(cmd->in); | ||
152 | |||
153 | if (need_out) | ||
154 | close(fdout[1]); | ||
155 | else if (cmd->out) | ||
156 | close(cmd->out); | ||
157 | |||
158 | if (need_err) | ||
159 | close(fderr[1]); | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | static int wait_or_whine(pid_t pid) | ||
165 | { | ||
166 | char sbuf[STRERR_BUFSIZE]; | ||
167 | |||
168 | for (;;) { | ||
169 | int status, code; | ||
170 | pid_t waiting = waitpid(pid, &status, 0); | ||
171 | |||
172 | if (waiting < 0) { | ||
173 | if (errno == EINTR) | ||
174 | continue; | ||
175 | fprintf(stderr, " Error: waitpid failed (%s)", | ||
176 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
177 | return -ERR_RUN_COMMAND_WAITPID; | ||
178 | } | ||
179 | if (waiting != pid) | ||
180 | return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; | ||
181 | if (WIFSIGNALED(status)) | ||
182 | return -ERR_RUN_COMMAND_WAITPID_SIGNAL; | ||
183 | |||
184 | if (!WIFEXITED(status)) | ||
185 | return -ERR_RUN_COMMAND_WAITPID_NOEXIT; | ||
186 | code = WEXITSTATUS(status); | ||
187 | switch (code) { | ||
188 | case 127: | ||
189 | return -ERR_RUN_COMMAND_EXEC; | ||
190 | case 0: | ||
191 | return 0; | ||
192 | default: | ||
193 | return -code; | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | |||
198 | int finish_command(struct child_process *cmd) | ||
199 | { | ||
200 | return wait_or_whine(cmd->pid); | ||
201 | } | ||
202 | |||
203 | int run_command(struct child_process *cmd) | ||
204 | { | ||
205 | int code = start_command(cmd); | ||
206 | if (code) | ||
207 | return code; | ||
208 | return finish_command(cmd); | ||
209 | } | ||
210 | |||
211 | static void prepare_run_command_v_opt(struct child_process *cmd, | ||
212 | const char **argv, | ||
213 | int opt) | ||
214 | { | ||
215 | memset(cmd, 0, sizeof(*cmd)); | ||
216 | cmd->argv = argv; | ||
217 | cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; | ||
218 | cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0; | ||
219 | cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; | ||
220 | } | ||
221 | |||
222 | int run_command_v_opt(const char **argv, int opt) | ||
223 | { | ||
224 | struct child_process cmd; | ||
225 | prepare_run_command_v_opt(&cmd, argv, opt); | ||
226 | return run_command(&cmd); | ||
227 | } | ||
diff --git a/tools/lib/subcmd/run-command.h b/tools/lib/subcmd/run-command.h new file mode 100644 index 000000000000..4a55393a6547 --- /dev/null +++ b/tools/lib/subcmd/run-command.h | |||
@@ -0,0 +1,60 @@ | |||
1 | #ifndef __PERF_RUN_COMMAND_H | ||
2 | #define __PERF_RUN_COMMAND_H | ||
3 | |||
4 | #include <unistd.h> | ||
5 | |||
6 | enum { | ||
7 | ERR_RUN_COMMAND_FORK = 10000, | ||
8 | ERR_RUN_COMMAND_EXEC, | ||
9 | ERR_RUN_COMMAND_PIPE, | ||
10 | ERR_RUN_COMMAND_WAITPID, | ||
11 | ERR_RUN_COMMAND_WAITPID_WRONG_PID, | ||
12 | ERR_RUN_COMMAND_WAITPID_SIGNAL, | ||
13 | ERR_RUN_COMMAND_WAITPID_NOEXIT, | ||
14 | }; | ||
15 | #define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK) | ||
16 | |||
17 | struct child_process { | ||
18 | const char **argv; | ||
19 | pid_t pid; | ||
20 | /* | ||
21 | * Using .in, .out, .err: | ||
22 | * - Specify 0 for no redirections (child inherits stdin, stdout, | ||
23 | * stderr from parent). | ||
24 | * - Specify -1 to have a pipe allocated as follows: | ||
25 | * .in: returns the writable pipe end; parent writes to it, | ||
26 | * the readable pipe end becomes child's stdin | ||
27 | * .out, .err: returns the readable pipe end; parent reads from | ||
28 | * it, the writable pipe end becomes child's stdout/stderr | ||
29 | * The caller of start_command() must close the returned FDs | ||
30 | * after it has completed reading from/writing to it! | ||
31 | * - Specify > 0 to set a channel to a particular FD as follows: | ||
32 | * .in: a readable FD, becomes child's stdin | ||
33 | * .out: a writable FD, becomes child's stdout/stderr | ||
34 | * .err > 0 not supported | ||
35 | * The specified FD is closed by start_command(), even in case | ||
36 | * of errors! | ||
37 | */ | ||
38 | int in; | ||
39 | int out; | ||
40 | int err; | ||
41 | const char *dir; | ||
42 | const char *const *env; | ||
43 | unsigned no_stdin:1; | ||
44 | unsigned no_stdout:1; | ||
45 | unsigned no_stderr:1; | ||
46 | unsigned exec_cmd:1; /* if this is to be external sub-command */ | ||
47 | unsigned stdout_to_stderr:1; | ||
48 | void (*preexec_cb)(void); | ||
49 | }; | ||
50 | |||
51 | int start_command(struct child_process *); | ||
52 | int finish_command(struct child_process *); | ||
53 | int run_command(struct child_process *); | ||
54 | |||
55 | #define RUN_COMMAND_NO_STDIN 1 | ||
56 | #define RUN_EXEC_CMD 2 /*If this is to be external sub-command */ | ||
57 | #define RUN_COMMAND_STDOUT_TO_STDERR 4 | ||
58 | int run_command_v_opt(const char **argv, int opt); | ||
59 | |||
60 | #endif /* __PERF_RUN_COMMAND_H */ | ||
diff --git a/tools/lib/subcmd/sigchain.c b/tools/lib/subcmd/sigchain.c new file mode 100644 index 000000000000..3537c348a18e --- /dev/null +++ b/tools/lib/subcmd/sigchain.c | |||
@@ -0,0 +1,53 @@ | |||
1 | #include <signal.h> | ||
2 | #include "subcmd-util.h" | ||
3 | #include "sigchain.h" | ||
4 | |||
5 | #define SIGCHAIN_MAX_SIGNALS 32 | ||
6 | |||
7 | struct sigchain_signal { | ||
8 | sigchain_fun *old; | ||
9 | int n; | ||
10 | int alloc; | ||
11 | }; | ||
12 | static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS]; | ||
13 | |||
14 | static void check_signum(int sig) | ||
15 | { | ||
16 | if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS) | ||
17 | die("BUG: signal out of range: %d", sig); | ||
18 | } | ||
19 | |||
20 | static int sigchain_push(int sig, sigchain_fun f) | ||
21 | { | ||
22 | struct sigchain_signal *s = signals + sig; | ||
23 | check_signum(sig); | ||
24 | |||
25 | ALLOC_GROW(s->old, s->n + 1, s->alloc); | ||
26 | s->old[s->n] = signal(sig, f); | ||
27 | if (s->old[s->n] == SIG_ERR) | ||
28 | return -1; | ||
29 | s->n++; | ||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | int sigchain_pop(int sig) | ||
34 | { | ||
35 | struct sigchain_signal *s = signals + sig; | ||
36 | check_signum(sig); | ||
37 | if (s->n < 1) | ||
38 | return 0; | ||
39 | |||
40 | if (signal(sig, s->old[s->n - 1]) == SIG_ERR) | ||
41 | return -1; | ||
42 | s->n--; | ||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | void sigchain_push_common(sigchain_fun f) | ||
47 | { | ||
48 | sigchain_push(SIGINT, f); | ||
49 | sigchain_push(SIGHUP, f); | ||
50 | sigchain_push(SIGTERM, f); | ||
51 | sigchain_push(SIGQUIT, f); | ||
52 | sigchain_push(SIGPIPE, f); | ||
53 | } | ||
diff --git a/tools/lib/subcmd/sigchain.h b/tools/lib/subcmd/sigchain.h new file mode 100644 index 000000000000..959d64eb5557 --- /dev/null +++ b/tools/lib/subcmd/sigchain.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef __PERF_SIGCHAIN_H | ||
2 | #define __PERF_SIGCHAIN_H | ||
3 | |||
4 | typedef void (*sigchain_fun)(int); | ||
5 | |||
6 | int sigchain_pop(int sig); | ||
7 | |||
8 | void sigchain_push_common(sigchain_fun f); | ||
9 | |||
10 | #endif /* __PERF_SIGCHAIN_H */ | ||
diff --git a/tools/lib/subcmd/subcmd-config.c b/tools/lib/subcmd/subcmd-config.c new file mode 100644 index 000000000000..d017c728bd1b --- /dev/null +++ b/tools/lib/subcmd/subcmd-config.c | |||
@@ -0,0 +1,11 @@ | |||
1 | #include "subcmd-config.h" | ||
2 | |||
3 | #define UNDEFINED "SUBCMD_HAS_NOT_BEEN_INITIALIZED" | ||
4 | |||
5 | struct subcmd_config subcmd_config = { | ||
6 | .exec_name = UNDEFINED, | ||
7 | .prefix = UNDEFINED, | ||
8 | .exec_path = UNDEFINED, | ||
9 | .exec_path_env = UNDEFINED, | ||
10 | .pager_env = UNDEFINED, | ||
11 | }; | ||
diff --git a/tools/lib/subcmd/subcmd-config.h b/tools/lib/subcmd/subcmd-config.h new file mode 100644 index 000000000000..cc8514030b5c --- /dev/null +++ b/tools/lib/subcmd/subcmd-config.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef __PERF_SUBCMD_CONFIG_H | ||
2 | #define __PERF_SUBCMD_CONFIG_H | ||
3 | |||
4 | struct subcmd_config { | ||
5 | const char *exec_name; | ||
6 | const char *prefix; | ||
7 | const char *exec_path; | ||
8 | const char *exec_path_env; | ||
9 | const char *pager_env; | ||
10 | }; | ||
11 | |||
12 | extern struct subcmd_config subcmd_config; | ||
13 | |||
14 | #endif /* __PERF_SUBCMD_CONFIG_H */ | ||
diff --git a/tools/lib/subcmd/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h new file mode 100644 index 000000000000..321aeb11a381 --- /dev/null +++ b/tools/lib/subcmd/subcmd-util.h | |||
@@ -0,0 +1,91 @@ | |||
1 | #ifndef __PERF_SUBCMD_UTIL_H | ||
2 | #define __PERF_SUBCMD_UTIL_H | ||
3 | |||
4 | #include <stdarg.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <stdio.h> | ||
7 | |||
8 | #define NORETURN __attribute__((__noreturn__)) | ||
9 | |||
10 | static inline void report(const char *prefix, const char *err, va_list params) | ||
11 | { | ||
12 | char msg[1024]; | ||
13 | vsnprintf(msg, sizeof(msg), err, params); | ||
14 | fprintf(stderr, " %s%s\n", prefix, msg); | ||
15 | } | ||
16 | |||
17 | static NORETURN inline void die(const char *err, ...) | ||
18 | { | ||
19 | va_list params; | ||
20 | |||
21 | va_start(params, err); | ||
22 | report(" Fatal: ", err, params); | ||
23 | exit(128); | ||
24 | va_end(params); | ||
25 | } | ||
26 | |||
27 | #define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) | ||
28 | |||
29 | #define alloc_nr(x) (((x)+16)*3/2) | ||
30 | |||
31 | /* | ||
32 | * Realloc the buffer pointed at by variable 'x' so that it can hold | ||
33 | * at least 'nr' entries; the number of entries currently allocated | ||
34 | * is 'alloc', using the standard growing factor alloc_nr() macro. | ||
35 | * | ||
36 | * DO NOT USE any expression with side-effect for 'x' or 'alloc'. | ||
37 | */ | ||
38 | #define ALLOC_GROW(x, nr, alloc) \ | ||
39 | do { \ | ||
40 | if ((nr) > alloc) { \ | ||
41 | if (alloc_nr(alloc) < (nr)) \ | ||
42 | alloc = (nr); \ | ||
43 | else \ | ||
44 | alloc = alloc_nr(alloc); \ | ||
45 | x = xrealloc((x), alloc * sizeof(*(x))); \ | ||
46 | } \ | ||
47 | } while(0) | ||
48 | |||
49 | static inline void *xrealloc(void *ptr, size_t size) | ||
50 | { | ||
51 | void *ret = realloc(ptr, size); | ||
52 | if (!ret && !size) | ||
53 | ret = realloc(ptr, 1); | ||
54 | if (!ret) { | ||
55 | ret = realloc(ptr, size); | ||
56 | if (!ret && !size) | ||
57 | ret = realloc(ptr, 1); | ||
58 | if (!ret) | ||
59 | die("Out of memory, realloc failed"); | ||
60 | } | ||
61 | return ret; | ||
62 | } | ||
63 | |||
64 | #define astrcatf(out, fmt, ...) \ | ||
65 | ({ \ | ||
66 | char *tmp = *(out); \ | ||
67 | if (asprintf((out), "%s" fmt, tmp ?: "", ## __VA_ARGS__) == -1) \ | ||
68 | die("asprintf failed"); \ | ||
69 | free(tmp); \ | ||
70 | }) | ||
71 | |||
72 | static inline void astrcat(char **out, const char *add) | ||
73 | { | ||
74 | char *tmp = *out; | ||
75 | |||
76 | if (asprintf(out, "%s%s", tmp ?: "", add) == -1) | ||
77 | die("asprintf failed"); | ||
78 | |||
79 | free(tmp); | ||
80 | } | ||
81 | |||
82 | static inline int prefixcmp(const char *str, const char *prefix) | ||
83 | { | ||
84 | for (; ; str++, prefix++) | ||
85 | if (!*prefix) | ||
86 | return 0; | ||
87 | else if (*str != *prefix) | ||
88 | return (unsigned char)*prefix - (unsigned char)*str; | ||
89 | } | ||
90 | |||
91 | #endif /* __PERF_SUBCMD_UTIL_H */ | ||