334 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			334 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright (C)2005-2019 Haxe Foundation
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Permission is hereby granted, free of charge, to any person obtaining a
							 | 
						||
| 
								 | 
							
								 * copy of this software and associated documentation files (the "Software"),
							 | 
						||
| 
								 | 
							
								 * to deal in the Software without restriction, including without limitation
							 | 
						||
| 
								 | 
							
								 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
							 | 
						||
| 
								 | 
							
								 * and/or sell copies of the Software, and to permit persons to whom the
							 | 
						||
| 
								 | 
							
								 * Software is furnished to do so, subject to the following conditions:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The above copyright notice and this permission notice shall be included in
							 | 
						||
| 
								 | 
							
								 * all copies or substantial portions of the Software.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
							 | 
						||
| 
								 | 
							
								 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
							 | 
						||
| 
								 | 
							
								 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
							 | 
						||
| 
								 | 
							
								 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
							 | 
						||
| 
								 | 
							
								 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
							 | 
						||
| 
								 | 
							
								 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
							 | 
						||
| 
								 | 
							
								 * DEALINGS IN THE SOFTWARE.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package haxe.io;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
									This class provides a convenient way of working with paths. It supports the
							 | 
						||
| 
								 | 
							
									common path formats:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									- `directory1/directory2/filename.extension`
							 | 
						||
| 
								 | 
							
									- `directory1\directory2\filename.extension`
							 | 
						||
| 
								 | 
							
								**/
							 | 
						||
