Path Manipulation: Difference between revisions
(→Warning: Add link to safe version of AddToPath, remove smartmontools link) |
|||
(8 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
{{PageAuthor|turnec2}} | {{PageAuthor|turnec2}} | ||
<div style="background-color:#ff9999; color:#f00; border:2px solid #f00; padding:1em;"><font size="+1"><b>WARNING:</b> Strings longer than ${NSIS_MAX_STRLEN} will get truncated/corrupted. Do NOT use this function to update %PATH%, use the [[EnVar_plug-in]] instead.</font> (See [[#Warning|below]] for a link to an alternative implementation.)</div> | |||
Line 14: | Line 16: | ||
== Usage == | == Usage == | ||
See [[ | See [[Environmental Variables: append, prepend, and remove entries]] for more details. | ||
=== Warning === | === Warning === | ||
Line 20: | Line 22: | ||
'''Warning : this code will replace paths rather than append if the existing path exceeds the maximum string length in the NSIS build you are using. Some setup crash can also occurs.''' | '''Warning : this code will replace paths rather than append if the existing path exceeds the maximum string length in the NSIS build you are using. Some setup crash can also occurs.''' | ||
Default maximum string length is 1024, see [[ | Default maximum string length is 1024, see [[Special Builds]] for a 8192 max length. | ||
The | The problem occurs because the function [[Reference/ReadRegStr|ReadRegStr]] returns the same result if 'value is too long' and if 'value is not set'. | ||
the | |||
[[ | |||
This could be fixed by using the native Win32 function [https://msdn.microsoft.com/en-us/library/windows/desktop/ms724911.aspx RegQueryValueEx()] instead. | |||
See [[AddToPath safe|this alternative implementation]] for a working example. | |||
=== Syntax === | === Syntax === | ||
Line 123: | Line 91: | ||
== Function Code == | == Function Code == | ||
[[ | [[Environmental Variables: append, prepend, and remove entries#Function Code|EnvVarUpdate function code]] | ||
== Functions for Windows 95, 98, and ME == | == Functions for Windows 95, 98, and ME == |
Latest revision as of 14:40, 13 December 2022
Author: turnec2 (talk, contrib) |
Description
Version: 1.0
Append, prepend or remove a pathname from the PATH environmental variable in either the "all users" (HKLM) or the "current user" (HKCU) section of the registry.
Duplicates entries, leading and trailing spaces around each entry, and duplicate semicolons are removed from the contents of PATH before performing the requested operation. This step prevents the corruption of the variable's contents (e.g., when removing a target pathname that is a subset of another pathname).
Usage
See Environmental Variables: append, prepend, and remove entries for more details.
Warning
Warning : this code will replace paths rather than append if the existing path exceeds the maximum string length in the NSIS build you are using. Some setup crash can also occurs.
Default maximum string length is 1024, see Special Builds for a 8192 max length.
The problem occurs because the function ReadRegStr returns the same result if 'value is too long' and if 'value is not set'.
This could be fixed by using the native Win32 function RegQueryValueEx() instead. See this alternative implementation for a working example.
Syntax
${EnvVarUpdate} "ResultVar" "EnvVarName" "Action" "RegLoc" "Pathname"
or
Push "EnvVarName" Push "Action" Push "RegLoc" Push "Pathname" Call EnvVarUpdate Pop "ResultVar"
Parameters
- ResultVar
- Updated contents PATH returned by the function
- EnvVarName
- "PATH"
- Action
- "A" = Append
- "P" = Prepend
- "R" = Remove
- RegLoc
- "HKLM" = the "all users" section of the registry
- "HKCU" = the "current user" section
- Pathname
- Pathname to add to or remove from the contents of PATH (e.g., "C:\MyApp")
Examples
${EnvVarUpdate} $0 "PATH" "A" "HKLM" "C:\Program Files\Windows Resource Kits\Tools" ${EnvVarUpdate} $0 "PATH" "P" "HKCU" "%WinDir%\System32" ${EnvVarUpdate} $0 "PATH" "R" "HKLM" "C:\Program Files\MyApp-v1.0" ${EnvVarUpdate} $0 "PATH" "A" "HKLM" "C:\Program Files\MyApp-v2.0" ${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" "C:\MyLib-v1.0" ${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" "C:\MyLib-v2.0"
Dependencies
- EnvVarUpdate (available at Environmental_Variables:_append,_prepend,_and_remove_entries)
- StrTok, StrContains, and StrReplace: Included in the EnvVarUpdate function code
- LogicLib header file: "LogicLib.nsh"
Credits
- Version 1.0: Cal Turney (turnec2)
- Amir Szekely (KiCHiK) and e-circ for developing the forerunners of this function: AddToPath, un.RemoveFromPath, AddToEnvVar, un.RemoveFromEnvVar, WriteEnvStr, and un.DeleteEnvStr
- Diego Pedroso (deguix) for StrTok
- Kevin English (kenglish_hi)for StrContains
- Hendri Adriaens (Smile2Me), Diego Pedroso (deguix), and Dan Fuhry (dandaman32) for StrReplace
Function Code
Functions for Windows 95, 98, and ME
Author: KiCHiK (talk, contrib) |
The following routines, AddToPath, un.RemoveFromPath, AddToEnvVar, and un.RemoveFromEnvVar are provided for Windows 95, 98, or ME in that they support the update of the PATH variable within the autoexec.bat file.
NOTE: These routines can corrupt the contents of the PATH variable if the path being removed happens to be a subset of another path. For example, if PATH contains "C:\Windows\system32\wbem" and you remove "C:\Windows\system32" the result is "\wbem". They also do not prevent duplication of entries if the user runs the installer multiple times.
Description
Here are four functions, two for adding to and removing directories from the search path (i.e. PATH environment variable) of Windows and two more for adding to and removing directories from a user defined environment variable such as INCLUDE or LIB etc. These functions should work on every version of Windows including 95, 98, ME, NT, 2000, and XP.
For Windows NT/2000/XP they will write to HKEY_CURRENT_USER\Environment\PATH. For 9x/ME they will write to the autoexec.bat file.
Changes on 9x machines will take place after a reboot. These functions set the reboot flag if the path variable was changed on 9x.
These functions require NSIS 2.0b0 and above due to the usage of SendMessage with /TIMEOUT. It is possible to use it with earlier versions of NSIS with a little application I have created called RefreshEnv. It does exactly the same as SendMessage with /TIMEOUT. You can find it at this forum thread.
The directory added to the path must exist prior to calling this function.
Usage Example
!include "LogicLib.nsh" !include "EnvVarUpdate.nsh" #download http://nsis.sourceforge.net/mediawiki/images/a/ad/EnvVarUpdate.7z Section "Add to path" Push $INSTDIR Call AddToPath Push "LIB" Push "c:\Mylibrary\lib" Call AddToEnvVar ;likewise AddToPath could be Push "PATH" Push $INSTDIR Call AddToEnvVar SectionEnd # ... Section "uninstall" # ... Push $INSTDIR Call un.RemoveFromPath # ... Push "LIB" Push "c:\Mylibrary\lib" Call un.RemoveFromEnvVar ;Likewise RemoveFromPath could be Push "PATH" Push $INSTDIR Call un.RemoveFromEnvVar SectionEnd
The Functions
The functions in the flesh:
!ifndef _AddToPath_nsh !define _AddToPath_nsh !verbose 3 !include "WinMessages.NSH" !verbose 4 !ifndef WriteEnvStr_RegKey !ifdef ALL_USERS !define WriteEnvStr_RegKey \ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !else !define WriteEnvStr_RegKey 'HKCU "Environment"' !endif !endif ; AddToPath - Adds the given dir to the search path. ; Input - head of the stack ; Note - Win9x systems requires reboot Function AddToPath Exch $0 Push $1 Push $2 Push $3 # don't add if the path doesn't exist IfFileExists "$0\*.*" "" AddToPath_done ReadEnvStr $1 PATH Push "$1;" Push "$0;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$0\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done GetFullPathName /SHORT $3 $0 Push "$1;" Push "$3;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$3\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Call IsNT Pop $1 StrCmp $1 1 AddToPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" a FileSeek $1 -1 END FileReadByte $1 $2 IntCmp $2 26 0 +2 +2 # DOS EOF FileSeek $1 -1 END # write over EOF FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" FileClose $1 SetRebootFlag true Goto AddToPath_done AddToPath_NT: ReadRegStr $1 ${WriteEnvStr_RegKey} "PATH" StrCmp $1 "" AddToPath_NTdoIt Push $1 Call Trim Pop $1 StrCpy $0 "$1;$0" AddToPath_NTdoIt: WriteRegExpandStr ${WriteEnvStr_RegKey} "PATH" $0 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 AddToPath_done: Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ; RemoveFromPath - Remove a given dir from the path ; Input: head of the stack Function un.RemoveFromPath Exch $0 Push $1 Push $2 Push $3 Push $4 Push $5 Push $6 IntFmt $6 "%c" 26 # DOS EOF Call un.IsNT Pop $1 StrCmp $1 1 unRemoveFromPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" r GetTempFileName $4 FileOpen $2 $4 w GetFullPathName /SHORT $0 $0 StrCpy $0 "SET PATH=%PATH%;$0" Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoop: FileRead $1 $3 StrCpy $5 $3 1 -1 # read last char StrCmp $5 $6 0 +2 # if DOS EOF StrCpy $3 $3 -1 # remove DOS EOF so we can compare StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "" unRemoveFromPath_dosLoopEnd FileWrite $2 $3 Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopRemoveLine: SetRebootFlag true Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopEnd: FileClose $2 FileClose $1 StrCpy $1 $WINDIR 2 Delete "$1\autoexec.bat" CopyFiles /SILENT $4 "$1\autoexec.bat" Delete $4 Goto unRemoveFromPath_done unRemoveFromPath_NT: ReadRegStr $1 ${WriteEnvStr_RegKey} "PATH" StrCpy $5 $1 1 -1 # copy last char StrCmp $5 ";" +2 # if last char != ; StrCpy $1 "$1;" # append ; Push $1 Push "$0;" Call un.StrStr ; Find `$0;` in $1 Pop $2 ; pos of our dir StrCmp $2 "" unRemoveFromPath_done ; else, it is in path # $0 - path to add # $1 - path var StrLen $3 "$0;" StrLen $4 $2 StrCpy $5 $1 -$4 # $5 is now the part before the path to remove StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove StrCpy $3 $5$6 StrCpy $5 $3 1 -1 # copy last char StrCmp $5 ";" 0 +2 # if last char == ; StrCpy $3 $3 -1 # remove last char WriteRegExpandStr ${WriteEnvStr_RegKey} "PATH" $3 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 unRemoveFromPath_done: Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ; AddToEnvVar - Adds the given value to the given environment var ; Input - head of the stack $0 environement variable $1=value to add ; Note - Win9x systems requires reboot Function AddToEnvVar Exch $1 ; $1 has environment variable value Exch Exch $0 ; $0 has environment variable name DetailPrint "Adding $1 to $0" Push $2 Push $3 Push $4 ReadEnvStr $2 $0 Push "$2;" Push "$1;" Call StrStr Pop $3 StrCmp $3 "" "" AddToEnvVar_done Push "$2;" Push "$1\;" Call StrStr Pop $3 StrCmp $3 "" "" AddToEnvVar_done Call IsNT Pop $2 StrCmp $2 1 AddToEnvVar_NT ; Not on NT StrCpy $2 $WINDIR 2 FileOpen $2 "$2\autoexec.bat" a FileSeek $2 -1 END FileReadByte $2 $3 IntCmp $3 26 0 +2 +2 # DOS EOF FileSeek $2 -1 END # write over EOF FileWrite $2 "$\r$\nSET $0=%$0%;$4$\r$\n" FileClose $2 SetRebootFlag true Goto AddToEnvVar_done AddToEnvVar_NT: ReadRegStr $2 ${WriteEnvStr_RegKey} $0 StrCpy $3 $2 1 -1 # copy last char StrCmp $3 ";" 0 +2 # if last char == ; StrCpy $2 $2 -1 # remove last char StrCmp $2 "" AddToEnvVar_NTdoIt StrCpy $1 "$2;$1" AddToEnvVar_NTdoIt: WriteRegExpandStr ${WriteEnvStr_RegKey} $0 $1 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 AddToEnvVar_done: Pop $4 Pop $3 Pop $2 Pop $0 Pop $1 FunctionEnd ; RemoveFromEnvVar - Remove a given value from a environment var ; Input: head of the stack Function un.RemoveFromEnvVar Exch $1 ; $1 has environment variable value Exch Exch $0 ; $0 has environment variable name DetailPrint "Removing $1 from $0" Push $2 Push $3 Push $4 Push $5 Push $6 Push $7 IntFmt $7 "%c" 26 # DOS EOF Call un.IsNT Pop $2 StrCmp $2 1 unRemoveFromEnvVar_NT ; Not on NT StrCpy $2 $WINDIR 2 FileOpen $2 "$2\autoexec.bat" r GetTempFileName $5 FileOpen $3 $5 w GetFullPathName /SHORT $1 $1 StrCpy $1 "SET $0=%$0%;$1" Goto unRemoveFromEnvVar_dosLoop unRemoveFromEnvVar_dosLoop: FileRead $2 $4 StrCpy $6 $4 1 -1 # read last char StrCmp $6 $7 0 +2 # if DOS EOF StrCpy $4 $4 -1 # remove DOS EOF so we can compare StrCmp $4 "$1$\r$\n" unRemoveFromEnvVar_dosLoopRemoveLine StrCmp $4 "$1$\n" unRemoveFromEnvVar_dosLoopRemoveLine StrCmp $4 "$1" unRemoveFromEnvVar_dosLoopRemoveLine StrCmp $4 "" unRemoveFromEnvVar_dosLoopEnd FileWrite $3 $4 Goto unRemoveFromEnvVar_dosLoop unRemoveFromEnvVar_dosLoopRemoveLine: SetRebootFlag true Goto unRemoveFromEnvVar_dosLoop unRemoveFromEnvVar_dosLoopEnd: FileClose $3 FileClose $2 StrCpy $2 $WINDIR 2 Delete "$2\autoexec.bat" CopyFiles /SILENT $5 "$2\autoexec.bat" Delete $5 Goto unRemoveFromEnvVar_done unRemoveFromEnvVar_NT: ReadRegStr $2 ${WriteEnvStr_RegKey} $0 StrCpy $6 $2 1 -1 # copy last char StrCmp $6 ";" +2 # if last char != ; StrCpy $2 "$2;" # append ; Push $2 Push "$1;" Call un.StrStr ; Find `$1;` in $2 Pop $3 ; pos of our dir StrCmp $3 "" unRemoveFromEnvVar_done ; else, it is in path # $1 - path to add # $2 - path var StrLen $4 "$1;" StrLen $5 $3 StrCpy $6 $2 -$5 # $6 is now the part before the path to remove StrCpy $7 $3 "" $4 # $7 is now the part after the path to remove StrCpy $4 $6$7 StrCpy $6 $4 1 -1 # copy last char StrCmp $6 ";" 0 +2 # if last char == ; StrCpy $4 $4 -1 # remove last char WriteRegExpandStr ${WriteEnvStr_RegKey} $0 $4 ; delete reg value if null StrCmp $4 "" 0 +2 # if null delete reg DeleteRegValue ${WriteEnvStr_RegKey} $0 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 unRemoveFromEnvVar_done: Pop $7 Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd !ifndef IsNT_KiCHiK !define IsNT_KiCHiK ########################################### # Utility Functions # ########################################### ; IsNT ; no input ; output, top of the stack = 1 if NT or 0 if not ; ; Usage: ; Call IsNT ; Pop $R0 ; ($R0 at this point is 1 or 0) !macro IsNT un Function ${un}IsNT Push $0 ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion StrCmp $0 "" 0 IsNT_yes ; we are not NT. Pop $0 Push 0 Return IsNT_yes: ; NT!!! Pop $0 Push 1 FunctionEnd !macroend !insertmacro IsNT "" !insertmacro IsNT "un." !endif ; IsNT_KiCHiK ; StrStr ; input, top of stack = string to search for ; top of stack-1 = string to search in ; output, top of stack (replaces with the portion of the string remaining) ; modifies no other variables. ; ; Usage: ; Push "this is a long ass string" ; Push "ass" ; Call StrStr ; Pop $R0 ; ($R0 at this point is "ass string") !macro StrStr un Function ${un}StrStr Exch $R1 ; st=haystack,old$R1, $R1=needle Exch ; st=old$R1,haystack Exch $R2 ; st=old$R1,old$R2, $R2=haystack Push $R3 Push $R4 Push $R5 StrLen $R3 $R1 StrCpy $R4 0 ; $R1=needle ; $R2=haystack ; $R3=len(needle) ; $R4=cnt ; $R5=tmp loop: StrCpy $R5 $R2 $R3 $R4 StrCmp $R5 $R1 done StrCmp $R5 "" done IntOp $R4 $R4 + 1 Goto loop done: StrCpy $R1 $R2 "" $R4 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Exch $R1 FunctionEnd !macroend !insertmacro StrStr "" !insertmacro StrStr "un." Function Trim ; Added by Pelaca Exch $R1 Push $R2 Loop: StrCpy $R2 "$R1" 1 -1 StrCmp "$R2" " " RTrim StrCmp "$R2" "$\n" RTrim StrCmp "$R2" "$\r" RTrim StrCmp "$R2" ";" RTrim GoTo Done RTrim: StrCpy $R1 "$R1" -1 Goto Loop Done: Pop $R2 Exch $R1 FunctionEnd !endif ; _AddToPath_nsh