]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_subr/properties.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_subr / properties.c
1 /*
2  * properties.c:  stuff related to Subversion properties
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #include <apr_pools.h>
27 #include <apr_hash.h>
28 #include <apr_tables.h>
29 #include <string.h>       /* for strncmp() */
30 #include "svn_hash.h"
31 #include "svn_string.h"
32 #include "svn_props.h"
33 #include "svn_error.h"
34 #include "svn_ctype.h"
35 #include "private/svn_subr_private.h"
36
37
38 /* All Subversion-specific versioned node properties
39  * known to this client, that are applicable to both a file and a dir.
40  */
41 #define SVN_PROP__NODE_COMMON_PROPS SVN_PROP_MERGEINFO, \
42                                     SVN_PROP_TEXT_TIME, \
43                                     SVN_PROP_OWNER, \
44                                     SVN_PROP_GROUP, \
45                                     SVN_PROP_UNIX_MODE,
46
47 /* All Subversion-specific versioned node properties
48  * known to this client, that are applicable to a dir only.
49  */
50 #define SVN_PROP__NODE_DIR_ONLY_PROPS SVN_PROP_IGNORE, \
51                                       SVN_PROP_INHERITABLE_IGNORES, \
52                                       SVN_PROP_INHERITABLE_AUTO_PROPS, \
53                                       SVN_PROP_EXTERNALS,
54
55 /* All Subversion-specific versioned node properties
56  * known to this client, that are applicable to a file only.
57  */
58 #define SVN_PROP__NODE_FILE_ONLY_PROPS SVN_PROP_MIME_TYPE, \
59                                        SVN_PROP_EOL_STYLE, \
60                                        SVN_PROP_KEYWORDS, \
61                                        SVN_PROP_EXECUTABLE, \
62                                        SVN_PROP_NEEDS_LOCK, \
63                                        SVN_PROP_SPECIAL,
64
65 static const char *const known_rev_props[]
66  = { SVN_PROP_REVISION_ALL_PROPS
67      NULL };
68
69 static const char *const known_node_props[]
70  = { SVN_PROP__NODE_COMMON_PROPS
71      SVN_PROP__NODE_DIR_ONLY_PROPS
72      SVN_PROP__NODE_FILE_ONLY_PROPS
73      NULL };
74
75 static const char *const known_dir_props[]
76  = { SVN_PROP__NODE_COMMON_PROPS
77      SVN_PROP__NODE_DIR_ONLY_PROPS
78      NULL };
79
80 static const char *const known_file_props[]
81  = { SVN_PROP__NODE_COMMON_PROPS
82      SVN_PROP__NODE_FILE_ONLY_PROPS
83      NULL };
84
85 static svn_boolean_t
86 is_known_prop(const char *prop_name,
87               const char *const *known_props)
88 {
89   while (*known_props)
90     {
91       if (strcmp(prop_name, *known_props++) == 0)
92         return TRUE;
93     }
94   return FALSE;
95 }
96
97 svn_boolean_t
98 svn_prop_is_known_svn_rev_prop(const char *prop_name)
99 {
100   return is_known_prop(prop_name, known_rev_props);
101 }
102
103 svn_boolean_t
104 svn_prop_is_known_svn_node_prop(const char *prop_name)
105 {
106   return is_known_prop(prop_name, known_node_props);
107 }
108
109 svn_boolean_t
110 svn_prop_is_known_svn_file_prop(const char *prop_name)
111 {
112   return is_known_prop(prop_name, known_file_props);
113 }
114
115 svn_boolean_t
116 svn_prop_is_known_svn_dir_prop(const char *prop_name)
117 {
118   return is_known_prop(prop_name, known_dir_props);
119 }
120
121
122 svn_boolean_t
123 svn_prop_is_svn_prop(const char *prop_name)
124 {
125   return strncmp(prop_name, SVN_PROP_PREFIX, (sizeof(SVN_PROP_PREFIX) - 1))
126          == 0;
127 }
128
129
130 svn_boolean_t
131 svn_prop_has_svn_prop(const apr_hash_t *props, apr_pool_t *pool)
132 {
133   apr_hash_index_t *hi;
134   const void *prop_name;
135
136   if (! props)
137     return FALSE;
138
139   for (hi = apr_hash_first(pool, (apr_hash_t *)props); hi;
140        hi = apr_hash_next(hi))
141     {
142       apr_hash_this(hi, &prop_name, NULL, NULL);
143       if (svn_prop_is_svn_prop((const char *) prop_name))
144         return TRUE;
145     }
146
147   return FALSE;
148 }
149
150
151 #define SIZEOF_WC_PREFIX (sizeof(SVN_PROP_WC_PREFIX) - 1)
152 #define SIZEOF_ENTRY_PREFIX (sizeof(SVN_PROP_ENTRY_PREFIX) - 1)
153
154 svn_prop_kind_t
155 svn_property_kind2(const char *prop_name)
156 {
157
158   if (strncmp(prop_name, SVN_PROP_WC_PREFIX, SIZEOF_WC_PREFIX) == 0)
159     return svn_prop_wc_kind;
160
161   if (strncmp(prop_name, SVN_PROP_ENTRY_PREFIX, SIZEOF_ENTRY_PREFIX) == 0)
162     return svn_prop_entry_kind;
163
164   return svn_prop_regular_kind;
165 }
166
167
168 /* NOTE: this function is deprecated, but we cannot move it to deprecated.c
169    because we need the SIZEOF_*_PREFIX constant symbols defined above.  */
170 svn_prop_kind_t
171 svn_property_kind(int *prefix_len,
172                   const char *prop_name)
173 {
174   svn_prop_kind_t kind = svn_property_kind2(prop_name);
175
176   if (prefix_len)
177     {
178       if (kind == svn_prop_wc_kind)
179         *prefix_len = SIZEOF_WC_PREFIX;
180       else if (kind == svn_prop_entry_kind)
181         *prefix_len = SIZEOF_ENTRY_PREFIX;
182       else
183         *prefix_len = 0;
184     }
185
186   return kind;
187 }
188
189
190 svn_error_t *
191 svn_categorize_props(const apr_array_header_t *proplist,
192                      apr_array_header_t **entry_props,
193                      apr_array_header_t **wc_props,
194                      apr_array_header_t **regular_props,
195                      apr_pool_t *pool)
196 {
197   int i;
198   if (entry_props)
199     *entry_props = apr_array_make(pool, 1, sizeof(svn_prop_t));
200   if (wc_props)
201     *wc_props = apr_array_make(pool, 1, sizeof(svn_prop_t));
202   if (regular_props)
203     *regular_props = apr_array_make(pool, 1, sizeof(svn_prop_t));
204
205   for (i = 0; i < proplist->nelts; i++)
206     {
207       svn_prop_t *prop, *newprop;
208       enum svn_prop_kind kind;
209
210       prop = &APR_ARRAY_IDX(proplist, i, svn_prop_t);
211       kind = svn_property_kind2(prop->name);
212       newprop = NULL;
213
214       if (kind == svn_prop_regular_kind)
215         {
216           if (regular_props)
217             newprop = apr_array_push(*regular_props);
218         }
219       else if (kind == svn_prop_wc_kind)
220         {
221           if (wc_props)
222             newprop = apr_array_push(*wc_props);
223         }
224       else if (kind == svn_prop_entry_kind)
225         {
226           if (entry_props)
227             newprop = apr_array_push(*entry_props);
228         }
229       else
230         /* Technically this can't happen, but might as well have the
231            code ready in case that ever changes. */
232         return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
233                                  "Bad property kind for property '%s'",
234                                  prop->name);
235
236       if (newprop)
237         {
238           newprop->name = prop->name;
239           newprop->value = prop->value;
240         }
241     }
242
243   return SVN_NO_ERROR;
244 }
245
246
247 svn_error_t *
248 svn_prop_diffs(apr_array_header_t **propdiffs,
249                const apr_hash_t *target_props,
250                const apr_hash_t *source_props,
251                apr_pool_t *pool)
252 {
253   apr_hash_index_t *hi;
254   apr_array_header_t *ary = apr_array_make(pool, 1, sizeof(svn_prop_t));
255
256   /* Note: we will be storing the pointers to the keys (from the hashes)
257      into the propdiffs array.  It is acceptable for us to
258      reference the same memory as the base/target_props hash. */
259
260   /* Loop over SOURCE_PROPS and examine each key.  This will allow us to
261      detect any `deletion' events or `set-modification' events.  */
262   for (hi = apr_hash_first(pool, (apr_hash_t *)source_props); hi;
263        hi = apr_hash_next(hi))
264     {
265       const void *key;
266       apr_ssize_t klen;
267       void *val;
268       const svn_string_t *propval1, *propval2;
269
270       /* Get next property */
271       apr_hash_this(hi, &key, &klen, &val);
272       propval1 = val;
273
274       /* Does property name exist in TARGET_PROPS? */
275       propval2 = apr_hash_get((apr_hash_t *)target_props, key, klen);
276
277       if (propval2 == NULL)
278         {
279           /* Add a delete event to the array */
280           svn_prop_t *p = apr_array_push(ary);
281           p->name = key;
282           p->value = NULL;
283         }
284       else if (! svn_string_compare(propval1, propval2))
285         {
286           /* Add a set (modification) event to the array */
287           svn_prop_t *p = apr_array_push(ary);
288           p->name = key;
289           p->value = svn_string_dup(propval2, pool);
290         }
291     }
292
293   /* Loop over TARGET_PROPS and examine each key.  This allows us to
294      detect `set-creation' events */
295   for (hi = apr_hash_first(pool, (apr_hash_t *)target_props); hi;
296        hi = apr_hash_next(hi))
297     {
298       const void *key;
299       apr_ssize_t klen;
300       void *val;
301       const svn_string_t *propval;
302
303       /* Get next property */
304       apr_hash_this(hi, &key, &klen, &val);
305       propval = val;
306
307       /* Does property name exist in SOURCE_PROPS? */
308       if (NULL == apr_hash_get((apr_hash_t *)source_props, key, klen))
309         {
310           /* Add a set (creation) event to the array */
311           svn_prop_t *p = apr_array_push(ary);
312           p->name = key;
313           p->value = svn_string_dup(propval, pool);
314         }
315     }
316
317   /* Done building our array of user events. */
318   *propdiffs = ary;
319
320   return SVN_NO_ERROR;
321 }
322
323 apr_hash_t *
324 svn_prop__patch(const apr_hash_t *original_props,
325                 const apr_array_header_t *prop_changes,
326                 apr_pool_t *pool)
327 {
328   apr_hash_t *props = apr_hash_copy(pool, original_props);
329   int i;
330
331   for (i = 0; i < prop_changes->nelts; i++)
332     {
333       const svn_prop_t *p = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t);
334
335       svn_hash_sets(props, p->name, p->value);
336     }
337   return props;
338 }
339
340 /**
341  * Reallocate the members of PROP using POOL.
342  */
343 static void
344 svn_prop__members_dup(svn_prop_t *prop, apr_pool_t *pool)
345 {
346   if (prop->name)
347     prop->name = apr_pstrdup(pool, prop->name);
348   if (prop->value)
349     prop->value = svn_string_dup(prop->value, pool);
350 }
351
352 svn_prop_t *
353 svn_prop_dup(const svn_prop_t *prop, apr_pool_t *pool)
354 {
355   svn_prop_t *new_prop = apr_palloc(pool, sizeof(*new_prop));
356
357   *new_prop = *prop;
358
359   svn_prop__members_dup(new_prop, pool);
360
361   return new_prop;
362 }
363
364 apr_array_header_t *
365 svn_prop_array_dup(const apr_array_header_t *array, apr_pool_t *pool)
366 {
367   int i;
368   apr_array_header_t *new_array = apr_array_copy(pool, array);
369   for (i = 0; i < new_array->nelts; ++i)
370     {
371       svn_prop_t *elt = &APR_ARRAY_IDX(new_array, i, svn_prop_t);
372       svn_prop__members_dup(elt, pool);
373     }
374   return new_array;
375 }
376
377 apr_array_header_t *
378 svn_prop_hash_to_array(const apr_hash_t *hash,
379                        apr_pool_t *pool)
380 {
381   apr_hash_index_t *hi;
382   apr_array_header_t *array = apr_array_make(pool,
383                                              apr_hash_count((apr_hash_t *)hash),
384                                              sizeof(svn_prop_t));
385
386   for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi;
387        hi = apr_hash_next(hi))
388     {
389       const void *key;
390       void *val;
391       svn_prop_t prop;
392
393       apr_hash_this(hi, &key, NULL, &val);
394       prop.name = key;
395       prop.value = val;
396       APR_ARRAY_PUSH(array, svn_prop_t) = prop;
397     }
398
399   return array;
400 }
401
402 apr_hash_t *
403 svn_prop_hash_dup(const apr_hash_t *hash,
404                   apr_pool_t *pool)
405 {
406   apr_hash_index_t *hi;
407   apr_hash_t *new_hash = apr_hash_make(pool);
408
409   for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi;
410        hi = apr_hash_next(hi))
411     {
412       const void *key;
413       apr_ssize_t klen;
414       void *prop;
415
416       apr_hash_this(hi, &key, &klen, &prop);
417       apr_hash_set(new_hash, apr_pstrmemdup(pool, key, klen), klen,
418                    svn_string_dup(prop, pool));
419     }
420   return new_hash;
421 }
422
423 apr_hash_t *
424 svn_prop_array_to_hash(const apr_array_header_t *properties,
425                        apr_pool_t *pool)
426 {
427   int i;
428   apr_hash_t *prop_hash = apr_hash_make(pool);
429
430   for (i = 0; i < properties->nelts; i++)
431     {
432       const svn_prop_t *prop = &APR_ARRAY_IDX(properties, i, svn_prop_t);
433       svn_hash_sets(prop_hash, prop->name, prop->value);
434     }
435
436   return prop_hash;
437 }
438
439 svn_boolean_t
440 svn_prop_is_boolean(const char *prop_name)
441 {
442   /* If we end up with more than 3 of these, we should probably put
443      them in a table and use bsearch.  With only three, it doesn't
444      make any speed difference.  */
445   if (strcmp(prop_name, SVN_PROP_EXECUTABLE) == 0
446       || strcmp(prop_name, SVN_PROP_NEEDS_LOCK) == 0
447       || strcmp(prop_name, SVN_PROP_SPECIAL) == 0)
448     return TRUE;
449   return FALSE;
450 }
451
452
453 svn_boolean_t
454 svn_prop_needs_translation(const char *propname)
455 {
456   /* ### Someday, we may want to be picky and choosy about which
457      properties require UTF8 and EOL conversion.  For now, all "svn:"
458      props need it.  */
459
460   return svn_prop_is_svn_prop(propname);
461 }
462
463
464 svn_boolean_t
465 svn_prop_name_is_valid(const char *prop_name)
466 {
467   const char *p = prop_name;
468
469   /* The characters we allow use identical representations in UTF8
470      and ASCII, so we can just test for the appropriate ASCII codes.
471      But we can't use standard C character notation ('A', 'B', etc)
472      because there's no guarantee that this C environment is using
473      ASCII. */
474
475   if (!(svn_ctype_isalpha(*p)
476         || *p == SVN_CTYPE_ASCII_COLON
477         || *p == SVN_CTYPE_ASCII_UNDERSCORE))
478     return FALSE;
479   p++;
480   for (; *p; p++)
481     {
482       if (!(svn_ctype_isalnum(*p)
483             || *p == SVN_CTYPE_ASCII_MINUS
484             || *p == SVN_CTYPE_ASCII_DOT
485             || *p == SVN_CTYPE_ASCII_COLON
486             || *p == SVN_CTYPE_ASCII_UNDERSCORE))
487         return FALSE;
488     }
489   return TRUE;
490 }
491
492 const char *
493 svn_prop_get_value(const apr_hash_t *props,
494                    const char *prop_name)
495 {
496   svn_string_t *str;
497
498   if (!props)
499     return NULL;
500
501   str = svn_hash_gets((apr_hash_t *)props, prop_name);
502
503   if (str)
504     return str->data;
505
506   return NULL;
507 }