~ubuntu-branches/ubuntu/trusty/subversion/trusty-proposed

« back to all changes in this revision

Viewing changes to subversion/libsvn_subr/path.c

  • Committer: Package Import Robot
  • Author(s): Andy Whitcroft
  • Date: 2012-06-21 15:36:36 UTC
  • mfrom: (0.4.13 sid)
  • Revision ID: package-import@ubuntu.com-20120621153636-amqqmuidgwgxz1ly
Tags: 1.7.5-1ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - Create pot file on build.
  - Build a python-subversion-dbg package.
  - Build-depend on python-dbg.
  - Build-depend on default-jre-headless/-jdk.
  - Do not apply java-build patch.
  - debian/rules: Manually create the doxygen output directory, otherwise
    we get weird build failures when running parallel builds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 * paths.c:   a path manipulation library using svn_stringbuf_t
3
3
 *
4
4
 * ====================================================================
5
 
 * Copyright (c) 2000-2007, 2009 CollabNet.  All rights reserved.
6
 
 *
7
 
 * This software is licensed as described in the file COPYING, which
8
 
 * you should have received as part of this distribution.  The terms
9
 
 * are also available at http://subversion.tigris.org/license-1.html.
10
 
 * If newer versions of this license are posted there, you may use a
11
 
 * newer version instead, at your option.
12
 
 *
13
 
 * This software consists of voluntary contributions made by many
14
 
 * individuals.  For exact contribution history, see the revision
15
 
 * history and logs, available at http://subversion.tigris.org/.
 
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.
16
21
 * ====================================================================
17
22
 */
18
23
 
33
38
#include "svn_io.h"                     /* for svn_io_stat() */
34
39
#include "svn_ctype.h"
35
40
 
36
 
#include "private_uri.h"
 
41
#include "dirent_uri.h"
 
42
 
37
43
 
38
44
/* The canonical empty path.  Can this be changed?  Well, change the empty
39
45
   test below and the path library will work, not so sure about the fs/wc
49
55
#define SVN_PATH_IS_PLATFORM_EMPTY(s,n) ((n) == 1 && (s)[0] == '.')
50
56
 
51
57
 
52
 
const char *
53
 
svn_path_internal_style(const char *path, apr_pool_t *pool)
54
 
{
55
 
#if defined(WIN32) || defined(__CYGWIN__)
56
 
  if ((path[0] == '/' || path[0] == '\\') && path[1] == path[0])
57
 
    return svn_dirent_internal_style(path, pool);
58
 
#endif
59
 
  return svn_uri_internal_style(path, pool);
60
 
}
61
 
 
62
 
 
63
 
const char *
64
 
svn_path_local_style(const char *path, apr_pool_t *pool)
65
 
{
66
 
#if defined(WIN32) || defined(__CYGWIN__)
67
 
  if (path[0] == '/' && path[1] == '/')
68
 
    return svn_dirent_local_style(path, pool);
69
 
#endif
70
 
  return svn_uri_local_style(path, pool);
71
 
}
72
58
 
73
59
 
74
60
#ifndef NDEBUG
95
81
  return (! SVN_PATH_IS_PLATFORM_EMPTY(path, len)
96
82
          && strstr(path, "/./") == NULL
97
83
          && (len == 0
 
84
              || (len == 1 && path[0] == '/')
 
85
              || (path[len-1] != '/')
 
86
#if defined(WIN32) || defined(__CYGWIN__)
98
87
              || svn_dirent_is_root(path, len)
99
 
              /* The len > 0 check is redundant, but here to make
100
 
               * sure we never ever end up indexing with -1. */
101
 
              || (len > 0 && path[len-1] != '/')));
 
88
#endif
 
89
              ));
102
90
}
103
91
#endif
104
92
 
391
379
  return apr_pstrmemdup(pool, path + start, len - start);
392
380
}
393
381
 
394
 
 
395
 
void
396
 
svn_path_split(const char *path,
397
 
               const char **dirpath,
398
 
               const char **base_name,
399
 
               apr_pool_t *pool)
400
 
