Repair path names with ..

From NSIS Wiki
Jump to navigationJump to search

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