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