{
401
 
  assert(dirpath != base_name);
402
 
 
403
 
  if (dirpath)
404
 
    *dirpath = svn_path_dirname(path, pool);
405
 
 
406
 
  if (base_name)
407
 
    *base_name = svn_path_basename(path, pool);
408
 
}
409
 
 
410
 
 
411
382
int
412
383
svn_path_is_empty(const char *path)
413
384
{
457
428
  return (unsigned char)(path1[i]) < (unsigned char)(path2[i]) ? -1 : 1;
458
429
}
459
430
 
 
431
/* Return the string length of the longest common ancestor of PATH1 and PATH2.
 
432
 *
 
433
 * This function handles everything except the URL-handling logic
 
434
 * of svn_path_get_longest_ancestor, and assumes that PATH1 and
 
435
 * PATH2 are *not* URLs.
 
436
 *
 
437
 * If the two paths do not share a common ancestor, return 0.
 
438
 *
 
439
 * New strings are allocated in POOL.
 
440
 */
 
441
static apr_size_t
 
442
get_path_ancestor_length(const char *path1,
 
443
                         const char *path2,
 
444
                         apr_pool_t *pool)
 
445
{
 
446
  apr_size_t path1_len, path2_len;
 
447
  apr_size_t i = 0;
 
448
  apr_size_t last_dirsep = 0;
 
449
 
 
450
  path1_len = strlen(path1);
 
451
  path2_len = strlen(path2);
 
452
 
 
453
  if (SVN_PATH_IS_EMPTY(path1) || SVN_PATH_IS_EMPTY(path2))
 
454
    return 0;
 
455
 
 
456
  while (path1[i] == path2[i])
 
457
    {
 
458
      /* Keep track of the last directory separator we hit. */
 
459
      if (path1[i] == '/')
 
460
        last_dirsep = i;
 
461
 
 
462
      i++;
 
463
 
 
464
      /* If we get to the end of either path, break out. */
 
465
      if ((i == path1_len) || (i == path2_len))
 
466
        break;
 
467
    }
 
468
 
 
469
  /* two special cases:
 
470
     1. '/' is the longest common ancestor of '/' and '/foo'
 
471
     2. '/' is the longest common ancestor of '/rif' and '/raf' */
 
472
  if (i == 1 && path1[0] == '/' && path2[0] == '/')
 
473
    return 1;
 
474
 
 
475
  /* last_dirsep is now the offset of the last directory separator we
 
476
     crossed before reaching a non-matching byte.  i is the offset of
 
477
     that non-matching byte. */
 
478
  if (((i == path1_len) && (path2[i] == '/'))
 
479
           || ((i == path2_len) && (path1[i] == '/'))
 
480
           || ((i == path1_len) && (i == path2_len)))
 
481
    return i;
 
482
  else
 
483
    if (last_dirsep == 0 && path1[0] == '/' && path2[0] == '/')
 
484
      return 1;
 
485
    return last_dirsep;
 
486
}
 
487
 
 
488
 
460
489
char *
461
490
svn_path_get_longest_ancestor(const char *path1,
462
491
                              const char *path2,
463
492
                              apr_pool_t *pool)
464
493
{
465
 
  return svn_uri_get_longest_ancestor(path1, path2, pool);
 
494
  svn_boolean_t path1_is_url = svn_path_is_url(path1);
 
495
  svn_boolean_t path2_is_url = svn_path_is_url(path2);
 
496
 
 
497
  /* Are we messing with URLs?  If we have a mix of URLs and non-URLs,
 
498
     there's nothing common between them.  */
 
499
  if (path1_is_url && path2_is_url)
 
500
    {
 
501
      return svn_uri_get_longest_ancestor(path1, path2, pool);
 
502
    }
 
503
  else if ((! path1_is_url) && (! path2_is_url))
 
504
    {
 
505
      return apr_pstrndup(pool, path1,
 
506
                          get_path_ancestor_length(path1, path2, pool));
 
507
    }
 
508
  else
 
509
    {
 
510
      /* A URL and a non-URL => no common prefix */
 
511
      return apr_pmemdup(pool, SVN_EMPTY_PATH, sizeof(SVN_EMPTY_PATH));
 
512
    }
466
513
}
467
514
 
