Repair path names with ..: Difference between revisions

From NSIS Wiki
Jump to navigationJump to search
No edit summary
 
m (added third note and function-using section)
 
(4 intermediate revisions by 3 users not shown)
Line 1: Line 1:
Seeing as how this page didn't exist yet, but the topic did, hopefully somebody else can clean it up...
you have a path with relative path parts, such as
<highlight-nsis>
\\ ; does nothing
\.\ ; stay in same folder
\..\ ; go up a folder
</highlight-nsis>


Below is a very naive piece of code, which uses LogicLib a bit to keep things sane.
And you need the absolute path, then you can use the "GetFullPathName" command in NSIS to get the absolute path. For example:
<highlight-nsis>
<highlight-nsis>
Section "Bladeebla"
Section Test
    StrCpy $R0 "C:\folder\subfolder\.\anotherfolder\..\someotherfolder\somefile.ext"
GetFullPathName $0 "c:\program files\myApp\somedir\subdir\..\somefile.ext"
    StrCpy $4 $R0 1 0
; $0 will now contain "c:\program files\myApp\somedir\somefile.ext"
    StrLen $1 $R0
SectionEnd
    StrCpy $2 0
</highlight-nsis>
    Push "RelAbs"
_loop:
    IntOp $2 $2 + 1
    ${If} $2 > $1
        goto _end
${EndIf}
StrCpy $0 $R0 3 $2
${If} $0 == "..\"
    Pop $4
    Pop $4
    IntOp $2 $2 + 2
${ElseIf} $0 == "\.\"
IntOp $2 $2 + 1
${Else}
    StrCpy $3 $R0 1 $2
    StrCpy $4 "$4$3"
    ${If} $3 == "\"
Push $4
${EndIf}
${EndIf}
goto _loop
_end:


_loop2:
Note 1: if you specify a filename, that file has to exist or the result is null.It is therefore recommended that if you need the absolute path without necessarily having the file in existence, that you strip the filename from the path.
Pop $0
${If} $0 == "RelAbs"
    goto _end2
${EndIf}
goto _loop2
_end2:


MessageBox MB_OK "$R0$\r$\n$4"
Note 2: this command is limited to a 256-character string, even if the resulting absolute path is shorter than 256 characters.
 
Note 3: if the string being fed does not start with an absolute reference, it will be treated as if relative to the current working folder, and the absolute path result will reflect this.  E.g.
<highlight-nsis>
Section Test
GetFullPathName $0 "FooBar\"
; $0 may now contain "c:\test\Foobar\" if the installer was executed in "c:\test\"
SectionEnd
SectionEnd
</highlight-nsis>
</highlight-nsis>


This will display a messagebox with:
C:\folder\subfolder\.\anotherfolder\..\someotherfolder\somefile.ext
C:\folder\subfolder\someotherfolder\somefile.ext


Note that instead of lots of string manipulations, this code is either lazy or smart, depending on how you look at it.  Everytime a "\.\" is encountered, it ignores it.  Everytime a "..\" is encountered, it also ignores it, and Pops the currently collected path off the stack, twice, to get back to what the collected path was before the parent folder was added.
If any of the above notes may preclude you from using this Command, try the below Function instead.
If a "\" is encountered not as part of the above two, the currently collected path is pushed to the stack, effectively pushing the path with each folder encountered to the stack.
Then at the end the stack is cleaned up by popping off until "RelAbs" is the value popped.


Please note that this means you can't feed it paths like "..\whatever" because it doesn't know where to start in. It also doesn't deal with forward slashes "/". It doesn't bother cleaning up "\\", etc. So the above code really is quite naive, should be cleaned up, etc. but may be a good starting point as it doesn't do insane things with strings using a ton of variables.
<highlight-nsis>
Section "Rel2Abs"
Push "c:\Alpha\\\Bravo\\Charlie..charles\\.\delta\\echo\\..\..\foxtrot\\..\Golf\\.\Hotel\\.\india\\..\\\\juliet\\..\kilo\\\..\Li.....ma\\.\..Mike\\\November\\.\oscar\\..\Papa\\\Quebec..\\\\\\\\Romeo\\sierra\\..\tang.o\\uniform\\victor\\..\whiskey\\..\\x-ray\\yankee\\..\..\.\..\..\Zu.lu\\file.ext"
Call Abs2Rel
Pop $0
MessageBox MB_OK "Expected:$\tc:\Alpha\Bravo\Charlie..charles\Golf\Hotel\Li.....ma\..Mike\November\Papa\Quebec..\Romeo\Zu.lu\file.ext$\r$\nResult:$\t$\t$0"
SectionEnd
 
