001 /* 002 * Jpkg - Java library and tools for operating system package creation. 003 * 004 * Copyright (c) 2007 Three Rings Design, Inc. 005 * All rights reserved. 006 * Copyright (c) 2004, Regents of the University of California 007 * 008 * Redistribution and use in source and binary forms, with or without 009 * modification, are permitted provided that the following conditions 010 * are met: 011 * 1. Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 2. Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer in the 015 * documentation and/or other materials provided with the distribution. 016 * 3. Neither the name of the copyright owner nor the names of contributors 017 * may be used to endorse or promote products derived from this software 018 * without specific prior written permission. 019 * 020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 030 * POSSIBILITY OF SUCH DAMAGE. 031 */ 032 package com.threerings.jpkg; 033 034 import java.io.File; 035 036 /** 037 * Small utility class for working with file paths. 038 */ 039 public class PathUtils 040 { 041 /** 042 * Normalize a path by removing all relative attributes, such as ./.. and stripping 043 * any separators from the start and end of the path. 044 */ 045 public static String normalize (String path) 046 { 047 /* Create a buffer in which we can normalize the Path. */ 048 final StringBuilder trimPath = new StringBuilder(); 049 050 /* Remove any leading or trailing whitespace from the Path. */ 051 trimPath.append(path.trim()); 052 053 /* Determine the length of the Path. */ 054 int len = trimPath.length(); 055 int lastSlash = -1; 056 int lastLastSlash = -1; 057 058 for (int i = 0; i < len; i++) { 059 assert len == trimPath.length(); 060 061 /* Remove any double slashes created by concatenating partial 062 * normalized paths together. */ 063 if (i < len - 1 && 064 trimPath.charAt(i) == '/' && 065 trimPath.charAt(i + 1) == '/') 066 { 067 trimPath.deleteCharAt(i); 068 len--; 069 i--; 070 continue; 071 } 072 073 /* Remove ./ */ 074 if (i < len - 1 && 075 i == lastSlash + 1 && 076 trimPath.charAt(i) == '.' && 077 trimPath.charAt(i + 1) == '/') 078 { 079 trimPath.delete(i, i + 2); 080 len -= 2; 081 } 082 083 /* Remove xxx/../ and ../../[xxx] */ 084 if (i < len - 2 && 085 i == lastSlash + 1 && 086 trimPath.charAt(i) == '.' && 087 trimPath.charAt(i + 1) == '.' && 088 trimPath.charAt(i + 2) == '/') 089 { 090 if (lastLastSlash >= 0) { 091 trimPath.delete(lastLastSlash, i + 2); 092 len -= (i + 2 - lastLastSlash); 093 i = lastLastSlash; 094 lastSlash = trimPath.lastIndexOf("/", lastLastSlash - 1); 095 } else { 096 /* First ../ at start of path. Attempted traversal beyond 097 * the path -- we strip off the '..' and leave '/' */ 098 trimPath.delete(i, i + 2); 099 len -= (i + 2); 100 lastSlash = 0; 101 lastLastSlash = 0; 102 } 103 } 104 105 if (trimPath.charAt(i) == '/') { 106 lastLastSlash = lastSlash; 107 lastSlash = i; 108 } 109 } 110 assert len == trimPath.length(); 111 112 /* If the normalized Path is empty, return an empty string. */ 113 if (len == 0) { 114 return ""; 115 } 116 117 /* Remove any trailing '/' if it exists. */ 118 if (trimPath.charAt(len - 1) == '/') { 119 trimPath.deleteCharAt(len - 1); 120 } 121 122 /* Return the resulting normalized Path. */ 123 return trimPath.toString(); 124 } 125 126 /** 127 * Strip any leading path separators from the start of the path. 128 */ 129 public static String stripLeadingSeparators (String path) { 130 if (path.length() == 0) { 131 return path; 132 } 133 134 if (path.charAt(0) != File.separatorChar) { 135 return path; 136 } 137 138 int ii; 139 for (ii = 0; ii < path.length(); ii++) { 140 if (path.charAt(ii) != File.separatorChar) { 141 break; 142 } 143 } 144 145 return path.substring(ii); 146 } 147 }