environ.c 12.2 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4 5 6 7 8
/*
 * Process environment management
 *
 * Copyright 1996, 1998 Alexandre Julliard
 */

#include <stdlib.h>
#include <string.h>
9 10
#include "windef.h"
#include "wingdi.h"
11 12
#include "winuser.h"
#include "wine/winestring.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
#include "process.h"
#include "heap.h"
#include "selectors.h"
#include "winerror.h"

/* Format of an environment block:
 * ASCIIZ   string 1 (xx=yy format)
 * ...
 * ASCIIZ   string n
 * BYTE     0
 * WORD     1
 * ASCIIZ   program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
 *
 * Notes:
 * - contrary to Microsoft docs, the environment strings do not appear
 *   to be sorted on Win95 (although they are on NT); so we don't bother
 *   to sort them either.
 */

static const char ENV_program_name[] = "C:\\WINDOWS\\SYSTEM\\KRNL386.EXE";

Alexandre Julliard's avatar
Alexandre Julliard committed
34 35
/* Maximum length of a Win16 environment string (including NULL) */
#define MAX_WIN16_LEN  128
Alexandre Julliard's avatar
Alexandre Julliard committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

/* Extra bytes to reserve at the end of an environment */
#define EXTRA_ENV_SIZE (sizeof(BYTE) + sizeof(WORD) + sizeof(ENV_program_name))

/* Fill the extra bytes with the program name and stuff */
#define FILL_EXTRA_ENV(p) \
    *(p) = '\0'; \
    PUT_WORD( (p) + 1, 1 ); \
    strcpy( (p) + 3, ENV_program_name );


/***********************************************************************
 *           ENV_FindVariable
 *
 * Find a variable in the environment and return a pointer to the value.
 * Helper function for GetEnvironmentVariable and ExpandEnvironmentStrings.
 */
53
static LPCSTR ENV_FindVariable( LPCSTR env, LPCSTR name, INT len )
Alexandre Julliard's avatar
Alexandre Julliard committed
54 55 56
{
    while (*env)
    {
57
        if (!lstrncmpiA( name, env, len ) && (env[len] == '='))
Alexandre Julliard's avatar
Alexandre Julliard committed
58 59 60 61 62 63 64 65 66 67 68 69
            return env + len + 1;
        env += strlen(env) + 1;
    }
    return NULL;
}


/***********************************************************************
 *           ENV_BuildEnvironment
 *
 * Build the environment for the initial process
 */
