help.con commit builtin-prune: memory diet. (16157b8)
   1/*
   2 * builtin-help.c
   3 *
   4 * Builtin help-related commands (help, usage, version)
   5 */
   6#include "cache.h"
   7#include "builtin.h"
   8#include "exec_cmd.h"
   9#include "common-cmds.h"
  10#include <sys/ioctl.h>
  11
  12/* most GUI terminals set COLUMNS (although some don't export it) */
  13static int term_columns(void)
  14{
  15        char *col_string = getenv("COLUMNS");
  16        int n_cols;
  17
  18        if (col_string && (n_cols = atoi(col_string)) > 0)
  19                return n_cols;
  20
  21#ifdef TIOCGWINSZ
  22        {
  23                struct winsize ws;
  24                if (!ioctl(1, TIOCGWINSZ, &ws)) {
  25                        if (ws.ws_col)
  26                                return ws.ws_col;
  27                }
  28        }
  29#endif
  30
  31        return 80;
  32}
  33
  34static void oom(void)
  35{
  36        fprintf(stderr, "git: out of memory\n");
  37        exit(1);
  38}
  39
  40static inline void mput_char(char c, unsigned int num)
  41{
  42        while(num--)
  43                putchar(c);
  44}
  45
  46static struct cmdname {
  47        size_t len;
  48        char name[1];
  49} **cmdname;
  50static int cmdname_alloc, cmdname_cnt;
  51
  52static void add_cmdname(const char *name, int len)
  53{
  54        struct cmdname *ent;
  55        if (cmdname_alloc <= cmdname_cnt) {
  56                cmdname_alloc = cmdname_alloc + 200;
  57                cmdname = realloc(cmdname, cmdname_alloc * sizeof(*cmdname));
  58                if (!cmdname)
  59                        oom();
  60        }
  61        ent = malloc(sizeof(*ent) + len);
  62        if (!ent)
  63                oom();
  64        ent->len = len;
  65        memcpy(ent->name, name, len);
  66        ent->name[len] = 0;
  67        cmdname[cmdname_cnt++] = ent;
  68}
  69
  70static int cmdname_compare(const void *a_, const void *b_)
  71{
  72        struct cmdname *a = *(struct cmdname **)a_;
  73        struct cmdname *b = *(struct cmdname **)b_;
  74        return strcmp(a->name, b->name);
  75}
  76
  77static void pretty_print_string_list(struct cmdname **cmdname, int longest)
  78{
  79        int cols = 1, rows;
  80        int space = longest + 1; /* min 1 SP between words */
  81        int max_cols = term_columns() - 1; /* don't print *on* the edge */
  82        int i, j;
  83
  84        if (space < max_cols)
  85                cols = max_cols / space;
  86        rows = (cmdname_cnt + cols - 1) / cols;
  87
  88        qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare);
  89
  90        for (i = 0; i < rows; i++) {
  91                printf("  ");
  92
  93                for (j = 0; j < cols; j++) {
  94                        int n = j * rows + i;
  95                        int size = space;
  96                        if (n >= cmdname_cnt)
  97                                break;
  98                        if (j == cols-1 || n + rows >= cmdname_cnt)
  99                                size = 1;
 100                        printf("%-*s", size, cmdname[n]->name);
 101                }
 102                putchar('\n');
 103        }
 104}
 105
 106static void list_commands(const char *exec_path, const char *pattern)
 107{
 108        unsigned int longest = 0;
 109        char path[PATH_MAX];
 110        int dirlen;
 111        DIR *dir = opendir(exec_path);
 112        struct dirent *de;
 113
 114        if (!dir) {
 115                fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno));
 116                exit(1);
 117        }
 118
 119        dirlen = strlen(exec_path);
 120        if (PATH_MAX - 20 < dirlen) {
 121                fprintf(stderr, "git: insanely long exec-path '%s'\n",
 122                        exec_path);
 123                exit(1);
 124        }
 125
 126        memcpy(path, exec_path, dirlen);
 127        path[dirlen++] = '/';
 128
 129        while ((de = readdir(dir)) != NULL) {
 130                struct stat st;
 131                int entlen;
 132
 133                if (strncmp(de->d_name, "git-", 4))
 134                        continue;
 135                strcpy(path+dirlen, de->d_name);
 136                if (stat(path, &st) || /* stat, not lstat */
 137                    !S_ISREG(st.st_mode) ||
 138                    !(st.st_mode & S_IXUSR))
 139                        continue;
 140
 141                entlen = strlen(de->d_name);
 142                if (has_extension(de->d_name, ".exe"))
 143                        entlen -= 4;
 144
 145                if (longest < entlen)
 146                        longest = entlen;
 147
 148                add_cmdname(de->d_name + 4, entlen-4);
 149        }
 150        closedir(dir);
 151
 152        printf("git commands available in '%s'\n", exec_path);
 153        printf("----------------------------");
 154        mput_char('-', strlen(exec_path));
 155        putchar('\n');
 156        pretty_print_string_list(cmdname, longest - 4);
 157        putchar('\n');
 158}
 159
 160static void list_common_cmds_help(void)
 161{
 162        int i, longest = 0;
 163
 164        for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
 165                if (longest < strlen(common_cmds[i].name))
 166                        longest = strlen(common_cmds[i].name);
 167        }
 168
 169        puts("The most commonly used git commands are:");
 170        for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
 171                printf("    %s", common_cmds[i].name);
 172                mput_char(' ', longest - strlen(common_cmds[i].name) + 4);
 173                puts(common_cmds[i].help);
 174        }
 175        puts("(use 'git help -a' to get a list of all installed git commands)");
 176}
 177
 178static void show_man_page(const char *git_cmd)
 179{
 180        const char *page;
 181
 182        if (!strncmp(git_cmd, "git", 3))
 183                page = git_cmd;
 184        else {
 185                int page_len = strlen(git_cmd) + 4;
 186                char *p = xmalloc(page_len + 1);
 187                strcpy(p, "git-");
 188                strcpy(p + 4, git_cmd);
 189                p[page_len] = 0;
 190                page = p;
 191        }
 192
 193        execlp("man", "man", page, NULL);
 194}
 195
 196void help_unknown_cmd(const char *cmd)
 197{
 198        printf("git: '%s' is not a git-command\n\n", cmd);
 199        list_common_cmds_help();
 200        exit(1);
 201}
 202
 203int cmd_version(int argc, const char **argv, const char *prefix)
 204{
 205        printf("git version %s\n", git_version_string);
 206        return 0;
 207}
 208
 209int cmd_help(int argc, const char **argv, const char *prefix)
 210{
 211        const char *help_cmd = argc > 1 ? argv[1] : NULL;
 212        const char *exec_path = git_exec_path();
 213
 214        if (!help_cmd) {
 215                printf("usage: %s\n\n", git_usage_string);
 216                list_common_cmds_help();
 217                exit(1);
 218        }
 219
 220        else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) {
 221                printf("usage: %s\n\n", git_usage_string);
 222                if(exec_path)
 223                        list_commands(exec_path, "git-*");
 224                exit(1);
 225        }
 226
 227        else
 228                show_man_page(help_cmd);
 229
 230        return 0;
 231}
 232
 233