Coverage Report

Created: 2026-10-17 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/alias.c
Line
Count
Source
1
#define USE_THE_REPOSITORY_VARIABLE
2
3
#include "git-compat-util.h"
4
#include "alias.h"
5
#include "config.h"
6
#include "gettext.h"
7
#include "strbuf.h"
8
#include "string-list.h"
9
10
struct config_alias_data {
11
  const char *alias;
12
  char *v;
13
  struct string_list *list;
14
};
15
16
static int config_alias_cb(const char *var, const char *value,
17
         const struct config_context *ctx UNUSED, void *d)
18
0
{
19
0
  struct config_alias_data *data = d;
20
0
  const char *subsection, *key;
21
0
  size_t subsection_len;
22
23
0
  if (parse_config_key(var, "alias", &subsection, &subsection_len,
24
0
           &key) < 0)
25
0
    return 0;
26
27
  /*
28
   * Two config syntaxes:
29
   * - alias.name = value   (without subsection, case-insensitive)
30
   * - [alias "name"]
31
   *       command = value  (with subsection, case-sensitive)
32
   */
33
  /* Treat [alias ""] (empty subsection) the same as plain [alias]. */
34
0
  if (subsection && !subsection_len)
35
0
    subsection = NULL;
36
37
0
  if (subsection && strcmp(key, "command")) {
38
    /*
39
     * We have historically supported the "alias.name" form when
40
     * "name" happens to contain dots (e.g., alias.foo.bar to allow
41
     * "git foo.bar". But our parsing above would split that into
42
     * subsection "foo".
43
     *
44
     * If we do not understand the final key in a subsection-style
45
     * variable, fall back to treating it as a two-level alias.
46
     */
47
0
    key = var + strlen("alias.");
48
0
    subsection = NULL;
49
0
    subsection_len = 0;
50
0
  }
51
52
0
  if (data->alias) {
53
0
    int match;
54
55
0
    if (subsection)
56
0
      match = (strlen(data->alias) == subsection_len &&
57
0
         !strncmp(data->alias, subsection,
58
0
            subsection_len));
59
0
    else
60
0
      match = !strcasecmp(data->alias, key);
61
62
0
    if (match) {
63
0
      FREE_AND_NULL(data->v);
64
0
      return git_config_string(&data->v,
65
0
             var, value);
66
0
    }
67
0
  } else if (data->list) {
68
0
    struct string_list_item *item;
69
70
0
    if (!value)
71
0
      return config_error_nonbool(var);
72
73
0
    if (subsection)
74
0
      item = string_list_append_nodup(data->list,
75
0
        xmemdupz(subsection, subsection_len));
76
0
    else
77
0
      item = string_list_append(data->list, key);
78
0
    item->util = xstrdup(value);
79
0
  }
80
81
0
  return 0;
82
0
}
83
84
char *alias_lookup(const char *alias)
85
0
{
86
0
  struct config_alias_data data = { alias, NULL };
87
88
0
  read_early_config(the_repository, config_alias_cb, &data);
89
90
0
  return data.v;
91
0
}
92
93
void list_aliases(struct string_list *list)
94
0
{
95
0
  struct config_alias_data data = { NULL, NULL, list };
96
97
0
  read_early_config(the_repository, config_alias_cb, &data);
98
0
}
99
100
void quote_cmdline(struct strbuf *buf, const char **argv)
101
0
{
102
0
  for (const char **argp = argv; *argp; argp++) {
103
0
    if (argp != argv)
104
0
      strbuf_addch(buf, ' ');
105
0
    strbuf_addch(buf, '"');
106
0
    for (const char *p = *argp; *p; p++) {
107
0
      const char c = *p;
108
109
0
      if (c == '"' || c =='\\')
110
0
        strbuf_addch(buf, '\\');
111
0
      strbuf_addch(buf, c);
112
0
    }
113
0
    strbuf_addch(buf, '"');
114
0
  }
115
0
}
116
117
0
#define SPLIT_CMDLINE_BAD_ENDING 1
118
0
#define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
119
0
#define SPLIT_CMDLINE_ARGC_OVERFLOW 3
120
static const char *split_cmdline_errors[] = {
121
  N_("cmdline ends with \\"),
122
  N_("unclosed quote"),
123
  N_("too many arguments"),
124
};
125
126
int split_cmdline(char *cmdline, const char ***argv)
127
0
{
128
0
  size_t src, dst, count = 0, size = 16;
129
0
  char quoted = 0;
130
131
0
  ALLOC_ARRAY(*argv, size);
132
133
  /* split alias_string */
134
0
  (*argv)[count++] = cmdline;
135
0
  for (src = dst = 0; cmdline[src];) {
136
0
    char c = cmdline[src];
137
0
    if (!quoted && isspace(c)) {
138
0
      cmdline[dst++] = 0;
139
0
      while (cmdline[++src]
140
0
          && isspace(cmdline[src]))
141
0
        ; /* skip */
142
0
      ALLOC_GROW(*argv, count + 1, size);
143
0
      (*argv)[count++] = cmdline + dst;
144
0
    } else if (!quoted && (c == '\'' || c == '"')) {
145
0
      quoted = c;
146
0
      src++;
147
0
    } else if (c == quoted) {
148
0
      quoted = 0;
149
0
      src++;
150
0
    } else {
151
0
      if (c == '\\' && quoted != '\'') {
152
0
        src++;
153
0
        c = cmdline[src];
154
0
        if (!c) {
155
0
          FREE_AND_NULL(*argv);
156
0
          return -SPLIT_CMDLINE_BAD_ENDING;
157
0
        }
158
0
      }
159
0
      cmdline[dst++] = c;
160
0
      src++;
161
0
    }
162
0
  }
163
164
0
  cmdline[dst] = 0;
165
166
0
  if (quoted) {
167
0
    FREE_AND_NULL(*argv);
168
0
    return -SPLIT_CMDLINE_UNCLOSED_QUOTE;
169
0
  }
170
171
0
  if (count >= INT_MAX) {
172
0
    FREE_AND_NULL(*argv);
173
0
    return -SPLIT_CMDLINE_ARGC_OVERFLOW;
174
0
  }
175
176
0
  ALLOC_GROW(*argv, count + 1, size);
177
0
  (*argv)[count] = NULL;
178
179
0
  return count;
180
0
}
181
182
const char *split_cmdline_strerror(int split_cmdline_errno)
183
0
{
184
0
  return split_cmdline_errors[-split_cmdline_errno - 1];
185
0
}