468
515
const char *
470
517
                  const char *path2,
471
518
                  apr_pool_t *pool)
472
519
{
473
 
  return svn_uri_is_child(path1, path2, pool);
 
520
  apr_size_t i;
 
521
 
 
522
  /* assert (is_canonical (path1, strlen (path1)));  ### Expensive strlen */
 
523
  /* assert (is_canonical (path2, strlen (path2)));  ### Expensive strlen */
 
524
 
 
525
  /* Allow "" and "foo" to be parent/child */
 
526
  if (SVN_PATH_IS_EMPTY(path1))               /* "" is the parent  */
 
527
    {
 
528
      if (SVN_PATH_IS_EMPTY(path2)            /* "" not a child    */
 
529
          || path2[0] == '/')                  /* "/foo" not a child */
 
530
        return NULL;
 
531
      else
 
532
        /* everything else is child */
 
533
        return pool ? apr_pstrdup(pool, path2) : path2;
 
534
    }
 
535
 
 
536
  /* Reach the end of at least one of the paths.  How should we handle
 
537
     things like path1:"foo///bar" and path2:"foo/bar/baz"?  It doesn't
 
538
     appear to arise in the current Subversion code, it's not clear to me
 
539
     if they should be parent/child or not. */
 
540
  for (i = 0; path1[i] && path2[i]; i++)
 
541
    if (path1[i] != path2[i])
 
542
      return NULL;
 
543
 
 
544
  /* There are two cases that are parent/child
 
545
          ...      path1[i] == '\0'
 
546
          .../foo  path2[i] == '/'
 
547
      or
 
548
          /        path1[i] == '\0'
 
549
          /foo     path2[i] != '/'
 
550
  */
 
551
  if (path1[i] == '\0' && path2[i])
 
552
    {
 
553
      if (path2[i] == '/')
 
554
        return pool ? apr_pstrdup(pool, path2 + i + 1) : path2 + i + 1;
 
555
      else if (i == 1 && path1[0] == '/')
 
556
        return pool ? apr_pstrdup(pool, path2 + 1) : path2 + 1;
 
557
    }
 
558
 
 
559
  /* Otherwise, path2 isn't a child. */
 
560
  return NULL;
474
561
}
475
562
 
476
563
 
477
564
svn_boolean_t
478
565
svn_path_is_ancestor(const char *path1, const char *path2)
479
566
{
480
 
  return svn_uri_is_ancestor(path1, path2);
 
567
  apr_size_t path1_len = strlen(path1);
 
568
 
 
569
  /* If path1 is empty and path2 is not absoulte, then path1 is an ancestor. */
 
570
  if (SVN_PATH_IS_EMPTY(path1))
 
571
    return *path2 != '/';
 
572
 
 
573
  /* If path1 is a prefix of path2, then:
 
574
     - If path1 ends in a path separator,
 
575
     - If the paths are of the same length
 
576
     OR
 
577
     - path2 starts a new path component after the common prefix,
 
578
     then path1 is an ancestor. */
 
579
  if (strncmp(path1, path2, path1_len) == 0)
 
580
    return path1[path1_len - 1] == '/'
 
581
      || (path2[path1_len] == '/' || path2[path1_len] == '\0');
 
582
 
 
583
  return FALSE;
481
584
}
482
585
 
483
586
 
