load_editor: Use svn17_compat
[svnrdump.git] / svn17_compat.c
bloba2906b9b81297eec17fa6f885d89a7db6803f05d
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 "svn17_compat.h"
47 /* TRUE if s is the canonical empty path, FALSE otherwise
48 From libsvn_subr/dirent_uri.c. */
49 #define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0')
51 /* Path type definition. Used only by internal functions.
52 From libsvn_subr/dirent_uri.c. */
53 typedef enum {
54 type_uri,
55 type_dirent,
56 type_relpath
57 } path_type_t;
59 /* Here is the BNF for path components in a URI. "pchar" is a
60 character in a path component.
62 pchar = unreserved | escaped |
63 ":" | "@" | "&" | "=" | "+" | "$" | ","
64 unreserved = alphanum | mark
65 mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
67 Note that "escaped" doesn't really apply to what users can put in
68 their paths, so that really means the set of characters is:
70 alphanum | mark | ":" | "@" | "&" | "=" | "+" | "$" | ","
72 From libsvn_subr/path.c. */
73 static const char svn_uri__char_validity[256] = {
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
77 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0,
79 /* 64 */
80 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
81 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
82 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
83 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0,
85 /* 128 */
86 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
91 /* 192 */
92 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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,
98 /* From libsvn_subr/dirent_uri.c. */
99 static svn_boolean_t
100 svn_uri_is_canonical(const char *uri, apr_pool_t *pool)
102 const char *ptr = uri, *seg = uri;
103 const char *schema_data = NULL;
105 /* URI is canonical if it has:
106 * - no '.' segments
107 * - no closing '/', unless for the root path '/' itself
108 * - no '//'
109 * - lowercase URL scheme
110 * - lowercase URL hostname
113 if (*uri == '\0')
114 return TRUE;
116 /* Maybe parse hostname and scheme. */
117 if (*ptr != '/')
119 while (*ptr && (*ptr != '/') && (*ptr != ':'))
120 ptr++;
122 if (*ptr == ':' && *(ptr+1) == '/' && *(ptr+2) == '/')
124 /* Found a scheme, check that it's all lowercase. */
125 ptr = uri;
126 while (*ptr != ':')
128 if (*ptr >= 'A' && *ptr <= 'Z')
129 return FALSE;
130 ptr++;
132 /* Skip :// */
133 ptr += 3;
135 /* This might be the hostname */
136 seg = ptr;
137 while (*ptr && (*ptr != '/') && (*ptr != '@'))
138 ptr++;
140 if (! *ptr)
141 return TRUE;
143 if (*ptr == '@')
144 seg = ptr + 1;
146 /* Found a hostname, check that it's all lowercase. */
147 ptr = seg;
148 while (*ptr && *ptr != '/')
150 if (*ptr >= 'A' && *ptr <= 'Z')
151 return FALSE;
152 ptr++;
155 schema_data = ptr;
157 else
159 /* Didn't find a scheme; finish the segment. */
160 while (*ptr && *ptr != '/')
161 ptr++;
165 #ifdef SVN_USE_DOS_PATHS
166 if (schema_data && *ptr == '/')
168 /* If this is a file url, ptr now points to the third '/' in
169 file:///C:/path. Check that if we have such a URL the drive
170 letter is in uppercase. */
171 if (strncmp(uri, "file:", 5) == 0 &&
172 ! (*(ptr+1) >= 'A' && *(ptr+1) <= 'Z') &&
173 *(ptr+2) == ':')
174 return FALSE;
176 #endif /* SVN_USE_DOS_PATHS */
178 /* Now validate the rest of the URI. */
179 while(1)
181 apr_size_t seglen = ptr - seg;
183 if (seglen == 1 && *seg == '.')
184 return FALSE; /* /./ */
186 if (*ptr == '/' && *(ptr+1) == '/')
187 return FALSE; /* // */
189 if (! *ptr && *(ptr - 1) == '/' && ptr - 1 != uri)
190 return FALSE; /* foo/ */
192 if (! *ptr)
193 break;
195 if (*ptr == '/')
196 ptr++;
197 seg = ptr;
200 while (*ptr && (*ptr != '/'))
201 ptr++;
204 if (schema_data)
206 ptr = schema_data;
208 while (*ptr)
210 if (*ptr == '%')
212 char digitz[3];
213 int val;
215 /* Can't use apr_isxdigit() because lower case letters are
216 not in our canonical format */
217 if (((*(ptr+1) < '0' || (*ptr+1) > '9'))
218 && (*(ptr+1) < 'A' || (*ptr+1) > 'F'))
219 return FALSE;
220 else if (((*(ptr+2) < '0' || (*ptr+2) > '9'))
221 && (*(ptr+2) < 'A' || (*ptr+2) > 'F'))
222 return FALSE;
224 digitz[0] = *(++ptr);
225 digitz[1] = *(++ptr);
226 digitz[2] = '\0';
227 val = (int)strtol(digitz, NULL, 16);
229 if (svn_uri__char_validity[val])
230 return FALSE; /* Should not have been escaped */
232 else if (*ptr != '/' && !svn_uri__char_validity[(unsigned char)*ptr])
233 return FALSE; /* Character should have been escaped */
234 ptr++;
238 return TRUE;
241 /* From libsvn_subr/dirent_uri.c. */
242 static svn_boolean_t
243 svn_uri_is_absolute(const char *uri)
245 /* uri is absolute if it starts with '/' */
246 if (uri && uri[0] == '/')
247 return TRUE;
249 /* URLs are absolute. */
250 return svn_path_is_url(uri);
253 /* Calculates the length occupied by the schema defined root of URI
254 From libsvn_subr/dirent_uri.c. */
255 static apr_size_t
256 uri_schema_root_length(const char *uri, apr_size_t len)
258 apr_size_t i;
260 for (i = 0; i < len; i++)
262 if (uri[i] == '/')
264 if (i > 0 && uri[i-1] == ':' && i < len-1 && uri[i+1] == '/')
266 /* We have an absolute uri */
267 if (i == 5 && strncmp("file", uri, 4) == 0)
268 return 7; /* file:// */
269 else
271 for (i += 2; i < len; i++)
272 if (uri[i] == '/')
273 return i;
275 return len; /* Only a hostname is found */
278 else
279 return 0;
283 return 0;
286 /* From libsvn_subr/dirent_uri.c. */
287 char *
288 svn_uri_join(const char *base, const char *component, apr_pool_t *pool)
290 apr_size_t blen = strlen(base);
291 apr_size_t clen = strlen(component);
292 char *path;
294 assert(svn_uri_is_canonical(base, pool));
295 assert(svn_uri_is_canonical(component, pool));
297 /* If either is empty return the other */
298 if (SVN_PATH_IS_EMPTY(base))
299 return apr_pmemdup(pool, component, clen + 1);
300 if (SVN_PATH_IS_EMPTY(component))
301 return apr_pmemdup(pool, base, blen + 1);
303 /* If the component is absolute, then return it. */
304 if (svn_uri_is_absolute(component))
306 if (*component != '/')
307 return apr_pmemdup(pool, component, clen + 1);
308 else
310 /* The uri is not absolute enough; use only the root from base */
311 apr_size_t n = uri_schema_root_length(base, blen);
313 path = apr_palloc(pool, n + clen + 1);
315 if (n > 0)
316 memcpy(path, base, n);
318 memcpy(path + n, component, clen + 1); /* Include '\0' */
320 return path;
324 if (blen == 1 && base[0] == '/')
325 blen = 0; /* Ignore base, just return separator + component */
327 /* Construct the new, combined path. */
328 path = apr_palloc(pool, blen + 1 + clen + 1);
329 memcpy(path, base, blen);
330 path[blen] = '/';
331 memcpy(path + blen + 1, component, clen + 1);
333 return path;
336 /* From libsvn_subr/dirent_uri.c. */
337 static svn_boolean_t
338 svn_relpath_is_canonical(const char *relpath,
339 apr_pool_t *pool)
341 const char *ptr = relpath, *seg = relpath;
343 /* RELPATH is canonical if it has:
344 * - no '.' segments
345 * - no start and closing '/'
346 * - no '//'
349 if (*relpath == '\0')
350 return TRUE;
352 if (*ptr == '/')
353 return FALSE;
355 /* Now validate the rest of the path. */
356 while(1)
358 apr_size_t seglen = ptr - seg;
360 if (seglen == 1 && *seg == '.')
361 return FALSE; /* /./ */
363 if (*ptr == '/' && *(ptr+1) == '/')
364 return FALSE; /* // */
366 if (! *ptr && *(ptr - 1) == '/')
367 return FALSE; /* foo/ */
369 if (! *ptr)
370 break;
372 if (*ptr == '/')
373 ptr++;
374 seg = ptr;
376 while (*ptr && (*ptr != '/'))
377 ptr++;
380 return TRUE;
383 /* From libsvn_subr/dirent_uri.c. */
384 const char *
385 svn_relpath_basename(const char *relpath,
386 apr_pool_t *pool)
388 apr_size_t len = strlen(relpath);
389 apr_size_t start;
391 assert(!pool || svn_relpath_is_canonical(relpath, pool));
393 start = len;
394 while (start > 0 && relpath[start - 1] != '/')
395 --start;
397 if (pool)
398 return apr_pstrmemdup(pool, relpath + start, len - start);
399 else
400 return relpath + start;
403 /* Return the length of substring necessary to encompass the entire
404 * previous relpath segment in RELPATH, which should be a LEN byte string.
406 * A trailing slash will not be included in the returned length.
407 * From libsvn_subr/dirent_uri.c.
409 static apr_size_t
410 relpath_previous_segment(const char *relpath,
411 apr_size_t len)
413 if (len == 0)
414 return 0;
416 --len;
417 while (len > 0 && relpath[len] != '/')
418 --len;
420 return len;
423 /* From libsvn_subr/dirent_uri.c. */
424 char *
425 svn_relpath_dirname(const char *relpath,
426 apr_pool_t *pool)
428 apr_size_t len = strlen(relpath);
430 assert(svn_relpath_is_canonical(relpath, pool));
432 return apr_pstrmemdup(pool, relpath,
433 relpath_previous_segment(relpath, len));
436 /* From libsvn_subr/dirent_uri.c. */
437 const char *
438 svn_dirent_basename(const char *dirent, apr_pool_t *pool)
440 apr_size_t len = strlen(dirent);
441 apr_size_t start;
443 assert(!pool || svn_dirent_is_canonical(dirent, pool));
445 if (svn_dirent_is_root(dirent, len))
446 return "";
447 else
449 start = len;
450 while (start > 0 && dirent[start - 1] != '/'
451 #ifdef SVN_USE_DOS_PATHS
452 && dirent[start - 1] != ':'
453 #endif
455 --start;
458 if (pool)
459 return apr_pstrmemdup(pool, dirent + start, len - start);
460 else
461 return dirent + start;
464 /* Return the string length of the longest common ancestor of PATH1 and PATH2.
465 * Pass type_uri for TYPE if PATH1 and PATH2 are URIs, and type_dirent if
466 * PATH1 and PATH2 are regular paths.
468 * If the two paths do not share a common ancestor, return 0.
470 * New strings are allocated in POOL.
471 * From libsvn_sbur/dirent_uri.c.
473 static apr_size_t
474 get_longest_ancestor_length(path_type_t types,
475 const char *path1,
476 const char *path2,
477 apr_pool_t *pool)
479 apr_size_t path1_len, path2_len;
480 apr_size_t i = 0;
481 apr_size_t last_dirsep = 0;
482 #ifdef SVN_USE_DOS_PATHS
483 svn_boolean_t unc = FALSE;
484 #endif
486 path1_len = strlen(path1);
487 path2_len = strlen(path2);
489 if (SVN_PATH_IS_EMPTY(path1) || SVN_PATH_IS_EMPTY(path2))
490 return 0;
492 while (path1[i] == path2[i])
494 /* Keep track of the last directory separator we hit. */
495 if (path1[i] == '/')
496 last_dirsep = i;
498 i++;
500 /* If we get to the end of either path, break out. */
501 if ((i == path1_len) || (i == path2_len))
502 break;
505 /* two special cases:
506 1. '/' is the longest common ancestor of '/' and '/foo' */
507 if (i == 1 && path1[0] == '/' && path2[0] == '/')
508 return 1;
509 /* 2. '' is the longest common ancestor of any non-matching
510 * strings 'foo' and 'bar' */
511 if (types == type_dirent && i == 0)
512 return 0;
514 /* Handle some windows specific cases */
515 #ifdef SVN_USE_DOS_PATHS
516 if (types == type_dirent)
518 /* don't count the '//' from UNC paths */
519 if (last_dirsep == 1 && path1[0] == '/' && path1[1] == '/')
521 last_dirsep = 0;
522 unc = TRUE;
525 /* X:/ and X:/foo */
526 if (i == 3 && path1[2] == '/' && path1[1] == ':')
527 return i;
529 /* Cannot use SVN_ERR_ASSERT here, so we'll have to crash, sorry.
530 * Note that this assertion triggers only if the code above has
531 * been broken. The code below relies on this assertion, because
532 * it uses [i - 1] as index. */
533 assert(i > 0);
535 /* X: and X:/ */
536 if ((path1[i - 1] == ':' && path2[i] == '/') ||
537 (path2[i - 1] == ':' && path1[i] == '/'))
538 return 0;
539 /* X: and X:foo */
540 if (path1[i - 1] == ':' || path2[i - 1] == ':')
541 return i;
543 #endif /* SVN_USE_DOS_PATHS */
545 /* last_dirsep is now the offset of the last directory separator we
546 crossed before reaching a non-matching byte. i is the offset of
547 that non-matching byte, and is guaranteed to be <= the length of
548 whichever path is shorter.
549 If one of the paths is the common part return that. */
550 if (((i == path1_len) && (path2[i] == '/'))
551 || ((i == path2_len) && (path1[i] == '/'))
552 || ((i == path1_len) && (i == path2_len)))
553 return i;
554 else
556 /* Nothing in common but the root folder '/' or 'X:/' for Windows
557 dirents. */
558 #ifdef SVN_USE_DOS_PATHS
559 if (! unc)
561 /* X:/foo and X:/bar returns X:/ */
562 if ((types == type_dirent) &&
563 last_dirsep == 2 && path1[1] == ':' && path1[2] == '/'
564 && path2[1] == ':' && path2[2] == '/')
565 return 3;
566 #endif /* SVN_USE_DOS_PATHS */
567 if (last_dirsep == 0 && path1[0] == '/' && path2[0] == '/')
568 return 1;
569 #ifdef SVN_USE_DOS_PATHS
571 #endif
574 return last_dirsep;
577 /* From libsvn_subr/dirent_uri.c. */
578 char *
579 svn_relpath_get_longest_ancestor(const char *relpath1,
580 const char *relpath2,
581 apr_pool_t *pool)
583 return apr_pstrndup(pool, relpath1,
584 get_longest_ancestor_length(type_relpath, relpath1,
585 relpath2, pool));
588 /* From libsvn_subr/dirent_uri.c. */
589 const char *
590 svn_relpath_skip_ancestor(const char *parent_relpath,
591 const char *child_relpath)
593 apr_size_t len = strlen(parent_relpath);
595 if (0 != memcmp(parent_relpath, child_relpath, len))
596 return child_relpath; /* parent_relpath is no ancestor of child_relpath */
598 if (child_relpath[len] == 0)
599 return ""; /* parent_relpath == child_relpath */
601 if (len == 1 && child_relpath[0] == '/')
602 return child_relpath + 1;
604 if (child_relpath[len] == '/')
605 return child_relpath + len + 1;
607 return child_relpath;
610 /* From libsvn_subr/dirent_uri.c. */
611 char *
612 svn_relpath_join(const char *base,
613 const char *component,
614 apr_pool_t *pool)
616 apr_size_t blen = strlen(base);
617 apr_size_t clen = strlen(component);
618 char *path;
620 assert(svn_relpath_is_canonical(base, pool));
621 assert(svn_relpath_is_canonical(component, pool));
623 /* If either is empty return the other */
624 if (blen == 0)
625 return apr_pmemdup(pool, component, clen + 1);
626 if (clen == 0)
627 return apr_pmemdup(pool, base, blen + 1);
629 path = apr_palloc(pool, blen + 1 + clen + 1);
630 memcpy(path, base, blen);
631 path[blen] = '/';
632 memcpy(path + blen + 1, component, clen + 1);
634 return path;
637 /* Locale insensitive tolower() for converting parts of dirents and urls
638 while canonicalizing. From libsvn_subr/dirent_uri.c. */
639 static char
640 canonicalize_to_lower(char c)
642 if (c < 'A' || c > 'Z')
643 return c;
644 else
645 return c - 'A' + 'a';
648 /* Locale insensitive toupper() for converting parts of dirents and urls
649 while canonicalizing. From libsvn_subr/dirent_uri.c. */
650 static char
651 canonicalize_to_upper(char c)
653 if (c < 'a' || c > 'z')
654 return c;
655 else
656 return c - 'a' + 'A';
660 /* Return the canonicalized version of PATH, of type TYPE, allocated in
661 * POOL. From libsvn_subr/dirent_uri.c.
663 static const char *
664 canonicalize(path_type_t type, const char *path, apr_pool_t *pool)
666 char *canon, *dst;
667 const char *src;
668 apr_size_t seglen;
669 apr_size_t schemelen = 0;
670 apr_size_t canon_segments = 0;
671 svn_boolean_t url = FALSE;
672 char *schema_data = NULL;
674 /* "" is already canonical, so just return it; note that later code
675 depends on path not being zero-length. */
676 if (SVN_PATH_IS_EMPTY(path))
677 return "";
679 dst = canon = apr_pcalloc(pool, strlen(path) + 1);
681 /* If this is supposed to be an URI and it starts with "scheme://", then
682 copy the scheme, host name, etc. to DST and set URL = TRUE. */
683 src = path;
684 if (type == type_uri && *src != '/')
686 while (*src && (*src != '/') && (*src != ':'))
687 src++;
689 if (*src == ':' && *(src+1) == '/' && *(src+2) == '/')
691 const char *seg;
693 url = TRUE;
695 /* Found a scheme, convert to lowercase and copy to dst. */
696 src = path;
697 while (*src != ':')
699 *(dst++) = canonicalize_to_lower((*src++));
700 schemelen++;
702 *(dst++) = ':';
703 *(dst++) = '/';
704 *(dst++) = '/';
705 src += 3;
706 schemelen += 3;
708 /* This might be the hostname */
709 seg = src;
710 while (*src && (*src != '/') && (*src != '@'))
711 src++;
713 if (*src == '@')
715 /* Copy the username & password. */
716 seglen = src - seg + 1;
717 memcpy(dst, seg, seglen);
718 dst += seglen;
719 src++;
721 else
722 src = seg;
724 /* Found a hostname, convert to lowercase and copy to dst. */
725 while (*src && (*src != '/'))
726 *(dst++) = canonicalize_to_lower((*src++));
728 /* Copy trailing slash, or null-terminator. */
729 *(dst) = *(src);
731 /* Move src and dst forward only if we are not
732 * at null-terminator yet. */
733 if (*src)
735 src++;
736 dst++;
737 schema_data = dst;
740 canon_segments = 1;
744 /* Copy to DST any separator or drive letter that must come before the
745 first regular path segment. */
746 if (! url && type != type_relpath)
748 src = path;
749 /* If this is an absolute path, then just copy over the initial
750 separator character. */
751 if (*src == '/')
753 *(dst++) = *(src++);
755 #ifdef SVN_USE_DOS_PATHS
756 /* On Windows permit two leading separator characters which means an
757 * UNC path. */
758 if ((type == type_dirent) && *src == '/')
759 *(dst++) = *(src++);
760 #endif /* SVN_USE_DOS_PATHS */
762 #ifdef SVN_USE_DOS_PATHS
763 /* On Windows the first segment can be a drive letter, which we normalize
764 to upper case. */
765 else if (type == type_dirent &&
766 ((*src >= 'a' && *src <= 'z') ||
767 (*src >= 'A' && *src <= 'Z')) &&
768 (src[1] == ':'))
770 *(dst++) = canonicalize_to_upper(*(src++));
771 /* Leave the ':' to be processed as (or as part of) a path segment
772 by the following code block, so we need not care whether it has
773 a slash after it. */
775 #endif /* SVN_USE_DOS_PATHS */
778 while (*src)
780 /* Parse each segment, find the closing '/' */
781 const char *next = src;
782 while (*next && (*next != '/'))
783 ++next;
785 seglen = next - src;
787 if (seglen == 0 || (seglen == 1 && src[0] == '.'))
789 /* Noop segment, so do nothing. */
791 #ifdef SVN_USE_DOS_PATHS
792 /* If this is the first path segment of a file:// URI and it contains a
793 windows drive letter, convert the drive letter to upper case. */
794 else if (url && canon_segments == 1 && seglen == 2 &&
795 (strncmp(canon, "file:", 5) == 0) &&
796 src[0] >= 'a' && src[0] <= 'z' && src[1] == ':')
798 *(dst++) = canonicalize_to_upper(src[0]);
799 *(dst++) = ':';
800 if (*next)
801 *(dst++) = *next;
802 canon_segments++;
804 #endif /* SVN_USE_DOS_PATHS */
805 else
807 /* An actual segment, append it to the destination path */
808 if (*next)
809 seglen++;
810 memcpy(dst, src, seglen);
811 dst += seglen;
812 canon_segments++;
815 /* Skip over trailing slash to the next segment. */
816 src = next;
817 if (*src)
818 src++;
821 /* Remove the trailing slash if there was at least one
822 * canonical segment and the last segment ends with a slash.
824 * But keep in mind that, for URLs, the scheme counts as a
825 * canonical segment -- so if path is ONLY a scheme (such
826 * as "https://") we should NOT remove the trailing slash. */
827 if ((canon_segments > 0 && *(dst - 1) == '/')
828 && ! (url && path[schemelen] == '\0'))
830 dst --;
833 *dst = '\0';
835 #ifdef SVN_USE_DOS_PATHS
836 /* Skip leading double slashes when there are less than 2
837 * canon segments. UNC paths *MUST* have two segments. */
838 if ((type == type_dirent) && canon[0] == '/' && canon[1] == '/')
840 if (canon_segments < 2)
841 return canon + 1;
842 else
844 /* Now we're sure this is a valid UNC path, convert the server name
845 (the first path segment) to lowercase as Windows treats it as case
846 insensitive.
847 Note: normally the share name is treated as case insensitive too,
848 but it seems to be possible to configure Samba to treat those as
849 case sensitive, so better leave that alone. */
850 dst = canon + 2;
851 while (*dst && *dst != '/')
852 *(dst++) = canonicalize_to_lower(*dst);
855 #endif /* SVN_USE_DOS_PATHS */
857 /* Check the normalization of characters in a uri */
858 if (schema_data)
860 int need_extra = 0;
861 src = schema_data;
863 while (*src)
865 switch (*src)
867 case '/':
868 break;
869 case '%':
870 if (!apr_isxdigit(*(src+1)) || !apr_isxdigit(*(src+2)))
871 need_extra += 2;
872 else
873 src += 2;
874 break;
875 default:
876 if (!svn_uri__char_validity[(unsigned char)*src])
877 need_extra += 2;
878 break;
880 src++;
883 if (need_extra > 0)
885 apr_size_t pre_schema_size = (apr_size_t)(schema_data - canon);
887 dst = apr_palloc(pool, (apr_size_t)(src - canon) + need_extra + 1);
888 memcpy(dst, canon, pre_schema_size);
889 canon = dst;
891 dst += pre_schema_size;
893 else
894 dst = schema_data;
896 src = schema_data;
898 while (*src)
900 switch (*src)
902 case '/':
903 *(dst++) = '/';
904 break;
905 case '%':
906 if (!apr_isxdigit(*(src+1)) || !apr_isxdigit(*(src+2)))
908 *(dst++) = '%';
909 *(dst++) = '2';
910 *(dst++) = '5';
912 else
914 char digitz[3];
915 int val;
917 digitz[0] = *(++src);
918 digitz[1] = *(++src);
919 digitz[2] = 0;
921 val = (int)strtol(digitz, NULL, 16);
923 if (svn_uri__char_validity[(unsigned char)val])
924 *(dst++) = (char)val;
925 else
927 *(dst++) = '%';
928 *(dst++) = canonicalize_to_upper(digitz[0]);
929 *(dst++) = canonicalize_to_upper(digitz[1]);
932 break;
933 default:
934 if (!svn_uri__char_validity[(unsigned char)*src])
936 apr_snprintf(dst, 4, "%%%02X", (unsigned char)*src);
937 dst += 3;
939 else
940 *(dst++) = *src;
941 break;
943 src++;
945 *dst = '\0';
948 return canon;
951 /* From libsvn_subr/dirent_uri.c. */
952 const char *
953 svn_uri_canonicalize(const char *uri, apr_pool_t *pool)
955 return canonicalize(type_uri, uri, pool);
958 /* From libsvn_subr/dirent_uri.c. */
959 const char *
960 svn_relpath_canonicalize(const char *relpath, apr_pool_t *pool)
962 return canonicalize(type_relpath, relpath, pool);
965 /* New code (public domain). */
966 svn_error_t *
967 svn_io_remove_file2(const char *path,
968 svn_boolean_t ignore_enoent,
969 apr_pool_t *scratch_pool)
971 svn_error_t *err = svn_io_remove_file(path, scratch_pool);
972 if (ignore_enoent && err && APR_STATUS_IS_ENOENT(err->apr_err))
974 svn_error_clear(err);
975 return SVN_NO_ERROR;
977 return err;
980 /* From libsvn_subr/cmdline.c. */
981 svn_error_t *
982 svn_cmdline__apply_config_options(apr_hash_t *config,
983 const apr_array_header_t *config_options,
984 const char *prefix,
985 const char *argument_name)
987 int i;
989 for (i = 0; i < config_options->nelts; i++)
991 svn_config_t *cfg;
992 svn_cmdline__config_argument_t *arg =
993 APR_ARRAY_IDX(config_options, i,
994 svn_cmdline__config_argument_t *);
996 cfg = apr_hash_get(config, arg->file, APR_HASH_KEY_STRING);
998 if (cfg)
1000 svn_config_set(cfg, arg->section, arg->option, arg->value);
1002 else
1004 svn_error_t *err = svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1005 _("Unrecognized file in argument of %s"), argument_name);
1007 svn_handle_warning2(stderr, err, prefix);
1008 svn_error_clear(err);
1012 return SVN_NO_ERROR;
1015 /* From libsvn_subr/cmdline.c. */
1016 svn_error_t *
1017 svn_cmdline__parse_config_option(apr_array_header_t *config_options,
1018 const char *opt_arg,
1019 apr_pool_t *pool)
1021 svn_cmdline__config_argument_t *config_option;
1022 const char *first_colon, *second_colon, *equals_sign;
1023 apr_size_t len = strlen(opt_arg);
1024 if ((first_colon = strchr(opt_arg, ':')) && (first_colon != opt_arg))
1026 if ((second_colon = strchr(first_colon + 1, ':')) &&
1027 (second_colon != first_colon + 1))
1029 if ((equals_sign = strchr(second_colon + 1, '=')) &&
1030 (equals_sign != second_colon + 1))
1032 config_option = apr_pcalloc(pool, sizeof(*config_option));
1033 config_option->file = apr_pstrndup(pool, opt_arg,
1034 first_colon - opt_arg);
1035 config_option->section = apr_pstrndup(pool, first_colon + 1,
1036 second_colon - first_colon - 1);
1037 config_option->option = apr_pstrndup(pool, second_colon + 1,
1038 equals_sign - second_colon -
1041 if (! (strchr(config_option->option, ':')))
1043 config_option->value = apr_pstrndup(pool, equals_sign + 1,
1044 opt_arg + len - equals_sign - 1);
1045 APR_ARRAY_PUSH(config_options, svn_cmdline__config_argument_t
1047 = config_option;
1048 return SVN_NO_ERROR;
1053 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
1054 _("Invalid syntax of argument of --config-option"));