Function Abs2Rel
; Stack -> ; <relative path> ; This and the stuff in the column below is just to keep track of the stack before and after the function so that it's clean at the end.
Exch $R0 ; R0 ; $R0 now holds the relative path
Push $0 ; 0 R0 ; $0 will hold the absolute path as it is being built
Push $1 ; 1 0 R0 ; $1 will hold the length of the relative path
Push $2 ; 2 1 0 R0 ; $2 will hold a counter for running along the path
Push $3 ; 3 2 1 0 R0 ; $3 will hold a substring of the paths to test against
 
StrCpy $0 $R0 1 0 ; Start by getting the first character of the relative path into the absolute path var
StrLen $1 $R0 ; Get the length of the relative path
StrCpy $2 0 ; And start the counter off at zero
 
; example path at this point
; c:\Alpha\\\Bravo\\Charlie..charles\\.\delta\\echo\\..\..\foxtrot\\..\Golf\\.\Hotel\\.\india\\..\\\\juliet\\..\kilo\\\..\Li.....ma\\.\..Mike\\\November\\.\oscar\\..\Papa\\\Quebec..\\\\\\\\Romeo\\sierra\\..\tang.o\\uniform\\victor\\..\whiskey\\..\\x-ray\\yankee\\..\..\.\..\..\Zu.lu\\file.ext
 
; double ; This section will get rid of all double backslashes.
_loop_double: ; top of the loop
IntOp $2 $2 + 1 ; increase the counter
IntCmp $2 $1 0 0 _sameDir ; if the counter exceeds the relative path's length, move on
StrCpy $3 $R0 2 $2 ; get two characters from the relative pah, starting at the position of the counter
StrCmp $3 "\\" _loop_double ; if it's a double backslash, just go back to the top of the loop
StrCpy $3 $R0 1 $2 ; otherwise, get a single character from the relative path, starting at the position of the counter
StrCpy $0 "$0$3" ; and add that character to the absolute path being built
goto _loop_double ; then go back to the top of the loop
 
; example path at this point
; c:\Alpha\Bravo\Charlie..charles\.\delta\echo\..\..\foxtrot\..\Golf\.\Hotel\.\india\..\juliet\..\kilo\..\Li.....ma\.\..Mike\November\.\oscar\..\Papa\Quebec..\Romeo\sierra\..\tang.o\uniform\victor\..\whiskey\..\x-ray\yankee\..\..\.\..\..\Zu.lu\file.ext
 
_sameDir: ; This section will get rid of all \.\ occurrences.
StrCpy $R0 $0 ; First, copy the working copy absolute path into the relative path var. We won't need the original anymore.
StrCpy $0 $R0 1 0 ; Again, get the first character
StrLen $1 $R0 ; Get the length
StrCpy $2 0 ; Start the counter at zero
 
_loop_sameDir: ; top of the loop
IntOp $2 $2 + 1 ; increase the counter
IntCmp $2 $1 0 0 _dirup ; if the counter exceeds the relative path's length, move on
StrCpy $3 $R0 3 $2 ; get three characters from the relative path, starting at the position of the counter
StrCmp $3 "\.\" 0 +3 ; if that's \.\ , then we'll have to...
IntOp $2 $2 + 1 ; increase the counter by 1 to stop going over the period.
goto _loop_sameDir ; and go back to the top of the loop
 
StrCpy $3 $R0 1 $2 ; otherwise, get a single character from the relative path, starting at the position of the counter
StrCpy $0 "$0$3" ; and add that character to the absolute path being built
goto _loop_sameDir ; then go back to the top of the loop
 
; example path at this point
; c:\Alpha\Bravo\Charlie..charles\delta\echo\..\..\foxtrot\..\Golf\Hotel\india\..\juliet\..\kilo\..\Li.....ma\..Mike\November\oscar\..\Papa\Quebec..\Romeo\sierra\..\tang.o\uniform\victor\..\whiskey\..\x-ray\yankee\..\..\..\..\Zu.lu\file.ext
 
