root/tools/lib/subcmd/help.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. add_cmdname
  2. clean_cmdnames
  3. cmdname_compare
  4. uniq
  5. exclude_cmds
  6. get_term_dimensions
  7. pretty_print_string_list
  8. is_executable
  9. has_extension
  10. list_commands_in_dir
  11. load_command_list
  12. list_commands
  13. is_in_cmdlist

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

/* [<][>][^][v][top][bottom][index][help] */