nsis.envvarupdate.nsh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /**
  2. * EnvVarUpdate.nsh
  3. * : Environmental Variables: append, prepend, and remove entries
  4. *
  5. * WARNING: If you use StrFunc.nsh header then include it before this file
  6. * with all required definitions. This is to avoid conflicts
  7. *
  8. * Usage:
  9. * ${EnvVarUpdate} "ResultVar" "EnvVarName" "Action" "RegLoc" "PathString"
  10. *
  11. * Credits:
  12. * Version 1.0
  13. * * Cal Turney (turnec2)
  14. * * Amir Szekely (KiCHiK) and e-circ for developing the forerunners of this
  15. * function: AddToPath, un.RemoveFromPath, AddToEnvVar, un.RemoveFromEnvVar,
  16. * WriteEnvStr, and un.DeleteEnvStr
  17. * * Diego Pedroso (deguix) for StrTok
  18. * * Kevin English (kenglish_hi) for StrContains
  19. * * Hendri Adriaens (Smile2Me), Diego Pedroso (deguix), and Dan Fuhry
  20. * (dandaman32) for StrReplace
  21. *
  22. * Version 1.1 (compatibility with StrFunc.nsh)
  23. * * techtonik
  24. *
  25. * http://nsis.sourceforge.net/Environmental_Variables:_append%2C_prepend%2C_and_remove_entries
  26. *
  27. */
  28. !ifndef ENVVARUPDATE_FUNCTION
  29. !define ENVVARUPDATE_FUNCTION
  30. !verbose push
  31. !verbose 3
  32. !include "LogicLib.nsh"
  33. !include "WinMessages.NSH"
  34. !include "StrFunc.nsh"
  35. ; ---- Fix for conflict if StrFunc.nsh is already includes in main file -----------------------
  36. !macro _IncludeStrFunction StrFuncName
  37. !ifndef ${StrFuncName}_INCLUDED
  38. ${${StrFuncName}}
  39. !endif
  40. !ifndef Un${StrFuncName}_INCLUDED
  41. ${Un${StrFuncName}}
  42. !endif
  43. !define un.${StrFuncName} "${Un${StrFuncName}}"
  44. !macroend
  45. !insertmacro _IncludeStrFunction StrTok
  46. !insertmacro _IncludeStrFunction StrStr
  47. !insertmacro _IncludeStrFunction StrRep
  48. ; ---------------------------------- Macro Definitions ----------------------------------------
  49. !macro _EnvVarUpdateConstructor ResultVar EnvVarName Action Regloc PathString
  50. Push "${EnvVarName}"
  51. Push "${Action}"
  52. Push "${RegLoc}"
  53. Push "${PathString}"
  54. Call EnvVarUpdate
  55. Pop "${ResultVar}"
  56. !macroend
  57. !define EnvVarUpdate '!insertmacro "_EnvVarUpdateConstructor"'
  58. !macro _unEnvVarUpdateConstructor ResultVar EnvVarName Action Regloc PathString
  59. Push "${EnvVarName}"
  60. Push "${Action}"
  61. Push "${RegLoc}"
  62. Push "${PathString}"
  63. Call un.EnvVarUpdate
  64. Pop "${ResultVar}"
  65. !macroend
  66. !define un.EnvVarUpdate '!insertmacro "_unEnvVarUpdateConstructor"'
  67. ; ---------------------------------- Macro Definitions end-------------------------------------
  68. ;----------------------------------- EnvVarUpdate start----------------------------------------
  69. !define hklm_all_users 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
  70. !define hkcu_current_user 'HKCU "Environment"'
  71. !macro EnvVarUpdate UN
  72. Function ${UN}EnvVarUpdate
  73. Push $0
  74. Exch 4
  75. Exch $1
  76. Exch 3
  77. Exch $2
  78. Exch 2
  79. Exch $3
  80. Exch
  81. Exch $4
  82. Push $5
  83. Push $6
  84. Push $7
  85. Push $8
  86. Push $9
  87. Push $R0
  88. /* After this point:
  89. -------------------------
  90. $0 = ResultVar (returned)
  91. $1 = EnvVarName (input)
  92. $2 = Action (input)
  93. $3 = RegLoc (input)
  94. $4 = PathString (input)
  95. $5 = Orig EnvVar (read from registry)
  96. $6 = Len of $0 (temp)
  97. $7 = tempstr1 (temp)
  98. $8 = Entry counter (temp)
  99. $9 = tempstr2 (temp)
  100. $R0 = tempChar (temp) */
  101. ; Step 1: Read contents of EnvVarName from RegLoc
  102. ;
  103. ; Check for empty EnvVarName
  104. ${If} $1 == ""
  105. SetErrors
  106. DetailPrint "ERROR: EnvVarName is blank"
  107. Goto EnvVarUpdate_Restore_Vars
  108. ${EndIf}
  109. ; Check for valid Action
  110. ${If} $2 != "A"
  111. ${AndIf} $2 != "P"
  112. ${AndIf} $2 != "R"
  113. SetErrors
  114. DetailPrint "ERROR: Invalid Action - must be A, P, or R"
  115. Goto EnvVarUpdate_Restore_Vars
  116. ${EndIf}
  117. ${If} $3 == HKLM
  118. ReadRegStr $5 ${hklm_all_users} $1 ; Get EnvVarName from all users into $5
  119. ${ElseIf} $3 == HKCU
  120. ReadRegStr $5 ${hkcu_current_user} $1 ; Read EnvVarName from current user into $5
  121. ${Else}
  122. SetErrors
  123. DetailPrint 'ERROR: Action is [$3] but must be "HKLM" or HKCU"'
  124. Goto EnvVarUpdate_Restore_Vars
  125. ${EndIf}
  126. ; Check for empty PathString
  127. ${If} $4 == ""
  128. SetErrors
  129. DetailPrint "ERROR: PathString is blank"
  130. Goto EnvVarUpdate_Restore_Vars
  131. ${EndIf}
  132. ; Make sure we've got some work to do
  133. ${If} $5 == ""
  134. ${AndIf} $2 == "R"
  135. SetErrors
  136. DetailPrint "$1 is empty - Nothing to remove"
  137. Goto EnvVarUpdate_Restore_Vars
  138. ${EndIf}
  139. ; Step 2: Scrub EnvVar
  140. ;
  141. StrCpy $0 $5 ; Copy the contents to $0
  142. ; Remove spaces around semicolons (NOTE: spaces before the 1st entry or
  143. ; after the last one are not removed here but instead in Step 3)
  144. ${If} $0 != "" ; If EnvVar is not empty ...
  145. ${Do}
  146. ${${UN}StrStr} $7 $0 " ;"
  147. ${If} $7 == ""
  148. ${ExitDo}
  149. ${EndIf}
  150. ${${UN}StrRep} $0 $0 " ;" ";" ; Remove '<space>;'
  151. ${Loop}
  152. ${Do}
  153. ${${UN}StrStr} $7 $0 "; "
  154. ${If} $7 == ""
  155. ${ExitDo}
  156. ${EndIf}
  157. ${${UN}StrRep} $0 $0 "; " ";" ; Remove ';<space>'
  158. ${Loop}
  159. ${Do}
  160. ${${UN}StrStr} $7 $0 ";;"
  161. ${If} $7 == ""
  162. ${ExitDo}
  163. ${EndIf}
  164. ${${UN}StrRep} $0 $0 ";;" ";"
  165. ${Loop}
  166. ; Remove a leading or trailing semicolon from EnvVar
  167. StrCpy $7 $0 1 0
  168. ${If} $7 == ";"
  169. StrCpy $0 $0 "" 1 ; Change ';<EnvVar>' to '<EnvVar>'
  170. ${EndIf}
  171. StrLen $6 $0
  172. IntOp $6 $6 - 1
  173. StrCpy $7 $0 1 $6
  174. ${If} $7 == ";"
  175. StrCpy $0 $0 $6 ; Change ';<EnvVar>' to '<EnvVar>'
  176. ${EndIf}
  177. ; DetailPrint "Scrubbed $1: [$0]" ; Uncomment to debug
  178. ${EndIf}
  179. /* Step 3. Remove all instances of the target path/string (even if "A" or "P")
  180. $6 = bool flag (1 = found and removed PathString)
  181. $7 = a string (e.g. path) delimited by semicolon(s)
  182. $8 = entry counter starting at 0
  183. $9 = copy of $0
  184. $R0 = tempChar */
  185. ${If} $5 != "" ; If EnvVar is not empty ...
  186. StrCpy $9 $0
  187. StrCpy $0 ""
  188. StrCpy $8 0
  189. StrCpy $6 0
  190. ${Do}
  191. ${${UN}StrTok} $7 $9 ";" $8 "0" ; $7 = next entry, $8 = entry counter
  192. ${If} $7 == "" ; If we've run out of entries,
  193. ${ExitDo} ; were done
  194. ${EndIf} ;
  195. ; Remove leading and trailing spaces from this entry (critical step for Action=Remove)
  196. ${Do}
  197. StrCpy $R0 $7 1
  198. ${If} $R0 != " "
  199. ${ExitDo}
  200. ${EndIf}
  201. StrCpy $7 $7 "" 1 ; Remove leading space
  202. ${Loop}
  203. ${Do}
  204. StrCpy $R0 $7 1 -1
  205. ${If} $R0 != " "
  206. ${ExitDo}
  207. ${EndIf}
  208. StrCpy $7 $7 -1 ; Remove trailing space
  209. ${Loop}
  210. ${If} $7 == $4 ; If string matches, remove it by not appending it
  211. StrCpy $6 1 ; Set 'found' flag
  212. ${ElseIf} $7 != $4 ; If string does NOT match
  213. ${AndIf} $0 == "" ; and the 1st string being added to $0,
  214. StrCpy $0 $7 ; copy it to $0 without a prepended semicolon
  215. ${ElseIf} $7 != $4 ; If string does NOT match
  216. ${AndIf} $0 != "" ; and this is NOT the 1st string to be added to $0,
  217. StrCpy $0 $0;$7 ; append path to $0 with a prepended semicolon
  218. ${EndIf} ;
  219. IntOp $8 $8 + 1 ; Bump counter
  220. ${Loop} ; Check for duplicates until we run out of paths
  221. ${EndIf}
  222. ; Step 4: Perform the requested Action
  223. ;
  224. ${If} $2 != "R" ; If Append or Prepend
  225. ${If} $6 == 1 ; And if we found the target
  226. DetailPrint "Target is already present in $1. It will be removed and"
  227. ${EndIf}
  228. ${If} $0 == "" ; If EnvVar is (now) empty
  229. StrCpy $0 $4 ; just copy PathString to EnvVar
  230. ${If} $6 == 0 ; If found flag is either 0
  231. ${OrIf} $6 == "" ; or blank (if EnvVarName is empty)
  232. DetailPrint "$1 was empty and has been updated with the target"
  233. ${EndIf}
  234. ${ElseIf} $2 == "A" ; If Append (and EnvVar is not empty),
  235. StrCpy $0 $0;$4 ; append PathString
  236. ${If} $6 == 1
  237. DetailPrint "appended to $1"
  238. ${Else}
  239. DetailPrint "Target was appended to $1"
  240. ${EndIf}
  241. ${Else} ; If Prepend (and EnvVar is not empty),
  242. StrCpy $0 $4;$0 ; prepend PathString
  243. ${If} $6 == 1
  244. DetailPrint "prepended to $1"
  245. ${Else}
  246. DetailPrint "Target was prepended to $1"
  247. ${EndIf}
  248. ${EndIf}
  249. ${Else} ; If Action = Remove
  250. ${If} $6 == 1 ; and we found the target
  251. DetailPrint "Target was found and removed from $1"
  252. ${Else}
  253. DetailPrint "Target was NOT found in $1 (nothing to remove)"
  254. ${EndIf}
  255. ${If} $0 == ""
  256. DetailPrint "$1 is now empty"
  257. ${EndIf}
  258. ${EndIf}
  259. ; Step 5: Update the registry at RegLoc with the updated EnvVar and announce the change
  260. ;
  261. ClearErrors
  262. ${If} $3 == HKLM
  263. WriteRegExpandStr ${hklm_all_users} $1 $0 ; Write it in all users section
  264. ${ElseIf} $3 == HKCU
  265. WriteRegExpandStr ${hkcu_current_user} $1 $0 ; Write it to current user section
  266. ${EndIf}
  267. IfErrors 0 +4
  268. MessageBox MB_OK|MB_ICONEXCLAMATION "Could not write updated $1 to $3"
  269. DetailPrint "Could not write updated $1 to $3"
  270. Goto EnvVarUpdate_Restore_Vars
  271. ; "Export" our change
  272. SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
  273. EnvVarUpdate_Restore_Vars:
  274. ;
  275. ; Restore the user's variables and return ResultVar
  276. Pop $R0
  277. Pop $9
  278. Pop $8
  279. Pop $7
  280. Pop $6
  281. Pop $5
  282. Pop $4
  283. Pop $3
  284. Pop $2
  285. Pop $1
  286. Push $0 ; Push my $0 (ResultVar)
  287. Exch
  288. Pop $0 ; Restore his $0
  289. FunctionEnd
  290. !macroend ; EnvVarUpdate UN
  291. !insertmacro EnvVarUpdate ""
  292. !insertmacro EnvVarUpdate "un."
  293. ;----------------------------------- EnvVarUpdate end----------------------------------------
  294. !verbose pop
  295. !endif