| 151 | } |
| 152 | |
| 153 | void PathUtils::concatPath(PathName& result, |
| 154 | const PathName& first, |
| 155 | const PathName& second) |
| 156 | { |
| 157 | if (first.length() == 0) |
| 158 | { |
| 159 | result = second; |
| 160 | return; |
| 161 | } |
| 162 | |
| 163 | result = first; |
| 164 | |
| 165 | // First path used to be from trusted sources like getRootDirectory, etc. |
| 166 | // Second path is mostly user-entered and must be carefully parsed to avoid hacking |
| 167 | if (second.isEmpty()) |
| 168 | { |
| 169 | return; |
| 170 | } |
| 171 | |
| 172 | ensureSeparator(result); |
| 173 | |
| 174 | PathName::size_type cur_pos = 0; |
| 175 | |
| 176 | for (PathName::size_type pos = 0; cur_pos < second.length(); cur_pos = pos + 1) |
| 177 | { |
| 178 | pos = second.find(dir_sep, cur_pos); |
| 179 | |
| 180 | if (pos == PathName::npos) // simple name, simple handling |
| 181 | pos = second.length(); |
| 182 | |
| 183 | if (pos == cur_pos) // Empty piece, ignore |
| 184 | continue; |
| 185 | |
| 186 | if (pos == cur_pos + curr_dir_link_len && |
| 187 | memcmp(second.c_str() + cur_pos, curr_dir_link, curr_dir_link_len) == 0) // Current dir, ignore |
| 188 | { |
| 189 | continue; |
| 190 | } |
| 191 | |
| 192 | if (pos == cur_pos + up_dir_link_len && |
| 193 | memcmp(second.c_str() + cur_pos, up_dir_link, up_dir_link_len) == 0) // One dir up |
| 194 | { |
| 195 | if (result.length() < 2) |
| 196 | { |
| 197 | // We have nothing to cut off, ignore this piece (may be throw an error?..) |
| 198 | continue; |
| 199 | } |
| 200 | |
| 201 | const PathName::size_type up_dir = result.rfind(dir_sep, result.length() - 2); |
| 202 | if (up_dir == PathName::npos) |
| 203 | continue; |
| 204 | |
| 205 | result.erase(up_dir + 1); |
| 206 | continue; |
| 207 | } |
| 208 | |
| 209 | result.append(second, cur_pos, pos - cur_pos + 1); // append the piece including separator |
| 210 | } |