1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.debug;
  24 
  25 import java.io.File;
  26 import java.io.IOException;
  27 import java.nio.file.FileAlreadyExistsException;
  28 import java.nio.file.Files;
  29 import java.nio.file.InvalidPathException;
  30 import java.nio.file.Path;
  31 import java.nio.file.Paths;
  32 import java.util.concurrent.atomic.AtomicLong;
  33 
  34 import org.graalvm.compiler.options.OptionKey;
  35 import org.graalvm.compiler.options.OptionValues;
  36 
  37 /**
  38  * Miscellaneous methods for modifying and generating file system paths.
  39  */
  40 public class PathUtilities {
  41 
  42     private static final AtomicLong globalTimeStamp = new AtomicLong();
  43 
  44     /**
  45      * Gets a time stamp for the current process. This method will always return the same value for
  46      * the current VM execution.
  47      */
  48     public static long getGlobalTimeStamp() {
  49         if (globalTimeStamp.get() == 0) {
  50             globalTimeStamp.compareAndSet(0, System.currentTimeMillis());
  51         }
  52         return globalTimeStamp.get();
  53     }
  54 
  55     /**
  56      * Gets a value based on {@code name} that can be passed to {@link Paths#get(String, String...)}
  57      * without causing an {@link InvalidPathException}.
  58      *
  59      * @return {@code name} with all characters invalid for the current file system replaced by
  60      *         {@code '_'}
  61      */
  62     public static String sanitizeFileName(String name) {
  63         try {
  64             Path path = Paths.get(name);
  65             if (path.getNameCount() == 0) {
  66                 return name;
  67             }
  68         } catch (InvalidPathException e) {
  69             // fall through
  70         }
  71         StringBuilder buf = new StringBuilder(name.length());
  72         for (int i = 0; i < name.length(); i++) {
  73             char c = name.charAt(i);
  74             if (c != File.separatorChar && c != ' ' && !Character.isISOControl(c)) {
  75                 try {
  76                     Paths.get(String.valueOf(c));
  77                     buf.append(c);
  78                     continue;
  79                 } catch (InvalidPathException e) {
  80                 }
  81             }
  82             buf.append('_');
  83         }
  84         return buf.toString();
  85     }
  86 
  87     /**
  88      * A maximum file name length supported by most file systems. There is no platform independent
  89      * way to get this in Java.
  90      */
  91     private static final int MAX_FILE_NAME_LENGTH = 255;
  92 
  93     private static final String ELLIPSIS = "...";
  94 
  95     static Path createUnique(OptionValues options, OptionKey<String> baseNameOption, String id, String label, String ext, boolean createDirectory) throws IOException {
  96         String uniqueTag = "";
  97         int dumpCounter = 1;
  98         String prefix;
  99         if (id == null) {
 100             prefix = baseNameOption.getValue(options);
 101             int slash = prefix.lastIndexOf(File.separatorChar);
 102             prefix = prefix.substring(slash + 1);
 103         } else {
 104             prefix = id;
 105         }
 106         for (;;) {
 107             int fileNameLengthWithoutLabel = uniqueTag.length() + ext.length() + prefix.length() + "[]".length();
 108             int labelLengthLimit = MAX_FILE_NAME_LENGTH - fileNameLengthWithoutLabel;
 109             String fileName;
 110             if (labelLengthLimit < ELLIPSIS.length()) {
 111                 // This means `id` is very long
 112                 String suffix = uniqueTag + ext;
 113                 int idLengthLimit = Math.min(MAX_FILE_NAME_LENGTH - suffix.length(), prefix.length());
 114                 fileName = sanitizeFileName(prefix.substring(0, idLengthLimit) + suffix);
 115             } else {
 116                 if (label == null) {
 117                     fileName = sanitizeFileName(prefix + uniqueTag + ext);
 118                 } else {
 119                     String adjustedLabel = label;
 120                     if (label.length() > labelLengthLimit) {
 121                         adjustedLabel = label.substring(0, labelLengthLimit - ELLIPSIS.length()) + ELLIPSIS;
 122                     }
 123                     fileName = sanitizeFileName(prefix + '[' + adjustedLabel + ']' + uniqueTag + ext);
 124                 }
 125             }
 126             Path dumpDir = DebugOptions.getDumpDirectory(options);
 127             Path result = Paths.get(dumpDir.toString(), fileName);
 128             try {
 129                 if (createDirectory) {
 130                     return Files.createDirectory(result);
 131                 } else {
 132                     return Files.createFile(result);
 133                 }
 134             } catch (FileAlreadyExistsException e) {
 135                 uniqueTag = "_" + dumpCounter++;
 136             }
 137         }
 138     }
 139 
 140 }