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
++];
76 while (ci
< cmds
->cnt
)
77 cmds
->names
[cj
++] = cmds
->names
[ci
++];
82 static void get_term_dimensions(struct winsize
*ws
)
84 char *s
= getenv("LINES");
88 s
= getenv("COLUMNS");
91 if (ws
->ws_row
&& ws
->ws_col
)
96 if (ioctl(1, TIOCGWINSZ
, ws
) == 0 &&
97 ws
->ws_row
&& ws
->ws_col
)
104 static void pretty_print_string_list(struct cmdnames
*cmds
, int longest
)
107 int space
= longest
+ 1; /* min 1 SP between words */
112 get_term_dimensions(&win
);
113 max_cols
= win
.ws_col
- 1; /* don't print *on* the edge */
115 if (space
< max_cols
)
116 cols
= max_cols
/ space
;
117 rows
= (cmds
->cnt
+ cols
- 1) / cols
;
119 for (i
= 0; i
< rows
; i
++) {
122 for (j
= 0; j
< cols
; j
++) {
123 unsigned int n
= j
* rows
+ i
;
124 unsigned int size
= space
;
128 if (j
== cols
-1 || n
+ rows
>= cmds
->cnt
)
130 printf("%-*s", size
, cmds
->names
[n
]->name
);
136 static int is_executable(const char *name
)
140 if (stat(name
, &st
) || /* stat, not lstat */
141 !S_ISREG(st
.st_mode
))
144 return st
.st_mode
& S_IXUSR
;
147 static int has_extension(const char *filename
, const char *ext
)
149 size_t len
= strlen(filename
);
150 size_t extlen
= strlen(ext
);
152 return len
> extlen
&& !memcmp(filename
+ len
- extlen
, ext
, extlen
);
155 static void list_commands_in_dir(struct cmdnames
*cmds
,
160 DIR *dir
= opendir(path
);
168 prefix_len
= strlen(prefix
);
170 astrcatf(&buf
, "%s/", path
);
172 while ((de
= readdir(dir
)) != NULL
) {
175 if (!strstarts(de
->d_name
, prefix
))
178 astrcat(&buf
, de
->d_name
);
179 if (!is_executable(buf
))
182 entlen
= strlen(de
->d_name
) - prefix_len
;
183 if (has_extension(de
->d_name
, ".exe"))
186 add_cmdname(cmds
, de
->d_name
+ prefix_len
, entlen
);
192 void load_command_list(const char *prefix
,
193 struct cmdnames
*main_cmds
,
194 struct cmdnames
*other_cmds
)
196 const char *env_path
= getenv("PATH");
197 char *exec_path
= get_argv_exec_path();
200 list_commands_in_dir(main_cmds
, exec_path
, prefix
);
201 qsort(main_cmds
->names
, main_cmds
->cnt
,
202 sizeof(*main_cmds
->names
), cmdname_compare
);
207 char *paths
, *path
, *colon
;
208 path
= paths
= strdup(env_path
);
210 if ((colon
= strchr(path
, ':')))
212 if (!exec_path
|| strcmp(path
, exec_path
))
213 list_commands_in_dir(other_cmds
, path
, prefix
);
221 qsort(other_cmds
->names
, other_cmds
->cnt
,
222 sizeof(*other_cmds
->names
), cmdname_compare
);
226 exclude_cmds(other_cmds
, main_cmds
);
229 void list_commands(const char *title
, struct cmdnames
*main_cmds
,
230 struct cmdnames
*other_cmds
)
232 unsigned int i
, longest
= 0;
234 for (i
= 0; i
< main_cmds
->cnt
; i
++)
235 if (longest
< main_cmds
->names
[i
]->len
)
236 longest
= main_cmds
->names
[i
]->len
;
237 for (i
= 0; i
< other_cmds
->cnt
; i
++)
238 if (longest
< other_cmds
->names
[i
]->len
)
239 longest
= other_cmds
->names
[i
]->len
;
241 if (main_cmds
->cnt
) {
242 char *exec_path
= get_argv_exec_path();
243 printf("available %s in '%s'\n", title
, exec_path
);
244 printf("----------------");
245 mput_char('-', strlen(title
) + strlen(exec_path
));
247 pretty_print_string_list(main_cmds
, longest
);
252 if (other_cmds
->cnt
) {
253 printf("%s available from elsewhere on your $PATH\n", title
);
254 printf("---------------------------------------");
255 mput_char('-', strlen(title
));
257 pretty_print_string_list(other_cmds
, longest
);
262 int is_in_cmdlist(struct cmdnames
*c
, const char *s
)
266 for (i
= 0; i
< c
->cnt
; i
++)
267 if (!strcmp(s
, c
->names
[i
]->name
))