submodule: rewrite `module_list` shell function in C
[git] / builtin / submodule--helper.c
1 #include "builtin.h"
2 #include "cache.h"
3 #include "parse-options.h"
4 #include "quote.h"
5 #include "pathspec.h"
6 #include "dir.h"
7 #include "utf8.h"
8
9 struct module_list {
10         const struct cache_entry **entries;
11         int alloc, nr;
12 };
13 #define MODULE_LIST_INIT { NULL, 0, 0 }
14
15 static int module_list_compute(int argc, const char **argv,
16                                const char *prefix,
17                                struct pathspec *pathspec,
18                                struct module_list *list)
19 {
20         int i, result = 0;
21         char *max_prefix, *ps_matched = NULL;
22         int max_prefix_len;
23         parse_pathspec(pathspec, 0,
24                        PATHSPEC_PREFER_FULL |
25                        PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
26                        prefix, argv);
27
28         /* Find common prefix for all pathspec's */
29         max_prefix = common_prefix(pathspec);
30         max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
31
32         if (pathspec->nr)
33                 ps_matched = xcalloc(pathspec->nr, 1);
34
35         if (read_cache() < 0)
36                 die(_("index file corrupt"));
37
38         for (i = 0; i < active_nr; i++) {
39                 const struct cache_entry *ce = active_cache[i];
40
41                 if (!S_ISGITLINK(ce->ce_mode) ||
42                     !match_pathspec(pathspec, ce->name, ce_namelen(ce),
43                                     max_prefix_len, ps_matched, 1))
44                         continue;
45
46                 ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
47                 list->entries[list->nr++] = ce;
48                 while (i + 1 < active_nr &&
49                        !strcmp(ce->name, active_cache[i + 1]->name))
50                         /*
51                          * Skip entries with the same name in different stages
52                          * to make sure an entry is returned only once.
53                          */
54                         i++;
55         }
56         free(max_prefix);
57
58         if (ps_matched && report_path_error(ps_matched, pathspec, prefix))
59                 result = -1;
60
61         free(ps_matched);
62
63         return result;
64 }
65
66 static int module_list(int argc, const char **argv, const char *prefix)
67 {
68         int i;
69         struct pathspec pathspec;
70         struct module_list list = MODULE_LIST_INIT;
71
72         struct option module_list_options[] = {
73                 OPT_STRING(0, "prefix", &prefix,
74                            N_("path"),
75                            N_("alternative anchor for relative paths")),
76                 OPT_END()
77         };
78
79         const char *const git_submodule_helper_usage[] = {
80                 N_("git submodule--helper list [--prefix=<path>] [<path>...]"),
81                 NULL
82         };
83
84         argc = parse_options(argc, argv, prefix, module_list_options,
85                              git_submodule_helper_usage, 0);
86
87         if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0) {
88                 printf("#unmatched\n");
89                 return 1;
90         }
91
92         for (i = 0; i < list.nr; i++) {
93                 const struct cache_entry *ce = list.entries[i];
94
95                 if (ce_stage(ce))
96                         printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
97                 else
98                         printf("%06o %s %d\t", ce->ce_mode, sha1_to_hex(ce->sha1), ce_stage(ce));
99
100                 utf8_fprintf(stdout, "%s\n", ce->name);
101         }
102         return 0;
103 }
104
105
106 struct cmd_struct {
107         const char *cmd;
108         int (*fn)(int, const char **, const char *);
109 };
110
111 static struct cmd_struct commands[] = {
112         {"list", module_list},
113 };
114
115 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
116 {
117         int i;
118         if (argc < 2)
119                 die(_("fatal: submodule--helper subcommand must be "
120                       "called with a subcommand"));
121
122         for (i = 0; i < ARRAY_SIZE(commands); i++)
123                 if (!strcmp(argv[1], commands[i].cmd))
124                         return commands[i].fn(argc - 1, argv + 1, prefix);
125
126         die(_("fatal: '%s' is not a valid submodule--helper "
127               "subcommand"), argv[1]);
128 }