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