_dirup: ; This section will deal with \..\ occurrences, removing them -and- the parent folder
StrCpy $R0 $0 ; this
StrCpy $0 $R0 1 0 ; should
StrLen $1 $R0 ; look
StrCpy $2 0 ; familar
Push "Rel2Abs" ; push a tag to the top of the stack. We'll be doing some popping further down and want to make sure we don't exceed the stack/etc.
_loop_dirup: ; more
IntOp $2 $2 + 1 ; familiar
IntCmp $2 $1 0 0 _loop_cleanStack ; things
 
StrCpy $3 $R0 1 $2 ; get a single character from the relative path, starting at the position of the counter
StrCpy $0 "$0$3" ; add that character to the absolute path being built
StrCmp $3 "\" 0 _loop_dirup ; if the character was a backslash, continue - otherwise, go back to the top of the stack
StrCpy $3 $0 4 -4 ; get -four- characters from the relative path, starting at the position of the counter
StrCmp $3 "\..\" 0 _subDir ; if those four were \..\, continue to process this as going up a dir - otherwise we're just going down a dir
Pop $0 ; Get the top of the stack into our absolute path. Say the stack looked like "c:\test\", "c:\test\hello\", and we just encountered "c:\test\hello\,,\", then this will be "c:\test\hello\"
StrCmp $0 "Rel2Abs" _error ; If the value is our tag, then we just went up the stack too far, and the path this function was fed is invalid, so error out.
Pop $0 ; Get the top of the stack in our absolute path. Going with the previous example, this value will now be "c:\test\", and as such as we just moved up a dir.
StrCmp $0 "Rel2Abs" _error ; If the value is our tag, then we just went up the stack too far, and the path this function was fed is invalid, so error out.
Push $0 ; Push this value back onto the stack because we'll need it on there.
goto _loop_dirup ; And go back to the top of the loop
_subDir: ; Otherwise we went down a dir
Push $0 ; So we'll just push this absolute path so far onto the stacak.
goto _loop_dirup ; and go back to the top of the loop
 
; example path at this point
; c:\Alpha\Bravo\Charlie..charles\Golf\Hotel\Li.....ma\..Mike\November\Papa\Quebec..\Romeo\Zu.lu\file.ext
 
_loop_cleanStack: ; At this point we already have our relative path - but there's a bunch of bits stuck on the stack
Pop $1 ; So we pop the stack
StrCmp $1 "Rel2Abs" 0 _loop_cleanStack ; and as long as the value isn't our tag, we'll keep on popping
 
StrCpy $R0 $0 ; Once it is, we're done entirely - copy the absolute path into $R0 for end-function stack purposes
goto _end ; goto the end (skip over the error)
 
_error: ; This is where we went if the path being fed was mangled or we ran into our tag early otherwise
StrCpy $R0 "-1" ; we'll just set the return value to -1
 
_end: ; end of the function, time to restore variables off the stack and push the result onto it.
; Stack -> ; 3 2 1 0 R0
Pop $3 ; 2 1 0 R0
Pop $2 ; 1 0 R0
Pop $1 ; 0 R0
Pop $0 ; R0
Exch $R0 ; <absolute path>
FunctionEnd
</highlight-nsis>

Latest revision as of 21:11, 1 October 2006

you have a path with relative path parts, such as

\\	; does nothing
\.\	; stay in same folder
\..\	; go up a folder

And you need the absolute path, then you can use the "GetFullPathName" command in NSIS to get the absolute path. For example:

Section Test
	GetFullPathName $0 "c:\program files\myApp\somedir\subdir\..\somefile.ext"
	; $0 will now contain "c:\program files\myApp\somedir\somefile.ext"
SectionEnd

Note 1: if you specify a filename, that file has to exist or the result is null.It is therefore recommended that if you need the absolute path without necessarily having the file in existence, that you strip the filename from the path.

Note 2: this command is limited to a 256-character string, even if the resulting absolute path is shorter than 256 characters.

Note 3: if the string being fed does not start with an absolute reference, it will be treated as if relative to the current working folder, and the absolute path result will reflect this. E.g.

