in core/src/main/java/hudson/FilePath.java [320:375]
public static String normalize(@NonNull String path) {
StringBuilder buf = new StringBuilder();
// Check for prefix designating absolute path
Matcher m = ABSOLUTE_PREFIX_PATTERN.matcher(path);
if (m.find()) {
buf.append(m.group(1));
path = path.substring(m.end());
}
boolean isAbsolute = buf.length() > 0;
// Split remaining path into tokens, trimming any duplicate or trailing separators
List<String> tokens = new ArrayList<>();
int s = 0, end = path.length();
for (int i = 0; i < end; i++) {
char c = path.charAt(i);
if (c == '/' || c == '\\') {
tokens.add(path.substring(s, i));
s = i;
// Skip any extra separator chars
//noinspection StatementWithEmptyBody
while (++i < end && ((c = path.charAt(i)) == '/' || c == '\\'))
;
// Add token for separator unless we reached the end
if (i < end) tokens.add(path.substring(s, s+1));
s = i;
}
}
if (s < end) tokens.add(path.substring(s));
// Look through tokens for "." or ".."
for (int i = 0; i < tokens.size();) {
String token = tokens.get(i);
if (token.equals(".")) {
tokens.remove(i);
if (tokens.size() > 0)
tokens.remove(i > 0 ? i - 1 : i);
} else if (token.equals("..")) {
if (i == 0) {
// If absolute path, just remove: /../something
// If relative path, not collapsible so leave as-is
tokens.remove(0);
if (tokens.size() > 0) token += tokens.remove(0);
if (!isAbsolute) buf.append(token);
} else {
// Normalize: remove something/.. plus separator before/after
i -= 2;
for (int j = 0; j < 3; j++) tokens.remove(i);
if (i > 0) tokens.remove(i-1);
else if (tokens.size() > 0) tokens.remove(0);
}
} else
i += 2;
}
// Recombine tokens
for (String token : tokens) buf.append(token);
if (buf.length() == 0) buf.append('.');
return buf.toString();
}