File Index
Files and directories
Sign in
FILE

help.c

File preview with syntax highlighting.

linux-6.19.14/tools/lib/subcmd/help.c C
  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
 17void 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
 31void 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
 42int 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
 49void 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
 73void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
 74{
 75	size_t ci, cj, ei;
 76	int cmp;
 77
 78	if (!excludes->cnt)
 79		return;
 80
 81	ci = cj = ei = 0;
 82	while (ci < cmds->cnt && ei < excludes->cnt) {
 83		cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name);
 84		if (cmp < 0) {
 85			if (ci == cj) {
 86				ci++;
 87				cj++;
 88			} else {
 89				cmds->names[cj++] = cmds->names[ci];
 90				cmds->names[ci++] = NULL;
 91			}
 92		} else if (cmp == 0) {
 93			zfree(&cmds->names[ci]);
 94			ci++;
 95			ei++;
 96		} else if (cmp > 0) {
 97			ei++;
 98		}
 99	}
100	while (ci < cmds->cnt) {
101		if (ci != cj) {
102			cmds->names[cj] = cmds->names[ci];
103			cmds->names[ci] = NULL;
104		}
105		ci++;
106		cj++;
107	}
108	for (ci = cj; ci < cmds->cnt; ci++)
109		assert(cmds->names[ci] == NULL);
110	cmds->cnt = cj;
111}
112
113static void get_term_dimensions(struct winsize *ws)
114{
115	char *s = getenv("LINES");
116
117	if (s != NULL) {
118		ws->ws_row = atoi(s);
119		s = getenv("COLUMNS");
120		if (s != NULL) {
121			ws->ws_col = atoi(s);
122			if (ws->ws_row && ws->ws_col)
123				return;
124		}
125	}
126#ifdef TIOCGWINSZ
127	if (ioctl(1, TIOCGWINSZ, ws) == 0 &&
128	    ws->ws_row && ws->ws_col)
129		return;
130#endif
131	ws->ws_row = 25;
132	ws->ws_col = 80;
133}
134
135static void pretty_print_string_list(struct cmdnames *cmds, int longest)
136{
137	int cols = 1, rows;
138	int space = longest + 1; /* min 1 SP between words */
139	struct winsize win;
140	int max_cols;
141	int i, j;
142
143	get_term_dimensions(&win);
144	max_cols = win.ws_col - 1; /* don't print *on* the edge */
145
146	if (space < max_cols)
147		cols = max_cols / space;
148	rows = (cmds->cnt + cols - 1) / cols;
149
150	for (i = 0; i < rows; i++) {
151		printf("  ");
152
153		for (j = 0; j < cols; j++) {
154			unsigned int n = j * rows + i;
155			unsigned int size = space;
156
157			if (n >= cmds->cnt)
158				break;
159			if (j == cols-1 || n + rows >= cmds->cnt)
160				size = 1;
161			printf("%-*s", size, cmds->names[n]->name);
162		}
163		putchar('\n');
164	}
165}
166
167static int is_executable(const char *name)
168{
169	struct stat st;
170
171	if (stat(name, &st) || /* stat, not lstat */
172	    !S_ISREG(st.st_mode))
173		return 0;
174
175	return st.st_mode & S_IXUSR;
176}
177
178static int has_extension(const char *filename, const char *ext)
179{
180	size_t len = strlen(filename);
181	size_t extlen = strlen(ext);
182
183	return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
184}
185
186static void list_commands_in_dir(struct cmdnames *cmds,
187					 const char *path,
188					 const char *prefix)
189{
190	int prefix_len;
191	DIR *dir = opendir(path);
192	struct dirent *de;
193	char *buf = NULL;
194
195	if (!dir)
196		return;
197	if (!prefix)
198		prefix = "perf-";
199	prefix_len = strlen(prefix);
200
201	astrcatf(&buf, "%s/", path);
202
203	while ((de = readdir(dir)) != NULL) {
204		int entlen;
205
206		if (!strstarts(de->d_name, prefix))
207			continue;
208
209		astrcat(&buf, de->d_name);
210		if (!is_executable(buf))
211			continue;
212
213		entlen = strlen(de->d_name) - prefix_len;
214		if (has_extension(de->d_name, ".exe"))
215			entlen -= 4;
216
217		add_cmdname(cmds, de->d_name + prefix_len, entlen);
218	}
219	closedir(dir);
220	free(buf);
221}
222
223void load_command_list(const char *prefix,
224		struct cmdnames *main_cmds,
225		struct cmdnames *other_cmds)
226{
227	const char *env_path = getenv("PATH");
228	char *exec_path = get_argv_exec_path();
229
230	if (exec_path) {
231		list_commands_in_dir(main_cmds, exec_path, prefix);
232		qsort(main_cmds->names, main_cmds->cnt,
233		      sizeof(*main_cmds->names), cmdname_compare);
234		uniq(main_cmds);
235	}
236
237	if (env_path) {
238		char *paths, *path, *colon;
239		path = paths = strdup(env_path);
240		while (1) {
241			if ((colon = strchr(path, ':')))
242				*colon = 0;
243			if (!exec_path || strcmp(path, exec_path))
244				list_commands_in_dir(other_cmds, path, prefix);
245
246			if (!colon)
247				break;
248			path = colon + 1;
249		}
250		free(paths);
251
252		qsort(other_cmds->names, other_cmds->cnt,
253		      sizeof(*other_cmds->names), cmdname_compare);
254		uniq(other_cmds);
255	}
256	free(exec_path);
257	exclude_cmds(other_cmds, main_cmds);
258}
259
260void list_commands(const char *title, struct cmdnames *main_cmds,
261		   struct cmdnames *other_cmds)
262{
263	unsigned int i, longest = 0;
264
265	for (i = 0; i < main_cmds->cnt; i++)
266		if (longest < main_cmds->names[i]->len)
267			longest = main_cmds->names[i]->len;
268	for (i = 0; i < other_cmds->cnt; i++)
269		if (longest < other_cmds->names[i]->len)
270			longest = other_cmds->names[i]->len;
271
272	if (main_cmds->cnt) {
273		char *exec_path = get_argv_exec_path();
274		printf("available %s in '%s'\n", title, exec_path);
275		printf("----------------");
276		mput_char('-', strlen(title) + strlen(exec_path));
277		putchar('\n');
278		pretty_print_string_list(main_cmds, longest);
279		putchar('\n');
280		free(exec_path);
281	}
282
283	if (other_cmds->cnt) {
284		printf("%s available from elsewhere on your $PATH\n", title);
285		printf("---------------------------------------");
286		mput_char('-', strlen(title));
287		putchar('\n');
288		pretty_print_string_list(other_cmds, longest);
289		putchar('\n');
290	}
291}
292
293int is_in_cmdlist(struct cmdnames *c, const char *s)
294{
295	unsigned int i;
296
297	for (i = 0; i < c->cnt; i++)
298		if (!strcmp(s, c->names[i]->name))
299			return 1;
300	return 0;
301}
~/ Read-only file preview.