Page 1 of 1

RefinePath()

Posted: 22 Jan 2015, 12:34
by Learning one
RefinePath() allows you to use relative paths, absolute paths, and it may ease your life if you are working with portable drives. Some examples:

Code: Select all

Run, % RefinePath("Joe USB3:\Music\Song.mp3")	; use drive name (label) instead of letter - useful for USB sticks.
Run, % RefinePath("\Documents\Info.txt")		; "A_ScriptDrive" effect - start with a backslash to refer to the same drive where the script is
Run, % RefinePath("..\..\App.exe")				; go 2 levels up relative to the current folder (A_ScriptDir by default) and run an App located there
; and more functionalities...
For more info, see the comments in the code. Download: RefinePath.ahk

Re: RefinePath()

Posted: 22 Jan 2015, 22:11
by Chef
Nice, but isn't it supposed to return the full path?
Your function returns the exact input.

Test code:

Code: Select all

list=
(lTrim c
	..\MyScript.ahk
	..\..\CCleaner.exe
	\Pictures
	res\folder
	?variable?\Master Of Puppets.mp3
)

loop, parse, list, `n, `r
	msgbox, % a_loopfield "`n" RefinePath( a_loopfield )
Image

I have an unfinished but similar function, thanks to emmanuel d

Code: Select all

