1
2;
3;Inno Setup Extensions Knowledge Base
4;Article 44 - Native ISX procedures for PATH modification
5;http://www13.brinkster.com/vincenzog/isxart.asp?idart=44
6;Author: Thomas Vedel
7;
8
9[Code]
10
11// Version log:
12// 03/31/2003: Initial release (thv@lr.dk)
13
14const
15  // Modification method
16  pmAddToBeginning = $1;      // Add dir to beginning of Path
17  pmAddToEnd = $2;            // Add dir to end of Path
18  pmAddAllways = $4;          // Add also if specified dir is already included in existing path
19  pmAddOnlyIfDirExists = $8;  // Add only if specified dir actually exists
20  pmRemove = $10;             // Remove dir from path
21  pmRemoveSubdirsAlso = $20;  // Remove dir and all subdirs from path
22
23  // Scope
24  psCurrentUser = 1;          // Modify path for current user
25  psAllUsers = 2;             // Modify path for all users
26
27  // Error results
28  mpOK = 0;                   // No errors
29  mpMissingRights = -1;       // User has insufficient rights
30  mpAutoexecNoWriteacc = -2;  // Autoexec can not be written (may be readonly)
31  mpBothAddAndRemove = -3;    // User has specified that dir should both be removed from and added to path
32
33
34{ Helper procedure: Split a path environment variable into individual dirnames }
35procedure SplitPath(Path: string; var Dirs: TStringList);
36var
37  pos: integer;
38  s: string;
39begin
40  Dirs.Clear;
41  s := '';
42  pos := 1;
43  while (pos<=Length(Path)) do
44  begin
45    if (Path[pos]<>';') then
46      s := s + Path[pos];
47    if ((Path[pos]=';') or (pos=Length(Path))) then
48    begin
49      s := Trim(s);
50      s := RemoveQuotes(s);
51      s := Trim(s);
52      if (s <> '') then
53        Dirs.Add(s);
54      s := '';
55    end;
56    Pos := Pos + 1;
57  end;
58end; // procedure SplitPath
59
60
61{ Helper procedure: Concatenate individual dirnames into a path environment variable }
62procedure ConcatPath(Dirs: TStringList; Quotes: boolean; var Path: string);
63var
64  Index, MaxIndex: integer;
65  s: string;
66begin
67  MaxIndex := Dirs.Count-1;
68  Path := '';
69  for Index := 0 to MaxIndex do
70  begin
71    s := Dirs.Strings[Index];
72    if ((Quotes) and (pos(' ',s) > 0)) then
73      s := AddQuotes(s);
74    Path := Path + s;
75    if (Index < MaxIndex) then
76      Path := Path + ';'
77  end;
78end; // procedure ConcatPath
79
80
81{ Helper function: Modifies path environment string }
82procedure ModifyPathString(OldPath, DirName: string; Method: integer; Quotes: boolean; var ResultPath: string);
83var
84  Dirs: TStringList;
85  DirNotInPath: Boolean;
86  i: integer;
87begin
88  // Create Dirs variable
89  Dirs := TStringList.Create;
90
91  // Remove quotes form DirName
92  DirName := Trim(DirName);
93  DirName := RemoveQuotes(DirName);
94  DirName := Trim(DirName);
95
96  // Split old path in individual directory names
97  SplitPath(OldPath, Dirs);
98
99  // Check if dir is allready in path
100  DirNotInPath := True;
101  for i:=Dirs.Count-1 downto 0 do
102  begin
103    if (uppercase(Dirs.Strings[i]) = uppercase(DirName)) then
104      DirNotInPath := False;
105  end;
106
107  // Should dir be removed from existing Path?
108  if ((Method and (pmRemove or pmRemoveSubdirsAlso)) > 0) then
109  begin
110    for i:=Dirs.Count-1 downto 0 do
111    begin
112      if (((Method and pmRemoveSubdirsAlso) > 0) and (pos(uppercase(DirName)+'\', uppercase(Dirs.Strings[i])) = 1)) or
113         (((Method and (pmRemove) or (pmRemoveSubdirsAlso)) > 0) and (uppercase(DirName) = uppercase(Dirs.Strings[i])))
114      then
115        Dirs.Delete(i);
116    end;
117  end;
118
119  // Should dir be added to existing Path?
120  if ((Method and (pmAddToBeginning or pmAddToEnd)) > 0) then
121  begin
122    // Add dir to path
123    if (((Method and pmAddAllways) > 0) or DirNotInPath) then
124    begin
125      // Dir is not in path allready or should be added anyway
126      if (((Method and pmAddOnlyIfDirExists) = 0) or (DirExists(DirName))) then
127      begin
128        // Dir actually exsists or should be added anyway
129        if ((Method and pmAddToBeginning) > 0) then
130          Dirs.Insert(0, DirName)
131        else
132          Dirs.Append(DirName);
133      end;
134    end;
135  end;
136
137  // Concatenate directory names into one single path variable
138  ConcatPath(Dirs, Quotes, ResultPath);
139  // Finally free Dirs object
140  Dirs.Free;
141end; // ModifyPathString
142
143
144{ Helper function: Modify path on Windows 9x }
145function ModifyPath9x(DirName: string; Method: integer): integer;
146var
147  AutoexecLines: TStringList;
148  ActualLine: String;
149  PathLineNos: TStringList;
150  FirstPathLineNo: Integer;
151  OldPath, ResultPath: String;
152  LineNo, CharNo, Index: integer;
153
154  TempString: String;
155begin
156  // Expect everything to be OK
157  result := mpOK;
158
159  // Create stringslists
160  AutoexecLines := TStringList.Create;
161  PathLineNos := TStringList.Create;
162
163  // Read existing path
164  OldPath := '';
165  LoadStringFromFile('c:\Autoexec.bat', TempString);
166  AutoexecLines.Text := TempString;
167  PathLineNos.Clear;
168  // Read Autoexec line by line
169  for LineNo := 0 to AutoexecLines.Count - 1 do begin
170    ActualLine := AutoexecLines.Strings[LineNo];
171    // Check if line starts with "PATH=" after first stripping spaces and other "fill-chars"
172    if Pos('=', ActualLine) > 0 then
173    begin
174      for CharNo := Pos('=', ActualLine)-1 downto 1 do
175        if (ActualLine[CharNo]=' ') or (ActualLine[CharNo]=#9) then
176          Delete(ActualLine, CharNo, 1);
177      if Pos('@', ActualLine) = 1 then
178        Delete(ActualLine, 1, 1);
179      if (Pos('PATH=', uppercase(ActualLine))=1) or (Pos('SETPATH=', uppercase(ActualLine))=1) then
180      begin
181        // Remove 'PATH=' and add path to "OldPath" variable
182        Delete(ActualLine, 1, pos('=', ActualLine));
183        // Check if an earlier PATH variable is referenced, but there has been no previous PATH defined in Autoexec
184        if (pos('%PATH%',uppercase(ActualLine))>0) and (PathLineNos.Count=0) then
185          OldPath := ExpandConstant('{win}') + ';' + ExpandConstant('{win}')+'\COMMAND';
186        if (pos('%PATH%',uppercase(ActualLine))>0) then
187        begin
188          ActualLine := Copy(ActualLine, 1, pos('%PATH%',uppercase(ActualLine))-1) +
189                        OldPath +
190                        Copy(ActualLine, pos('%PATH%',uppercase(ActualLine))+6, Length(ActualLine));
191        end;
192        OldPath := ActualLine;
193
194        // Update list of line numbers holding path variables
195        PathLineNos.Add(IntToStr(LineNo));
196      end;
197    end;
198  end;
199
200  // Save first line number in Autoexec.bat which modifies path environment variable
201  if PathLineNos.Count > 0 then
202    FirstPathLineNo := StrToInt(PathLineNos.Strings[0])
203  else
204    FirstPathLineNo := 0;
205
206  // Modify path
207  ModifyPathString(OldPath, DirName, Method, True, ResultPath);
208
209  // Write Modified path back to Autoexec.bat
210  // First delete all existing path references from Autoexec.bat
211  Index := PathLineNos.Count-1;
212  while (Index>=0) do
213  begin
214    AutoexecLines.Delete(StrToInt(PathLineNos.Strings[Index]));
215    Index := Index-1;
216  end;
217  // Then insert new path variable into Autoexec.bat
218  AutoexecLines.Insert(FirstPathLineNo, '@PATH='+ResultPath);
219  // Delete old Autoexec.bat from disk
220  if not DeleteFile('c:\Autoexec.bat') then
221    result := mpAutoexecNoWriteAcc;
222  Sleep(500);
223  // And finally write Autoexec.bat back to disk
224  if not (result=mpAutoexecNoWriteAcc) then
225    SaveStringToFile('c:\Autoexec.bat', AutoexecLines.Text, false);
226
227  // Free stringlists
228  PathLineNos.Free;
229  AutoexecLines.Free;
230end; // ModifyPath9x
231
232
233{ Helper function: Modify path on Windows NT, 2000 and XP }
234function ModifyPathNT(DirName: string; Method, Scope: integer): integer;
235var
236  RegRootKey: integer;
237  RegSubKeyName: string;
238  RegValueName: string;
239  OldPath, ResultPath: string;
240  OK: boolean;
241begin
242  // Expect everything to be OK
243  result := mpOK;
244
245  // Initialize registry key and value names to reflect if changes should be global or local to current user only
246  case Scope of
247    psCurrentUser:
248      begin
249        RegRootKey := HKEY_CURRENT_USER;
250        RegSubKeyName := 'Environment';
251        RegValueName := 'Path';
252      end;
253    psAllUsers:
254      begin
255        RegRootKey := HKEY_LOCAL_MACHINE;
256        RegSubKeyName := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
257        RegValueName := 'Path';
258      end;
259  end;
260
261  // Read current path value from registry
262  OK := RegQueryStringValue(RegRootKey, RegSubKeyName, RegValueName, OldPath);
263  if not OK then
264  begin
265    result := mpMissingRights;
266    Exit;
267  end;
268
269  // Modify path
270  ModifyPathString(OldPath, DirName, Method, False, ResultPath);
271
272  // Write new path value to registry
273  if not RegWriteStringValue(RegRootKey, RegSubKeyName, RegValueName, ResultPath) then
274  begin
275    result := mpMissingRights;
276    Exit;
277
278  end;
279end; // ModifyPathNT
280
281
282{ Main function: Modify path }
283function ModifyPath(Path: string; Method, Scope: integer): integer;
284begin
285  // Check if both add and remove has been specified (= error!)
286  if (Method and (pmAddToBeginning or pmAddToEnd) and (pmRemove or pmRemoveSubdirsAlso)) > 0 then
287  begin
288    result := mpBothAddAndRemove;
289    Exit;
290  end;
291
292  // Perform directory constant expansion
293  Path := ExpandConstantEx(Path, ' ', ' ');
294
295  // Test if Win9x
296  if InstallOnThisVersion('4,0','0,0') = irInstall then
297    ModifyPath9x(Path, Method);
298
299  // Test if WinNT, 2000 or XP
300  if InstallOnThisVersion('0,4','0,0') = irInstall then
301    ModifyPathNT(Path, Method, Scope);
302end; // ModifyPath
303