Merge branch 'gh/gpg-doc-markup-fix'
[git/debian.git] / reftable / refname.c
blob957349693247ae34c0b248a130e1e97f83ba1a71
1 /*
2 Copyright 2020 Google LLC
4 Use of this source code is governed by a BSD-style
5 license that can be found in the LICENSE file or at
6 https://developers.google.com/open-source/licenses/bsd
7 */
9 #include "system.h"
10 #include "reftable-error.h"
11 #include "basics.h"
12 #include "refname.h"
13 #include "reftable-iterator.h"
15 struct find_arg {
16 char **names;
17 const char *want;
20 static int find_name(size_t k, void *arg)
22 struct find_arg *f_arg = arg;
23 return strcmp(f_arg->names[k], f_arg->want) >= 0;
26 static int modification_has_ref(struct modification *mod, const char *name)
28 struct reftable_ref_record ref = { NULL };
29 int err = 0;
31 if (mod->add_len > 0) {
32 struct find_arg arg = {
33 .names = mod->add,
34 .want = name,
36 int idx = binsearch(mod->add_len, find_name, &arg);
37 if (idx < mod->add_len && !strcmp(mod->add[idx], name)) {
38 return 0;
42 if (mod->del_len > 0) {
43 struct find_arg arg = {
44 .names = mod->del,
45 .want = name,
47 int idx = binsearch(mod->del_len, find_name, &arg);
48 if (idx < mod->del_len && !strcmp(mod->del[idx], name)) {
49 return 1;
53 err = reftable_table_read_ref(&mod->tab, name, &ref);
54 reftable_ref_record_release(&ref);
55 return err;
58 static void modification_release(struct modification *mod)
60 /* don't delete the strings themselves; they're owned by ref records.
62 FREE_AND_NULL(mod->add);
63 FREE_AND_NULL(mod->del);
64 mod->add_len = 0;
65 mod->del_len = 0;
68 static int modification_has_ref_with_prefix(struct modification *mod,
69 const char *prefix)
71 struct reftable_iterator it = { NULL };
72 struct reftable_ref_record ref = { NULL };
73 int err = 0;
75 if (mod->add_len > 0) {
76 struct find_arg arg = {
77 .names = mod->add,
78 .want = prefix,
80 int idx = binsearch(mod->add_len, find_name, &arg);
81 if (idx < mod->add_len &&
82 !strncmp(prefix, mod->add[idx], strlen(prefix)))
83 goto done;
85 err = reftable_table_seek_ref(&mod->tab, &it, prefix);
86 if (err)
87 goto done;
89 while (1) {
90 err = reftable_iterator_next_ref(&it, &ref);
91 if (err)
92 goto done;
94 if (mod->del_len > 0) {
95 struct find_arg arg = {
96 .names = mod->del,
97 .want = ref.refname,
99 int idx = binsearch(mod->del_len, find_name, &arg);
100 if (idx < mod->del_len &&
101 !strcmp(ref.refname, mod->del[idx])) {
102 continue;
106 if (strncmp(ref.refname, prefix, strlen(prefix))) {
107 err = 1;
108 goto done;
110 err = 0;
111 goto done;
114 done:
115 reftable_ref_record_release(&ref);
116 reftable_iterator_destroy(&it);
117 return err;
120 static int validate_refname(const char *name)
122 while (1) {
123 char *next = strchr(name, '/');
124 if (!*name) {
125 return REFTABLE_REFNAME_ERROR;
127 if (!next) {
128 return 0;
130 if (next - name == 0 || (next - name == 1 && *name == '.') ||
131 (next - name == 2 && name[0] == '.' && name[1] == '.'))
132 return REFTABLE_REFNAME_ERROR;
133 name = next + 1;
135 return 0;
138 int validate_ref_record_addition(struct reftable_table tab,
139 struct reftable_ref_record *recs, size_t sz)
141 struct modification mod = {
142 .tab = tab,
143 .add = reftable_calloc(sizeof(char *) * sz),
144 .del = reftable_calloc(sizeof(char *) * sz),
146 int i = 0;
147 int err = 0;
148 for (; i < sz; i++) {
149 if (reftable_ref_record_is_deletion(&recs[i])) {
150 mod.del[mod.del_len++] = recs[i].refname;
151 } else {
152 mod.add[mod.add_len++] = recs[i].refname;
156 err = modification_validate(&mod);
157 modification_release(&mod);
158 return err;
161 static void strbuf_trim_component(struct strbuf *sl)
163 while (sl->len > 0) {
164 int is_slash = (sl->buf[sl->len - 1] == '/');
165 strbuf_setlen(sl, sl->len - 1);
166 if (is_slash)
167 break;
171 int modification_validate(struct modification *mod)
173 struct strbuf slashed = STRBUF_INIT;
174 int err = 0;
175 int i = 0;
176 for (; i < mod->add_len; i++) {
177 err = validate_refname(mod->add[i]);
178 if (err)
179 goto done;
180 strbuf_reset(&slashed);
181 strbuf_addstr(&slashed, mod->add[i]);
182 strbuf_addstr(&slashed, "/");
184 err = modification_has_ref_with_prefix(mod, slashed.buf);
185 if (err == 0) {
186 err = REFTABLE_NAME_CONFLICT;
187 goto done;
189 if (err < 0)
190 goto done;
192 strbuf_reset(&slashed);
193 strbuf_addstr(&slashed, mod->add[i]);
194 while (slashed.len) {
195 strbuf_trim_component(&slashed);
196 err = modification_has_ref(mod, slashed.buf);
197 if (err == 0) {
198 err = REFTABLE_NAME_CONFLICT;
199 goto done;
201 if (err < 0)
202 goto done;
205 err = 0;
206 done:
207 strbuf_release(&slashed);
208 return err;