refspec-api: avoid uninitialized field in refspec item
[git] / refspec.c
1 #include "cache.h"
2 #include "refs.h"
3 #include "refspec.h"
4
5 static struct refspec_item s_tag_refspec = {
6         0,
7         1,
8         0,
9         0,
10         "refs/tags/*",
11         "refs/tags/*"
12 };
13
14 /* See TAG_REFSPEC for the string version */
15 const struct refspec_item *tag_refspec = &s_tag_refspec;
16
17 /*
18  * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
19  * Returns 1 if successful and 0 if the refspec is invalid.
20  */
21 static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch)
22 {
23         size_t llen;
24         int is_glob;
25         const char *lhs, *rhs;
26         int flags;
27
28         is_glob = 0;
29
30         lhs = refspec;
31         if (*lhs == '+') {
32                 item->force = 1;
33                 lhs++;
34         }
35
36         rhs = strrchr(lhs, ':');
37
38         /*
39          * Before going on, special case ":" (or "+:") as a refspec
40          * for pushing matching refs.
41          */
42         if (!fetch && rhs == lhs && rhs[1] == '\0') {
43                 item->matching = 1;
44                 return 1;
45         }
46
47         if (rhs) {
48                 size_t rlen = strlen(++rhs);
49                 is_glob = (1 <= rlen && strchr(rhs, '*'));
50                 item->dst = xstrndup(rhs, rlen);
51         } else {
52                 item->dst = NULL;
53         }
54
55         llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
56         if (1 <= llen && memchr(lhs, '*', llen)) {
57                 if ((rhs && !is_glob) || (!rhs && fetch))
58                         return 0;
59                 is_glob = 1;
60         } else if (rhs && is_glob) {
61                 return 0;
62         }
63
64         item->pattern = is_glob;
65         item->src = xstrndup(lhs, llen);
66         flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
67
68         if (fetch) {
69                 struct object_id unused;
70
71                 /* LHS */
72                 if (!*item->src)
73                         ; /* empty is ok; it means "HEAD" */
74                 else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(item->src, &unused))
75                         item->exact_sha1 = 1; /* ok */
76                 else if (!check_refname_format(item->src, flags))
77                         ; /* valid looking ref is ok */
78                 else
79                         return 0;
80                 /* RHS */
81                 if (!item->dst)
82                         ; /* missing is ok; it is the same as empty */
83                 else if (!*item->dst)
84                         ; /* empty is ok; it means "do not store" */
85                 else if (!check_refname_format(item->dst, flags))
86                         ; /* valid looking ref is ok */
87                 else
88                         return 0;
89         } else {
90                 /*
91                  * LHS
92                  * - empty is allowed; it means delete.
93                  * - when wildcarded, it must be a valid looking ref.
94                  * - otherwise, it must be an extended SHA-1, but
95                  *   there is no existing way to validate this.
96                  */
97                 if (!*item->src)
98                         ; /* empty is ok */
99                 else if (is_glob) {
100                         if (check_refname_format(item->src, flags))
101                                 return 0;
102                 }
103                 else
104                         ; /* anything goes, for now */
105                 /*
106                  * RHS
107                  * - missing is allowed, but LHS then must be a
108                  *   valid looking ref.
109                  * - empty is not allowed.
110                  * - otherwise it must be a valid looking ref.
111                  */
112                 if (!item->dst) {
113                         if (check_refname_format(item->src, flags))
114                                 return 0;
115                 } else if (!*item->dst) {
116                         return 0;
117                 } else {
118                         if (check_refname_format(item->dst, flags))
119                                 return 0;
120                 }
121         }
122
123         return 1;
124 }
125
126 void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
127 {
128         memset(item, 0, sizeof(*item));
129
130         if (!parse_refspec(item, refspec, fetch))
131                 die("Invalid refspec '%s'", refspec);
132 }
133
134 void refspec_item_clear(struct refspec_item *item)
135 {
136         FREE_AND_NULL(item->src);
137         FREE_AND_NULL(item->dst);
138         item->force = 0;
139         item->pattern = 0;
140         item->matching = 0;
141         item->exact_sha1 = 0;
142 }
143
144 void refspec_init(struct refspec *rs, int fetch)
145 {
146         memset(rs, 0, sizeof(*rs));
147         rs->fetch = fetch;
148 }
149
150 void refspec_append(struct refspec *rs, const char *refspec)
151 {
152         struct refspec_item item;
153
154         refspec_item_init(&item, refspec, rs->fetch);
155
156         ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);
157         rs->items[rs->nr++] = item;
158
159         ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc);
160         rs->raw[rs->raw_nr++] = xstrdup(refspec);
161 }
162
163 void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)
164 {
165         int i;
166         for (i = 0; i < nr; i++)
167                 refspec_append(rs, refspecs[i]);
168 }
169
170 void refspec_clear(struct refspec *rs)
171 {
172         int i;
173
174         for (i = 0; i < rs->nr; i++)
175                 refspec_item_clear(&rs->items[i]);
176
177         FREE_AND_NULL(rs->items);
178         rs->alloc = 0;
179         rs->nr = 0;
180
181         for (i = 0; i < rs->raw_nr; i++)
182                 free((char *)rs->raw[i]);
183         FREE_AND_NULL(rs->raw);
184         rs->raw_alloc = 0;
185         rs->raw_nr = 0;
186
187         rs->fetch = 0;
188 }
189
190 int valid_fetch_refspec(const char *fetch_refspec_str)
191 {
192         struct refspec_item refspec;
193         int ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);
194         refspec_item_clear(&refspec);
195         return ret;
196 }