Section Test
	GetFullPathName $0 "FooBar\"
	; $0 may now contain "c:\test\Foobar\" if the installer was executed in "c:\test\"
SectionEnd


If any of the above notes may preclude you from using this Command, try the below Function instead.

Section "Rel2Abs"
	Push "c:\Alpha\\\Bravo\\Charlie..charles\\.\delta\\echo\\..\..\foxtrot\\..\Golf\\.\Hotel\\.\india\\..\\\\juliet\\..\kilo\\\..\Li.....ma\\.\..Mike\\\November\\.\oscar\\..\Papa\\\Quebec..\\\\\\\\Romeo\\sierra\\..\tang.o\\uniform\\victor\\..\whiskey\\..\\x-ray\\yankee\\..\..\.\..\..\Zu.lu\\file.ext"
	Call Abs2Rel
	Pop $0
	MessageBox MB_OK "Expected:$\tc:\Alpha\Bravo\Charlie..charles\Golf\Hotel\Li.....ma\..Mike\November\Papa\Quebec..\Romeo\Zu.lu\file.ext$\r$\nResult:$\t$\t$0"
SectionEnd
 
Function Abs2Rel
	; Stack ->		; <relative path>			; This and the stuff in the column below is just to keep track of the stack before and after the function so that it's clean at the end.
	Exch $R0		; R0						; $R0 now holds the relative path
	Push $0			; 0 R0						; $0 will hold the absolute path as it is being built
	Push $1			; 1 0 R0					; $1 will hold the length of the relative path
	Push $2			; 2 1 0 R0					; $2 will hold a counter for running along the path
	Push $3			; 3 2 1 0 R0				; $3 will hold a substring of the paths to test against
 
	StrCpy $0 $R0 1 0						 	; Start by getting the first character of the relative path into the absolute path var
	StrLen $1 $R0								 ; Get the length of the relative path
	StrCpy $2 0								 ; And start the counter off at zero
 
	; example path at this point
	; c:\Alpha\\\Bravo\\Charlie..charles\\.\delta\\echo\\..\..\foxtrot\\..\Golf\\.\Hotel\\.\india\\..\\\\juliet\\..\kilo\\\..\Li.....ma\\.\..Mike\\\November\\.\oscar\\..\Papa\\\Quebec..\\\\\\\\Romeo\\sierra\\..\tang.o\\uniform\\victor\\..\whiskey\\..\\x-ray\\yankee\\..\..\.\..\..\Zu.lu\\file.ext
 
	; double									; This section will get rid of all double backslashes.
		_loop_double:							 ; top of the loop
			IntOp $2 $2 + 1					 ; increase the counter
			IntCmp $2 $1 0 0 _sameDir			 ; if the counter exceeds the relative path's length, move on
			StrCpy $3 $R0 2 $2					; get two characters from the relative pah, starting at the position of the counter
			StrCmp $3 "\\" _loop_double		 ; if it's a double backslash, just go back to the top of the loop
			StrCpy $3 $R0 1 $2					; otherwise, get a single character from the relative path, starting at the position of the counter
			StrCpy $0 "$0$3"					; and add that character to the absolute path being built
			goto _loop_double					 ; then go back to the top of the loop
 
	; example path at this point
	; c:\Alpha\Bravo\Charlie..charles\.\delta\echo\..\..\foxtrot\..\Golf\.\Hotel\.\india\..\juliet\..\kilo\..\Li.....ma\.\..Mike\November\.\oscar\..\Papa\Quebec..\Romeo\sierra\..\tang.o\uniform\victor\..\whiskey\..\x-ray\yankee\..\..\.\..\..\Zu.lu\file.ext
 
	_sameDir:									 ; This section will get rid of all \.\ occurrences.
		StrCpy $R0 $0							 ; First, copy the working copy absolute path into the relative path var.	We won't need the original anymore.
		StrCpy $0 $R0 1 0						 ; Again, get the first character
		StrLen $1 $R0							 ; Get the length
		StrCpy $2 0							 ; Start the counter at zero
 
		_loop_sameDir:							; top of the loop
			IntOp $2 $2 + 1					 ; increase the counter
			IntCmp $2 $1 0 0 _dirup			 ; if the counter exceeds the relative path's length, move on
			StrCpy $3 $R0 3 $2					; get three characters from the relative path, starting at the position of the counter
			StrCmp $3 "\.\" 0 +3				; if that's \.\ , then we'll have to...
				IntOp $2 $2 + 1				 	; increase the counter by 1 to stop going over the period.
				goto _loop_sameDir					; and go back to the top of the loop
 
			StrCpy $3 $R0 1 $2					; otherwise, get a single character from the relative path, starting at the position of the counter
			StrCpy $0 "$0$3"					; and add that character to the absolute path being built
			goto _loop_sameDir					; then go back to the top of the loop
 
	; example path at this point
	; c:\Alpha\Bravo\Charlie..charles\delta\echo\..\..\foxtrot\..\Golf\Hotel\india\..\juliet\..\kilo\..\Li.....ma\..Mike\November\oscar\..\Papa\Quebec..\Romeo\sierra\..\tang.o\uniform\victor\..\whiskey\..\x-ray\yankee\..\..\..\..\Zu.lu\file.ext
 
	_dirup:									 ; This section will deal with \..\ occurrences, removing them -and- the parent folder
		StrCpy $R0 $0							 ; this
		StrCpy $0 $R0 1 0						 ; should
		StrLen $1 $R0							 ; look
		StrCpy $2 0							 ; familar
		Push "Rel2Abs"							; push a tag to the top of the stack.	We'll be doing some popping further down and want to make sure we don't exceed the stack/etc.
		_loop_dirup:							; more
			IntOp $2 $2 + 1					 ; familiar
			IntCmp $2 $1 0 0 _loop_cleanStack	 ; things
 
			StrCpy $3 $R0 1 $2					; get a single character from the relative path, starting at the position of the counter
			StrCpy $0 "$0$3"					; add that character to the absolute path being built
			StrCmp $3 "\" 0 _loop_dirup		 ; if the character was a backslash, continue - otherwise, go back to the top of the stack
				StrCpy $3 $0 4 -4				 ; get -four- characters from the relative path, starting at the position of the counter
				StrCmp $3 "\..\" 0 _subDir		; if those four were \..\, continue to process this as going up a dir - otherwise we're just going down a dir
					Pop $0							; Get the top of the stack into our absolute path. Say the stack looked like "c:\test\", "c:\test\hello\", and we just encountered "c:\test\hello\,,\", then this will be "c:\test\hello\"
					StrCmp $0 "Rel2Abs" _error		; If the value is our tag, then we just went up the stack too far, and the path this function was fed is invalid, so error out.
					Pop $0							; Get the top of the stack in our absolute path.	Going with the previous example, this value will now be "c:\test\", and as such as we just moved up a dir.
					StrCmp $0 "Rel2Abs" _error		; If the value is our tag, then we just went up the stack too far, and the path this function was fed is invalid, so error out.
					Push $0						 ; Push this value back onto the stack because we'll need it on there.
					goto _loop_dirup				; And go back to the top of the loop
				_subDir:						; Otherwise we went down a dir
					Push $0						 ; So we'll just push this absolute path so far onto the stacak.
					goto _loop_dirup				; and go back to the top of the loop
 
	; example path at this point
	; c:\Alpha\Bravo\Charlie..charles\Golf\Hotel\Li.....ma\..Mike\November\Papa\Quebec..\Romeo\Zu.lu\file.ext
 
	_loop_cleanStack:							 ; At this point we already have our relative path - but there's a bunch of bits stuck on the stack
		Pop $1									; So we pop the stack
		StrCmp $1 "Rel2Abs" 0 _loop_cleanStack	; and as long as the value isn't our tag, we'll keep on popping
 
	StrCpy $R0 $0							 	; Once it is, we're done entirely - copy the absolute path into $R0 for end-function stack purposes
	goto _end									 ; goto the end (skip over the error)
 
	_error:									 ; This is where we went if the path being fed was mangled or we ran into our tag early otherwise
		StrCpy $R0 "-1"						 ; we'll just set the return value to -1
 
	_end:										 ; end of the function, time to restore variables off the stack and push the result onto it.
	; Stack ->		; 3 2 1 0 R0
	Pop $3			; 2 1 0 R0
	Pop $2			; 1 0 R0
	Pop $1			; 0 R0
	Pop $0			; R0
	Exch $R0		; <absolute path>
FunctionEnd