| 
								 | 
							
								class Path {
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										The directory.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										This is the leading part of the path that is not part of the file name
							 | 
						||
| 
								 | 
							
										and the extension.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										Does not end with a `/` or `\` separator.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If the path has no directory, the value is `null`.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public var dir:Null<String>;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										The file name.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										This is the part of the part between the directory and the extension.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If there is no file name, e.g. for `".htaccess"` or `"/dir/"`, the value
							 | 
						||
| 
								 | 
							
										is the empty String `""`.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public var file:String;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										The file extension.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										It is separated from the file name by a dot. This dot is not part of
							 | 
						||
| 
								 | 
							
										the extension.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If the path has no extension, the value is `null`.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public var ext:Null<String>;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										`true` if the last directory separator is a backslash, `false` otherwise.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public var backslash:Bool;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Creates a new `Path` instance by parsing `path`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										Path information can be retrieved by accessing the `dir`, `file` and `ext`
							 | 
						||
| 
								 | 
							
										properties.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public function new(path:String) {
							 | 
						||
| 
								 | 
							
										switch (path) {
							 | 
						||
| 
								 | 
							
											case "." | "..":
							 | 
						||
| 
								 | 
							
												dir = path;
							 | 
						||
| 
								 | 
							
												file = "";
							 | 
						||
| 
								 | 
							
												return;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										var c1 = path.lastIndexOf("/");
							 | 
						||
| 
								 | 
							
										var c2 = path.lastIndexOf("\\");
							 | 
						||
| 
								 | 
							
										if (c1 < c2) {
							 | 
						||
| 
								 | 
							
											dir = path.substr(0, c2);
							 | 
						||
| 
								 | 
							
											path = path.substr(c2 + 1);
							 | 
						||
| 
								 | 
							
											backslash = true;
							 | 
						||
| 
								 | 
							
										} else if (c2 < c1) {
							 | 
						||
| 
								 | 
							
											dir = path.substr(0, c1);
							 | 
						||
| 
								 | 
							
											path = path.substr(c1 + 1);
							 | 
						||
| 
								 | 
							
										} else
							 | 
						||
| 
								 | 
							
											dir = null;
							 | 
						||
| 
								 | 
							
										var cp = path.lastIndexOf(".");
							 | 
						||
| 
								 | 
							
										if (cp != -1) {
							 | 
						||
| 
								 | 
							
											ext = path.substr(cp + 1);
							 | 
						||
| 
								 | 
							
											file = path.substr(0, cp);
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											ext = null;
							 | 
						||
| 
								 | 
							
											file = path;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Returns a String representation of `this` path.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `this.backslash` is `true`, backslash is used as directory separator,
							 | 
						||
| 
								 | 
							
										otherwise slash is used. This only affects the separator between
							 | 
						||
| 
								 | 
							
										`this.dir` and `this.file`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `this.directory` or `this.extension` is `null`, their representation
							 | 
						||
| 
								 | 
							
										is the empty String `""`.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public function toString():String {
							 | 
						||
| 
								 | 
							
										return (if (dir == null) "" else dir + if (backslash) "\\" else "/") + file + (if (ext == null) "" else "." + ext);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Returns the String representation of `path` without the file extension.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` is `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function withoutExtension(path:String):String {
							 | 
						||
| 
								 | 
							
										var s = new Path(path);
							 | 
						||
| 
								 | 
							
										s.ext = null;
							 | 
						||
| 
								 | 
							
										return s.toString();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Returns the String representation of `path` without the directory.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` is `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function withoutDirectory(path):String {
							 | 
						||
| 
								 | 
							
										var s = new Path(path);
							 | 
						||
| 
								 | 
							
										s.dir = null;
							 | 
						||
| 
								 | 
							
										return s.toString();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Returns the directory of `path`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If the directory is `null`, the empty String `""` is returned.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` is `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function directory(path):String {
							 | 
						||
| 
								 | 
							
										var s = new Path(path);
							 | 
						||
| 
								 | 
							
										if (s.dir == null)
							 | 
						||
| 
								 | 
							
											return "";
							 | 
						||
| 
								 | 
							
										return s.dir;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Returns the extension of `path`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` has no extension, the empty String `""` is returned.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` is `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function extension(path):String {
							 | 
						||
| 
								 | 
							
										var s = new Path(path);
							 | 
						||
| 
								 | 
							
										if (s.ext == null)
							 | 
						||
| 
								 | 
							
											return "";
							 | 
						||
| 
								 | 
							
										return s.ext;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Returns a String representation of `path` where the extension is `ext`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` has no extension, `ext` is added as extension.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` or `ext` are `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function withExtension(path, ext):String {
							 | 
						||
| 
								 | 
							
										var s = new Path(path);
							 | 
						||
| 
								 | 
							
										s.ext = ext;
							 | 
						||
| 
								 | 
							
										return s.toString();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Joins all paths in `paths` together.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `paths` is empty, the empty String `""` is returned. Otherwise the
							 | 
						||
| 
								 | 
							
										paths are joined with a slash between them.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `paths` is `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function join(paths:Array<String>):String {
							 | 
						||
| 
								 | 
							
										var paths = paths.filter(function(s) return s != null && s != "");
							 | 
						||
| 
								 | 
							
										if (paths.length == 0) {
							 | 
						||
| 
								 | 
							
											return "";
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										var path = paths[0];
							 | 
						||
| 
								 | 
							
										for (i in 1...paths.length) {
							 | 
						||
| 
								 | 
							
											path = addTrailingSlash(path);
							 | 
						||
| 
								 | 
							
											path += paths[i];
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return normalize(path);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Normalize a given `path` (e.g. turn `'/usr/local/../lib'` into `'/usr/lib'`).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										Also replaces backslashes `\` with slashes `/` and afterwards turns
							 | 
						||
| 
								 | 
							
										multiple slashes into a single one.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` is `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function normalize(path:String):String {
							 | 
						||
| 
								 | 
							
										var slash = "/";
							 | 
						||
| 
								 | 
							
										path = path.split("\\").join(slash);
							 | 
						||
| 
								 | 
							
										if (path == slash)
							 | 
						||
| 
								 | 
							
											return slash;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var target = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for (token in path.split(slash)) {
							 | 
						||
| 
								 | 
							
											if (token == '..' && target.length > 0 && target[target.length - 1] != "..") {
							 | 
						||
| 
								 | 
							
												target.pop();
							 | 
						||
| 
								 | 
							
											} else if (token == '') {
							 | 
						||
| 
								 | 
							
												if (target.length > 0 || path.charCodeAt(0) == '/'.code) {
							 | 
						||
| 
								 | 
							
													target.push(token);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											} else if (token != '.') {
							 | 
						||
| 
								 | 
							
												target.push(token);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										var tmp = target.join(slash);
							 | 
						||
| 
								 | 
							
										var acc = new StringBuf();
							 | 
						||
| 
								 | 
							
										var colon = false;
							 | 
						||
| 
								 | 
							
										var slashes = false;
							 | 
						||
| 
								 | 
							
										#if utf16
							 | 
						||
| 
								 | 
							
										for (c in haxe.iterators.StringIteratorUnicode.unicodeIterator(tmp)) {
							 | 
						||
| 
								 | 
							
											switch (c) {
							 | 
						||
| 
								 | 
							
										#else
							 | 
						||
| 
								 | 
							
										for (i in 0...tmp.length) {
							 | 
						||
| 
								 | 
							
											switch (StringTools.fastCodeAt(tmp, i)) {
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
												case ":".code:
							 | 
						||
| 
								 | 
							
													acc.add(":");
							 | 
						||
| 
								 | 
							
													colon = true;
							 | 
						||
| 
								 | 
							
												case "/".code if (!colon):
							 | 
						||
| 
								 | 
							
													slashes = true;
							 | 
						||
| 
								 | 
							
												case var i:
							 | 
						||
| 
								 | 
							
													colon = false;
							 | 
						||
| 
								 | 
							
													if (slashes) {
							 | 
						||
| 
								 | 
							
														acc.add("/");
							 | 
						||
| 
								 | 
							
														slashes = false;
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
													acc.addChar(i);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return acc.toString();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Adds a trailing slash to `path`, if it does not have one already.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If the last slash in `path` is a backslash, a backslash is appended to
							 | 
						||
| 
								 | 
							
										`path`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If the last slash in `path` is a slash, or if no slash is found, a slash
							 | 
						||
| 
								 | 
							
										is appended to `path`. In particular, this applies to the empty String
							 | 
						||
| 
								 | 
							
										`""`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` is `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function addTrailingSlash(path:String):String {
							 | 
						||
| 
								 | 
							
										if (path.length == 0)
							 | 
						||
| 
								 | 
							
											return "/";
							 | 
						||
| 
								 | 
							
										var c1 = path.lastIndexOf("/");
							 | 
						||
| 
								 | 
							
										var c2 = path.lastIndexOf("\\");
							 | 
						||
| 
								 | 
							
										return if (c1 < c2) {
							 | 
						||
| 
								 | 
							
											if (c2 != path.length - 1)
							 | 
						||
| 
								 | 
							
												path + "\\";
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
												path;
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											if (c1 != path.length - 1)
							 | 
						||
| 
								 | 
							
												path + "/";
							 | 
						||
| 
								 | 
							
											else
							 | 
						||
| 
								 | 
							
												path;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Removes trailing slashes from `path`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` does not end with a `/` or `\`, `path` is returned unchanged.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										Otherwise the substring of `path` excluding the trailing slashes or
							 | 
						||
| 
								 | 
							
										backslashes is returned.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `path` is `null`, the result is unspecified.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function removeTrailingSlashes(path:String):String {
							 | 
						||
| 
								 | 
							
										while (true) {
							 | 
						||
| 
								 | 
							
											switch (path.charCodeAt(path.length - 1)) {
							 | 
						||
| 
								 | 
							
												case '/'.code | '\\'.code:
							 | 
						||
| 
								 | 
							
													path = path.substr(0, -1);
							 | 
						||
| 
								 | 
							
												case _:
							 | 
						||
| 
								 | 
							
													break;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return path;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Returns `true` if the path is an absolute path, and `false` otherwise.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									public static function isAbsolute(path:String):Bool {
							 | 
						||
| 
								 | 
							
										if (StringTools.startsWith(path, '/'))
							 | 
						||
| 
								 | 
							
											return true;
							 | 
						||
| 
								 | 
							
										if (path.charAt(1) == ':')
							 | 
						||
| 
								 | 
							
											return true;
							 | 
						||
| 
								 | 
							
										if (StringTools.startsWith(path, '\\\\'))
							 | 
						||
| 
								 | 
							
											return true;
							 | 
						||
| 
								 | 
							
										return false;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									private static function unescape(path:String):String {
							 | 
						||
| 
								 | 
							
										var regex = ~/-x([0-9][0-9])/g;
							 | 
						||
| 
								 | 
							
										return regex.map(path, function(regex) return String.fromCharCode(Std.parseInt(regex.matched(1))));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									private static function escape(path:String, allowSlashes:Bool = false):String {
							 | 
						||
| 
								 | 
							
										var regex = allowSlashes ? ~/[^A-Za-z0-9_\/\\\.]/g : ~/[^A-Za-z0-9_\.]/g;
							 | 
						||
| 
								 | 
							
										return regex.map(path, function(v) return '-x' + v.matched(0).charCodeAt(0));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |