Send message for WSAAsyncSelect sockets directly from the server,
[wine] / memory / environ.c
1 /*
2  * Process environment management
3  *
4  * Copyright 1996, 1998 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "windef.h"
28 #include "winerror.h"
29
30 #include "wine/winbase16.h"
31 #include "heap.h"
32 #include "ntddk.h"
33 #include "selectors.h"
34
35 /* Win32 process environment database */
36 typedef struct _ENVDB
37 {
38     LPSTR            environ;          /* 00 Process environment strings */
39     DWORD            unknown1;         /* 04 Unknown */
40     LPSTR            cmd_line;         /* 08 Command line */
41     LPSTR            cur_dir;          /* 0c Current directory */
42     STARTUPINFOA    *startup_info;     /* 10 Startup information */
43     HANDLE           hStdin;           /* 14 Handle for standard input */
44     HANDLE           hStdout;          /* 18 Handle for standard output */
45     HANDLE           hStderr;          /* 1c Handle for standard error */
46     DWORD            unknown2;         /* 20 Unknown */
47     DWORD            inherit_console;  /* 24 Inherit console flag */
48     DWORD            break_type;       /* 28 Console events flag */
49     void            *break_sem;        /* 2c SetConsoleCtrlHandler semaphore */
50     void            *break_event;      /* 30 SetConsoleCtrlHandler event */
51     void            *break_thread;     /* 34 SetConsoleCtrlHandler thread */
52     void            *break_handlers;   /* 38 List of console handlers */
53 } ENVDB;
54
55
56 /* Format of an environment block:
57  * ASCIIZ   string 1 (xx=yy format)
58  * ...
59  * ASCIIZ   string n
60  * BYTE     0
61  * WORD     1
62  * ASCIIZ   program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
63  *
64  * Notes:
65  * - contrary to Microsoft docs, the environment strings do not appear
66  *   to be sorted on Win95 (although they are on NT); so we don't bother
67  *   to sort them either.
68  */
69
70 static const char ENV_program_name[] = "C:\\WINDOWS\\SYSTEM\\KRNL386.EXE";
71
72 /* Maximum length of a Win16 environment string (including NULL) */
73 #define MAX_WIN16_LEN  128
74
75 /* Extra bytes to reserve at the end of an environment */
76 #define EXTRA_ENV_SIZE (sizeof(BYTE) + sizeof(WORD) + sizeof(ENV_program_name))
77
78 /* Fill the extra bytes with the program name and stuff */
79 #define FILL_EXTRA_ENV(p) \
80     *(p) = '\0'; \
81     PUT_UA_WORD( (p) + 1, 1 ); \
82     strcpy( (p) + 3, ENV_program_name );
83
84 STARTUPINFOA current_startupinfo =
85 {
86     sizeof(STARTUPINFOA),    /* cb */
87     0,                       /* lpReserved */
88     0,                       /* lpDesktop */
89     0,                       /* lpTitle */
90     0,                       /* dwX */
91     0,                       /* dwY */
92     0,                       /* dwXSize */
93     0,                       /* dwYSize */
94     0,                       /* dwXCountChars */
95     0,                       /* dwYCountChars */
96     0,                       /* dwFillAttribute */
97     0,                       /* dwFlags */
98     0,                       /* wShowWindow */
99     0,                       /* cbReserved2 */
100     0,                       /* lpReserved2 */
101     0,                       /* hStdInput */
102     0,                       /* hStdOutput */
103     0                        /* hStdError */
104 };
105
106 ENVDB current_envdb =
107 {
108     0,                       /* environ */
109     0,                       /* unknown1 */
110     0,                       /* cmd_line */
111     0,                       /* cur_dir */
112     &current_startupinfo,    /* startup_info */
113     0,                       /* hStdin */
114     0,                       /* hStdout */
115     0,                       /* hStderr */
116     0,                       /* unknown2 */
117     0,                       /* inherit_console */
118     0,                       /* break_type */
119     0,                       /* break_sem */
120     0,                       /* break_event */
121     0,                       /* break_thread */
122     0                        /* break_handlers */
123 };
124
125
126 static WCHAR *cmdlineW;  /* Unicode command line */
127 static WORD env_sel;     /* selector to the environment */
128
129 /***********************************************************************
130  *           ENV_FindVariable
131  *
132  * Find a variable in the environment and return a pointer to the value.
133  * Helper function for GetEnvironmentVariable and ExpandEnvironmentStrings.
134  */
135 static LPCSTR ENV_FindVariable( LPCSTR env, LPCSTR name, INT len )
136 {
137     while (*env)
138     {
139         if (!strncasecmp( name, env, len ) && (env[len] == '='))
140             return env + len + 1;
141         env += strlen(env) + 1;
142     }
143     return NULL;
144 }
145
146
147 /***********************************************************************
148  *           ENV_BuildEnvironment
149  *
150  * Build the environment for the initial process
151  */
152 ENVDB *ENV_BuildEnvironment(void)
153 {
154     extern char **environ;
155     LPSTR p, *e;
156     int size;
157
158     /* Compute the total size of the Unix environment */
159
160     size = EXTRA_ENV_SIZE;
161     for (e = environ; *e; e++) size += strlen(*e) + 1;
162
163     /* Now allocate the environment */
164
165     if (!(p = HeapAlloc( GetProcessHeap(), 0, size ))) return NULL;
166     current_envdb.environ = p;
167     env_sel = SELECTOR_AllocBlock( p, 0x10000, WINE_LDT_FLAGS_DATA );
168
169     /* And fill it with the Unix environment */
170
171     for (e = environ; *e; e++)
172     {
173         strcpy( p, *e );
174         p += strlen(p) + 1;
175     }
176
177     /* Now add the program name */
178
179     FILL_EXTRA_ENV( p );
180     return &current_envdb;
181 }
182
183
184 /***********************************************************************
185  *           ENV_BuildCommandLine
186  *
187  * Build the command line of a process from the argv array.
188  *
189  * Note that it does NOT necessarily include the file name.
190  * Sometimes we don't even have any command line options at all.
191  *
192  * We must quote and escape characters so that the argv array can be rebuilt 
193  * from the command line:
194  * - spaces and tabs must be quoted
195  *   'a b'   -> '"a b"'
196  * - quotes must be escaped
197  *   '"'     -> '\"'
198  * - if '\'s are followed by a '"', they must be doubled and followed by '\"', 
199  *   resulting in an odd number of '\' followed by a '"'
200  *   '\"'    -> '\\\"'
201  *   '\\"'   -> '\\\\\"'
202  * - '\'s that are not followed by a '"' can be left as is
203  *   'a\b'   == 'a\b'
204  *   'a\\b'  == 'a\\b'
205  */
206 BOOL ENV_BuildCommandLine( char **argv )
207 {
208     int len;
209     char *p, **arg;
210
211     len = 0;
212     for (arg = argv; *arg; arg++)
213     {
214         int has_space,bcount;
215         char* a;
216
217         has_space=0;
218         bcount=0;
219         a=*arg;
220         while (*a!='\0') {
221             if (*a=='\\') {
222                 bcount++;
223             } else {
224                 if (*a==' ' || *a=='\t') {
225                     has_space=1;
226                 } else if (*a=='"') {
227                     /* doubling of '\' preceeding a '"', 
228                      * plus escaping of said '"'
229                      */
230                     len+=2*bcount+1;
231                 }
232                 bcount=0;
233             }
234             a++;
235         }
236         len+=(a-*arg)+1 /* for the separating space */;
237         if (has_space)
238             len+=2; /* for the quotes */
239     }
240
241     if (!(current_envdb.cmd_line = HeapAlloc( GetProcessHeap(), 0, len )))
242         return FALSE;
243
244     p = current_envdb.cmd_line;
245     for (arg = argv; *arg; arg++)
246     {
247         int has_space,has_quote;
248         char* a;
249
250         /* Check for quotes and spaces in this argument */
251         has_space=has_quote=0;
252         a=*arg;
253         while (*a!='\0') {
254             if (*a==' ' || *a=='\t') {
255                 has_space=1;
256                 if (has_quote)
257                     break;
258             } else if (*a=='"') {
259                 has_quote=1;
260                 if (has_space)
261                     break;
262             }
263             a++;
264         }
265
266         /* Now transfer it to the command line */
267         if (has_space)
268             *p++='"';
269         if (has_quote) {
270             int bcount;
271             char* a;
272
273             bcount=0;
274             a=*arg;
275             while (*a!='\0') {
276                 if (*a=='\\') {
277                     *p++=*a;
278                     bcount++;
279                 } else {
280                     if (*a=='"') {
281                         int i;
282
283                         /* Double all the '\\' preceeding this '"', plus one */
284                         for (i=0;i<=bcount;i++)
285                             *p++='\\';
286                         *p++='"';
287                     } else {
288                         *p++=*a;
289                     }
290                     bcount=0;
291                 }
292                 a++;
293             }
294         } else {
295             strcpy(p,*arg);
296             p+=strlen(*arg);
297         }
298         if (has_space)
299             *p++='"';
300         *p++=' ';
301     }
302     if (p > current_envdb.cmd_line)
303         p--;  /* remove last space */
304     *p = '\0';
305
306     /* now allocate the Unicode version */
307     len = MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, NULL, 0 );
308     if (!(cmdlineW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
309         return FALSE;
310     MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, cmdlineW, len );
311     return TRUE;
312 }
313
314
315 /***********************************************************************
316  *           GetCommandLineA      (KERNEL32.@)
317  *
318  * WARNING: there's a Windows incompatibility lurking here !
319  * Win32s always includes the full path of the program file,
320  * whereas Windows NT only returns the full file path plus arguments
321  * in case the program has been started with a full path.
322  * Win9x seems to have inherited NT behaviour.
323  * 
324  * Note that both Start Menu Execute and Explorer start programs with
325  * fully specified quoted app file paths, which is why probably the only case
326  * where you'll see single file names is in case of direct launch
327  * via CreateProcess or WinExec.
328  *
329  * Perhaps we should take care of Win3.1 programs here (Win32s "feature").
330  * 
331  * References: MS KB article q102762.txt (special Win32s handling)
332  */
333 LPSTR WINAPI GetCommandLineA(void)
334 {
335     return current_envdb.cmd_line;
336 }
337
338 /***********************************************************************
339  *           GetCommandLineW      (KERNEL32.@)
340  */
341 LPWSTR WINAPI GetCommandLineW(void)
342 {
343     return cmdlineW;
344 }
345
346
347 /***********************************************************************
348  *           GetEnvironmentStrings    (KERNEL32.@)
349  *           GetEnvironmentStringsA   (KERNEL32.@)
350  */
351 LPSTR WINAPI GetEnvironmentStringsA(void)
352 {
353     return current_envdb.environ;
354 }
355
356
357 /***********************************************************************
358  *           GetEnvironmentStringsW   (KERNEL32.@)
359  */
360 LPWSTR WINAPI GetEnvironmentStringsW(void)
361 {
362     INT size;
363     LPWSTR ret;
364
365     RtlAcquirePebLock();
366     size = HeapSize( GetProcessHeap(), 0, current_envdb.environ );
367     if ((ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )) != NULL)
368     {
369         LPSTR pA = current_envdb.environ;
370         LPWSTR pW = ret;
371         while (size--) *pW++ = (WCHAR)(BYTE)*pA++;
372     }
373     RtlReleasePebLock();
374     return ret;
375 }
376
377
378 /***********************************************************************
379  *           FreeEnvironmentStringsA   (KERNEL32.@)
380  */
381 BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr )
382 {
383     if (ptr != current_envdb.environ)
384     {
385         SetLastError( ERROR_INVALID_PARAMETER );
386         return FALSE;
387     }
388     return TRUE;
389 }
390
391
392 /***********************************************************************
393  *           FreeEnvironmentStringsW   (KERNEL32.@)
394  */
395 BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr )
396 {
397     return HeapFree( GetProcessHeap(), 0, ptr );
398 }
399
400
401 /***********************************************************************
402  *           GetEnvironmentVariableA   (KERNEL32.@)
403  */
404 DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size )
405 {
406     LPCSTR p;
407     INT ret = 0;
408
409     if (!name || !*name)
410     {
411         SetLastError( ERROR_INVALID_PARAMETER );
412         return 0;
413     }
414     RtlAcquirePebLock();
415     if ((p = ENV_FindVariable( current_envdb.environ, name, strlen(name) )))
416     {
417         ret = strlen(p);
418         if (size <= ret)
419         {
420             /* If not enough room, include the terminating null
421              * in the returned size and return an empty string */
422             ret++;
423             if (value) *value = '\0';
424         }
425         else if (value) strcpy( value, p );
426     }
427     RtlReleasePebLock();
428     if (!ret)
429         SetLastError( ERROR_ENVVAR_NOT_FOUND );
430     return ret;
431 }
432
433
434 /***********************************************************************
435  *           GetEnvironmentVariableW   (KERNEL32.@)
436  */
437 DWORD WINAPI GetEnvironmentVariableW( LPCWSTR nameW, LPWSTR valW, DWORD size)
438 {
439     LPSTR name = HEAP_strdupWtoA( GetProcessHeap(), 0, nameW );
440     LPSTR val  = valW ? HeapAlloc( GetProcessHeap(), 0, size ) : NULL;
441     DWORD res  = GetEnvironmentVariableA( name, val, size );
442     HeapFree( GetProcessHeap(), 0, name );
443     if (val)
444     {
445         if (size > 0 && !MultiByteToWideChar( CP_ACP, 0, val, -1, valW, size ))
446             valW[size-1] = 0;
447         HeapFree( GetProcessHeap(), 0, val );
448     }
449     return res;
450 }
451
452
453 /***********************************************************************
454  *           SetEnvironmentVariableA   (KERNEL32.@)
455  */
456 BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value )
457 {
458     INT old_size, len, res;
459     LPSTR p, env, new_env;
460     BOOL ret = FALSE;
461
462     RtlAcquirePebLock();
463     env = p = current_envdb.environ;
464
465     /* Find a place to insert the string */
466
467     res = -1;
468     len = strlen(name);
469     while (*p)
470     {
471         if (!strncasecmp( name, p, len ) && (p[len] == '=')) break;
472         p += strlen(p) + 1;
473     }
474     if (!value && !*p) goto done;  /* Value to remove doesn't exist */
475
476     /* Realloc the buffer */
477
478     len = value ? strlen(name) + strlen(value) + 2 : 0;
479     if (*p) len -= strlen(p) + 1;  /* The name already exists */
480     old_size = HeapSize( GetProcessHeap(), 0, env );
481     if (len < 0)
482     {
483         LPSTR next = p + strlen(p) + 1;  /* We know there is a next one */
484         memmove( next + len, next, old_size - (next - env) );
485     }
486     if (!(new_env = HeapReAlloc( GetProcessHeap(), 0, env, old_size + len )))
487         goto done;
488     if (env_sel) env_sel = SELECTOR_ReallocBlock( env_sel, new_env, old_size + len );
489     p = new_env + (p - env);
490     if (len > 0) memmove( p + len, p, old_size - (p - new_env) );
491
492     /* Set the new string */
493
494     if (value)
495     {
496         strcpy( p, name );
497         strcat( p, "=" );
498         strcat( p, value );
499     }
500     current_envdb.environ = new_env;
501     ret = TRUE;
502
503 done:
504     RtlReleasePebLock();
505     return ret;
506 }
507
508
509 /***********************************************************************
510  *           SetEnvironmentVariableW   (KERNEL32.@)
511  */
512 BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value )
513 {
514     LPSTR nameA  = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
515     LPSTR valueA = HEAP_strdupWtoA( GetProcessHeap(), 0, value );
516     BOOL ret = SetEnvironmentVariableA( nameA, valueA );
517     HeapFree( GetProcessHeap(), 0, nameA );
518     HeapFree( GetProcessHeap(), 0, valueA );
519     return ret;
520 }
521
522
523 /***********************************************************************
524  *           ExpandEnvironmentStringsA   (KERNEL32.@)
525  *
526  * Note: overlapping buffers are not supported; this is how it should be.
527  */
528 DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count )
529 {
530     DWORD len, total_size = 1;  /* 1 for terminating '\0' */
531     LPCSTR p, var;
532
533     if (!count) dst = NULL;
534     RtlAcquirePebLock();
535
536     while (*src)
537     {
538         if (*src != '%')
539         {
540             if ((p = strchr( src, '%' ))) len = p - src;
541             else len = strlen(src);
542             var = src;
543             src += len;
544         }
545         else  /* we are at the start of a variable */
546         {
547             if ((p = strchr( src + 1, '%' )))
548             {
549                 len = p - src - 1;  /* Length of the variable name */
550                 if ((var = ENV_FindVariable( current_envdb.environ,
551                                              src + 1, len )))
552                 {
553                     src += len + 2;  /* Skip the variable name */
554                     len = strlen(var);
555                 }
556                 else
557                 {
558                     var = src;  /* Copy original name instead */
559                     len += 2;
560                     src += len;
561                 }
562             }
563             else  /* unfinished variable name, ignore it */
564             {
565                 var = src;
566                 len = strlen(src);  /* Copy whole string */
567                 src += len;
568             }
569         }
570         total_size += len;
571         if (dst)
572         {
573             if (count < len) len = count;
574             memcpy( dst, var, len );
575             dst += len;
576             count -= len;
577         }
578     }
579     RtlReleasePebLock();
580
581     /* Null-terminate the string */
582     if (dst)
583     {
584         if (!count) dst--;
585         *dst = '\0';
586     }
587     return total_size;
588 }
589
590
591 /***********************************************************************
592  *           ExpandEnvironmentStringsW   (KERNEL32.@)
593  */
594 DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len )
595 {
596     LPSTR srcA = HEAP_strdupWtoA( GetProcessHeap(), 0, src );
597     LPSTR dstA = dst ? HeapAlloc( GetProcessHeap(), 0, len ) : NULL;
598     DWORD ret  = ExpandEnvironmentStringsA( srcA, dstA, len );
599     if (dstA)
600     {
601         ret = MultiByteToWideChar( CP_ACP, 0, dstA, -1, dst, len );
602         HeapFree( GetProcessHeap(), 0, dstA );
603     }
604     HeapFree( GetProcessHeap(), 0, srcA );
605     return ret;
606 }
607
608
609 /***********************************************************************
610  *           GetDOSEnvironment     (KERNEL.131)
611  *           GetDOSEnvironment16   (KERNEL32.@)
612  */
613 SEGPTR WINAPI GetDOSEnvironment16(void)
614 {
615     return MAKESEGPTR( env_sel, 0 );
616 }
617
618
619 /***********************************************************************
620  *           GetStdHandle    (KERNEL32.@)
621  */
622 HANDLE WINAPI GetStdHandle( DWORD std_handle )
623 {
624     switch(std_handle)
625     {
626         case STD_INPUT_HANDLE:  return current_envdb.hStdin;
627         case STD_OUTPUT_HANDLE: return current_envdb.hStdout;
628         case STD_ERROR_HANDLE:  return current_envdb.hStderr;
629     }
630     SetLastError( ERROR_INVALID_PARAMETER );
631     return INVALID_HANDLE_VALUE;
632 }
633
634
635 /***********************************************************************
636  *           SetStdHandle    (KERNEL32.@)
637  */
638 BOOL WINAPI SetStdHandle( DWORD std_handle, HANDLE handle )
639 {
640     switch(std_handle)
641     {
642         case STD_INPUT_HANDLE:  current_envdb.hStdin = handle;  return TRUE;
643         case STD_OUTPUT_HANDLE: current_envdb.hStdout = handle; return TRUE;
644         case STD_ERROR_HANDLE:  current_envdb.hStderr = handle; return TRUE;
645     }
646     SetLastError( ERROR_INVALID_PARAMETER );
647     return FALSE;
648 }
649
650
651 /***********************************************************************
652  *              GetStartupInfoA         (KERNEL32.@)
653  */
654 VOID WINAPI GetStartupInfoA( LPSTARTUPINFOA info )
655 {
656     *info = current_startupinfo;
657 }
658
659
660 /***********************************************************************
661  *              GetStartupInfoW         (KERNEL32.@)
662  */
663 VOID WINAPI GetStartupInfoW( LPSTARTUPINFOW info )
664 {
665     info->cb              = sizeof(STARTUPINFOW);
666     info->dwX             = current_startupinfo.dwX;
667     info->dwY             = current_startupinfo.dwY;
668     info->dwXSize         = current_startupinfo.dwXSize;
669     info->dwXCountChars   = current_startupinfo.dwXCountChars;
670     info->dwYCountChars   = current_startupinfo.dwYCountChars;
671     info->dwFillAttribute = current_startupinfo.dwFillAttribute;
672     info->dwFlags         = current_startupinfo.dwFlags;
673     info->wShowWindow     = current_startupinfo.wShowWindow;
674     info->cbReserved2     = current_startupinfo.cbReserved2;
675     info->lpReserved2     = current_startupinfo.lpReserved2;
676     info->hStdInput       = current_startupinfo.hStdInput;
677     info->hStdOutput      = current_startupinfo.hStdOutput;
678     info->hStdError       = current_startupinfo.hStdError;
679     info->lpReserved = HEAP_strdupAtoW (GetProcessHeap(), 0, current_startupinfo.lpReserved );
680     info->lpDesktop  = HEAP_strdupAtoW (GetProcessHeap(), 0, current_startupinfo.lpDesktop );
681     info->lpTitle    = HEAP_strdupAtoW (GetProcessHeap(), 0, current_startupinfo.lpTitle );
682 }