2 * Ntdll environment functions
4 * Copyright 1996, 1998 Alexandre Julliard
5 * Copyright 2003 Eric Pouech
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
32 #include "ntdll_misc.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(environ
);
36 /******************************************************************************
37 * RtlCreateEnvironment [NTDLL.@]
39 NTSTATUS WINAPI
RtlCreateEnvironment(BOOLEAN inherit
, PWSTR
* env
)
43 TRACE("(%u,%p)!\n", inherit
, env
);
47 MEMORY_BASIC_INFORMATION mbi
;
51 nts
= NtQueryVirtualMemory(NtCurrentProcess(), ntdll_get_process_pmts()->Environment
,
52 0, &mbi
, sizeof(mbi
), NULL
);
53 if (nts
== STATUS_SUCCESS
)
56 nts
= NtAllocateVirtualMemory(NtCurrentProcess(), (void**)env
, 0, &mbi
.RegionSize
,
57 MEM_RESERVE
| MEM_COMMIT
, PAGE_READWRITE
);
58 if (nts
== STATUS_SUCCESS
)
59 memcpy(*env
, ntdll_get_process_pmts()->Environment
, mbi
.RegionSize
);
67 nts
= NtAllocateVirtualMemory(NtCurrentProcess(), (void**)env
, 0, &size
,
68 MEM_RESERVE
| MEM_COMMIT
, PAGE_READWRITE
);
69 if (nts
== STATUS_SUCCESS
)
70 memset(*env
, 0, size
);
76 /******************************************************************************
77 * RtlDestroyEnvironment [NTDLL.@]
79 NTSTATUS WINAPI
RtlDestroyEnvironment(PWSTR env
)
83 TRACE("(%p)!\n", env
);
85 return NtFreeVirtualMemory(NtCurrentProcess(), (void**)&env
, &size
, MEM_RELEASE
);
88 static LPCWSTR
ENV_FindVariable(PCWSTR var
, PCWSTR name
, unsigned namelen
)
90 for (; *var
; var
+= strlenW(var
) + 1)
92 /* match var names, but avoid setting a var with a name including a '='
93 * (a starting '=' is valid though)
95 if (strncmpiW(var
, name
, namelen
) == 0 && var
[namelen
] == '=' &&
96 strchrW(var
+ 1, '=') == var
+ namelen
)
98 return var
+ namelen
+ 1;
104 /******************************************************************
105 * RtlQueryEnvironmentVariable_U [NTDLL.@]
107 * NOTES: when the buffer is too small, the string is not written, but if the
108 * terminating null char is the only char that cannot be written, then
109 * all chars (except the null) are written and success is returned
110 * (behavior of Win2k at least)
112 NTSTATUS WINAPI
RtlQueryEnvironmentVariable_U(PWSTR env
,
113 PUNICODE_STRING name
,
114 PUNICODE_STRING value
)
116 NTSTATUS nts
= STATUS_VARIABLE_NOT_FOUND
;
120 TRACE("%s %s %p\n", debugstr_w(env
), debugstr_w(name
->Buffer
), value
);
123 namelen
= name
->Length
/ sizeof(WCHAR
);
124 if (!namelen
) return nts
;
129 var
= ntdll_get_process_pmts()->Environment
;
133 var
= ENV_FindVariable(var
, name
->Buffer
, namelen
);
136 value
->Length
= strlenW(var
) * sizeof(WCHAR
);
138 if (value
->Length
<= value
->MaximumLength
)
140 memmove(value
->Buffer
, var
, min(value
->Length
+ sizeof(WCHAR
), value
->MaximumLength
));
141 nts
= STATUS_SUCCESS
;
143 else nts
= STATUS_BUFFER_TOO_SMALL
;
146 if (!env
) RtlReleasePebLock();
151 /******************************************************************
152 * RtlSetCurrentEnvironment [NTDLL.@]
155 void WINAPI
RtlSetCurrentEnvironment(PWSTR new_env
, PWSTR
* old_env
)
157 TRACE("(%p %p)\n", new_env
, old_env
);
161 if (old_env
) *old_env
= ntdll_get_process_pmts()->Environment
;
162 ntdll_get_process_pmts()->Environment
= new_env
;
168 /******************************************************************************
169 * RtlSetEnvironmentVariable [NTDLL.@]
171 NTSTATUS WINAPI
RtlSetEnvironmentVariable(PWSTR
* penv
, PUNICODE_STRING name
,
172 PUNICODE_STRING value
)
176 NTSTATUS nts
= STATUS_VARIABLE_NOT_FOUND
;
177 MEMORY_BASIC_INFORMATION mbi
;
179 TRACE("(%p,%s,%s)\n",
180 penv
, debugstr_w(name
->Buffer
),
181 value
? debugstr_w(value
->Buffer
) : "--nil--");
183 if (!name
|| !name
->Buffer
|| !name
->Buffer
[0])
184 return STATUS_INVALID_PARAMETER_1
;
185 /* variable names can't contain a '=' except as a first character */
186 if (strchrW(name
->Buffer
+ 1, '=')) return STATUS_INVALID_PARAMETER
;
191 env
= ntdll_get_process_pmts()->Environment
;
194 len
= name
->Length
/ sizeof(WCHAR
);
196 /* compute current size of environment */
197 for (p
= env
; *p
; p
+= strlenW(p
) + 1);
198 old_size
= p
+ 1 - env
;
200 /* Find a place to insert the string */
201 for (p
= env
; *p
; p
+= strlenW(p
) + 1)
203 if (!strncmpiW(name
->Buffer
, p
, len
) && (p
[len
] == '=')) break;
205 if (!value
&& !*p
) goto done
; /* Value to remove doesn't exist */
207 /* Realloc the buffer */
208 len
= value
? len
+ value
->Length
/ sizeof(WCHAR
) + 2 : 0;
209 if (*p
) len
-= strlenW(p
) + 1; /* The name already exists */
213 LPWSTR next
= p
+ strlenW(p
) + 1; /* We know there is a next one */
214 memmove(next
+ len
, next
, (old_size
- (next
- env
)) * sizeof(WCHAR
));
217 nts
= NtQueryVirtualMemory(NtCurrentProcess(), env
, 0,
218 &mbi
, sizeof(mbi
), NULL
);
219 if (nts
!= STATUS_SUCCESS
) goto done
;
221 if ((old_size
+ len
) * sizeof(WCHAR
) > mbi
.RegionSize
)
224 ULONG new_size
= (old_size
+ len
) * sizeof(WCHAR
);
227 nts
= NtAllocateVirtualMemory(NtCurrentProcess(), (void**)&new_env
, 0,
228 &new_size
, MEM_RESERVE
| MEM_COMMIT
, PAGE_READWRITE
);
229 if (nts
!= STATUS_SUCCESS
) goto done
;
231 memmove(new_env
, env
, (p
- env
) * sizeof(WCHAR
));
233 memmove(new_env
+ (p
- env
) + len
, p
, (old_size
- (p
- env
)) * sizeof(WCHAR
));
234 p
= new_env
+ (p
- env
);
236 RtlDestroyEnvironment(env
);
237 if (!penv
) ntdll_get_process_pmts()->Environment
= new_env
;
238 else *penv
= new_env
;
243 if (len
> 0) memmove(p
+ len
, p
, (old_size
- (p
- env
)) * sizeof(WCHAR
));
246 /* Set the new string */
249 static const WCHAR equalW
[] = {'=',0};
251 strcpyW(p
, name
->Buffer
);
253 strcatW(p
, value
->Buffer
);
256 if (!penv
) RtlReleasePebLock();
261 /******************************************************************
262 * RtlExpandEnvironmentStrings_U (NTDLL.@)
265 NTSTATUS WINAPI
RtlExpandEnvironmentStrings_U(PWSTR renv
, const UNICODE_STRING
* us_src
,
266 PUNICODE_STRING us_dst
, PULONG plen
)
268 DWORD len
, count
, total_size
= 1; /* 1 for terminating '\0' */
269 LPCWSTR env
, src
, p
, var
;
272 src
= us_src
->Buffer
;
273 count
= us_dst
->MaximumLength
/ sizeof(WCHAR
);
274 dst
= count
? us_dst
->Buffer
: NULL
;
279 env
= ntdll_get_process_pmts()->Environment
;
287 if ((p
= strchrW( src
, '%' ))) len
= p
- src
;
288 else len
= strlenW(src
);
292 else /* we are at the start of a variable */
294 if ((p
= strchrW( src
+ 1, '%' )))
296 len
= p
- src
- 1; /* Length of the variable name */
297 if ((var
= ENV_FindVariable( env
, src
+ 1, len
)))
299 src
+= len
+ 2; /* Skip the variable name */
304 var
= src
; /* Copy original name instead */
309 else /* unfinished variable name, ignore it */
312 len
= strlenW(src
); /* Copy whole string */
319 if (count
< len
) len
= count
;
320 memcpy(dst
, var
, len
* sizeof(WCHAR
));
326 if (!renv
) RtlReleasePebLock();
328 /* Null-terminate the string */
329 if (dst
&& count
) *dst
= '\0';
331 us_dst
->Length
= (dst
) ? (dst
- us_dst
->Buffer
) * sizeof(WCHAR
) : 0;
332 if (plen
) *plen
= total_size
* sizeof(WCHAR
);
334 return (count
) ? STATUS_SUCCESS
: STATUS_BUFFER_TOO_SMALL
;
337 /***********************************************************************
340 * Build the Win32 environment from the Unix environment
342 static NTSTATUS
build_initial_environment(void)
344 extern char **environ
;
351 /* Compute the total size of the Unix environment */
353 for (e
= environ
; *e
; e
++)
355 if (!memcmp(*e
, "PATH=", 5)) continue;
356 size
+= strlen(*e
) + 1;
358 size
*= sizeof(WCHAR
);
360 /* Now allocate the environment */
361 nts
= NtAllocateVirtualMemory(NtCurrentProcess(), (void**)&p
, 0, &size
,
362 MEM_RESERVE
| MEM_COMMIT
, PAGE_READWRITE
);
363 if (nts
!= STATUS_SUCCESS
) return nts
;
365 ntdll_get_process_pmts()->Environment
= p
;
367 /* And fill it with the Unix environment */
368 for (e
= environ
; *e
; e
++)
370 /* skip Unix PATH and store WINEPATH as PATH */
371 if (!memcmp(*e
, "PATH=", 5)) continue;
372 if (!memcmp(*e
, "WINEPATH=", 9 )) te
= *e
+ 4; else te
= *e
;
374 RtlMultiByteToUnicodeN(p
, len
* sizeof(WCHAR
), NULL
, te
, len
);
380 return STATUS_SUCCESS
;
383 static void init_unicode( UNICODE_STRING
* us
, const char** src
, size_t len
)
388 ansi
.Buffer
= (char*)*src
;
390 ansi
.MaximumLength
= len
;
391 /* FIXME: should check value returned */
392 RtlAnsiStringToUnicodeString( us
, &ansi
, TRUE
);
397 /***********************************************************************
398 * init_user_process_pmts
400 * Fill the RTL_USER_PROCESS_PARAMETERS structure from the server.
402 BOOL
init_user_process_pmts( size_t info_size
, char *main_exe_name
, size_t main_exe_size
)
408 RTL_USER_PROCESS_PARAMETERS
*rupp
;
410 if (build_initial_environment() != STATUS_SUCCESS
) return FALSE
;
411 if (!info_size
) return TRUE
;
412 if (!(data
= RtlAllocateHeap( ntdll_get_process_heap(), 0, info_size
)))
415 SERVER_START_REQ( get_startup_info
)
417 wine_server_set_reply( req
, data
, info_size
);
418 wine_server_call( req
);
419 info_size
= wine_server_reply_size( reply
);
423 if (info_size
< sizeof(info
.size
)) goto done
;
424 len
= min( info_size
, ((startup_info_t
*)data
)->size
);
425 memset( &info
, 0, sizeof(info
) );
426 memcpy( &info
, data
, len
);
427 src
= (char *)data
+ len
;
430 /* fixup the lengths */
431 if (info
.filename_len
> info_size
) info
.filename_len
= info_size
;
432 info_size
-= info
.filename_len
;
433 if (info
.cmdline_len
> info_size
) info
.cmdline_len
= info_size
;
434 info_size
-= info
.cmdline_len
;
435 if (info
.desktop_len
> info_size
) info
.desktop_len
= info_size
;
436 info_size
-= info
.desktop_len
;
437 if (info
.title_len
> info_size
) info
.title_len
= info_size
;
439 /* store the filename */
440 len
= min( info
.filename_len
, main_exe_size
-1 );
441 memcpy( main_exe_name
, src
, len
);
442 main_exe_name
[len
] = 0;
444 rupp
= NtCurrentTeb()->Peb
->ProcessParameters
;
446 init_unicode( &rupp
->ImagePathName
, &src
, info
.filename_len
);
447 init_unicode( &rupp
->CommandLine
, &src
, info
.cmdline_len
);
448 init_unicode( &rupp
->Desktop
, &src
, info
.desktop_len
);
449 init_unicode( &rupp
->WindowTitle
, &src
, info
.title_len
);
453 rupp
->dwXSize
= info
.cx
;
454 rupp
->dwYSize
= info
.cy
;
455 rupp
->dwXCountChars
= info
.x_chars
;
456 rupp
->dwYCountChars
= info
.y_chars
;
457 rupp
->dwFillAttribute
= info
.attribute
;
458 rupp
->wShowWindow
= info
.cmd_show
;
459 rupp
->dwFlags
= info
.flags
;
462 RtlFreeHeap( ntdll_get_process_heap(), 0, data
);
466 /***********************************************************************
469 * Set the Wine library argc/argv global variables.
471 static void set_library_argv( char **argv
)
476 DWORD total
= 0, len
, reslen
;
478 for (argc
= 0; argv
[argc
]; argc
++)
480 len
= strlen(argv
[argc
]) + 1;
481 RtlMultiByteToUnicodeN(NULL
, 0, &reslen
, argv
[argc
], len
);
484 wargv
= RtlAllocateHeap( ntdll_get_process_heap(), 0,
485 total
+ (argc
+ 1) * sizeof(*wargv
) );
486 p
= (WCHAR
*)(wargv
+ argc
+ 1);
487 for (argc
= 0; argv
[argc
]; argc
++)
489 len
= strlen(argv
[argc
]) + 1;
490 RtlMultiByteToUnicodeN(p
, total
, &reslen
, argv
[argc
], len
);
492 p
+= reslen
/ sizeof(WCHAR
);
497 __wine_main_argc
= argc
;
498 __wine_main_argv
= argv
;
499 __wine_main_wargv
= wargv
;
502 /***********************************************************************
505 * Build the command line of a process from the argv array.
507 * Note that it does NOT necessarily include the file name.
508 * Sometimes we don't even have any command line options at all.
510 * We must quote and escape characters so that the argv array can be rebuilt
511 * from the command line:
512 * - spaces and tabs must be quoted
514 * - quotes must be escaped
516 * - if '\'s are followed by a '"', they must be doubled and followed by '\"',
517 * resulting in an odd number of '\' followed by a '"'
520 * - '\'s that are not followed by a '"' can be left as is
524 BOOL
build_command_line( char **argv
)
529 RTL_USER_PROCESS_PARAMETERS
* rupp
;
531 set_library_argv( argv
);
533 rupp
= ntdll_get_process_pmts();
534 if (rupp
->CommandLine
.Buffer
) return TRUE
; /* already got it from the server */
537 for (arg
= argv
; *arg
; arg
++)
539 int has_space
,bcount
;
545 if( !*a
) has_space
=1;
550 if (*a
==' ' || *a
=='\t') {
552 } else if (*a
=='"') {
553 /* doubling of '\' preceeding a '"',
554 * plus escaping of said '"'
562 len
+=(a
-*arg
)+1 /* for the separating space */;
564 len
+=2; /* for the quotes */
567 if (!(rupp
->CommandLine
.Buffer
= RtlAllocateHeap( ntdll_get_process_heap(), 0, len
* sizeof(WCHAR
))))
570 p
= rupp
->CommandLine
.Buffer
;
571 rupp
->CommandLine
.Length
= (len
- 1) * sizeof(WCHAR
);
572 rupp
->CommandLine
.MaximumLength
= len
* sizeof(WCHAR
);
573 for (arg
= argv
; *arg
; arg
++)
575 int has_space
,has_quote
;
578 /* Check for quotes and spaces in this argument */
579 has_space
=has_quote
=0;
581 if( !*a
) has_space
=1;
583 if (*a
==' ' || *a
=='\t') {
587 } else if (*a
=='"') {
595 /* Now transfer it to the command line */
612 /* Double all the '\\' preceeding this '"', plus one */
613 for (i
=0;i
<=bcount
;i
++)
625 while ((*p
=*x
++)) p
++;
631 if (p
> rupp
->CommandLine
.Buffer
)
632 p
--; /* remove last space */