path_getFullPath( pPath )
{
	Static FirstRun, EnvVariables := "APPDATA|WinDir|AllUsersProfile|CLIENTNAME|CommonProgramFiles|COMPUTERNAME|HOMEDRIVE|HOMEPATH|LOGONSERVER|SESSIONNAME|SystemDrive|SystemRoot|USERDOMAIN|USERNAME|USERPROFILE|LOCALAPPDATA|ProgramData|PSModulePath|PUBLIC|TEMP|TMP|windows_tracing_logfile|Path"
	;Resolve environment variables:
	if !FurstRun
	{
		FurstRun := 1
		Loop, Parse,EnvVariables,|
			EnvGet,%A_loopfield%, %A_loopfield%
	}

	ifEqual,pPath,,Return

	Loop, Parse,EnvVariables,|
		if ( subStr( pPath, 1, strLen( A_LoopField )+2 ) = "`%" A_LoopField "`%" )
			StringReplace,pPath,pPath,`%%A_LoopField%`%,% %A_LoopField%

	; BuiltInVariables:
	BuiltInVariables = Desktop|DesktopCommon|ProgramFiles|MyDocuments|Startup|Programs|StartMenuCommon|StartMenu|AppDataCommon
	Loop, Parse,BuiltInVariables,|
		if ( subStr( pPath, 1, strLen( A_LoopField )+2 ) = "`%" A_LoopField "`%" )
			StringReplace,pPath,pPath,`%%A_LoopField%`%,% A_%A_LoopField%

	; Prepare:
	SplitPath, A_ScriptFullPath,,,,, ScriptDrive
	While Instr( pPath, "\.\" )
		StringReplace,pPath,pPath,\.\,\,All
	While RegExMatch( pPath, "\\{3}" )
		StringReplace,pPath,pPath,\\\,\\,All
	; replace "\\" with "\" if its not in the beginning
	a_path := pPath
	start := subStr( pPath,1,2 )
	rest := subStr( pPath,3 )
	while inStr( rest, "\\" )	;regExReplace( a_loopfield, "\\+","\" )
		StringReplace,rest,rest,\\,\,All
	pPath := start . rest

	; param is already a full path
	If RegExMatch( pPath, "i )^[a-z]:\\" )
		finalePath := pPath

	; param starts with "?\" or "?:\"
	else if RegExMatch( pPath, "^\?" )
	{
		DriveGet, Drivelist, list
		Loop, Parse, Drivelist
			If ( FileExist( P:= A_Loopfield . Substr( pPath, 2 ) ) || FileExist( P:= A_Loopfield ":" Substr( pPath, 2 ) ) )
			{
				finalePath := P
				break
			}
	}

	; param starts with "\", relative to script drive
	else If RegExMatch( pPath, "^\\[^\\]" )
		finalePath := ScriptDrive . pPath

	; param starts with "\\", network drive
	else if RegExMatch( pPath, "^\\{2}" )
		finalePath := pPath

	; param starts with "%DRIVE%", relative to script drive
	else If ( Substr( pPath, 1, 7 ) = "`%DRIVE`%" )
		finalePath := RegExReplace( pPath, "i )%DRIVE%", ScriptDrive )

	; param starts with ".\", relative to script subfolder
	else if RegExMatch( pPath, "^\.\\" )
		finalePath := A_workingDir . Substr( pPath, 2 )

	; param starts with "..\", relative to script folder
	else if RegExMatch( pPath, "^\.{2}\\" )
	{
		OutDir := A_workingDir
		while RegExMatch( pPath, "^\.{2}\\" )
		{
			if ( ScriptDrive = OutDir )
				return,,ReturnFrom:= A_LineNumber 	;"  ..\ was passed but resolved above the drive"
			SplitPath, OutDir,, OutDir 				; 1 dir UP
			pPath := Substr( pPath,4 ) 				; remove the ..\
		}
		finalePath := OutDir "\" pPath, ReturnFrom:= A_LineNumber ;"  ..\ at the start was passed"
	}

	;Strip it of ending "\" unless it's a network path that isn't just "\\"
	while ( subStr( finalePath,0 )="\" ) && !( subStr( finalePath,1 )="\\" )
		finalePath := subStr( finalePath, 1,-1 )
	Return finalePath
}

Re: RefinePath()

Posted: 23 Jan 2015, 06:55
by evilC
So this always returns an absolute path, even if you enter relative?

Sounds useful, and not always for the most obvious reasons... sometimes in code you end up with silly paths like ..\folder\..\folder\..\folder, I take it tames those properly?

Re: RefinePath()

Posted: 23 Jan 2015, 07:24
by Coco
I use this for simple cases: abs_path := ComObjCreate("Scripting.FileSystemObject").GetAbsolutePathName(pathspec), pathspec is relative to A_WorkingDir

Re: RefinePath()

Posted: 23 Jan 2015, 10:47
by Learning one
@Chef: Ooops, thanks for report, I updated the code. Try again, it should work fine now. Btw, when you use variable, you have to add it to the variables collection first - see the documentation. See also changes below; res\folder is no longer valid - you have to use backslash at beginning; \res\folder.
@evilC:Yes, it returns absolute path (if it is possible to retrieve it). It doesn't tame things like ..\folder\..\folder\..\folder yet. Currently it only supports double dots segments at the beginning of the path like ..\..\..\folder
@Coco:Nice :)

* * *

RefinePath() updated to v1.01
  • fixed - bugs
  • improved - the code doesn't use FileExist() any more - that was the wrong approach
  • changed - to refer to A_ScriptDir, you have to start with backslash. Example: RefinePath("\SubFolder\button.png"). In previous version, that first backslash was optional.
  • changed - RefinePath.MapDrives() method is not automatically called any more. For details see documentation.
P.S. maybe RefinePath should be renamed to GetAbsolutePath ?

Re: RefinePath()

Posted: 23 Jan 2015, 14:50
by carno
How about simply calling it GetPath?

Re: RefinePath()

Posted: 23 Jan 2015, 16:21
by joedf
FilePath() like FileExist()

Re: RefinePath()

Posted: 23 Jan 2015, 21:16
by Wamker
There are many functions to work with path, so path_<...> is good because it will be easy to keep it in your lib.

Re: RefinePath()

Posted: 06 Feb 2015, 18:35
by tuncay
Learning one wrote:[*]changed - to refer to A_ScriptDir, you have to start with backslash. Example: RefinePath("\SubFolder\button.png"). In previous version, that first backslash was optional.
Why is this changed? No backslash "path" or ".\path" could start from current working dir.

Re: RefinePath()

Posted: 07 Feb 2015, 04:04
by Learning one
Ok, I plan to do the following changes:
A_ScriptDrive - start with a backslash

Code: Select all

RefinePath("\SubFolder\button.png") ; example output: "C:\SubFolder\button.png"
RefinePath("\SubFolder")
A_ScriptDir - omit a backslash or start with ".\"

Code: Select all

RefinePath("SubFolder\button.png") ; example output: "C:\Scripts\SubFolder\button.png"
RefinePath("SubFolder")
RefinePath(".\SubFolder\button.png")
RefinePath(".\SubFolder")
The following will remain the same:
Level(s) up

Code: Select all

RefinePath("..\MyScript.ahk")		; one level up - example output: "C:\MyScript.ahk"
RefinePath("..\..\CCleaner.exe")	; two levels up
Drive labels

Code: Select all

RefinePath("Joe USB3:\Music\Song.mp3") ; example output: "F:\Music\Song.mp3"
RefinePath("Joe USB3:\")
Variables (in unusual way)

Code: Select all

RefinePath.Variables.Insert("variable", "C:\Music\Metallica")
RefinePath("?variable?\Master Of Puppets.mp3") ; example output: "C:\Music\Metallica\Master Of Puppets.mp3"
RefinePath("?variable?\One.mp3")	
RefinePath("?A_ScriptDir?\Image.png")		; use built-in wariable (in unusual way)
Absolute paths

Code: Select all

RefinePath("F:\Music\Song.mp3") ; example output: "F:\Music\Song.mp3" - exactly the same as input
Misc - changing default options

Code: Select all

RefinePath.RelativeTo := "D:\AnyFolder"	; default is A_ScriptDir
RefinePath.RelativeTo := A_WorkingDir
RefinePath.VariableSign := "%"	; default is ?
Any suggestions/tips? Does it look OK and work as you expect now?
P.S. tuncay, nice to see you're back to AHK ;)

resolvePath()

Posted: 07 Feb 2015, 07:31
by tuncay
Here is my attempt of this issue. I tried to resolve the multi level dot parts. Dot or nothing in front of path means relative to current working dir. Backslash means scripts directory. I did not test fully here, just wrote it today (got bored and I like to play with strings). Off course you can borrow any structure to enhance your script. This is only a basic idea how it could be done without checking much or all the other integrated enhancements like variables. Performance wise, I think its not fast, but that does not matter to me. And I am doing it in old fashion, as I was absent for years. Feel free to modify and republish any part.

And if someone copies the script, so please don't remove the license header.

Code: Select all

#Include resolvePath.ahk
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

List =
(lTrim c
..\folder\.\folder\..\folder
.\res\folder
\Pictures
..\..\CCleaner.exe
C:\hello\world\..\test.ahk
folder\..\folder
D:\..\file.ahk
)

Loop, Parse, List, `n, `r
    MsgBox, % A_LoopField . "`n" . resolvePath(A_LoopField)

Code: Select all

/*
Copyright (c) 2015, Tuncay
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer. 
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies, 
either expressed or implied, of the FreeBSD Project.
*/
resolvePath(p_Path)
{
    ; Set variables to default. 
    r_Path := ""
    Path := ""
    PathDrive := ""
    ; Check, if the path is absolute. Otherwise, use base directory.
    Path := Trim(p_Path)
    SplitPath, Path,,,,, PathDrive
    ; Makes it full path if it does not contain a drive.
    if (PathDrive = "")
    {
        ; Backslash indicates scripts own directory as the base.
        ; No slash or single dot and backslash means current working directory.
        if (SubStr(Path, 1, 1) = "\")
        {
            Path := A_ScriptDir . Path
        }
        else if (SubStr(Path, 1, 2) = ".\")
        {
            ; Dot means current working directory. Strip that dot and build full path.
            Path := A_WorkingDir . SubStr(Path, 2)
        }
        else
        {
            ; In all other cases it is the current working directory.
            Path := A_WorkingDir . "\" . Path
        }
    }
    ; Resolve every part and build step wise.
    r_Path := ""
    Loop, Parse, Path, \
    {
        SplitPath, A_LoopField, Name
        if (Name = "..")
        {
            ; Deletes last part of current path.
            r_Path := RegExReplace(r_Path, "DUS)\\[^\\]*\\$", "\", "", 1)
        }
        else if (Name = ".")
        {
            ; Just ignores, as dot means current path.
            r_Path := r_Path
        }
        else
        {
            ; Regular part will be added with trailing backslash.
            r_Path .= A_LoopField . "\"
        }
    }
    ; After all, last trailing slash will be stripped away.
    if (SubStr(r_Path, 0, 1) = "\")
    {
        StringTrimRight, r_Path, r_Path, 1
    }
    return r_Path
}

Re: RefinePath()

Posted: 11 Feb 2015, 16:05
by Learning one
RefinePath() updated to v1.02 Thanks for demonstrating your attempt of this issue Tuncay.