601
704
svn_boolean_t
602
705
svn_path_is_dotpath_present(const char *path)
603
706
{
604
 
  int len;
 
707
  size_t len;
605
708
 
606
709
  /* The empty string does not have a dotpath */
607
710
  if (path[0] == '\0')
627
730
svn_boolean_t
628
731
svn_path_is_backpath_present(const char *path)
629
732
{
630
 
  int len;
 
733
  size_t len;
631
734
 
632
735
  /* 0 and 1-length paths do not have a backpath */
633
736
  if (path[0] == '\0' || path[1] == '\0')
704
807
 
705
808
      alphanum | mark | ":" | "@" | "&" | "=" | "+" | "$" | ","
706
809
*/
707
 
static const char uri_char_validity[256] = {
 
810
const char svn_uri__char_validity[256] = {
708
811
  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
709
812
  0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0,
710
813
  0, 1, 0, 0, 1, 0, 1, 1,   1, 1, 1, 1, 1, 1, 1, 1,
756
859
      /* Allow '%XX' (where each X is a hex digit) */
757
860
      if (path[i] == '%')
758
861
        {
759
 
          if (apr_isxdigit(path[i + 1]) && apr_isxdigit(path[i + 2]))
 
862
          if (svn_ctype_isxdigit(path[i + 1]) &&
 
863
              svn_ctype_isxdigit(path[i + 2]))
760
864
            {
761
865
              i += 2;
762
866
              continue;
763
867
            }
764
868
          return FALSE;
765
869
        }
766
 
      else if (! uri_char_validity[((unsigned char)path[i])])
 
870
      else if (! svn_uri__char_validity[((unsigned char)path[i])])
767
871
        {
768
872
          return FALSE;
769
873
        }
800
904
        svn_stringbuf_appendbytes(retstr, path + copied,
801
905
                                  i - copied);
802
906
 
803
 
      /* Now, sprintf() in our escaped character, making sure our
804
 
         buffer is big enough to hold the '%' and two digits.  We cast
805
 
         the C to unsigned char here because the 'X' format character
806
 
         will be tempted to treat it as an unsigned int...which causes
807
 
         problem when messing with 0x80-0xFF chars.  We also need space
808
 
         for a null as sprintf will write one. */
 
907
      /* Now, write in our escaped character, consisting of the
 
908
         '%' and two digits.  We cast the C to unsigned char here because
 
909
         the 'X' format character will be tempted to treat it as an unsigned
 
910
         int...which causes problem when messing with 0x80-0xFF chars.
 
911
         We also need space for a null as apr_snprintf will write one. */
809
912
      svn_stringbuf_ensure(retstr, retstr->len + 4);
810
 
      sprintf(retstr->data + retstr->len, "%%%02X", (unsigned char)c);
 
913
      apr_snprintf(retstr->data + retstr->len, 4, "%%%02X", (unsigned char)c);
811
914
      retstr->len += 3;
812
915
 
813
916
      /* Finally, update our copy counter. */
822
925
  if (i - copied)
823
926
    svn_stringbuf_appendbytes(retstr, path + copied, i - copied);
824
927
 
825
 
  /* retstr is null-terminated either by sprintf or the svn_stringbuf
 
928
  /* retstr is null-terminated either by apr_snprintf or the svn_stringbuf
826
929
     functions. */
827
930
 
828
931
  return retstr->data;
834
937
{
835
938
  const char *ret;
836
939
 
837
 
  ret = uri_escape(path, uri_char_validity, pool);
 
940
  ret = uri_escape(path, svn_uri__char_validity, pool);
838
941
 
839
942
  /* Our interface guarantees a copy. */
840
943
  if (ret == path)
927
1030
           * RFC 2396, section 3.3  */
928
1031
          c = ' ';
929
1032
        }
930
 
      else if (c == '%' && apr_isxdigit(path[i + 1])
931
 
               && apr_isxdigit(path[i+2]))
 
1033
      else if (c == '%' && svn_ctype_isxdigit(path[i + 1])
 
1034
               && svn_ctype_isxdigit(path[i+2]))
932
1035
        {
933
1036
          char digitz[3];
934
1037
          digitz[0] = path[++i];
952
1055
                            const char *component,
953
1056
                            apr_pool_t *pool)
954
1057
{
955
 
  assert(svn_path_is_canonical(url, pool));
 
1058
  /* = svn_path_uri_encode() but without always copying */
 
1059
  component = uri_escape(component, svn_uri__char_validity, pool);
956
1060
 
957
 
  return svn_path_join(url, svn_path_uri_encode(component, pool), pool);
 
1061
  return svn_path_join(url, component, pool);
958
1062
}
959
1063
 
960
1064
svn_error_t *
970
1074
 
971
1075
  return svn_dirent_get_absolute(pabsolute, relative, pool);
972
1076
}
973
 
 
974
 
 
975
 
svn_error_t *
976
 
svn_path_split_if_file(const char *path,
977
 
                       const char **pdirectory,
978
 
                       const char **pfile,
979
 
                       apr_pool_t *pool)
980
 
{
981
 
  apr_finfo_t finfo;
982
 
  svn_error_t *err;
983
 
 
984
 
  SVN_ERR_ASSERT(svn_path_is_canonical(path, pool));
985
 
 
986
 
  err = svn_io_stat(&finfo, path, APR_FINFO_TYPE, pool);
987
 
  if (err && ! APR_STATUS_IS_ENOENT(err->apr_err))
988
 
    return err;
989
 
 
990
 
  if (err || finfo.filetype == APR_REG)
991
 
    {
992
 
      svn_error_clear(err);
993
 
      svn_path_split(path, pdirectory, pfile, pool);
994
 
    }
995
 
  else if (finfo.filetype == APR_DIR)
996
 
    {
997
 
      *pdirectory = path;
998
 
      *pfile = SVN_EMPTY_PATH;
999
 
    }
1000
 
  else
1001
 
    {
1002
 
      return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
1003
 
                               _("'%s' is neither a file nor a directory name"),
1004
 
                               svn_path_local_style(path, pool));
1005
 
    }
1006
 
 
1007
 
  return SVN_NO_ERROR;
1008
 
}
1009
 
 
1010
1077
 
1011
 
const char *
1012
 
svn_path_canonicalize(const char *path, apr_pool_t *pool)
1013
 
{
1014
 
#if defined(WIN32) || defined(__CYGWIN__)
1015
 
  if (path[0] == '/' && path[1] == '/')
1016
 
    return svn_dirent_canonicalize(path, pool);
1017
 
#endif
1018
 
  return svn_uri_canonicalize(path, pool);
1019
 
}
1020
 
 
1021
 
svn_boolean_t
1022
 
svn_path_is_canonical(const char *path, apr_pool_t *pool)
1023
 
{
1024
 
  return svn_uri_is_canonical(path, pool)
1025
 
#if defined(WIN32) || defined(__CYGWIN__)
1026
 
         || (path[0] == '/' && path[1] == '/' &&
1027
 
             svn_dirent_is_canonical(path, pool))
1028
 
#endif
1029
 
         ;
1030
 
}
1031
1078
 
1032
1079
 
1033
1080
/** Get APR's internal path encoding. */
1118
1165
      /*### The backslash separator doesn't work too great with Windows,
1119
1166
         but it's what we'll use for consistency with invalid utf8
1120
1167
         formatting (until someone has a better idea) */
1121
 
      sprintf(retstr->data + retstr->len, "\\%03o", (unsigned char)c);
 
1168
      apr_snprintf(retstr->data + retstr->len, 5, "\\%03o", (unsigned char)c);
1122
1169
      retstr->len += 4;
1123
1170
 
1124
1171
      /* Finally, update our copy counter. */
1133
1180
  if (i - copied)
1134
1181
    svn_stringbuf_appendbytes(retstr, path + copied, i - copied);
1135
1182
 
1136
 
  /* retstr is null-terminated either by sprintf or the svn_stringbuf
 
1183
  /* retstr is null-terminated either by apr_snprintf or the svn_stringbuf
1137
1184
     functions. */
1138
1185
 
1139
1186
  return retstr->data;
1152
1199
            (SVN_ERR_FS_PATH_SYNTAX, NULL,
1153
1200
             _("Invalid control character '0x%02x' in path '%s'"),
1154
1201
             (unsigned char)*c,
1155
 
             illegal_path_escape(svn_path_local_style(path, pool), pool));
 
1202
             illegal_path_escape(svn_dirent_local_style(path, pool), pool));
1156
1203
        }
1157
1204
    }
1158
1205