@@ -72,30 +72,57 @@ static unsigned char refname_disposition[256] = {
72
72
* - it ends with ".lock", or
73
73
* - it contains a "@{" portion
74
74
*/
75
- static int check_refname_component (const char * refname , int * flags )
75
+ static int check_refname_component (const char * refname , int * flags ,
76
+ struct strbuf * sanitized )
76
77
{
77
78
const char * cp ;
78
79
char last = '\0' ;
80
+ size_t component_start ;
81
+
82
+ if (sanitized )
83
+ component_start = sanitized -> len ;
79
84
80
85
for (cp = refname ; ; cp ++ ) {
81
86
int ch = * cp & 255 ;
82
87
unsigned char disp = refname_disposition [ch ];
88
+
89
+ if (sanitized && disp != 1 )
90
+ strbuf_addch (sanitized , ch );
91
+
83
92
switch (disp ) {
84
93
case 1 :
85
94
goto out ;
86
95
case 2 :
87
- if (last == '.' )
88
- return -1 ; /* Refname contains "..". */
96
+ if (last == '.' ) { /* Refname contains "..". */
97
+ if (sanitized )
98
+ sanitized -> len -- ; /* collapse ".." to single "." */
99
+ else
100
+ return -1 ;
101
+ }
89
102
break ;
90
103
case 3 :
91
- if (last == '@' )
92
- return -1 ; /* Refname contains "@{". */
104
+ if (last == '@' ) { /* Refname contains "@{". */
105
+ if (sanitized )
106
+ sanitized -> buf [sanitized -> len - 1 ] = '-' ;
107
+ else
108
+ return -1 ;
109
+ }
93
110
break ;
94
111
case 4 :
95
- return -1 ;
112
+ /* forbidden char */
113
+ if (sanitized )
114
+ sanitized -> buf [sanitized -> len - 1 ] = '-' ;
115
+ else
116
+ return -1 ;
117
+ break ;
96
118
case 5 :
97
- if (!(* flags & REFNAME_REFSPEC_PATTERN ))
98
- return -1 ; /* refspec can't be a pattern */
119
+ if (!(* flags & REFNAME_REFSPEC_PATTERN )) {
120
+ /* refspec can't be a pattern */
121
+ if (sanitized )
122
+ sanitized -> buf [sanitized -> len - 1 ] = '-' ;
123
+ else
124
+ return -1 ;
125
+ }
99
126
100
127
/*
101
128
* Unset the pattern flag so that we only accept
@@ -109,26 +136,48 @@ static int check_refname_component(const char *refname, int *flags)
109
136
out :
110
137
if (cp == refname )
111
138
return 0 ; /* Component has zero length. */
112
- if (refname [0 ] == '.' )
113
- return -1 ; /* Component starts with '.'. */
139
+
140
+ if (refname [0 ] == '.' ) { /* Component starts with '.'. */
141
+ if (sanitized )
142
+ sanitized -> buf [component_start ] = '-' ;
143
+ else
144
+ return -1 ;
145
+ }
114
146
if (cp - refname >= LOCK_SUFFIX_LEN &&
115
- !memcmp (cp - LOCK_SUFFIX_LEN , LOCK_SUFFIX , LOCK_SUFFIX_LEN ))
116
- return -1 ; /* Refname ends with ".lock". */
147
+ !memcmp (cp - LOCK_SUFFIX_LEN , LOCK_SUFFIX , LOCK_SUFFIX_LEN )) {
148
+ if (!sanitized )
149
+ return -1 ;
150
+ /* Refname ends with ".lock". */
151
+ while (strbuf_strip_suffix (sanitized , LOCK_SUFFIX )) {
152
+ /* try again in case we have .lock.lock */
153
+ }
154
+ }
117
155
return cp - refname ;
118
156
}
119
157
120
- int check_refname_format (const char * refname , int flags )
158
+ static int check_or_sanitize_refname (const char * refname , int flags ,
159
+ struct strbuf * sanitized )
121
160
{
122
161
int component_len , component_count = 0 ;
123
162
124
- if (!strcmp (refname , "@" ))
163
+ if (!strcmp (refname , "@" )) {
125
164
/* Refname is a single character '@'. */
126
- return -1 ;
165
+ if (sanitized )
166
+ strbuf_addch (sanitized , '-' );
167
+ else
168
+ return -1 ;
169
+ }
127
170
128
171
while (1 ) {
172
+ if (sanitized && sanitized -> len )
173
+ strbuf_complete (sanitized , '/' );
174
+
129
175
/* We are at the start of a path component. */
130
- component_len = check_refname_component (refname , & flags );
131
- if (component_len <= 0 )
176
+ component_len = check_refname_component (refname , & flags ,
177
+ sanitized );
178
+ if (sanitized && component_len == 0 )
179
+ ; /* OK, omit empty component */
180
+ else if (component_len <= 0 )
132
181
return -1 ;
133
182
134
183
component_count ++ ;
@@ -138,13 +187,29 @@ int check_refname_format(const char *refname, int flags)
138
187
refname += component_len + 1 ;
139
188
}
140
189
141
- if (refname [component_len - 1 ] == '.' )
142
- return -1 ; /* Refname ends with '.'. */
190
+ if (refname [component_len - 1 ] == '.' ) {
191
+ /* Refname ends with '.'. */
192
+ if (sanitized )
193
+ ; /* omit ending dot */
194
+ else
195
+ return -1 ;
196
+ }
143
197
if (!(flags & REFNAME_ALLOW_ONELEVEL ) && component_count < 2 )
144
198
return -1 ; /* Refname has only one component. */
145
199
return 0 ;
146
200
}
147
201
202
+ int check_refname_format (const char * refname , int flags )
203
+ {
204
+ return check_or_sanitize_refname (refname , flags , NULL );
205
+ }
206
+
207
+ void sanitize_refname_component (const char * refname , struct strbuf * out )
208
+ {
209
+ if (check_or_sanitize_refname (refname , REFNAME_ALLOW_ONELEVEL , out ))
210
+ BUG ("sanitizing refname '%s' check returned error" , refname );
211
+ }
212
+
148
213
int refname_is_safe (const char * refname )
149
214
{
150
215
const char * rest ;
0 commit comments