10 #include "subcmd-util.h"
14 void add_cmdname(struct cmdnames
*cmds
, const char *name
, size_t len
)
16 struct cmdname
*ent
= malloc(sizeof(*ent
) + len
+ 1);
19 memcpy(ent
->name
, name
, len
);
22 ALLOC_GROW(cmds
->names
, cmds
->cnt
+ 1, cmds
->alloc
);
23 cmds
->names
[cmds
->cnt
++] = ent
;
26 void clean_cmdnames(struct cmdnames
*cmds
)
30 for (i
= 0; i
< cmds
->cnt
; ++i
)
31 zfree(&cmds
->names
[i
]);
37 int cmdname_compare(const void *a_
, const void *b_
)
39 struct cmdname
*a
= *(struct cmdname
**)a_
;
40 struct cmdname
*b
= *(struct cmdname
**)b_
;
41 return strcmp(a
->name
, b
->name
);
44 void uniq(struct cmdnames
*cmds
)
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
];
58 void exclude_cmds(struct cmdnames
*cmds
, struct cmdnames
*excludes
)
64 while (ci
< cmds
->cnt
&& ei
< excludes
->cnt
) {
65 cmp
= strcmp(cmds
->names
[ci
]->name
, excludes
->names
[ei
]->name
);
67 cmds
->names
[cj
++] = cmds
->names
[ci
++];
74 while (ci
< cmds
->cnt
)
75 cmds
->names
[cj
++] = cmds
->names
[ci
++];
80 static void get_term_dimensions(struct winsize
*ws
)
82 char *s
= getenv("LINES");
86 s
= getenv("COLUMNS");
89 if (ws
->ws_row
&& ws
->ws_col
)
94 if (ioctl(1, TIOCGWINSZ
, ws
) == 0 &&
95 ws
->ws_row
&& ws
->ws_col
)
102 static void pretty_print_string_list(struct cmdnames
*cmds
, int longest
)
105 int space
= longest
+ 1; /* min 1 SP between words */
110 get_term_dimensions(&win
);
111 max_cols
= win
.ws_col
- 1; /* don't print *on* the edge */
113 if (space
< max_cols
)
114 cols
= max_cols
/ space
;
115 rows
= (cmds
->cnt
+ cols
- 1) / cols
;
117 for (i
= 0; i
< rows
; i
++) {
120 for (j
= 0; j
< cols
; j
++) {
121 unsigned int n
= j
* rows
+ i
;
122 unsigned int size
= space
;
126 if (j
== cols
-1 || n
+ rows
>= cmds
->cnt
)
128 printf("%-*s", size
, cmds
->names
[n
]->name
);
134 static int is_executable(const char *name
)
138 if (stat(name
, &st
) || /* stat, not lstat */
139 !S_ISREG(st
.st_mode
))
142 return st
.st_mode
& S_IXUSR
;
145 static int has_extension(const char *filename
, const char *ext
)
147 size_t len
= strlen(filename
);
148 size_t extlen
= strlen(ext
);
150 return len
> extlen
&& !memcmp(filename
+ len
- extlen
, ext
, extlen
);
153 static void list_commands_in_dir(struct cmdnames
*cmds
,
158 DIR *dir
= opendir(path
);
166 prefix_len
= strlen(prefix
);
168 astrcatf(&buf
, "%s/", path
);
170 while ((de
= readdir(dir
)) != NULL
) {
173 if (prefixcmp(de
->d_name
, prefix
))
176 astrcat(&buf
, de
->d_name
);
177 if (!is_executable(buf
))
180 entlen
= strlen(de
->d_name
) - prefix_len
;
181 if (has_extension(de
->d_name
, ".exe"))
184 add_cmdname(cmds
, de
->d_name
+ prefix_len
, entlen
);
190 void load_command_list(const char *prefix
,
191 struct cmdnames
*main_cmds
,
192 struct cmdnames
*other_cmds
)
194 const char *env_path
= getenv("PATH");
195 char *exec_path
= get_argv_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
);
205 char *paths
, *path
, *colon
;
206 path
= paths
= strdup(env_path
);
208 if ((colon
= strchr(path
, ':')))
210 if (!exec_path
|| strcmp(path
, exec_path
))
211 list_commands_in_dir(other_cmds
, path
, prefix
);
219 qsort(other_cmds
->names
, other_cmds
->cnt
,
220 sizeof(*other_cmds
->names
), cmdname_compare
);
224 exclude_cmds(other_cmds
, main_cmds
);
227 void list_commands(const char *title
, struct cmdnames
*main_cmds
,
228 struct cmdnames
*other_cmds
)
230 unsigned int i
, longest
= 0;
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
;
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
));
245 pretty_print_string_list(main_cmds
, longest
);
250 if (other_cmds
->cnt
) {
251 printf("%s available from elsewhere on your $PATH\n", title
);
252 printf("---------------------------------------");
253 mput_char('-', strlen(title
));
255 pretty_print_string_list(other_cmds
, longest
);
260 int is_in_cmdlist(struct cmdnames
*c
, const char *s
)
264 for (i
= 0; i
< c
->cnt
; i
++)
265 if (!strcmp(s
, c
->names
[i
]->name
))