Sync with upstream
[svnrdump.git] / svn17_compat.c
blobea1312898623133c16cbaadc8b8a456d7811f865
1 /*
2 * svn17_compat.c : a library to make Subversion 1.6 look like 1.7.
4 * ====================================================================
5 * This file is derived from code licensed to the Apache
6 * Software Foundation (ASF) under one or more contributor
7 * license agreements. See the NOTICE file distributed with
8 * this file for additional information regarding copyright
9 * ownership. The ASF licenses those portions to you under
10 * the Apache License, Version 2.0 (the "License"); you may
11 * not use those portions except in compliance with the
12 * License. You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * Unless required by applicable law or agreed to in writing,
17 * software distributed under the License is distributed on an
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 * KIND, either express or implied. See the License for the
20 * specific language governing permissions and limitations
21 * under the License.
23 * Any code in this file not licensed from the ASF is
24 * original code in the public domain. You may freely use,
25 * modify, distribute, and relicense such code.
26 * ====================================================================
29 #include <stdlib.h>
30 #include <string.h>
31 #include <assert.h>
33 #include <apr.h>
34 #include <apr_strings.h>
35 #include <apr_hash.h>
36 #include <apr_lib.h>
37 #include <apr_errno.h>
38 #include <apr_pools.h>
40 #include <svn_error.h>
41 #include <svn_config.h>
42 #include <svn_io.h>
43 #include <svn_dirent_uri.h>
44 #include <svn_path.h>
45 #include <svn_ctype.h>
46 #include "svn17_compat.h"
48 /* TRUE if s is the canonical empty path, FALSE otherwise
49 From libsvn_subr/dirent_uri.c. */
50 #define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0')
52 /* Path type definition. Used only by internal functions.
53 From libsvn_subr/dirent_uri.c. */
54 typedef enum {
55 type_uri,
56 type_dirent,
57 type_relpath
58 } path_type_t;
60 /* Here is the BNF for path components in a URI. "pchar" is a
61 character in a path component.
63 pchar = unreserved | escaped |
64 ":" | "@" | "&" | "=" | "+" | "$" | ","
65 unreserved = alphanum | mark
66 mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
68 Note that "escaped" doesn't really apply to what users can put in
69 their paths, so that really means the set of characters is:
71 alphanum | mark | ":" | "@" | "&" | "=" | "+" | "$" | ","
73 From libsvn_subr/path.c. */
74 static const char svn_uri__char_validity[256] = {
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
78 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
80 /* 64 */
81 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
82 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
83 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
84 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
86 /* 128 */
87 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
88 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
89 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
90 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
92 /* 192 */
93 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
94 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
95 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99 /* From libsvn_subr/dirent_uri.c. */
100 static svn_boolean_t
101 svn_uri_is_canonical(const char *uri, apr_pool_t *pool)
103 const char *ptr = uri, *seg = uri;
104 const char *schema_data = NULL;
106 /* URI is canonical if it has:
107 * - no '.' segments
108 * - no closing '/', unless for the root path '/' itself
109 * - no '//'
110 * - lowercase URL scheme
111 * - lowercase URL hostname
114 if (*uri == '\0')
115 return TRUE;
117 /* Maybe parse hostname and scheme. */
118 if (*ptr != '/')
120 while (*ptr && (*ptr != '/') && (*ptr != ':'))
121 ptr++;
123 if (*ptr == ':' && *(ptr+1) == '/' && *(ptr+2) == '/')
125 /* Found a scheme, check that it's all lowercase. */
126 ptr = uri;
127 while (*ptr != ':')
129 if (*ptr >= 'A' && *ptr <= 'Z')
130 return FALSE;
131 ptr++;
133 /* Skip :// */
134 ptr += 3;
136 /* This might be the hostname */
137 seg = ptr;
138 while (*ptr && (*ptr != '/') && (*ptr != '@'))
139 ptr++;
141 if (! *ptr)
142 return TRUE;
144 if (*ptr == '@')
145 seg = ptr + 1;
147 /* Found a hostname, check that it's all lowercase. */
148 ptr = seg;
149 while (*ptr && *ptr != '/')
151 if (*ptr >= 'A' && *ptr <= 'Z')
152 return FALSE;
153 ptr++;
156 schema_data = ptr;
158 else
160 /* Didn't find a scheme; finish the segment. */
161 while (*ptr && *ptr != '/')
162 ptr++;
166 #ifdef SVN_USE_DOS_PATHS
167 if (schema_data && *ptr == '/')
169 /* If this is a file url, ptr now points to the third '/' in
170 file:///C:/path. Check that if we have such a URL the drive
171 letter is in uppercase. */
172 if (strncmp(uri, "file:", 5) == 0 &&
173 ! (*(ptr+1) >= 'A' && *(ptr+1) <= 'Z') &&
174 *(ptr+2) == ':')
175 return FALSE;
177 #endif /* SVN_USE_DOS_PATHS */
179 /* Now validate the rest of the URI. */
180 while(1)
182 apr_size_t seglen = ptr - seg;
184 if (seglen == 1 && *seg == '.')
185 return FALSE; /* /./ */
187 if (*ptr == '/' && *(ptr+1) == '/')
188 return FALSE; /* // */
190 if (! *ptr && *(ptr - 1) == '/' && ptr - 1 != uri)
191 return FALSE; /* foo/ */
193 if (! *ptr)
194 break;
196 if (*ptr == '/')
197 ptr++;
198 seg = ptr;
201 while (*ptr && (*ptr != '/'))
202 ptr++;
205 if (schema_data)
207 ptr = schema_data;
209 while (*ptr)
211 if (*ptr == '%')
213 char digitz[3];
214 int val;
216 /* Can't use svn_ctype_isxdigit() because lower case letters are
217 not in our canonical format */
218 if (((*(ptr+1) < '0' || *(ptr+1) > '9'))
219 && (*(ptr+1) < 'A' || *(ptr+1) > 'F'))
220 return FALSE;
221 else if (((*(ptr+2) < '0' || *(ptr+2) > '9'))
222 && (*(ptr+2) < 'A' || *(ptr+2) > 'F'))
223 return FALSE;
225 digitz[0] = *(++ptr);
226 digitz[1] = *(++ptr);
227 digitz[2] = '\0';
228 val = (int)strtol(digitz, NULL, 16);
230 if (svn_uri__char_validity[val])
231 return FALSE; /* Should not have been escaped */
233 else if (*ptr != '/' && !svn_uri__char_validity[(unsigned char)*ptr])
234 return FALSE; /* Character should have been escaped */
235 ptr++;
239 return TRUE;
242 /* From libsvn_subr/dirent_uri.c. */
243 static svn_boolean_t
244 svn_uri_is_absolute(const char *uri)
246 /* uri is absolute if it starts with '/' */
247 if (uri && uri[0] == '/')
248 return TRUE;
250 /* URLs are absolute. */
251 return svn_path_is_url(uri);
254 /* Calculates the length occupied by the schema defined root of URI
255 From libsvn_subr/dirent_uri.c. */
256 static apr_size_t
257 uri_schema_root_length(const char *uri, apr_size_t len)
259 apr_size_t i;
261 for (i = 0; i < len; i++)
263 if (uri[i] == '/')
265 if (i > 0 && uri[i-1] == ':' && i < len-1 && uri[i+1] == '/')
267 /* We have an absolute uri */
268 if (i == 5 && strncmp("file", uri, 4) == 0)
269 return 7; /* file:// */
270 else
272 for (i += 2; i < len; i++)
273 if (uri[i] == '/')
274 return i;
276 return len; /* Only a hostname is found */
279 else
280 return 0;
284 return 0;
287 /* From libsvn_subr/dirent_uri.c. */
288 char *
289 svn_uri_join(const char *base, const char *component, apr_pool_t *pool)
291 apr_size_t blen = strlen(base);
292 apr_size_t clen = strlen(component);
293 char *path;
295 assert(svn_uri_is_canonical(base, pool));
296 assert(svn_uri_is_canonical(component, pool));
298 /* If either is empty return the other */
299 if (SVN_PATH_IS_EMPTY(base))
300 return apr_pmemdup(pool, component, clen + 1);
301 if (SVN_PATH_IS_EMPTY(component))
302 return apr_pmemdup(pool, base, blen + 1);
304 /* If the component is absolute, then return it. */
305 if (svn_uri_is_absolute(component))
307 if (*component != '/')
308 return apr_pmemdup(pool, component, clen + 1);
309 else
311 /* The uri is not absolute enough; use only the root from base */
312 apr_size_t n = uri_schema_root_length(base, blen);
314 path = apr_palloc(pool, n + clen + 1);
316 if (n > 0)
317 memcpy(path, base, n);
319 memcpy(path + n, component, clen + 1); /* Include '\0' */
321 return path;
325 if (blen == 1 && base[0] == '/')
326 blen = 0; /* Ignore base, just return separator + component */
328 /* Construct the new, combined path. */
329 path = apr_palloc(pool, blen + 1 + clen + 1);
330 memcpy(path, base, blen);
331 path[blen] = '/';
332 memcpy(path + blen + 1, component, clen + 1);
334 return path;
337 /* From libsvn_subr/dirent_uri.c. */
338 static svn_boolean_t
339 svn_relpath_is_canonical(const char *relpath,
340 apr_pool_t *pool)
342 const char *ptr = relpath, *seg = relpath;
344 /* RELPATH is canonical if it has:
345 * - no '.' segments
346 * - no start and closing '/'
347 * - no '//'
350 if (*relpath == '\0')
351 return TRUE;
353 if (*ptr == '/')
354 return FALSE;
356 /* Now validate the rest of the path. */
357 while(1)
359 apr_size_t seglen = ptr - seg;
361 if (seglen == 1 && *seg == '.')
362 return FALSE; /* /./ */
364 if (*ptr == '/' && *(ptr+1) == '/')
365 return FALSE; /* // */
367 if (! *ptr && *(ptr - 1) == '/')
368 return FALSE; /* foo/ */
370 if (! *ptr)
371 break;
373 if (*ptr == '/')
374 ptr++;
375 seg = ptr;
377 while (*ptr && (*ptr != '/'))
378 ptr++;
381 return TRUE;
384 /* From libsvn_subr/dirent_uri.c. */
385 const char *
386 svn_relpath_basename(const char *relpath,
387 apr_pool_t *pool)
389 apr_size_t len = strlen(relpath);
390 apr_size_t start;
392 assert(!pool || svn_relpath_is_canonical(relpath, pool));
394 start = len;
395 while (start > 0 && relpath[start - 1] != '/')
396 --start;
398 if (pool)
399 return apr_pstrmemdup(pool, relpath + start, len - start);
400 else
401 return relpath + start;
404 /* Return the length of substring necessary to encompass the entire
405 * previous relpath segment in RELPATH, which should be a LEN byte string.
407 * A trailing slash will not be included in the returned length.
408 * From libsvn_subr/dirent_uri.c.
410 static apr_size_t
411 relpath_previous_segment(const char *relpath,
412 apr_size_t len)
414 if (len == 0)
415 return 0;
417 --len;
418 while (len > 0 && relpath[len] != '/')
419 --len;
421 return len;
424 /* From libsvn_subr/dirent_uri.c. */
425 char *
426 svn_relpath_dirname(const char *relpath,
427 apr_pool_t *pool)
429 apr_size_t len = strlen(relpath);
431 assert(svn_relpath_is_canonical(relpath, pool));
433 return apr_pstrmemdup(pool, relpath,
434 relpath_previous_segment(relpath, len));
437 /* From libsvn_subr/dirent_uri.c. */
438 const char *
439 svn_dirent_basename(const char *dirent, apr_pool_t *pool)
441 apr_size_t len = strlen(dirent);
442 apr_size_t start;
444 assert(!pool || svn_dirent_is_canonical(dirent, pool));
446 if (svn_dirent_is_root(dirent, len))
447 return "";
448 else
450 start = len;
451 while (start > 0 && dirent[start - 1] != '/'
452 #ifdef SVN_USE_DOS_PATHS
453 && dirent[start - 1] != ':'
454 #endif
456 --start;
459 if (pool)
460 return apr_pstrmemdup(pool, dirent + start, len - start);
461 else
462 return dirent + start;
465 /* Return the string length of the longest common ancestor of PATH1 and PATH2.
466 * Pass type_uri for TYPE if PATH1 and PATH2 are URIs, and type_dirent if
467 * PATH1 and PATH2 are regular paths.
469 * If the two paths do not share a common ancestor, return 0.
471 * New strings are allocated in POOL.
472 * From libsvn_subr/dirent_uri.c.
474 static apr_size_t
475 get_longest_ancestor_length(path_type_t types,
476 const char *path1,
477 const char *path2,
478 apr_pool_t *pool)
480 apr_size_t path1_len, path2_len;
481 apr_size_t i = 0;
482 apr_size_t last_dirsep = 0;
483 #ifdef SVN_USE_DOS_PATHS
484 svn_boolean_t unc = FALSE;
485 #endif
487 path1_len = strlen(path1);
488 path2_len = strlen(path2);
490 if (SVN_PATH_IS_EMPTY(path1) || SVN_PATH_IS_EMPTY(path2))
491 return 0;
493 while (path1[i] == path2[i])
495 /* Keep track of the last directory separator we hit. */
496 if (path1[i] == '/')
497 last_dirsep = i;
499 i++;
501 /* If we get to the end of either path, break out. */
502 if ((i == path1_len) || (i == path2_len))
503 break;
506 /* two special cases:
507 1. '/' is the longest common ancestor of '/' and '/foo' */
508 if (i == 1 && path1[0] == '/' && path2[0] == '/')
509 return 1;
510 /* 2. '' is the longest common ancestor of any non-matching
511 * strings 'foo' and 'bar' */
512 if (types == type_dirent && i == 0)
513 return 0;
515 /* Handle some windows specific cases */
516 #ifdef SVN_USE_DOS_PATHS
517 if (types == type_dirent)
519 /* don't count the '//' from UNC paths */
520 if (last_dirsep == 1 && path1[0] == '/' && path1[1] == '/')
522 last_dirsep = 0;
523 unc = TRUE;
526 /* X:/ and X:/foo */
527 if (i == 3 && path1[2] == '/' && path1[1] == ':')
528 return i;
530 /* Cannot use SVN_ERR_ASSERT here, so we'll have to crash, sorry.
531 * Note that this assertion triggers only if the code above has
532 * been broken. The code below relies on this assertion, because
533 * it uses [i - 1] as index. */
534 assert(i > 0);
536 /* X: and X:/ */
537 if ((path1[i - 1] == ':' && path2[i] == '/') ||
538 (path2[i - 1] == ':' && path1[i] == '/'))
539 return 0;
540 /* X: and X:foo */
541 if (path1[i - 1] == ':' || path2[i - 1] == ':')
542 return i;
544 #endif /* SVN_USE_DOS_PATHS */
546 /* last_dirsep is now the offset of the last directory separator we
547 crossed before reaching a non-matching byte. i is the offset of
548 that non-matching byte, and is guaranteed to be <= the length of
549 whichever path is shorter.
550 If one of the paths is the common part return that. */
551 if (((i == path1_len) && (path2[i] == '/'))
552 || ((i == path2_len) && (path1[i] == '/'))
553 || ((i == path1_len) && (i == path2_len)))
554 return i;
555 else
557 /* Nothing in common but the root folder '/' or 'X:/' for Windows
558 dirents. */
559 #ifdef SVN_USE_DOS_PATHS
560 if (! unc)
562 /* X:/foo and X:/bar returns X:/ */
563 if ((types == type_dirent) &&
564 last_dirsep == 2 && path1[1] == ':' && path1[2] == '/'
565 && path2[1] == ':' && path2[2] == '/')
566 return 3;
567 #endif /* SVN_USE_DOS_PATHS */
568 if (last_dirsep == 0 && path1[0] == '/' && path2[0] == '/')
569 return 1;
570 #ifdef SVN_USE_DOS_PATHS
572 #endif
575 return last_dirsep;
578 /* From libsvn_subr/dirent_uri.c. */
579 char *
580 svn_relpath_get_longest_ancestor(const char *relpath1,
581 const char *relpath2,
582 apr_pool_t *pool)
584 return apr_pstrndup(pool, relpath1,
585 get_longest_ancestor_length(type_relpath, relpath1,
586 relpath2, pool));
589 /* From libsvn_subr/dirent_uri.c. */
590 const char *
591 svn_relpath_skip_ancestor(const char *parent_relpath,
592 const char *child_relpath)
594 apr_size_t len = strlen(parent_relpath);
596 if (0 != memcmp(parent_relpath, child_relpath, len))
597 return child_relpath; /* parent_relpath is no ancestor of child_relpath */
599 if (child_relpath[len] == 0)
600 return ""; /* parent_relpath == child_relpath */
602 if (len == 1 && child_relpath[0] == '/')
603 return child_relpath + 1;
605 if (child_relpath[len] == '/')
606 return child_relpath + len + 1;
608 return child_relpath;
611 /* From libsvn_subr/dirent_uri.c. */
612 char *
613 svn_relpath_join(const char *base,
614 const char *component,
615 apr_pool_t *pool)
617 apr_size_t blen = strlen(base);
618 apr_size_t clen = strlen(component);
619 char *path;
621 assert(svn_relpath_is_canonical(base, pool));
622 assert(svn_relpath_is_canonical(component, pool));
624 /* If either is empty return the other */
625 if (blen == 0)
626 return apr_pmemdup(pool, component, clen + 1);
627 if (clen == 0)
628 return apr_pmemdup(pool, base, blen + 1);
630 path = apr_palloc(pool, blen + 1 + clen + 1);
631 memcpy(path, base, blen);
632 path[blen] = '/';
633 memcpy(path + blen + 1, component, clen + 1);
635 return path;
638 /* Locale insensitive tolower() for converting parts of dirents and urls
639 while canonicalizing. From libsvn_subr/dirent_uri.c. */
640 static char
641 canonicalize_to_lower(char c)
643 if (c < 'A' || c > 'Z')
644 return c;
645 else
646 return c - 'A' + 'a';
649 /* Locale insensitive toupper() for converting parts of dirents and urls
650 while canonicalizing. From libsvn_subr/dirent_uri.c. */
651 static char
652 canonicalize_to_upper(char c)
654 if (c < 'a' || c > 'z')
655 return c;
656 else
657 return c - 'a' + 'A';
661 /* Return the canonicalized version of PATH, of type TYPE, allocated in
662 * POOL. From libsvn_subr/dirent_uri.c.
664 static const char *
665 canonicalize(path_type_t type, const char *path, apr_pool_t *pool)
667 char *canon, *dst;
668 const char *src;
669 apr_size_t seglen;
670 apr_size_t schemelen = 0;
671 apr_size_t canon_segments = 0;
672 svn_boolean_t url = FALSE;
673 char *schema_data = NULL;
675 /* "" is already canonical, so just return it; note that later code
676 depends on path not being zero-length. */
677 if (SVN_PATH_IS_EMPTY(path))
678 return "";
680 dst = canon = apr_pcalloc(pool, strlen(path) + 1);
682 /* If this is supposed to be an URI and it starts with "scheme://", then
683 copy the scheme, host name, etc. to DST and set URL = TRUE. */
684 src = path;
685 if (type == type_uri && *src != '/')
687 while (*src && (*src != '/') && (*src != ':'))
688 src++;
690 if (*src == ':' && *(src+1) == '/' && *(src+2) == '/')
692 const char *seg;
694 url = TRUE;
696 /* Found a scheme, convert to lowercase and copy to dst. */
697 src = path;
698 while (*src != ':')
700 *(dst++) = canonicalize_to_lower((*src++));
701 schemelen++;
703 *(dst++) = ':';
704 *(dst++) = '/';
705 *(dst++) = '/';
706 src += 3;
707 schemelen += 3;
709 /* This might be the hostname */
710 seg = src;
711 while (*src && (*src != '/') && (*src != '@'))
712 src++;
714 if (*src == '@')
716 /* Copy the username & password. */
717 seglen = src - seg + 1;
718 memcpy(dst, seg, seglen);
719 dst += seglen;
720 src++;
722 else
723 src = seg;
725 /* Found a hostname, convert to lowercase and copy to dst. */
726 while (*src && (*src != '/'))
727 *(dst++) = canonicalize_to_lower((*src++));
729 /* Copy trailing slash, or null-terminator. */
730 *(dst) = *(src);
732 /* Move src and dst forward only if we are not
733 * at null-terminator yet. */
734 if (*src)
736 src++;
737 dst++;
738 schema_data = dst;
741 canon_segments = 1;
745 /* Copy to DST any separator or drive letter that must come before the
746 first regular path segment. */
747 if (! url && type != type_relpath)
749 src = path;
750 /* If this is an absolute path, then just copy over the initial
751 separator character. */
752 if (*src == '/')
754 *(dst++) = *(src++);
756 #ifdef SVN_USE_DOS_PATHS
757 /* On Windows permit two leading separator characters which means an
758 * UNC path. */
759 if ((type == type_dirent) && *src == '/')
760 *(dst++) = *(src++);
761 #endif /* SVN_USE_DOS_PATHS */
763 #ifdef SVN_USE_DOS_PATHS
764 /* On Windows the first segment can be a drive letter, which we normalize
765 to upper case. */
766 else if (type == type_dirent &&
767 ((*src >= 'a' && *src <= 'z') ||
768 (*src >= 'A' && *src <= 'Z')) &&
769 (src[1] == ':'))
771 *(dst++) = canonicalize_to_upper(*(src++));
772 /* Leave the ':' to be processed as (or as part of) a path segment
773 by the following code block, so we need not care whether it has
774 a slash after it. */
776 #endif /* SVN_USE_DOS_PATHS */
779 while (*src)
781 /* Parse each segment, find the closing '/' */
782 const char *next = src;
783 while (*next && (*next != '/'))
784 ++next;
786 seglen = next - src;
788 if (seglen == 0 || (seglen == 1 && src[0] == '.'))
790 /* Noop segment, so do nothing. */
792 #ifdef SVN_USE_DOS_PATHS
793 /* If this is the first path segment of a file:// URI and it contains a
794 windows drive letter, convert the drive letter to upper case. */
795 else if (url && canon_segments == 1 && seglen == 2 &&
796 (strncmp(canon, "file:", 5) == 0) &&
797 src[0] >= 'a' && src[0] <= 'z' && src[1] == ':')
799 *(dst++) = canonicalize_to_upper(src[0]);
800 *(dst++) = ':';
801 if (*next)
802 *(dst++) = *next;
803 canon_segments++;
805 #endif /* SVN_USE_DOS_PATHS */
806 else
808 /* An actual segment, append it to the destination path */
809 if (*next)
810 seglen++;
811 memcpy(dst, src, seglen);
812 dst += seglen;
813 canon_segments++;
816 /* Skip over trailing slash to the next segment. */
817 src = next;
818 if (*src)
819 src++;
822 /* Remove the trailing slash if there was at least one
823 * canonical segment and the last segment ends with a slash.
825 * But keep in mind that, for URLs, the scheme counts as a
826 * canonical segment -- so if path is ONLY a scheme (such
827 * as "https://") we should NOT remove the trailing slash. */
828 if ((canon_segments > 0 && *(dst - 1) == '/')
829 && ! (url && path[schemelen] == '\0'))
831 dst --;
834 *dst = '\0';
836 #ifdef SVN_USE_DOS_PATHS
837 /* Skip leading double slashes when there are less than 2
838 * canon segments. UNC paths *MUST* have two segments. */
839 if ((type == type_dirent) && canon[0] == '/' && canon[1] == '/')
841 if (canon_segments < 2)
842 return canon + 1;
843 else
845 /* Now we're sure this is a valid UNC path, convert the server name
846 (the first path segment) to lowercase as Windows treats it as case
847 insensitive.
848 Note: normally the share name is treated as case insensitive too,
849 but it seems to be possible to configure Samba to treat those as
850 case sensitive, so better leave that alone. */
851 dst = canon + 2;
852 while (*dst && *dst != '/')
853 *(dst++) = canonicalize_to_lower(*dst);
856 #endif /* SVN_USE_DOS_PATHS */
858 /* Check the normalization of characters in a uri */
859 if (schema_data)
861 int need_extra = 0;
862 src = schema_data;
864 while (*src)
866 switch (*src)
868 case '/':
869 break;
870 case '%':
871 if (!svn_ctype_isxdigit(*(src+1)) ||
872 !svn_ctype_isxdigit(*(src+2)))
873 need_extra += 2;
874 else
875 src += 2;
876 break;
877 default:
878 if (!svn_uri__char_validity[(unsigned char)*src])
879 need_extra += 2;
880 break;
882 src++;
885 if (need_extra > 0)
887 apr_size_t pre_schema_size = (apr_size_t)(schema_data - canon);
889 dst = apr_palloc(pool, (apr_size_t)(src - canon) + need_extra + 1);
890 memcpy(dst, canon, pre_schema_size);
891 canon = dst;
893 dst += pre_schema_size;
895 else
896 dst = schema_data;
898 src = schema_data;
900 while (*src)
902 switch (*src)
904 case '/':
905 *(dst++) = '/';
906 break;
907 case '%':
908 if (!svn_ctype_isxdigit(*(src+1)) ||
909 !svn_ctype_isxdigit(*(src+2)))
911 *(dst++) = '%';
912 *(dst++) = '2';
913 *(dst++) = '5';
915 else
917 char digitz[3];
918 int val;
920 digitz[0] = *(++src);
921 digitz[1] = *(++src);
922 digitz[2] = 0;
924 val = (int)strtol(digitz, NULL, 16);
926 if (svn_uri__char_validity[(unsigned char)val])
927 *(dst++) = (char)val;
928 else
930 *(dst++) = '%';
931 *(dst++) = canonicalize_to_upper(digitz[0]);
932 *(dst++) = canonicalize_to_upper(digitz[1]);
935 break;
936 default:
937 if (!svn_uri__char_validity[(unsigned char)*src])
939 apr_snprintf(dst, 4, "%%%02X", (unsigned char)*src);
940 dst += 3;
942 else
943 *(dst++) = *src;
944 break;
946 src++;
948 *dst = '\0';
951 return canon;
954 /* From libsvn_subr/dirent_uri.c. */
955 const char *
956 svn_uri_canonicalize(const char *uri, apr_pool_t *pool)
958 return canonicalize(type_uri, uri, pool);
961 /* From libsvn_subr/dirent_uri.c. */
962 const char *
963 svn_relpath_canonicalize(const char *relpath, apr_pool_t *pool)
965 return canonicalize(type_relpath, relpath, pool);
968 /* New code (public domain). */
969 svn_error_t *
970 svn_io_remove_file2(const char *path,
971 svn_boolean_t ignore_enoent,
972 apr_pool_t *scratch_pool)
974 svn_error_t *err = svn_io_remove_file(path, scratch_pool);
975 if (ignore_enoent && err && APR_STATUS_IS_ENOENT(err->apr_err))
977 svn_error_clear(err);
978 return SVN_NO_ERROR;
980 return err;
983 /* Note about the type casts: apr_hash_this() does not expect a const hash
984 * index pointer even though it does not modify the hash index. In
985 * Subversion we're trying to be const-correct, so these functions all take
986 * a const hash index and we cast away the const when passing it down to
987 * APR. (A compiler may warn about casting away 'const', but at least this
988 * cast is explicit and gathered in one place.)
990 * From libsvn_subr/iter.c. */
991 const void *svn__apr_hash_index_key(const apr_hash_index_t *hi)
993 const void *key;
995 apr_hash_this((apr_hash_index_t *)hi, &key, NULL, NULL);
996 return key;
999 void *svn__apr_hash_index_val(const apr_hash_index_t *hi)
1001 void *val;
1003 apr_hash_this((apr_hash_index_t *)hi, NULL, NULL, &val);
1004 return val;
1008 /* From libsvn_subr/cmdline.c. */
1009 svn_error_t *
1010 svn_cmdline__apply_config_options(apr_hash_t *config,
1011 const apr_array_header_t *config_options,
1012 const char *prefix,
1013 const char *argument_name)
1015 int i;
1017 for (i = 0; i < config_options->nelts; i++)
1019 svn_config_t *cfg;
1020 svn_cmdline__config_argument_t *arg =
1021 APR_ARRAY_IDX(config_options, i,
1022 svn_cmdline__config_argument_t *);
1024 cfg = apr_hash_get(config, arg->file, APR_HASH_KEY_STRING);
1026 if (cfg)
1028 svn_config_set(cfg, arg->section, arg->option, arg->value);
1030 else
1032 svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1033 _("Unrecognized file in argument of %s"), argument_name);
1035 svn_handle_warning2(stderr, err, prefix);
1036 svn_error_clear(err);
1040 return SVN_NO_ERROR;
1043 /* From libsvn_subr/cmdline.c. */
1044 svn_error_t *
1045 svn_cmdline__parse_config_option(apr_array_header_t *config_options,
1046 const char *opt_arg,
1047 apr_pool_t *pool)
1049 svn_cmdline__config_argument_t *config_option;
1050 const char *first_colon, *second_colon, *equals_sign;
1051 apr_size_t len = strlen(opt_arg);
1052 if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg))
1054 if ((second_colon = strchr(first_colon + 1, ':')) &&
1055 (second_colon != first_colon + 1))
1057 if ((equals_sign = strchr(second_colon + 1, '=')) &&
1058 (equals_sign != second_colon + 1))
1060 config_option = apr_pcalloc(pool, sizeof(*config_option));
1061 config_option->file = apr_pstrndup(pool, opt_arg,
1062 first_colon - opt_arg);
1063 config_option->section = apr_pstrndup(pool, first_colon + 1,
1064 second_colon - first_colon - 1);
1065 config_option->option = apr_pstrndup(pool, second_colon + 1,
1066 equals_sign - second_colon - 1);
1068 if (! (strchr(config_option->option, ':')))
1070 config_option->value = apr_pstrndup(pool, equals_sign + 1,
1071 opt_arg + len - equals_sign - 1);
1072 APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t *)
1073 = config_option;
1074 return SVN_NO_ERROR;
1079 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1080 _("Invalid syntax of argument of --config-option"));