Branch data Line data Source code
1 : : /** @file fileutils.cc
2 : : * @brief File and path manipulation routines.
3 : : */
4 : : /* Copyright (C) 2008 Lemur Consulting Ltd
5 : : * Copyright (C) 2008,2009,2010 Olly Betts
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program; if not, write to the Free Software
19 : : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 : : */
21 : :
22 : : #include <config.h>
23 : :
24 : : #include "fileutils.h"
25 : :
26 : : #include <cstring>
27 : : #include <string>
28 : :
29 : : using namespace std;
30 : :
31 : : #ifdef __WIN32__
32 : : /// Return true iff a path starts with a drive letter.
33 : : static bool
34 : : has_drive(const string &path)
35 : : {
36 : : return (path.size() >= 2 && path[1] == ':');
37 : : }
38 : :
39 : : /// Return true iff path is a UNCW path.
40 : : static bool
41 : : uncw_path(const string & path)
42 : : {
43 : : return (path.size() >= 4 && memcmp(path.data(), "\\\\?\\", 4) == 0);
44 : : }
45 : :
46 : : inline bool slash(char ch)
47 : : {
48 : : return ch == '/' || ch == '\\';
49 : : }
50 : : #endif
51 : :
52 : : void
53 : 1126 : resolve_relative_path(string & path, const string & base)
54 : : {
55 : : #ifndef __WIN32__
56 [ + - ][ + - ]: 1126 : if (path.empty() || path[0] != '/') {
[ + - ]
57 : : // path is relative.
58 : 1126 : string::size_type last_slash = base.rfind('/');
59 [ + - ]: 1126 : if (last_slash != string::npos)
60 : 1126 : path.insert(0, base, 0, last_slash + 1);
61 : : }
62 : : #else
63 : : // Microsoft Windows paths may begin with a drive letter but still be
64 : : // relative within that drive.
65 : : bool drive = has_drive(path);
66 : : string::size_type p = (drive ? 2 : 0);
67 : : bool absolute = (p != path.size() && slash(path[p]));
68 : :
69 : : if (absolute) {
70 : : // If path is absolute and has a drive specifier, just return it.
71 : : if (drive)
72 : : return;
73 : :
74 : : // If base has a drive specifier prepend that to path.
75 : : if (has_drive(base)) {
76 : : path.insert(0, base, 0, 2);
77 : : return;
78 : : }
79 : :
80 : : // If base has a UNC (\\SERVER\\VOLUME) or \\?\ prefix, prepend that
81 : : // to path.
82 : : if (uncw_path(base)) {
83 : : string::size_type sl = 0;
84 : : if (base.size() >= 7 && memcmp(base.data() + 5, ":\\", 2) == 0) {
85 : : // "\\?\X:\"
86 : : sl = 6;
87 : : } else if (base.size() >= 8 &&
88 : : memcmp(base.data() + 4, "UNC\\", 4) == 0) {
89 : : // "\\?\UNC\server\volume\"
90 : : sl = base.find('\\', 8);
91 : : if (sl != string::npos)
92 : : sl = base.find('\\', sl + 1);
93 : : }
94 : : if (sl) {
95 : : // With the \\?\ prefix, '/' isn't recognised so change it
96 : : // to '\' in path.
97 : : string::iterator i;
98 : : for (i = path.begin(); i != path.end(); ++i) {
99 : : if (*i == '/')
100 : : *i = '\\';
101 : : }
102 : : path.insert(0, base, 0, sl);
103 : : }
104 : : } else if (base.size() >= 5 && slash(base[0]) && slash(base[1])) {
105 : : // Handle UNC base.
106 : : string::size_type sl = base.find_first_of("/\\", 2);
107 : : if (sl != string::npos) {
108 : : sl = base.find_first_of("/\\", sl + 1);
109 : : path.insert(0, base, 0, sl);
110 : : }
111 : : }
112 : : return;
113 : : }
114 : :
115 : : // path is relative, so if it has no drive specifier or the same drive
116 : : // specifier as base, then we want to qualify it using base.
117 : : bool base_drive = has_drive(base);
118 : : if (!drive || (base_drive && (path[0] | 32) == (base[0] | 32))) {
119 : : string::size_type last_slash = base.find_last_of("/\\");
120 : : if (last_slash == string::npos && !drive && base_drive)
121 : : last_slash = 1;
122 : : if (last_slash != string::npos) {
123 : : string::size_type b = (drive && base_drive ? 2 : 0);
124 : : if (uncw_path(base)) {
125 : : // With the \\?\ prefix, '/' isn't recognised so change it
126 : : // to '\' in path.
127 : : string::iterator i;
128 : : for (i = path.begin(); i != path.end(); ++i) {
129 : : if (*i == '/')
130 : : *i = '\\';
131 : : }
132 : : }
133 : : path.insert(b, base, b, last_slash + 1 - b);
134 : : }
135 : : }
136 : : #endif
137 : 1126 : }
|