1 // SPDX-License-Identifier: GPL-2.0
5 #include <linux/string.h>
12 #include "subcmd-util.h"
16 void add_cmdname(struct cmdnames
*cmds
, const char *name
, size_t len
)
18 struct cmdname
*ent
= malloc(sizeof(*ent
) + len
+ 1);
21 memcpy(ent
->name
, name
, len
);
24 ALLOC_GROW(cmds
->names
, cmds
->cnt
+ 1, cmds
->alloc
);
25 cmds
->names
[cmds
->cnt
++] = ent
;
28 void clean_cmdnames(struct cmdnames
*cmds
)
32 for (i
= 0; i
< cmds
->cnt
; ++i
)
33 zfree(&cmds
->names
[i
]);
39 int cmdname_compare(const void *a_
, const void *b_
)
41 struct cmdname
*a
= *(struct cmdname
**)a_
;
42 struct cmdname
*b
= *(struct cmdname
**)b_
;
43 return strcmp(a
->name
, b
->name
);
46 void uniq(struct cmdnames
*cmds
)
53 for (i
= j
= 1; i
< cmds
->cnt
; i
++)
54 if (strcmp(cmds
->names
[i
]->name
, cmds
->names
[i
-1]->name
))
55 cmds
->names
[j
++] = cmds
->names
[i
];
60 void exclude_cmds(struct cmdnames
*cmds
, struct cmdnames
*excludes
)
66 while (ci
< cmds
->cnt
&& ei
< excludes
->cnt
) {
67 cmp
= strcmp(cmds
->names
[ci
]->name
, excludes
->names
[ei
]->name
);
69 cmds
->names
[cj
++] = cmds
->names
[ci
++];
70 } else if (cmp
== 0) {
78 while (ci
< cmds
->cnt
)
79 cmds
->names
[cj
++] = cmds
->names
[ci
++];
84 static void get_term_dimensions(struct winsize
*ws
)
86 char *s
= getenv("LINES");
90 s
= getenv("COLUMNS");
93 if (ws
->ws_row
&& ws
->ws_col
)
98 if (ioctl(1, TIOCGWINSZ
, ws
) == 0 &&
99 ws
->ws_row
&& ws
->ws_col
)
106 static void pretty_print_string_list(struct cmdnames
*cmds
, int longest
)
109 int space
= longest
+ 1; /* min 1 SP between words */
114 get_term_dimensions(&win
);
115 max_cols
= win
.ws_col
- 1; /* don't print *on* the edge */
117 if (space
< max_cols
)
118 cols
= max_cols
/ space
;
119 rows
= (cmds
->cnt
+ cols
- 1) / cols
;
121 for (i
= 0; i
< rows
; i
++) {
124 for (j
= 0; j
< cols
; j
++) {
125 unsigned int n
= j
* rows
+ i
;
126 unsigned int size
= space
;
130 if (j
== cols
-1 || n
+ rows
>= cmds
->cnt
)
132 printf("%-*s", size
, cmds
->names
[n
]->name
);
138 static int is_executable(const char *name
)
142 if (stat(name
, &st
) || /* stat, not lstat */
143 !S_ISREG(st
.st_mode
))
146 return st
.st_mode
& S_IXUSR
;
149 static int has_extension(const char *filename
, const char *ext
)
151 size_t len
= strlen(filename
);
152 size_t extlen
= strlen(ext
);
154 return len
> extlen
&& !memcmp(filename
+ len
- extlen
, ext
, extlen
);
157 static void list_commands_in_dir(struct cmdnames
*cmds
,
162 DIR *dir
= opendir(path
);
170 prefix_len
= strlen(prefix
);
172 astrcatf(&buf
, "%s/", path
);
174 while ((de
= readdir(dir
)) != NULL
) {
177 if (!strstarts(de
->d_name
, prefix
))
180 astrcat(&buf
, de
->d_name
);
181 if (!is_executable(buf
))
184 entlen
= strlen(de
->d_name
) - prefix_len
;
185 if (has_extension(de
->d_name
, ".exe"))
188 add_cmdname(cmds
, de
->d_name
+ prefix_len
, entlen
);
194 void load_command_list(const char *prefix
,
195 struct cmdnames
*main_cmds
,
196 struct cmdnames
*other_cmds
)
198 const char *env_path
= getenv("PATH");
199 char *exec_path
= get_argv_exec_path();
202 list_commands_in_dir(main_cmds
, exec_path
, prefix
);
203 qsort(main_cmds
->names
, main_cmds
->cnt
,
204 sizeof(*main_cmds
->names
), cmdname_compare
);
209 char *paths
, *path
, *colon
;
210 path
= paths
= strdup(env_path
);
212 if ((colon
= strchr(path
, ':')))
214 if (!exec_path
|| strcmp(path
, exec_path
))
215 list_commands_in_dir(other_cmds
, path
, prefix
);
223 qsort(other_cmds
->names
, other_cmds
->cnt
,
224 sizeof(*other_cmds
->names
), cmdname_compare
);
228 exclude_cmds(other_cmds
, main_cmds
);
231 void list_commands(const char *title
, struct cmdnames
*main_cmds
,
232 struct cmdnames
*other_cmds
)
234 unsigned int i
, longest
= 0;
236 for (i
= 0; i
< main_cmds
->cnt
; i
++)
237 if (longest
< main_cmds
->names
[i
]->len
)
238 longest
= main_cmds
->names
[i
]->len
;
239 for (i
= 0; i
< other_cmds
->cnt
; i
++)
240 if (longest
< other_cmds
->names
[i
]->len
)
241 longest
= other_cmds
->names
[i
]->len
;
243 if (main_cmds
->cnt
) {
244 char *exec_path
= get_argv_exec_path();
245 printf("available %s in '%s'\n", title
, exec_path
);
246 printf("----------------");
247 mput_char('-', strlen(title
) + strlen(exec_path
));
249 pretty_print_string_list(main_cmds
, longest
);
254 if (other_cmds
->cnt
) {
255 printf("%s available from elsewhere on your $PATH\n", title
);
256 printf("---------------------------------------");
257 mput_char('-', strlen(title
));
259 pretty_print_string_list(other_cmds
, longest
);
264 int is_in_cmdlist(struct cmdnames
*c
, const char *s
)
268 for (i
= 0; i
< c
->cnt
; i
++)
269 if (!strcmp(s
, c
->names
[i
]->name
))