70
BOOL ENV_BuildEnvironment(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
71 72 73
{
    extern char **environ;
    LPSTR p, *e;
Alexandre Julliard's avatar
Alexandre Julliard committed
74
    int size;
Alexandre Julliard's avatar
Alexandre Julliard committed
75 76 77 78

    /* Compute the total size of the Unix environment */

    size = EXTRA_ENV_SIZE;
Alexandre Julliard's avatar
Alexandre Julliard committed
79
    for (e = environ; *e; e++) size += strlen(*e) + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
80 81 82

    /* Now allocate the environment */

83
    if (!(p = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
84
    PROCESS_Current()->env_db->environ = p;
Alexandre Julliard's avatar
Alexandre Julliard committed
85 86 87 88 89

    /* And fill it with the Unix environment */

    for (e = environ; *e; e++)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
90
        strcpy( p, *e );
Alexandre Julliard's avatar
Alexandre Julliard committed
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
        p += strlen(p) + 1;
    }

    /* Now add the program name */

    FILL_EXTRA_ENV( p );
    return TRUE;
}


/***********************************************************************
 *           ENV_InheritEnvironment
 *
 * Make a process inherit the environment from its parent or from an
 * explicit environment.
 */
107
BOOL ENV_InheritEnvironment( PDB *pdb, LPCSTR env )
Alexandre Julliard's avatar
Alexandre Julliard committed
108 109
{
    DWORD size;
Alexandre Julliard's avatar
Alexandre Julliard committed
110 111
    LPCSTR src;
    LPSTR dst;
Alexandre Julliard's avatar
Alexandre Julliard committed
112 113 114

    /* Compute the environment size */

Alexandre Julliard's avatar
Alexandre Julliard committed
115 116 117 118 119 120 121 122 123 124
    src = env;
    size = EXTRA_ENV_SIZE;
    while (*src)
    {
        int len = strlen(src) + 1;
        src += len;
        if ((len > MAX_WIN16_LEN) && (pdb->flags & PDB32_WIN16_PROC))
            len = MAX_WIN16_LEN;
        size += len;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
125 126 127

    /* Copy the environment */

128
    if (!(pdb->env_db->environ = HeapAlloc( GetProcessHeap(), 0, size )))
Alexandre Julliard's avatar
Alexandre Julliard committed
129 130 131 132
        return FALSE;
    pdb->env_db->env_sel = SELECTOR_AllocBlock( pdb->env_db->environ,
                                                0x10000, SEGMENT_DATA,
                                                FALSE, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
133 134 135 136 137
    src = env;
    dst = pdb->env_db->environ;
    while (*src)
    {
        if (pdb->flags & PDB32_WIN16_PROC)
138
            lstrcpynA( dst, src, MAX_WIN16_LEN );
Alexandre Julliard's avatar
Alexandre Julliard committed
139 140 141 142 143 144
        else
            strcpy( dst, src );
        src += strlen(src) + 1;
        dst += strlen(dst) + 1;
    }
    FILL_EXTRA_ENV( dst );
Alexandre Julliard's avatar
Alexandre Julliard committed
145 146 147 148 149 150 151 152 153
    return TRUE;
}


/***********************************************************************
 *           ENV_FreeEnvironment
 *
 * Free a process environment.
 */
154
void ENV_FreeEnvironment( PDB *pdb )
Alexandre Julliard's avatar
Alexandre Julliard committed
155 156 157 158
{
    if (!pdb->env_db) return;
    if (pdb->env_db->env_sel) SELECTOR_FreeBlock( pdb->env_db->env_sel, 1 );
    DeleteCriticalSection( &pdb->env_db->section );
159
    /* the storage will be deleted when the process heap is destroyed */
Alexandre Julliard's avatar
Alexandre Julliard committed
160 161 162 163
}


/***********************************************************************
164
 *           GetCommandLineA      (KERNEL32.289)
Alexandre Julliard's avatar
Alexandre Julliard committed
165
 */
166
LPCSTR WINAPI GetCommandLineA(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
167 168 169 170 171
{
    return PROCESS_Current()->env_db->cmd_line;
}

/***********************************************************************
172
 *           GetCommandLineW      (KERNEL32.290)
Alexandre Julliard's avatar
Alexandre Julliard committed
173
 */
174
LPCWSTR WINAPI GetCommandLineW(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
175
{
176
    PDB *pdb = PROCESS_Current();
Alexandre Julliard's avatar
Alexandre Julliard committed
177 178
    EnterCriticalSection( &pdb->env_db->section );
    if (!pdb->env_db->cmd_lineW)
179
        pdb->env_db->cmd_lineW = HEAP_strdupAtoW( GetProcessHeap(), 0,
Alexandre Julliard's avatar
Alexandre Julliard committed
180 181 182 183 184 185 186
                                                  pdb->env_db->cmd_line );
    LeaveCriticalSection( &pdb->env_db->section );
    return pdb->env_db->cmd_lineW;
}


/***********************************************************************
187
 *           GetEnvironmentStringsA   (KERNEL32.319) (KERNEL32.320)
Alexandre Julliard's avatar
Alexandre Julliard committed
188
 */
189
LPSTR WINAPI GetEnvironmentStringsA(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
190
{
191
    PDB *pdb = PROCESS_Current();
Alexandre Julliard's avatar
Alexandre Julliard committed
192 193 194 195 196
    return pdb->env_db->environ;
}


/***********************************************************************
197
 *           GetEnvironmentStringsW   (KERNEL32.321)
Alexandre Julliard's avatar
Alexandre Julliard committed
198
 */
199
LPWSTR WINAPI GetEnvironmentStringsW(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
200
{
201
    INT size;
Alexandre Julliard's avatar
Alexandre Julliard committed
202
    LPWSTR ret;
203
    PDB *pdb = PROCESS_Current();
Alexandre Julliard's avatar
Alexandre Julliard committed
204 205

    EnterCriticalSection( &pdb->env_db->section );
206 207
    size = HeapSize( GetProcessHeap(), 0, pdb->env_db->environ );
    if ((ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )) != NULL)
Alexandre Julliard's avatar
Alexandre Julliard committed
208 209 210 211 212 213 214 215 216 217 218
    {
        LPSTR pA = pdb->env_db->environ;
        LPWSTR pW = ret;
        while (size--) *pW++ = (WCHAR)(BYTE)*pA++;
    }
    LeaveCriticalSection( &pdb->env_db->section );
    return ret;
}


/***********************************************************************
219
 *           FreeEnvironmentStringsA   (KERNEL32.268)
Alexandre Julliard's avatar
Alexandre Julliard committed
220
 */
221
BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr )
Alexandre Julliard's avatar
Alexandre Julliard committed
222
{
223
    PDB *pdb = PROCESS_Current();
Alexandre Julliard's avatar
Alexandre Julliard committed
224 225 226 227 228 229 230 231 232 233
    if (ptr != pdb->env_db->environ)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }
    return TRUE;
}


/***********************************************************************
234
 *           FreeEnvironmentStringsW   (KERNEL32.269)
Alexandre Julliard's avatar
Alexandre Julliard committed
235
 */
236
BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr )
Alexandre Julliard's avatar
Alexandre Julliard committed
237 238 239 240 241 242
{
    return HeapFree( GetProcessHeap(), 0, ptr );
}


/***********************************************************************
243
 *           GetEnvironmentVariableA   (KERNEL32.322)
Alexandre Julliard's avatar
Alexandre Julliard committed
244
 */
245
DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size )
Alexandre Julliard's avatar
Alexandre Julliard committed
246 247
{
    LPCSTR p;
248 249
    INT ret = 0;
    PDB *pdb = PROCESS_Current();
Alexandre Julliard's avatar
Alexandre Julliard committed
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274

    if (!name || !*name)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return 0;
    }
    EnterCriticalSection( &pdb->env_db->section );
    if ((p = ENV_FindVariable( pdb->env_db->environ, name, strlen(name) )))
    {
        ret = strlen(p);
        if (size <= ret)
        {
            /* If not enough room, include the terminating null
             * in the returned size and return an empty string */
            ret++;
            if (value) *value = '\0';
        }
        else if (value) strcpy( value, p );
    }
    LeaveCriticalSection( &pdb->env_db->section );
    return ret;  /* FIXME: SetLastError */
}


/***********************************************************************
275
 *           GetEnvironmentVariableW   (KERNEL32.323)
Alexandre Julliard's avatar
Alexandre Julliard committed
276
 */
277
DWORD WINAPI GetEnvironmentVariableW( LPCWSTR nameW, LPWSTR valW, DWORD size)
Alexandre Julliard's avatar
Alexandre Julliard committed
278 279 280
{
    LPSTR name = HEAP_strdupWtoA( GetProcessHeap(), 0, nameW );
    LPSTR val  = valW ? HeapAlloc( GetProcessHeap(), 0, size ) : NULL;
281
    DWORD res  = GetEnvironmentVariableA( name, val, size );
Alexandre Julliard's avatar
Alexandre Julliard committed
282 283 284 285 286 287 288 289 290 291 292
    HeapFree( GetProcessHeap(), 0, name );
    if (val)
    {
        lstrcpynAtoW( valW, val, size );
        HeapFree( GetProcessHeap(), 0, val );
    }
    return res;
}


/***********************************************************************
293
 *           SetEnvironmentVariableA   (KERNEL32.641)
Alexandre Julliard's avatar
Alexandre Julliard committed
294
 */
295
BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value )
Alexandre Julliard's avatar
Alexandre Julliard committed
296
{
297
    INT old_size, len, res;
Alexandre Julliard's avatar
Alexandre Julliard committed
298
    LPSTR p, env, new_env;
299 300
    BOOL ret = FALSE;
    PDB *pdb = PROCESS_Current();
Alexandre Julliard's avatar
Alexandre Julliard committed
301 302 303 304 305 306 307 308 309 310

    EnterCriticalSection( &pdb->env_db->section );
    env = p = pdb->env_db->environ;

    /* Find a place to insert the string */

    res = -1;
    len = strlen(name);
    while (*p)
    {
311
        if (!lstrncmpiA( name, p, len ) && (p[len] == '=')) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
312 313 314 315 316 317 318 319
        p += strlen(p) + 1;
    }
    if (!value && !*p) goto done;  /* Value to remove doesn't exist */

    /* Realloc the buffer */

    len = value ? strlen(name) + strlen(value) + 2 : 0;
    if (*p) len -= strlen(p) + 1;  /* The name already exists */
320
    old_size = HeapSize( GetProcessHeap(), 0, env );
Alexandre Julliard's avatar
Alexandre Julliard committed
321 322 323 324 325
    if (len < 0)
    {
        LPSTR next = p + strlen(p) + 1;  /* We know there is a next one */
        memmove( next + len, next, old_size - (next - env) );
    }
326
    if (!(new_env = HeapReAlloc( GetProcessHeap(), 0, env, old_size + len )))
Alexandre Julliard's avatar
Alexandre Julliard committed
327
        goto done;
Alexandre Julliard's avatar
Alexandre Julliard committed
328 329
    if (pdb->env_db->env_sel)
        SELECTOR_MoveBlock( pdb->env_db->env_sel, new_env );
Alexandre Julliard's avatar
Alexandre Julliard committed
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
    p = new_env + (p - env);
    if (len > 0) memmove( p + len, p, old_size - (p - new_env) );

    /* Set the new string */

    if (value)
    {
        strcpy( p, name );
        strcat( p, "=" );
        strcat( p, value );
    }
    pdb->env_db->environ = new_env;
    ret = TRUE;

done:
    LeaveCriticalSection( &pdb->env_db->section );
    return ret;
}


/***********************************************************************
351
 *           SetEnvironmentVariableW   (KERNEL32.642)
Alexandre Julliard's avatar
Alexandre Julliard committed
352
 */
353
BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value )
Alexandre Julliard's avatar
Alexandre Julliard committed
354 355 356
{
    LPSTR nameA  = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
    LPSTR valueA = HEAP_strdupWtoA( GetProcessHeap(), 0, value );
357
    BOOL ret = SetEnvironmentVariableA( nameA, valueA );
Alexandre Julliard's avatar
Alexandre Julliard committed
358 359 360 361 362 363 364
    HeapFree( GetProcessHeap(), 0, nameA );
    HeapFree( GetProcessHeap(), 0, valueA );
    return ret;
}


