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