/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.cli;
import org.tmatesoft.svn.cli.svn.SVNCommandEnvironment;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.util.SVNFormatUtil;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.ISVNReturnValueCallback;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.util.SVNLogType;
import org.tmatesoft.svn.util.Version;
import java.io.*;
import java.text.MessageFormat;
import java.util.*;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class SVNCommandUtil {
public static String getLocalPath(String path) {
path = path.replace('/', File.separatorChar);
if ("".equals(path)) {
path = ".";
}
return path;
}
public static boolean isURL(String pathOrUrl){
return SVNPathUtil.isURL(pathOrUrl);
}
public static void mergeFileExternally(AbstractSVNCommandEnvironment env, String basePath, String repositoryPath,
String localPath, String mergeResultPath, String wcPath, final boolean[] remainsInConflict) throws SVNException {
String[] testEnvironment = SVNFileUtil.getTestEnvironment();
String mergeToolCommand = testEnvironment[1];
if (testEnvironment[1] == null) {
mergeToolCommand = SVNFileUtil.getEnvironmentVariable("SVN_MERGE");
if (mergeToolCommand == null) {
mergeToolCommand = env.getOptions().getMergeTool();
}
testEnvironment = null;
} else {
mergeToolCommand = testEnvironment[1];
testEnvironment = new String[] {"SVNTEST_EDITOR_FUNC=" + (testEnvironment[2] == null ? "" : testEnvironment[2])};
}
if (mergeToolCommand != null) {
mergeToolCommand = mergeToolCommand.trim();
if (mergeToolCommand.length() == 0) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_NO_EXTERNAL_MERGE_TOOL,
"The SVN_MERGE environment variable is empty or consists solely of whitespace. Expected a shell command.");
SVNErrorManager.error(err, SVNLogType.CLIENT);
}
} else {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_NO_EXTERNAL_MERGE_TOOL,
"The environment variable SVN_MERGE and the merge-tool-cmd run-time configuration option were not set.");
SVNErrorManager.error(err, SVNLogType.CLIENT);
}
String merger = mergeToolCommand;
if (SVNFileUtil.isWindows) {
merger = mergeToolCommand.toLowerCase();
}
ISVNReturnValueCallback runCallback = new ISVNReturnValueCallback() {
public void handleChar(char ch) throws SVNException {
}
public void handleReturnValue(int returnValue) throws SVNException {
if (returnValue != 0 && returnValue != 1) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.EXTERNAL_PROGRAM,
"The external merge tool exited with exit code {0}", String.valueOf(returnValue));
SVNErrorManager.error(err, SVNLogType.CLIENT);
}
if (remainsInConflict != null && remainsInConflict.length > 0) {
remainsInConflict[0] = returnValue == 1;
}
}
public boolean isHandleProgramOutput() {
return false;
}
};
runEditor(merger, new String[] { basePath, repositoryPath, localPath, mergeResultPath, wcPath }, testEnvironment, runCallback);
}
public static void editFileExternally(AbstractSVNCommandEnvironment env, String editorCommand, String path) throws SVNException {
editorCommand = getEditorCommand(env, editorCommand);
String testEnv[] = SVNFileUtil.getTestEnvironment();
if (testEnv[0] != null) {
testEnv = new String[] {"SVNTEST_EDITOR_FUNC=" + (testEnv[2] != null ? testEnv[2] : "")};
}
if (testEnv != null) {
LinkedList environment = new LinkedList();
for (int i = 0; i < testEnv.length; i++) {
if (testEnv[i] != null) {
environment.add(testEnv[i]);
}
}
if (!environment.isEmpty()) {
testEnv = (String[]) environment.toArray(new String[environment.size()]);
} else {
testEnv = null;
}
}
final int[] exitCode = { -1 };
ISVNReturnValueCallback procCallback = new ISVNReturnValueCallback() {
public void handleChar(char ch) throws SVNException {
}
public void handleReturnValue(int returnValue) throws SVNException {
exitCode[0] = returnValue;
}
public boolean isHandleProgramOutput() {
return false;
}
};
String result = runEditor(editorCommand, new String[] {path}, testEnv, procCallback);
if (result == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.EXTERNAL_PROGRAM, "system(''{0}'') returned {1}",
new Object[] { editorCommand + " " + path, String.valueOf(exitCode[0]) });
//SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.EXTERNAL_PROGRAM, "Editor command '" +
// editorCommand + " " + path + "' failed.");
SVNErrorManager.error(err, SVNLogType.CLIENT);
}
}
public static byte[] runEditor(AbstractSVNCommandEnvironment env, String editorCommand, byte[] existingValue, String prefix) throws SVNException {
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
File tmpFile = SVNFileUtil.createUniqueFile(tmpDir, prefix, ".tmp", false);
OutputStream os = null;
try {
os = SVNFileUtil.openFileForWriting(tmpFile);
os.write(existingValue);
} catch (IOException e) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getMessage());
SVNErrorManager.error(err, SVNLogType.CLIENT);
} finally {
SVNFileUtil.closeFile(os);
}
SVNFileUtil.setLastModified(tmpFile, System.currentTimeMillis() - 2000);
long timestamp = SVNFileUtil.getFileLastModified(tmpFile);
editorCommand = getEditorCommand(env, editorCommand);
String[] testEnv = SVNFileUtil.getTestEnvironment();
if (testEnv[0] != null) {
testEnv = new String[] {"SVNTEST_EDITOR_FUNC=" + (testEnv[2] != null ? testEnv[2] : "")};
} else {
testEnv = null;
}
try {
String result = runEditor(editorCommand, new String[] {tmpFile.getAbsolutePath()}, testEnv, null);
if (result == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Editor command '" + editorCommand + " " + tmpFile.getAbsolutePath() + "' failed.");
SVNErrorManager.error(err, SVNLogType.CLIENT);
}
// now read from file.
if (timestamp == SVNFileUtil.getFileLastModified(tmpFile)) {
return null;
}
InputStream is = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
try {
is = SVNFileUtil.openFileForReading(tmpFile);
while(true) {
int read = is.read(buffer);
if (read < 0) {
break;
}
bos.write(buffer, 0, read);
}
} catch (IOException e) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getMessage());
SVNErrorManager.error(err, SVNLogType.CLIENT);
} finally {
SVNFileUtil.closeFile(is);
}
return bos.toByteArray();
} finally {
SVNFileUtil.deleteFile(tmpFile);
}
}
private static String runEditor(String editorCommand, String[] args, String[] env, ISVNReturnValueCallback callback) throws SVNException {
String result = null;
if (SVNFileUtil.isWindows || SVNFileUtil.isOS2) {
String editor = editorCommand.trim().toLowerCase();
if (!(editor.endsWith(".exe") || editor.endsWith(".bat") || editor.endsWith(".cmd"))) {
String[] command = new String[3 + args.length];
command[0] = "cmd.exe";
command[1] = "/C";
command[2] = editorCommand;
for (int i = 0; i < args.length; i++) {
command[3 + i] = args[i];
}
result = SVNFileUtil.execCommand(command, env, false, callback);
} else {
String[] command = new String[1 + args.length];
command[0] = editorCommand;
for (int i = 0; i < args.length; i++) {
command[1 + i] = args[i];
}
result = SVNFileUtil.execCommand(command, env, false, callback);
}
} else if (SVNFileUtil.isLinux || SVNFileUtil.isBSD || SVNFileUtil.isOSX || SVNFileUtil.isSolaris){
if (env == null && !SVNFileUtil.isSolaris) {
String shellCommand = SVNFileUtil.getEnvironmentVariable("SHELL");
if (shellCommand == null || "".equals(shellCommand.trim())) {
shellCommand = "/bin/sh";
}
String[] command = new String[3];
command[0] = shellCommand;
command[1] = "-c";
command[2] = editorCommand;
for (int i = 0; i < args.length; i++) {
command[2] += " " + args[i];
}
command[2] += " < /dev/tty > /dev/tty";
result = SVNFileUtil.execCommand(command, env, false, callback);
} else {
// test mode, do not use bash and redirection.
String[] command = new String[1 + args.length];
command[0] = editorCommand;
for (int i = 0; i < args.length; i++) {
command[1 + i] = args[i];
}
result = SVNFileUtil.execCommand(command, env, false, callback);
}
} else if (SVNFileUtil.isOpenVMS) {
String[] command = new String[1 + args.length];
command[0] = editorCommand;
for (int i = 0; i < args.length; i++) {
command[1 + i] = args[i];
}
result = SVNFileUtil.execCommand(command, env, false, callback);
}
return result;
}
public static String prompt(String promptMessage, SVNCommandEnvironment env) throws SVNException {
System.err.print(promptMessage);
System.err.flush();
String input = null;
InputReader reader = new InputReader(System.in);
Thread readerThread = new Thread(reader);
readerThread.setDaemon(true);
readerThread.start();
while (true) {
env.checkCancelled();
if (reader.myIsFinished) {
input = reader.getReadInput();
break;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
if (reader.getError() != null) {
SVNErrorManager.error(reader.getError(), SVNLogType.CLIENT);
}
if (input == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR,
"Can't read stdin: End of file found");
SVNErrorManager.error(err, SVNLogType.CLIENT);
}
return input;
}
private static String getEditorCommand(AbstractSVNCommandEnvironment env, String editorCommand) throws SVNException {
if (editorCommand != null) {
return editorCommand;
}
String[] testEnvironment = SVNFileUtil.getTestEnvironment();
String command = testEnvironment[0];
if (command == null) {
command = SVNFileUtil.getEnvironmentVariable("SVN_EDITOR");
if (command == null) {
command = env.getOptions().getEditor();
}
if (command == null) {
command = SVNFileUtil.getEnvironmentVariable("VISUAL");
}
if (command == null) {
command = SVNFileUtil.getEnvironmentVariable("EDITOR");
}
}
String errorMessage = null;
if (command == null) {
errorMessage =
"None of the environment variables SVN_EDITOR, VISUAL or EDITOR is " +
"set, and no 'editor-cmd' run-time configuration option was found";
} else if ("".equals(command.trim())) {
errorMessage =
"The EDITOR, SVN_EDITOR or VISUAL environment variable or " +
"'editor-cmd' run-time configuration option is empty or " +
"consists solely of whitespace. Expected a shell command.";
}
if (errorMessage != null) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CL_NO_EXTERNAL_EDITOR, errorMessage), SVNLogType.CLIENT);
}
return command;
}
public static int getLinesCount(String str) {
if ("".equals(str)) {
return 1;
}
int count = 1;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '\r') {
count++;
if (i < str.length() - 1 && str.charAt(i + 1) == '\n') {
i++;
}
} else if (str.charAt(i) == '\n') {
count++;
}
}
if (count == 0) {
count++;
}
return count;
}
public static String[] breakToLines(String str) {
if (str == null) {
return null;
}
if ("".equals(str)) {
return new String[] { "" };
}
LinkedList list = new LinkedList();
int i = 0;
int start = 0;
for (; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch == '\r' || ch == '\n') {
if (i < str.length() - 1) {
char nextCh = str.charAt(i + 1);
if ((ch == '\r' && nextCh == '\n') || (ch == '\n' && nextCh == '\r')) {
i++;
}
list.add(str.substring(start, i + 1));
} else {
list.add(str.substring(start, i + 1));
}
start = i + 1;
}
}
if (start != i) {
list.add(str.substring(start, i));
}
return (String[]) list.toArray(new String[list.size()]);
}
public static String getCommandHelp(AbstractSVNCommand command, String programName, boolean printOptionAlias) {
StringBuffer help = new StringBuffer();
help.append(command.getName());
if (command.getAliases().length > 0) {
help.append(" (");
for (int i = 0; i < command.getAliases().length; i++) {
help.append(command.getAliases()[i]);
if (i + 1 < command.getAliases().length) {
help.append(", ");
}
}
help.append(")");
}
if (!"".equals(command.getName())) {
help.append(": ");
}
help.append(command.getDescription());
if (!command.getSupportedOptions().isEmpty()) {
help.append("\n");
if (!command.getValidOptions().isEmpty()) {
help.append("\nValid options:\n");
for (Iterator options = command.getValidOptions().iterator(); options.hasNext();) {
AbstractSVNOption option = (AbstractSVNOption) options.next();
help.append(" ");
String optionDesc = null;
if (option.getAlias() != null && printOptionAlias) {
optionDesc = "-" + option.getAlias() + " [--" + option.getName() + "]";
} else {
optionDesc = "--" + option.getName();
}
if (!option.isUnary()) {
optionDesc += " ARG";
}
int chars = optionDesc.length() < 24 ? 24 : optionDesc.length();
help.append(SVNFormatUtil.formatString(optionDesc, chars, true));
help.append(" : ");
help.append(option.getDescription(command, programName));
help.append("\n");
}
}
if (!command.getGlobalOptions().isEmpty()) {
help.append("\nGlobal options:\n");
for (Iterator options = command.getGlobalOptions().iterator(); options.hasNext();) {
AbstractSVNOption option = (AbstractSVNOption) options.next();
help.append(" ");
String optionDesc = null;
if (option.getAlias() != null) {
optionDesc = "-" + option.getAlias() + " [--" + option.getName() + "]";
} else {
optionDesc = "--" + option.getName();
}
if (!option.isUnary()) {
optionDesc += " ARG";
}
help.append(SVNFormatUtil.formatString(optionDesc, 24, true));
help.append(" : ");
help.append(option.getDescription(command, programName));
help.append("\n");
}
}
}
return help.toString();
}
public static String getVersion(AbstractSVNCommandEnvironment env, boolean quiet) {
String version = Version.getShortVersionString();
String revNumber = Version.getRevisionString() == null ? "SNAPSHOT" : Version.getRevisionString();
String message = MessageFormat.format(env.getProgramName() + ", version {0}\n", new Object[] {version + " (" + revNumber + ")"});
if (quiet) {
message = version;
}
if (!quiet) {
message +=
"\nCopyright (c) 2004-2012 TMate Software.\n" +
"SVNKit is an Open Source software, see http://svnkit.com/ for more information.\n" +
"SVNKit is a pure Java (TM) version of Subversion, see http://subversion.tigris.org/";
}
return message;
}
public static String getGenericHelp(String programName, String header, String footer, Comparator commandComparator) {
StringBuffer help = new StringBuffer();
if (header != null) {
String version = Version.getShortVersionString();
header = MessageFormat.format(header, new Object[] {programName, version});
help.append(header);
}
for (Iterator commands = AbstractSVNCommand.availableCommands(commandComparator); commands.hasNext();) {
AbstractSVNCommand command = (AbstractSVNCommand) commands.next();
help.append("\n ");
help.append(command.getName());
if (command.getAliases().length > 0) {
help.append(" (");
for (int i = 0; i < command.getAliases().length; i++) {
help.append(command.getAliases()[i]);
if (i + 1 < command.getAliases().length) {
help.append(", ");
}
}
help.append(")");
}
}
help.append("\n\n");
if (footer != null) {
help.append(footer);
}
return help.toString();
}
public static void parseConfigOption(String optionArg, Map configOptions, Map serversOptions) throws SVNException {
if (optionArg != null) {
int firstColonInd = optionArg.indexOf(':');
if (firstColonInd != -1 && firstColonInd != optionArg.length() - 1) {
int secondColonInd = optionArg.indexOf(':', firstColonInd + 1);
if (secondColonInd != -1 && secondColonInd != firstColonInd + 1) {
int equalsSignInd = optionArg.indexOf('=', secondColonInd + 1);
if (equalsSignInd != -1 && equalsSignInd != secondColonInd + 1) {
String fileName = optionArg.substring(0, firstColonInd);
String section = optionArg.substring(firstColonInd + 1, secondColonInd);
String option = optionArg.substring(secondColonInd + 1, equalsSignInd);
if (option.indexOf(':') == -1) {
String value = optionArg.substring(equalsSignInd + 1);
Map options = null;
if ("servers".equals(fileName)) {
options = serversOptions;
} else if ("config".equals(fileName)) {
options = configOptions;
}
if (options != null) {
Map values = (Map) options.get(section);
if (values == null) {
values = new HashMap();
options.put(section, values);
}
values.put(option, value);
return;
}
}
}
}
}
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "Invalid syntax of argument of --config-option");
SVNErrorManager.error(err, SVNLogType.CLIENT);
}
private static class InputReader implements Runnable {
private BufferedReader myReader;
private String myReadInput;
private SVNErrorMessage myError;
volatile boolean myIsFinished;
public InputReader(InputStream is) {
myReader = new BufferedReader(new InputStreamReader(is));
}
public void run() {
myIsFinished = false;
try {
myReadInput = myReader.readLine();
} catch (IOException e) {
myError = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Can''t read stdin: {0}",
e.getLocalizedMessage());
}
myIsFinished = true;
}
public String getReadInput() {
return myReadInput;
}
public SVNErrorMessage getError() {
return myError;
}
}
}