Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/lib/subcmd/help.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <stdio.h>
3
#include <stdlib.h>
4
#include <string.h>
5
#include <linux/string.h>
6
#include <termios.h>
7
#include <sys/ioctl.h>
8
#include <sys/types.h>
9
#include <sys/stat.h>
10
#include <unistd.h>
11
#include <dirent.h>
12
#include <assert.h>
13
#include "subcmd-util.h"
14
#include "help.h"
15
#include "exec-cmd.h"
16
17
void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
18
{
19
struct cmdname *ent = malloc(sizeof(*ent) + len + 1);
20
if (!ent)
21
return;
22
23
ent->len = len;
24
memcpy(ent->name, name, len);
25
ent->name[len] = 0;
26
27
ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc);
28
cmds->names[cmds->cnt++] = ent;
29
}
30
31
void clean_cmdnames(struct cmdnames *cmds)
32
{
33
unsigned int i;
34
35
for (i = 0; i < cmds->cnt; ++i)
36
zfree(&cmds->names[i]);
37
zfree(&cmds->names);
38
cmds->cnt = 0;
39
cmds->alloc = 0;
40
}
41
42
int cmdname_compare(const void *a_, const void *b_)
43
{
44
struct cmdname *a = *(struct cmdname **)a_;
45
struct cmdname *b = *(struct cmdname **)b_;
46
return strcmp(a->name, b->name);
47
}
48
49
void uniq(struct cmdnames *cmds)
50
{
51
unsigned int i, j;
52
53
if (!cmds->cnt)
54
return;
55
56
for (i = 1; i < cmds->cnt; i++) {
57
if (!strcmp(cmds->names[i]->name, cmds->names[i-1]->name))
58
zfree(&cmds->names[i - 1]);
59
}
60
for (i = 0, j = 0; i < cmds->cnt; i++) {
61
if (cmds->names[i]) {
62
if (i == j)
63
j++;
64
else
65
cmds->names[j++] = cmds->names[i];
66
}
67
}
68
cmds->cnt = j;
69
while (j < i)
70
cmds->names[j++] = NULL;
71
}
72
73
void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
74
{
75
size_t ci, cj, ei;
76
int cmp;
77
78
ci = cj = ei = 0;
79
while (ci < cmds->cnt && ei < excludes->cnt) {
80
cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
81
if (cmp < 0) {
82
if (ci == cj) {
83
ci++;
84
cj++;
85
} else {
86
cmds->names[cj++] = cmds->names[ci];
87
cmds->names[ci++] = NULL;
88
}
89
} else if (cmp == 0) {
90
zfree(&cmds->names[ci]);
91
ci++;
92
ei++;
93
} else if (cmp > 0) {
94
ei++;
95
}
96
}
97
if (ci != cj) {
98
while (ci < cmds->cnt) {
99
cmds->names[cj++] = cmds->names[ci];
100
cmds->names[ci++] = NULL;
101
}
102
}
103
for (ci = cj; ci < cmds->cnt; ci++)
104
assert(cmds->names[ci] == NULL);
105
cmds->cnt = cj;
106
}
107
108
static void get_term_dimensions(struct winsize *ws)
109
{
110
char *s = getenv("LINES");
111
112
if (s != NULL) {
113
ws->ws_row = atoi(s);
114
s = getenv("COLUMNS");
115
if (s != NULL) {
116
ws->ws_col = atoi(s);
117
if (ws->ws_row && ws->ws_col)
118
return;
119
}
120
}
121
#ifdef TIOCGWINSZ
122
if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
123
ws->ws_row && ws->ws_col)
124
return;
125
#endif
126
ws->ws_row = 25;
127
ws->ws_col = 80;
128
}
129
130
static void pretty_print_string_list(struct cmdnames *cmds, int longest)
131
{
132
int cols = 1, rows;
133
int space = longest + 1; /* min 1 SP between words */
134
struct winsize win;
135
int max_cols;
136
int i, j;
137
138
get_term_dimensions(&win);
139
max_cols = win.ws_col - 1; /* don't print *on* the edge */
140
141
if (space < max_cols)
142
cols = max_cols / space;
143
rows = (cmds->cnt + cols - 1) / cols;
144
145
for (i = 0; i < rows; i++) {
146
printf(" ");
147
148
for (j = 0; j < cols; j++) {
149
unsigned int n = j * rows + i;
150
unsigned int size = space;
151
152
if (n >= cmds->cnt)
153
break;
154
if (j == cols-1 || n + rows >= cmds->cnt)
155
size = 1;
156
printf("%-*s", size, cmds->names[n]->name);
157
}
158
putchar('\n');
159
}
160
}
161
162
static int is_executable(const char *name)
163
{
164
struct stat st;
165
166
if (stat(name, &st) || /* stat, not lstat */
167
!S_ISREG(st.st_mode))
168
return 0;
169
170
return st.st_mode & S_IXUSR;
171
}
172
173
static int has_extension(const char *filename, const char *ext)
174
{
175
size_t len = strlen(filename);
176
size_t extlen = strlen(ext);
177
178
return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
179
}
180
181
static void list_commands_in_dir(struct cmdnames *cmds,
182
const char *path,
183
const char *prefix)
184
{
185
int prefix_len;
186
DIR *dir = opendir(path);
187
struct dirent *de;
188
char *buf = NULL;
189
190
if (!dir)
191
return;
192
if (!prefix)
193
prefix = "perf-";
194
prefix_len = strlen(prefix);
195
196
astrcatf(&buf, "%s/", path);
197
198
while ((de = readdir(dir)) != NULL) {
199
int entlen;
200
201
if (!strstarts(de->d_name, prefix))
202
continue;
203
204
astrcat(&buf, de->d_name);
205
if (!is_executable(buf))
206
continue;
207
208
entlen = strlen(de->d_name) - prefix_len;
209
if (has_extension(de->d_name, ".exe"))
210
entlen -= 4;
211
212
add_cmdname(cmds, de->d_name + prefix_len, entlen);
213
}
214
closedir(dir);
215
free(buf);
216
}
217
218
void load_command_list(const char *prefix,
219
struct cmdnames *main_cmds,
220
struct cmdnames *other_cmds)
221
{
222
const char *env_path = getenv("PATH");
223
char *exec_path = get_argv_exec_path();
224
225
if (exec_path) {
226
list_commands_in_dir(main_cmds, exec_path, prefix);
227
qsort(main_cmds->names, main_cmds->cnt,
228
sizeof(*main_cmds->names), cmdname_compare);
229
uniq(main_cmds);
230
}
231
232
if (env_path) {
233
char *paths, *path, *colon;
234
path = paths = strdup(env_path);
235
while (1) {
236
if ((colon = strchr(path, ':')))
237
*colon = 0;
238
if (!exec_path || strcmp(path, exec_path))
239
list_commands_in_dir(other_cmds, path, prefix);
240
241
if (!colon)
242
break;
243
path = colon + 1;
244
}
245
free(paths);
246
247
qsort(other_cmds->names, other_cmds->cnt,
248
sizeof(*other_cmds->names), cmdname_compare);
249
uniq(other_cmds);
250
}
251
free(exec_path);
252
exclude_cmds(other_cmds, main_cmds);
253
}
254
255
void list_commands(const char *title, struct cmdnames *main_cmds,
256
struct cmdnames *other_cmds)
257
{
258
unsigned int i, longest = 0;
259
260
for (i = 0; i < main_cmds->cnt; i++)
261
if (longest < main_cmds->names[i]->len)
262
longest = main_cmds->names[i]->len;
263
for (i = 0; i < other_cmds->cnt; i++)
264
if (longest < other_cmds->names[i]->len)
265
longest = other_cmds->names[i]->len;
266
267
if (main_cmds->cnt) {
268
char *exec_path = get_argv_exec_path();
269
printf("available %s in '%s'\n", title, exec_path);
270
printf("----------------");
271
mput_char('-', strlen(title) + strlen(exec_path));
272
putchar('\n');
273
pretty_print_string_list(main_cmds, longest);
274
putchar('\n');
275
free(exec_path);
276
}
277
278
if (other_cmds->cnt) {
279
printf("%s available from elsewhere on your $PATH\n", title);
280
printf("---------------------------------------");
281
mput_char('-', strlen(title));
282
putchar('\n');
283
pretty_print_string_list(other_cmds, longest);
284
putchar('\n');
285
}
286
}
287
288
int is_in_cmdlist(struct cmdnames *c, const char *s)
289
{
290
unsigned int i;
291
292
for (i = 0; i < c->cnt; i++)
293
if (!strcmp(s, c->names[i]->name))
294
return 1;
295
return 0;
296
}
297
298