gt:             if (symlinkName == null) {
context: /*
   * ====================================================================
   * Copyright (c) 2004-2010 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.exist.versioning.svn.internal.wc;
  
  import java.io.BufferedInputStream;
  import java.io.BufferedOutputStream;
  import java.io.BufferedReader;
  import java.io.ByteArrayOutputStream;
  import java.io.File;
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.OutputStream;
  import java.io.RandomAccessFile;
  import java.io.Reader;
  import java.io.Writer;
  import java.lang.reflect.Method;
  import java.nio.ByteBuffer;
  import java.nio.CharBuffer;
  import java.nio.charset.CharsetDecoder;
  import java.security.MessageDigest;
  import java.security.NoSuchAlgorithmException;
  import java.util.Map;
  import java.util.Properties;
  import java.util.logging.Level;
  
  import org.exist.EXistException;
  import org.exist.security.Subject;
  import org.exist.storage.BrokerPool;
  import org.exist.storage.DBBroker;
  import org.exist.util.io.Resource;
  import org.exist.versioning.svn.internal.wc.admin.SVNTranslator;
  import org.exist.versioning.svn.wc.ISVNEventHandler;
  import org.exist.versioning.svn.wc.ISVNOptions;
  import org.tmatesoft.svn.core.ISVNCanceller;
  import org.tmatesoft.svn.core.SVNErrorCode;
  import org.tmatesoft.svn.core.SVNErrorMessage;
  import org.tmatesoft.svn.core.SVNException;
  import org.tmatesoft.svn.core.SVNNodeKind;
  import org.tmatesoft.svn.core.internal.util.SVNFormatUtil;
  import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
  import org.tmatesoft.svn.core.internal.util.SVNUUIDGenerator;
  import org.tmatesoft.svn.core.internal.util.jna.SVNJNAUtil;
  import org.tmatesoft.svn.core.internal.wc.ISVNInputFile;
  import org.tmatesoft.svn.core.internal.wc.ISVNReturnValueCallback;
  import org.tmatesoft.svn.util.SVNDebugLog;
  import org.tmatesoft.svn.util.SVNLogType;
  
  /**
   * @version 1.3
   * @author TMate Software Ltd., Peter Skoog
   */
  public class SVNFileUtil {
  
      private static final String ID_COMMAND;
      private static final String LN_COMMAND;
      public static final String LS_COMMAND;
      private static final String CHMOD_COMMAND;
      private static final String ATTRIB_COMMAND;
      private static final String ENV_COMMAND;
  
      public static final boolean is32Bit;
      public static final boolean is64Bit;
  
      public static final int STREAM_CHUNK_SIZE = 16384;
  
      public final static OutputStream DUMMY_OUT = new OutputStream() {
  
          public void write(int b) throws IOException {
          }
      };
      public final static InputStream DUMMY_IN = new InputStream() {
  
          public int read() throws IOException {
              return -1;
          }
      };
  
      private static boolean ourUseUnsafeCopyOnly = Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty("exist.svnkit.no.safe.copy", "true"));
      private static boolean ourCopyOnSetWritable = Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty("exist.svnkit.fast.setWritable", "true"));
      private static boolean ourUseNIOCopying = Boolean.TRUE.toString().equalsIgnoreCase(System.getProperty("exist.svnkit.nio.copy", "false"));
  
      private static String nativeEOLMarker;
      private static File ourAppDataPath;
      private static String ourAdminDirectoryName;
      private static File ourSystemAppDataPath;
      
      private static volatile boolean ourIsSleepForTimeStamp = true;
      
      public static final String BINARY_MIME_TYPE = "application/octet-stream";
  
      static {
          is32Bit = "32".equals(System.getProperty("sun.arch.data.model", "32"));
          is64Bit = "64".equals(System.getProperty("sun.arch.data.model", "64"));
          
          String prefix = "svnkit.program.";
  
          Properties props = new Properties();
          InputStream is = SVNFileUtil.class.getResourceAsStream("/svnkit.runtime.properties");
          if (is != null) {
              try {
                  props.load(is);
              } catch (IOException e) {
              } finally {
                  SVNFileUtil.closeFile(is);
              }
          }
  
          ID_COMMAND = props.getProperty(prefix + "id", "id");
          LN_COMMAND = props.getProperty(prefix + "ln", "ln");
          LS_COMMAND = props.getProperty(prefix + "ls", "ls");
          CHMOD_COMMAND = props.getProperty(prefix + "chmod", "chmod");
          ATTRIB_COMMAND = props.getProperty(prefix + "attrib", "attrib");
          ENV_COMMAND = props.getProperty(prefix + "env", "env");
      }
  
      public static synchronized boolean useUnsafeCopyOnly() {
          return ourUseUnsafeCopyOnly;
      }
  
      public static synchronized void setUseUnsafeCopyOnly(boolean useUnsafeCopyOnly) {
          ourUseUnsafeCopyOnly = useUnsafeCopyOnly;
      }
  
      public static synchronized boolean useCopyOnSetWritable() {
          return ourCopyOnSetWritable;
      }
  
      public static synchronized void setUseCopyOnSetWritable(boolean useCopyOnSetWritable) {
          ourCopyOnSetWritable = useCopyOnSetWritable;
      }
  
      public static synchronized boolean useNIOCopying() {
          return ourUseNIOCopying;
      }
  
      public static synchronized void setUseNIOCopying(boolean useNIOCopy) {
          ourUseNIOCopying = useNIOCopy;
      }
  
      public static String getIdCommand() {
          return ID_COMMAND;
      }
  
      public static String getLnCommand() {
          return LN_COMMAND;
      }
  
      public static String getLsCommand() {
          return LS_COMMAND;
      }
  
      public static String getChmodCommand() {
          return CHMOD_COMMAND;
      }
  
      public static String getAttribCommand() {
          return ATTRIB_COMMAND;
      }
  
      public static String getEnvCommand() {
          return ENV_COMMAND;
      }
      
      public static File getParentFile(File file) {
          String path = file.getAbsolutePath();
          path = path.replace(File.separatorChar, '/');
          path = SVNPathUtil.canonicalizePath(path);
          int up = 0;
          while (path.endsWith("/..")) {
              path = SVNPathUtil.removeTail(path);
              up++;
          } 
          for(int i = 0; i < up; i++) {
              path = SVNPathUtil.removeTail(path);
          }
          path = path.replace('/', Resource.separatorChar);
          file = new Resource(path);
          return file.getParentFile();
      }
  
      public static String readFile(File file) throws SVNException {
          InputStream is = null;
          try {
              is = openFileForReading(file, SVNLogType.WC);
              return readFile(is);
          } catch (IOException ioe) {
              SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, 
                      "Cannot read from file ''{0}'': {1}", new Object[] { file, ioe.getMessage() });
              SVNErrorManager.error(err, Level.FINE, SVNLogType.WC);
          } finally { 
              closeFile(is);
          }
          return null;
      }
      
      public static String readFile(InputStream input) throws IOException {
          byte[] buf = new byte[STREAM_CHUNK_SIZE];
          StringBuffer result = new StringBuffer();
          int r = -1;
          while ((r = input.read(buf)) != -1) {
              if (r == 0) {
                  continue;
              }
              result.append(new String(buf, 0, r, "UTF-8"));
          }
          return result.toString();
      }
      
      public static int readIntoBuffer(InputStream is, byte[] buff, int off, int len) throws IOException {
          int read = 0;
          while (len > 0) {
              int r = is.read(buff, off + read, len);
              if (r < 0) {
                  if (read == 0) {
                      read = -1;
                  }
                  break;
              }
              
              read += r;
              len -= r;
          }
          return read;
      }
      
      public static String getBasePath(File file) {
          File base = file.getParentFile();
          while (base != null) {
              if (base.isDirectory()) {
                  File adminDir = new Resource(base, getAdminDirectoryName());
                  if (adminDir.exists() && adminDir.isDirectory()) {
                      break;
                  }
              }
              base = base.getParentFile();
          }
          String path = file.getAbsolutePath();
          if (base != null) {
              path = path.substring(base.getAbsolutePath().length());
          }
          path = path.replace(File.separatorChar, '/');
          if (path.startsWith("/")) {
              path = path.substring(1);
          }
          return path;
      }
  
      public static void createEmptyFile(File file) throws SVNException {
          boolean created;
          if (file != null && file.getParentFile() != null && !file.getParentFile().exists()) {
              file.getParentFile().mkdirs();
          }
          
          IOException ioError = null;
          try {
              created = createNewFile(file);
          } catch (IOException ioe) {
              created = false;
              ioError = ioe;
          }
          if (!created) {
              SVNErrorMessage err = null;
              if (ioError != null) {
                  err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot create new file ''{0}'': {1}", 
                          new Object[] { file, ioError.getMessage() });
              } else {
                  err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "Cannot create new file ''{0}''", file);
              }
              SVNErrorManager.error(err, ioError != null ? ioError : new Exception(), Level.FINE, SVNLogType.WC);
          }
      }
  
      public static boolean createNewFile(File file) throws IOException {
          if (file == null) {
              return false;
          }
          
          return file.createNewFile();
      }
  
      /**
       * An internal method for ASCII bytes to write only!
       * 
       * @param file
       * @param contents
       * @throws SVNException
       */
      public static void createFile(File file, String contents, String charSet) throws SVNException {
          createEmptyFile(file);
          if (contents == null || contents.length() == 0) {
              return;
          }
          
          OutputStream os = null;
          try {
              os = SVNFileUtil.openFileForWriting(file); 
              if (charSet != null) {
                  os.write(contents.getBytes(charSet));
              } else {
                  os.write(contents.getBytes());
              }
          } catch (IOException ioe) {
              SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, 
                      "Cannot write to file ''{0}'': {1}", new Object[] {file, ioe.getMessage()});
              SVNErrorManager.error(err, ioe, Level.FINE, SVNLogType.DEFAULT);
          } catch (SVNException svne) {
              SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, 
                      "Cannot write to file ''{0}''", file);
              SVNErrorManager.error(err, svne, Level.FINE, SVNLogType.DEFAULT);
          } finally {
              SVNFileUtil.closeFile(os);
          }
      }
  
      public static void writeToFile(File file, String contents, String charSet) throws SVNException {
          if (contents == null || contents.length() == 0) {
              return;
          }
          
          OutputStream os = null;
          try {
              os = SVNFileUtil.openFileForWriting(file); 
              if (charSet != null) {
                  os.write(contents.getBytes(charSet));
              } else {
                  os.write(contents.getBytes());
              }
          } catch (IOException ioe) {
              SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, 
                      "Cannot write to file ''{0}'': {1}", new Object[] {file, ioe.getMessage()});
              SVNErrorManager.error(err, ioe, Level.FINE, SVNLogType.DEFAULT);
          } catch (SVNException svne) {
              SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, 
                      "Cannot write to file ''{0}''", file);
              SVNErrorManager.error(err, svne, Level.FINE, SVNLogType.DEFAULT);
          } finally {
              SVNFileUtil.closeFile(os);
          }
      }
  
      public static void writeVersionFile(File file, int version) throws SVNException {
          if (version < 0) {
              SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.INCORRECT_PARAMS, 
                      "Version {0} is not non-negative", new Integer(version));
              SVNErrorManager.error(err, Level.FINE, SVNLogType.WC);
          }
          
          String contents = version + "\n";
          File tmpFile = SVNFileUtil.createUniqueFile(file.getParentFile(), file.getName(), ".tmp", false);
          OutputStream os = null;
  
          try {
              os = SVNFileUtil.openFileForWriting(tmpFile);
              os.write(contents.getBytes("US-ASCII"));
          } catch (IOException e) {
              SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e.getMessage());
              SVNErrorManager.error(err, e, Level.FINE, SVNLogType.DEFAULT);
          } finally {
              SVNFileUtil.closeFile(os);
          }
          SVNFileUtil.rename(tmpFile, file);
          setReadonly(file, true);
      }
  
      public static synchronized File createUniqueFile(File parent, String name, String suffix, boolean useUUIDGenerator) throws SVNException {
          StringBuffer fileName = new StringBuffer();
          fileName.append(name);
          if (useUUIDGenerator) {
              fileName.append(".");
              fileName.append(SVNUUIDGenerator.generateUUIDString());
          }
          fileName.append(suffix);
          File file = new Resource(parent, fileName.toString());
          int i = 1;
          do {
              if (SVNFileType.getType(file) == SVNFileType.NONE) {
                  createEmptyFile(file);
                  return file;
              }
              fileName.setLength(0);
              fileName.append(name);
              fileName.append(".");            
              if (useUUIDGenerator) {
                  fileName.append(SVNUUIDGenerator.generateUUIDString());
              } else {
                  fileName.append(i);
              }
              fileName.append(suffix);
              file = new Resource(parent, fileName.toString());
              i++;
          } while (i < 99999);
  
          SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_UNIQUE_NAMES_EXHAUSTED, 
                  "Unable to make name for ''{0}''", new File(parent, name));
          SVNErrorManager.error(err, Level.FINE, SVNLogType.WC);
          return null;
      }
  
      public static void rename(File src, File dst) throws SVNException {
          if (SVNFileType.getType(src) == SVNFileType.NONE) {
              deleteFile(dst);
              return;
          }
          if (dst.isDirectory()) {
              SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, 
                      "Cannot rename file ''{0}'' to ''{1}''; file ''{1}'' is a directory", 
                      new Object[] { src, dst });
              SVNErrorManager.error(err, Level.FINE, SVNLogType.WC);
          }
          boolean renamed = src.renameTo(dst);
  
          if (!renamed) {
              SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, 
                      "Cannot rename file ''{0}'' to ''{1}''", new Object[] {src, dst});
              SVNErrorManager.error(err, Level.FINE, SVNLogType.WC);
          }
      }
  
      public static boolean setReadonly(File file, boolean readonly) {
          if (!file.exists()) {
              return false;
          }
          if (readonly)
              return file.setReadOnly();
          
          return true;
      }
  
      public static void setExecutable(File file, boolean executable) {
          if (file == null || !file.exists() || SVNFileType.getType(file) == SVNFileType.SYMLINK) {
              return;
          }
          if (SVNJNAUtil.setExecutable(file, executable)) {
              return;
          }
      }
  
      public static boolean symlinksSupported() {
          return SVNFileType.isSymlinkSupportEnabled();
      }
  
      public static void setSGID(File dir) {
          if (dir == null || !dir.exists() || !dir.isDirectory()) {
              return;
          }
          
          if (SVNJNAUtil.setSGID(dir)) {
              return;
          }
      }
  
      public static File resolveSymlinkToFile(File file) {
          if (!symlinksSupported()) {
              return null;
          }
          File targetFile = resolveSymlink(file);
          if (targetFile == null || !targetFile.isFile()) {
              return null;
          }
          return targetFile;
      }
  
      public static File resolveSymlink(File file) {
          if (!symlinksSupported()) {
              return null;
          }
          File targetFile = file;
          while (SVNFileType.getType(targetFile) == SVNFileType.SYMLINK) {
              String symlinkName = getSymlinkName(targetFile);
  