blob: 6b0380f8c8d994f89926761ed1c6d46fa83c696a [file] [log] [blame]
// 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 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;
}
}
}