/***********************************************************************
365
 *           ExpandEnvironmentStringsA   (KERNEL32.216)
Alexandre Julliard's avatar
Alexandre Julliard committed
366 367 368
 *
 * Note: overlapping buffers are not supported; this is how it should be.
 */
369
DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count )
Alexandre Julliard's avatar
Alexandre Julliard committed
370 371 372
{
    DWORD len, total_size = 1;  /* 1 for terminating '\0' */
    LPCSTR p, var;
373
    PDB *pdb = PROCESS_Current();
Alexandre Julliard's avatar
Alexandre Julliard committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433

    if (!count) dst = NULL;
    EnterCriticalSection( &pdb->env_db->section );

    while (*src)
    {
        if (*src != '%')
        {
            if ((p = strchr( src, '%' ))) len = p - src;
            else len = strlen(src);
            var = src;
            src += len;
        }
        else  /* we are at the start of a variable */
        {
            if ((p = strchr( src + 1, '%' )))
            {
                len = p - src - 1;  /* Length of the variable name */
                if ((var = ENV_FindVariable( pdb->env_db->environ,
                                             src + 1, len )))
                {
                    src += len + 2;  /* Skip the variable name */
                    len = strlen(var);
                }
                else
                {
                    var = src;  /* Copy original name instead */
                    len += 2;
                    src += len;
                }
            }
            else  /* unfinished variable name, ignore it */
            {
                var = src;
                len = strlen(src);  /* Copy whole string */
                src += len;
            }
        }
        total_size += len;
        if (dst)
        {
            if (count < len) len = count;
            memcpy( dst, var, len );
            dst += len;
            count -= len;
        }
    }
    LeaveCriticalSection( &pdb->env_db->section );

    /* Null-terminate the string */
    if (dst)
    {
        if (!count) dst--;
        *dst = '\0';
    }
    return total_size;
}


/***********************************************************************
434
 *           ExpandEnvironmentStringsW   (KERNEL32.217)
Alexandre Julliard's avatar
Alexandre Julliard committed
435
 */
436
DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len )
Alexandre Julliard's avatar
Alexandre Julliard committed
437 438 439
{
    LPSTR srcA = HEAP_strdupWtoA( GetProcessHeap(), 0, src );
    LPSTR dstA = dst ? HeapAlloc( GetProcessHeap(), 0, len ) : NULL;
440
    DWORD ret  = ExpandEnvironmentStringsA( srcA, dstA, len );
Alexandre Julliard's avatar
Alexandre Julliard committed
441 442 443 444 445 446 447 448 449
    if (dstA)
    {
        lstrcpyAtoW( dst, dstA );
        HeapFree( GetProcessHeap(), 0, dstA );
    }
    HeapFree( GetProcessHeap(), 0, srcA );
    return ret;
}