| // Copyright 2017 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| package com.google.devtools.build.lib.vfs; |
| |
| import com.google.devtools.build.lib.util.OS; |
| |
| /** |
| * An interface class representing the differences in path style between different OSs. |
| * |
| * <p>Eg. case sensitivity, '/' mounts vs. 'C:/', etc. |
| */ |
| public interface OsPathPolicy { |
| int NORMALIZED = 0; // Path is normalized |
| int NEEDS_NORMALIZE = 1; // Path requires normalization |
| |
| /** Returns required normalization level, passed to {@link #normalize}. */ |
| int needsToNormalize(String path); |
| |
| /** |
| * Returns the required normalization level if an already normalized string is concatenated with |
| * another normalized path fragment. |
| * |
| * <p>This method may be faster than {@link #needsToNormalize(String)}. |
| */ |
| int needsToNormalizeSuffix(String normalizedSuffix); |
| |
| /** |
| * Normalizes the passed string according to the passed normalization level. |
| * |
| * @param normalizationLevel The normalizationLevel from {@link #needsToNormalize} |
| */ |
| String normalize(String path, int normalizationLevel); |
| |
| /** |
| * Returns the length of the mount, eg. 1 for unix '/', 3 for Windows 'C:/'. |
| * |
| * <p>If the path is relative, 0 is returned |
| */ |
| int getDriveStrLength(String path); |
| |
| /** Compares two path strings, using the given OS case sensitivity. */ |
| int compare(String s1, String s2); |
| |
| /** Compares two characters, using the given OS case sensitivity. */ |
| int compare(char c1, char c2); |
| |
| /** Tests two path strings for equality, using the given OS case sensitivity. */ |
| boolean equals(String s1, String s2); |
| |
| /** Computes the hash code for a path string. */ |
| int hash(String s); |
| |
| /** |
| * Returns whether the passed string starts with the given prefix, given the OS case sensitivity. |
| * |
| * <p>This is a pure string operation and doesn't need to worry about matching path segments. |
| */ |
| boolean startsWith(String path, String prefix); |
| |
| /** |
| * Returns whether the passed string ends with the given prefix, given the OS case sensitivity. |
| * |
| * <p>This is a pure string operation and doesn't need to worry about matching path segments. |
| */ |
| boolean endsWith(String path, String suffix); |
| |
| /** Returns the separator used for normalized paths. */ |
| char getSeparator(); |
| |
| /** Returns whether the unnormalized character c is a separator. */ |
| boolean isSeparator(char c); |
| |
| boolean isCaseSensitive(); |
| |
| static OsPathPolicy getFilePathOs() { |
| switch (OS.getCurrent()) { |
| case LINUX: |
| case FREEBSD: |
| case OPENBSD: |
| case UNKNOWN: |
| return UnixOsPathPolicy.INSTANCE; |
| case DARWIN: |
| // NOTE: We *should* return a path policy that is case insensitive, |
| // but we currently don't handle this |
| return UnixOsPathPolicy.INSTANCE; |
| case WINDOWS: |
| return WindowsOsPathPolicy.INSTANCE; |
| default: |
| throw new AssertionError("Not covering all OSs"); |
| } |
| } |
| |
| /** Utilities for implementations of {@link OsPathPolicy}. */ |
| class Utils { |
| /** |
| * Normalizes any '.' and '..' in-place in the segment array by shifting other segments to the |
| * front. Returns the remaining number of items. |
| */ |
| static int removeRelativePaths(String[] segments, int starti, boolean isAbsolute) { |
| int segmentCount = 0; |
| int shift = starti; |
| int n = segments.length; |
| for (int i = starti; i < n; ++i) { |
| String segment = segments[i]; |
| switch (segment) { |
| case ".": |
| ++shift; |
| break; |
| case "..": |
| if (segmentCount > 0 && !segments[segmentCount - 1].equals("..")) { |
| // Remove the last segment, if there is one and it is not "..". This |
| // means that the resulting path can still contain ".." |
| // segments at the beginning. |
| segmentCount--; |
| shift += 2; |
| break; |
| } else if (isAbsolute) { |
| // If this is absolute, then just pop it the ".." off and remain at root |
| ++shift; |
| break; |
| } |
| // Fall through |
| default: |
| ++segmentCount; |
| if (shift > 0) { |
| segments[i - shift] = segments[i]; |
| } |
| break; |
| } |
| } |
| return segmentCount; |
| } |
| } |
| } |