blob: 998bca05506d4eddaa0b4e53709b8e2f840afafa [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.maven.wagon;
import java.io.File;
import java.util.StringTokenizer;
/**
* Various path (URL) manipulation routines
*
* @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
*
*/
public final class PathUtils {
private PathUtils() {}
/**
* Returns the directory path portion of a file specification string.
* Matches the equally named unix command.
*
* @return The directory portion excluding the ending file separator.
*/
public static String dirname(final String path) {
final int i = path.lastIndexOf("/");
return ((i >= 0) ? path.substring(0, i) : "");
}
/**
* Returns the filename portion of a file specification string.
*
* @return The filename string with extension.
*/
public static String filename(final String path) {
final int i = path.lastIndexOf("/");
return ((i >= 0) ? path.substring(i + 1) : path);
}
public static String[] dirnames(final String path) {
final String dirname = PathUtils.dirname(path);
return split(dirname, "/", -1);
}
private static String[] split(final String str, final String separator, final int max) {
final StringTokenizer tok;
if (separator == null) {
// Null separator means we're using StringTokenizer's default
// delimiter, which comprises all whitespace characters.
tok = new StringTokenizer(str);
} else {
tok = new StringTokenizer(str, separator);
}
int listSize = tok.countTokens();
if (max > 0 && listSize > max) {
listSize = max;
}
final String[] list = new String[listSize];
int i = 0;
int lastTokenBegin;
int lastTokenEnd = 0;
while (tok.hasMoreTokens()) {
if (max > 0 && i == listSize - 1) {
// In the situation where we hit the max yet have
// tokens left over in our input, the last list
// element gets all remaining text.
final String endToken = tok.nextToken();
lastTokenBegin = str.indexOf(endToken, lastTokenEnd);
list[i] = str.substring(lastTokenBegin);
break;
} else {
list[i] = tok.nextToken();
lastTokenBegin = str.indexOf(list[i], lastTokenEnd);
lastTokenEnd = lastTokenBegin + list[i].length();
}
i++;
}
return list;
}
/**
* Return the host name (Removes protocol and path from the URL) E.g: for input
* <code>http://www.codehause.org</code> this method will return <code>www.apache.org</code>
*
* @param url the url
* @return the host name
*/
public static String host(final String url) {
if (url == null || url.length() == 0) {
return "localhost";
}
String authorization = authorization(url);
int index = authorization.indexOf('@');
String host = (index >= 0) ? authorization.substring(index + 1) : authorization;
// In case we have IPv6 in the host portion of url
// we have to remove brackets '[' and ']'
return ((host.charAt(0) == '[') && (host.charAt(host.length() - 1) == ']'))
? host.substring(1, host.length() - 1)
: host;
}
/**
* This was changed from private to package local so that it can be unit tested.
*/
static String authorization(final String url) {
if (url == null) {
return "localhost";
}
String protocol = PathUtils.protocol(url);
if (protocol == null || protocol.equalsIgnoreCase("file")) {
return "localhost";
}
String host = url;
if (protocol.equalsIgnoreCase("scm")) {
// skip over type
host = host.substring(host.indexOf(":", 4) + 1).trim();
}
protocol = PathUtils.protocol(host);
if (protocol.equalsIgnoreCase("file")) {
return "localhost";
}
// skip over protocol
host = host.substring(host.indexOf(":") + 1).trim();
if (host.startsWith("//")) {
host = host.substring(2);
}
int pos = host.indexOf("/");
if (pos > 0) {
host = host.substring(0, pos);
}
pos = host.indexOf('@');
pos = (pos > 0) ? endOfHostPosition(host, pos) : endOfHostPosition(host, 0);
if (pos > 0) {
host = host.substring(0, pos);
}
return host;
}
private static int endOfHostPosition(String host, int pos) {
// if this is IPv6 then it will be in IPv6 Literal Addresses in URL's format
// see: http://www.ietf.org/rfc/rfc2732.txt
int endOfIPv6Pos = host.indexOf(']', pos);
return (endOfIPv6Pos > 0) ? endOfIPv6Pos + 1 : host.indexOf(":", pos);
}
/**
* /**
* Return the protocol name.
* <br/>
* E.g: for input
* <code>http://www.codehause.org</code> this method will return <code>http</code>
*
* @param url the url
* @return the host name
*/
public static String protocol(final String url) {
final int pos = url.indexOf(":");
if (pos == -1) {
return "";
}
return url.substring(0, pos).trim();
}
/**
* @param url
* @return the port or {@link WagonConstants#UNKNOWN_PORT} if not existent
*/
public static int port(String url) {
final String protocol = PathUtils.protocol(url);
if (protocol == null || protocol.equalsIgnoreCase("file")) {
return WagonConstants.UNKNOWN_PORT;
}
final String authorization = PathUtils.authorization(url);
if (authorization == null) {
return WagonConstants.UNKNOWN_PORT;
}
if (protocol.equalsIgnoreCase("scm")) {
// skip over type
url = url.substring(url.indexOf(":", 4) + 1).trim();
}
if (url.regionMatches(true, 0, "file:", 0, 5) || url.regionMatches(true, 0, "local:", 0, 6)) {
return WagonConstants.UNKNOWN_PORT;
}
// skip over protocol
url = url.substring(url.indexOf(":") + 1).trim();
if (url.startsWith("//")) {
url = url.substring(2);
}
int start = authorization.length();
if (url.length() > start && url.charAt(start) == ':') {
int end = url.indexOf('/', start);
if (end == start + 1) {
// it is :/
return WagonConstants.UNKNOWN_PORT;
}
if (end == -1) {
end = url.length();
}
return Integer.parseInt(url.substring(start + 1, end));
} else {
return WagonConstants.UNKNOWN_PORT;
}
}
/**
* Derive the path portion of the given URL.
*
* @param url the repository URL
* @return the basedir of the repository
* @todo need to URL decode for spaces?
*/
public static String basedir(String url) {
String protocol = PathUtils.protocol(url);
String retValue = null;
if (protocol.equalsIgnoreCase("scm")) {
// skip over SCM bits
if (url.regionMatches(true, 0, "scm:svn:", 0, 8)) {
url = url.substring(url.indexOf(":", 4) + 1);
protocol = PathUtils.protocol(url);
}
}
if (protocol.equalsIgnoreCase("file")) {
retValue = url.substring(protocol.length() + 1);
retValue = decode(retValue);
// special case: if omitted // on protocol, keep path as is
if (retValue.startsWith("//")) {
retValue = retValue.substring(2);
if (retValue.length() >= 2 && (retValue.charAt(1) == '|' || retValue.charAt(1) == ':')) {
// special case: if there is a windows drive letter, then keep the original return value
retValue = retValue.charAt(0) + ":" + retValue.substring(2);
} else {
// Now we expect the host
int index = retValue.indexOf("/");
if (index >= 0) {
retValue = retValue.substring(index + 1);
}
// special case: if there is a windows drive letter, then keep the original return value
if (retValue.length() >= 2 && (retValue.charAt(1) == '|' || retValue.charAt(1) == ':')) {
retValue = retValue.charAt(0) + ":" + retValue.substring(2);
} else if (index >= 0) {
// leading / was previously stripped
retValue = "/" + retValue;
}
}
}
// special case: if there is a windows drive letter using |, switch to :
if (retValue.length() >= 2 && retValue.charAt(1) == '|') {
retValue = retValue.charAt(0) + ":" + retValue.substring(2);
}
} else {
final String authorization = PathUtils.authorization(url);
final int port = PathUtils.port(url);
int pos = 0;
if (protocol.equalsIgnoreCase("scm")) {
pos = url.indexOf(":", 4) + 1;
pos = url.indexOf(":", pos) + 1;
} else {
int index = url.indexOf("://");
if (index != -1) {
pos = index + 3;
}
}
pos += authorization.length();
if (port != WagonConstants.UNKNOWN_PORT) {
pos = pos + Integer.toString(port).length() + 1;
}
if (url.length() > pos) {
retValue = url.substring(pos);
if (retValue.startsWith(":")) {
// this is for :/ after the host
retValue = retValue.substring(1);
}
// one module may be allowed in the path in CVS
retValue = retValue.replace(':', '/');
}
}
if (retValue == null) {
retValue = "/";
}
return retValue.trim();
}
/**
* Decodes the specified (portion of a) URL. <strong>Note:</strong> This decoder assumes that ISO-8859-1 is used to
* convert URL-encoded octets to characters.
*
* @param url The URL to decode, may be <code>null</code>.
* @return The decoded URL or <code>null</code> if the input was <code>null</code>.
*/
private static String decode(String url) {
String decoded = url;
if (url != null) {
int pos = -1;
while ((pos = decoded.indexOf('%', pos + 1)) >= 0) {
if (pos + 2 < decoded.length()) {
String hexStr = decoded.substring(pos + 1, pos + 3);
char ch = (char) Integer.parseInt(hexStr, 16);
decoded = decoded.substring(0, pos) + ch + decoded.substring(pos + 3);
}
}
}
return decoded;
}
public static String user(String url) {
String host = authorization(url);
int index = host.indexOf('@');
if (index > 0) {
String userInfo = host.substring(0, index);
index = userInfo.indexOf(':');
if (index > 0) {
return userInfo.substring(0, index);
} else if (index < 0) {
return userInfo;
}
}
return null;
}
public static String password(String url) {
String host = authorization(url);
int index = host.indexOf('@');
if (index > 0) {
String userInfo = host.substring(0, index);
index = userInfo.indexOf(':');
if (index >= 0) {
return userInfo.substring(index + 1);
}
}
return null;
}
// TODO: move to plexus-utils or use something appropriate from there
public static String toRelative(File basedir, String absolutePath) {
String relative;
absolutePath = absolutePath.replace('\\', '/');
String basedirPath = basedir.getAbsolutePath().replace('\\', '/');
if (absolutePath.startsWith(basedirPath)) {
relative = absolutePath.substring(basedirPath.length());
if (relative.startsWith("/")) {
relative = relative.substring(1);
}
if (relative.length() <= 0) {
relative = ".";
}
} else {
relative = absolutePath;
}
return relative;
}
}