- Minor fixes to get Wine NTDLL environment tests pass.
[reactos.git] / reactos / lib / rtl / env.c
1 /* $Id: env.c,v 1.5 2004/12/27 20:43:42 navaraf Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/rtl/env.c
6 * PURPOSE: Environment functions
7 * PROGRAMMER: Eric Kohl
8 * UPDATE HISTORY:
9 * Created 30/09/98
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <ntdll/rtl.h>
16 #include <napi/teb.h>
17 #include <ntos/minmax.h>
18 #include <string.h>
19
20 #define NDEBUG
21 #include <ntdll/ntdll.h>
22
23 /* FUNCTIONS *****************************************************************/
24
25 /*
26 * @implemented
27 */
28 NTSTATUS STDCALL
29 RtlCreateEnvironment(BOOLEAN Inherit,
30 PWSTR *Environment)
31 {
32 MEMORY_BASIC_INFORMATION MemInfo;
33 PVOID EnvPtr = NULL;
34 NTSTATUS Status = STATUS_SUCCESS;
35 ULONG RegionSize = PAGE_SIZE;
36
37 if (Inherit == TRUE)
38 {
39 RtlAcquirePebLock();
40
41 if (NtCurrentPeb()->ProcessParameters->Environment != NULL)
42 {
43 Status = NtQueryVirtualMemory(NtCurrentProcess(),
44 NtCurrentPeb()->ProcessParameters->Environment,
45 MemoryBasicInformation,
46 &MemInfo,
47 sizeof(MEMORY_BASIC_INFORMATION),
48 NULL);
49 if (!NT_SUCCESS(Status))
50 {
51 RtlReleasePebLock();
52 *Environment = NULL;
53 return(Status);
54 }
55
56 RegionSize = MemInfo.RegionSize;
57 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
58 &EnvPtr,
59 0,
60 &RegionSize,
61 MEM_RESERVE | MEM_COMMIT,
62 PAGE_READWRITE);
63 if (!NT_SUCCESS(Status))
64 {
65 RtlReleasePebLock();
66 *Environment = NULL;
67 return(Status);
68 }
69
70 memmove(EnvPtr,
71 NtCurrentPeb ()->ProcessParameters->Environment,
72 MemInfo.RegionSize);
73
74 *Environment = EnvPtr;
75 }
76
77 RtlReleasePebLock ();
78 }
79 else
80 {
81 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
82 &EnvPtr,
83 0,
84 &RegionSize,
85 MEM_RESERVE | MEM_COMMIT,
86 PAGE_READWRITE);
87 if (NT_SUCCESS(Status))
88 {
89 memset(EnvPtr,
90 0,
91 RegionSize);
92 *Environment = EnvPtr;
93 }
94 }
95
96 return(Status);
97 }
98
99
100 /*
101 * @implemented
102 */
103 VOID STDCALL
104 RtlDestroyEnvironment(PWSTR Environment)
105 {
106 ULONG Size = 0;
107
108 NtFreeVirtualMemory(NtCurrentProcess(),
109 (PVOID)&Environment,
110 &Size,
111 MEM_RELEASE);
112 }
113
114
115 /*
116 * @implemented
117 */
118 NTSTATUS STDCALL
119 RtlExpandEnvironmentStrings_U(PWSTR Environment,
120 PUNICODE_STRING Source,
121 PUNICODE_STRING Destination,
122 PULONG Length)
123 {
124 UNICODE_STRING Variable;
125 UNICODE_STRING Value;
126 NTSTATUS ReturnStatus = STATUS_SUCCESS;
127 NTSTATUS Status;
128 PWSTR SourceBuffer;
129 PWSTR DestBuffer;
130 PWSTR CopyBuffer;
131 PWSTR VariableEnd;
132 ULONG SourceLength;
133 ULONG DestMax;
134 ULONG CopyLength;
135 ULONG Tail;
136 ULONG TotalLength = 1; /* for terminating NULL */
137
138 DPRINT("RtlExpandEnvironmentStrings_U %p %wZ %p %p\n",
139 Environment, Source, Destination, Length);
140
141 SourceLength = Source->Length / sizeof(WCHAR);
142 SourceBuffer = Source->Buffer;
143 DestMax = Destination->MaximumLength / sizeof(WCHAR);
144 DestBuffer = Destination->Buffer;
145
146 while (SourceLength)
147 {
148 if (*SourceBuffer != L'%')
149 {
150 CopyBuffer = SourceBuffer;
151 CopyLength = 0;
152 while (SourceLength != 0 && *SourceBuffer != L'%')
153 {
154 SourceBuffer++;
155 CopyLength++;
156 SourceLength--;
157 }
158 }
159 else
160 {
161 /* Process environment variable. */
162
163 VariableEnd = SourceBuffer + 1;
164 Tail = SourceLength - 1;
165 while (*VariableEnd != L'%' && Tail != 0)
166 {
167 VariableEnd++;
168 Tail--;
169 }
170
171 if (Tail != 0)
172 {
173 Variable.MaximumLength =
174 Variable.Length = (VariableEnd - (SourceBuffer + 1)) * sizeof(WCHAR);
175 Variable.Buffer = SourceBuffer + 1;
176
177 Value.Length = 0;
178 Value.MaximumLength = DestMax * sizeof(WCHAR);
179 Value.Buffer = DestBuffer;
180
181 Status = RtlQueryEnvironmentVariable_U(Environment, &Variable,
182 &Value);
183 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL)
184 {
185 SourceBuffer = VariableEnd + 1;
186 SourceLength = Tail - 1;
187 TotalLength += Value.Length / sizeof(WCHAR);
188 if (Status != STATUS_BUFFER_TOO_SMALL)
189 {
190 DestBuffer += Value.Length / sizeof(WCHAR);
191 DestMax -= Value.Length / sizeof(WCHAR);
192 }
193 else
194 {
195 DestMax = 0;
196 ReturnStatus = STATUS_BUFFER_TOO_SMALL;
197 }
198 continue;
199 }
200 else
201 {
202 /* Variable not found. */
203 CopyBuffer = SourceBuffer;
204 CopyLength = SourceLength - Tail + 1;
205 SourceLength -= CopyLength;
206 SourceBuffer += CopyLength;
207 }
208 }
209 else
210 {
211 /* Unfinished variable name. */
212 CopyBuffer = SourceBuffer;
213 CopyLength = SourceLength;
214 SourceLength = 0;
215 }
216 }
217
218 TotalLength += CopyLength;
219 if (DestMax)
220 {
221 if (DestMax < CopyLength)
222 {
223 CopyLength = DestMax;
224 ReturnStatus = STATUS_BUFFER_TOO_SMALL;
225 }
226 RtlCopyMemory(DestBuffer, CopyBuffer, CopyLength * sizeof(WCHAR));
227 DestMax -= CopyLength;
228 DestBuffer += CopyLength;
229 }
230 }
231
232 /* NULL-terminate the buffer. */
233 if (DestMax)
234 *DestBuffer = 0;
235 else
236 ReturnStatus = STATUS_BUFFER_TOO_SMALL;
237
238 Destination->Length = (DestBuffer - Destination->Buffer) * sizeof(WCHAR);
239 if (Length != NULL)
240 *Length = TotalLength * sizeof(WCHAR);
241
242 DPRINT("Destination %wZ\n", Destination);
243
244 return ReturnStatus;
245 }
246
247
248 /*
249 * @implemented
250 */
251 VOID STDCALL
252 RtlSetCurrentEnvironment(PWSTR NewEnvironment,
253 PWSTR *OldEnvironment)
254 {
255 PVOID EnvPtr;
256
257 DPRINT("NewEnvironment %x OldEnvironment %x\n",
258 NewEnvironment, OldEnvironment);
259
260 RtlAcquirePebLock();
261
262 EnvPtr = NtCurrentPeb()->ProcessParameters->Environment;
263 NtCurrentPeb()->ProcessParameters->Environment = NewEnvironment;
264
265 if (OldEnvironment != NULL)
266 *OldEnvironment = EnvPtr;
267
268 RtlReleasePebLock();
269 }
270
271
272 /*
273 * @implemented
274 */
275 NTSTATUS STDCALL
276 RtlSetEnvironmentVariable(PWSTR *Environment,
277 PUNICODE_STRING Name,
278 PUNICODE_STRING Value)
279 {
280 MEMORY_BASIC_INFORMATION mbi;
281 UNICODE_STRING var;
282 int hole_len, new_len, env_len = 0;
283 WCHAR *new_env = 0, *env_end = 0, *wcs, *env, *val = 0, *tail = 0, *hole = 0;
284 PWSTR head = NULL;
285 ULONG size = 0, new_size;
286 LONG f = 1;
287 NTSTATUS Status = STATUS_SUCCESS;
288
289 DPRINT("RtlSetEnvironmentVariable(Environment %p Name %wZ Value %wZ)\n",
290 Environment, Name, Value);
291
292 /* Variable names can't contain a '=' except as a first character. */
293 for (wcs = Name->Buffer + 1;
294 wcs < Name->Buffer + (Name->Length / sizeof(WCHAR));
295 wcs++)
296 {
297 if (*wcs == L'=')
298 return STATUS_INVALID_PARAMETER;
299 }
300
301 if (Environment)
302 {
303 env = *Environment;
304 }
305 else
306 {
307 RtlAcquirePebLock();
308 env = NtCurrentPeb()->ProcessParameters->Environment;
309 }
310
311 if (env)
312 {
313 /* get environment length */
314 wcs = env_end = env;
315 do
316 {
317 env_end += wcslen(env_end) + 1;
318 }
319 while (*env_end);
320 env_end++;
321 env_len = env_end - env;
322 DPRINT("environment length %ld characters\n", env_len);
323
324 /* find where to insert */
325 while (*wcs)
326 {
327 var.Buffer = wcs++;
328 wcs = wcschr(wcs, L'=');
329 if (wcs == NULL)
330 {
331 wcs = var.Buffer + wcslen(var.Buffer);
332 }
333 if (*wcs)
334 {
335 var.Length = (wcs - var.Buffer) * sizeof(WCHAR);
336 var.MaximumLength = var.Length;
337 val = ++wcs;
338 wcs += wcslen(wcs);
339 f = RtlCompareUnicodeString(&var, Name, TRUE);
340 if (f >= 0)
341 {
342 if (f) /* Insert before found */
343 {
344 hole = tail = var.Buffer;
345 }
346 else /* Exact match */
347 {
348 head = var.Buffer;
349 tail = ++wcs;
350 hole = val;
351 }
352 goto found;
353 }
354 }
355 wcs++;
356 }
357 hole = tail = wcs; /* Append to environment */
358 }
359
360 found:
361 if (Value != NULL && Value->Length > 0)
362 {
363 hole_len = tail - hole;
364 /* calculate new environment size */
365 new_size = Value->Length + sizeof(WCHAR);
366 /* adding new variable */
367 if (f)
368 new_size += Name->Length + sizeof(WCHAR);
369 new_len = new_size / sizeof(WCHAR);
370 if (hole_len < new_len)
371 {
372 /* enlarge environment size */
373 /* check the size of available memory */
374 new_size += (env_len - hole_len) * sizeof(WCHAR);
375 new_size = ROUNDUP(new_size, PAGE_SIZE);
376 mbi.RegionSize = 0;
377 DPRINT("new_size %lu\n", new_size);
378
379 if (env)
380 {
381 Status = NtQueryVirtualMemory(NtCurrentProcess(),
382 env,
383 MemoryBasicInformation,
384 &mbi,
385 sizeof(MEMORY_BASIC_INFORMATION),
386 NULL);
387 if (!NT_SUCCESS(Status))
388 {
389 if (Environment == NULL)
390 {
391 RtlReleasePebLock();
392 }
393 return(Status);
394 }
395 }
396
397 if (new_size > mbi.RegionSize)
398 {
399 /* reallocate memory area */
400 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
401 (PVOID)&new_env,
402 0,
403 &new_size,
404 MEM_RESERVE | MEM_COMMIT,
405 PAGE_READWRITE);
406 if (!NT_SUCCESS(Status))
407 {
408 if (Environment == NULL)
409 {
410 RtlReleasePebLock();
411 }
412 return(Status);
413 }
414
415 if (env)
416 {
417 memmove(new_env,
418 env,
419 (hole - env) * sizeof(WCHAR));
420 hole = new_env + (hole - env);
421 }
422 else
423 {
424 /* absolutely new environment */
425 tail = hole = new_env;
426 *hole = 0;
427 env_end = hole + 1;
428 }
429 }
430 }
431
432 /* move tail */
433 memmove (hole + new_len, tail, (env_end - tail) * sizeof(WCHAR));
434
435 if (new_env)
436 {
437 /* we reallocated environment, let's free the old one */
438 if (Environment)
439 *Environment = new_env;
440 else
441 NtCurrentPeb()->ProcessParameters->Environment = new_env;
442
443 if (env)
444 {
445 size = 0;
446 NtFreeVirtualMemory(NtCurrentProcess(),
447 (PVOID)&env,
448 &size,
449 MEM_RELEASE);
450 }
451 }
452
453 /* and now copy given stuff */
454 if (f)
455 {
456 /* copy variable name and '=' character */
457 memmove(hole,
458 Name->Buffer,
459 Name->Length);
460 hole += Name->Length / sizeof(WCHAR);
461 *hole++ = L'=';
462 }
463
464 /* copy value */
465 memmove(hole,
466 Value->Buffer,
467 Value->Length);
468 hole += Value->Length / sizeof(WCHAR);
469 *hole = 0;
470 }
471 else
472 {
473 /* remove the environment variable */
474 if (f == 0)
475 {
476 memmove(head,
477 tail,
478 (env_end - tail) * sizeof(WCHAR));
479 }
480 else
481 {
482 Status = STATUS_VARIABLE_NOT_FOUND;
483 }
484 }
485
486 if (Environment == NULL)
487 {
488 RtlReleasePebLock();
489 }
490
491 return(Status);
492 }
493
494
495 /*
496 * @implemented
497 */
498 NTSTATUS STDCALL
499 RtlQueryEnvironmentVariable_U(PWSTR Environment,
500 PUNICODE_STRING Name,
501 PUNICODE_STRING Value)
502 {
503 NTSTATUS Status;
504 PWSTR wcs;
505 UNICODE_STRING var;
506 PWSTR val;
507 BOOLEAN SysEnvUsed = FALSE;
508
509 DPRINT("RtlQueryEnvironmentVariable_U Environment %p Variable %wZ Value %p\n",
510 Environment, Name, Value);
511
512 if (Environment == NULL)
513 {
514 Environment = NtCurrentPeb()->ProcessParameters->Environment;
515 SysEnvUsed = TRUE;
516 }
517
518 if (Environment == NULL)
519 return(STATUS_VARIABLE_NOT_FOUND);
520
521 Value->Length = 0;
522 if (SysEnvUsed == TRUE)
523 RtlAcquirePebLock();
524
525 wcs = Environment;
526 while (*wcs)
527 {
528 var.Buffer = wcs++;
529 wcs = wcschr(wcs, L'=');
530 if (wcs == NULL)
531 {
532 wcs = var.Buffer + wcslen(var.Buffer);
533 }
534 if (*wcs)
535 {
536 var.Length = var.MaximumLength = (wcs - var.Buffer) * sizeof(WCHAR);
537 val = ++wcs;
538 wcs += wcslen(wcs);
539
540 if (RtlEqualUnicodeString(&var, Name, TRUE))
541 {
542 Value->Length = (wcs - val) * sizeof(WCHAR);
543 if (Value->Length <= Value->MaximumLength)
544 {
545 memcpy(Value->Buffer, val,
546 min(Value->Length + sizeof(WCHAR), Value->MaximumLength));
547 DPRINT("Value %S\n", val);
548 DPRINT("Return STATUS_SUCCESS\n");
549 Status = STATUS_SUCCESS;
550 }
551 else
552 {
553 DPRINT("Return STATUS_BUFFER_TOO_SMALL\n");
554 Status = STATUS_BUFFER_TOO_SMALL;
555 }
556
557 if (SysEnvUsed == TRUE)
558 RtlReleasePebLock();
559
560 return(Status);
561 }
562 }
563 wcs++;
564 }
565
566 if (SysEnvUsed == TRUE)
567 RtlReleasePebLock();
568
569 DPRINT("Return STATUS_VARIABLE_NOT_FOUND\n");
570 return(STATUS_VARIABLE_NOT_FOUND);
571 }
572
573 /* EOF */