/* * Process environment management * * Copyright 1996, 1998 Alexandre Julliard */ #include #include #include "windef.h" #include "wingdi.h" #include "winuser.h" #include "wine/winestring.h" #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"; /* Maximum length of a Win16 environment string (including NULL) */ #define MAX_WIN16_LEN 128 /* 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. */ static LPCSTR ENV_FindVariable( LPCSTR env, LPCSTR name, INT len ) { while (*env) { if (!lstrncmpiA( name, env, len ) && (env[len] == '=')) return env + len + 1; env += strlen(env) + 1; } return NULL; } /*********************************************************************** * ENV_BuildEnvironment * * Build the environment for the initial process */ BOOL ENV_BuildEnvironment(void) { extern char **environ; LPSTR p, *e; int size; /* Compute the total size of the Unix environment */ size = EXTRA_ENV_SIZE; for (e = environ; *e; e++) size += strlen(*e) + 1; /* Now allocate the environment */ if (!(p = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; PROCESS_Current()->env_db->environ = p; /* And fill it with the Unix environment */ for (e = environ; *e; e++) { strcpy( p, *e ); 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. */ BOOL ENV_InheritEnvironment( PDB *pdb, LPCSTR env ) { DWORD size; LPCSTR src; LPSTR dst; /* Compute the environment size */ 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; } /* Copy the environment */ if (!(pdb->env_db->environ = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; pdb->env_db->env_sel = SELECTOR_AllocBlock( pdb->env_db->environ, 0x10000, SEGMENT_DATA, FALSE, FALSE ); src = env; dst = pdb->env_db->environ; while (*src) { if (pdb->flags & PDB32_WIN16_PROC) lstrcpynA( dst, src, MAX_WIN16_LEN ); else strcpy( dst, src ); src += strlen(src) + 1; dst += strlen(dst) + 1; } FILL_EXTRA_ENV( dst ); return TRUE; } /*********************************************************************** * ENV_FreeEnvironment * * Free a process environment. */ void ENV_FreeEnvironment( PDB *pdb ) { if (!pdb->env_db) return; if (pdb->env_db->env_sel) SELECTOR_FreeBlock( pdb->env_db->env_sel, 1 ); DeleteCriticalSection( &pdb->env_db->section ); /* the storage will be deleted when the process heap is destroyed */ } /*********************************************************************** * GetCommandLineA (KERNEL32.289) */ LPCSTR WINAPI GetCommandLineA(void) { return PROCESS_Current()->env_db->cmd_line; } /*********************************************************************** * GetCommandLineW (KERNEL32.290) */ LPCWSTR WINAPI GetCommandLineW(void) { PDB *pdb = PROCESS_Current(); EnterCriticalSection( &pdb->env_db->section ); if (!pdb->env_db->cmd_lineW) pdb->env_db->cmd_lineW = HEAP_strdupAtoW( GetProcessHeap(), 0, pdb->env_db->cmd_line ); LeaveCriticalSection( &pdb->env_db->section ); return pdb->env_db->cmd_lineW; } /*********************************************************************** * GetEnvironmentStringsA (KERNEL32.319) (KERNEL32.320) */ LPSTR WINAPI GetEnvironmentStringsA(void) { PDB *pdb = PROCESS_Current(); return pdb->env_db->environ; } /*********************************************************************** * GetEnvironmentStringsW (KERNEL32.321) */ LPWSTR WINAPI GetEnvironmentStringsW(void) { INT size; LPWSTR ret; PDB *pdb = PROCESS_Current(); EnterCriticalSection( &pdb->env_db->section ); size = HeapSize( GetProcessHeap(), 0, pdb->env_db->environ ); if ((ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )) != NULL) { LPSTR pA = pdb->env_db->environ; LPWSTR pW = ret; while (size--) *pW++ = (WCHAR)(BYTE)*pA++; } LeaveCriticalSection( &pdb->env_db->section ); return ret; } /*********************************************************************** * FreeEnvironmentStringsA (KERNEL32.268) */ BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr ) { PDB *pdb = PROCESS_Current(); if (ptr != pdb->env_db->environ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } return TRUE; } /*********************************************************************** * FreeEnvironmentStringsW (KERNEL32.269) */ BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr ) { return HeapFree( GetProcessHeap(), 0, ptr ); } /*********************************************************************** * GetEnvironmentVariableA (KERNEL32.322) */ DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size ) { LPCSTR p; INT ret = 0; PDB *pdb = PROCESS_Current(); 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 */ } /*********************************************************************** * GetEnvironmentVariableW (KERNEL32.323) */ DWORD WINAPI GetEnvironmentVariableW( LPCWSTR nameW, LPWSTR valW, DWORD size) { LPSTR name = HEAP_strdupWtoA( GetProcessHeap(), 0, nameW ); LPSTR val = valW ? HeapAlloc( GetProcessHeap(), 0, size ) : NULL; DWORD res = GetEnvironmentVariableA( name, val, size ); HeapFree( GetProcessHeap(), 0, name ); if (val) { lstrcpynAtoW( valW, val, size ); HeapFree( GetProcessHeap(), 0, val ); } return res; } /*********************************************************************** * SetEnvironmentVariableA (KERNEL32.641) */ BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value ) { INT old_size, len, res; LPSTR p, env, new_env; BOOL ret = FALSE; PDB *pdb = PROCESS_Current(); 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) { if (!lstrncmpiA( name, p, len ) && (p[len] == '=')) break; 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 */ old_size = HeapSize( GetProcessHeap(), 0, env ); if (len < 0) { LPSTR next = p + strlen(p) + 1; /* We know there is a next one */ memmove( next + len, next, old_size - (next - env) ); } if (!(new_env = HeapReAlloc( GetProcessHeap(), 0, env, old_size + len ))) goto done; if (pdb->env_db->env_sel) SELECTOR_MoveBlock( pdb->env_db->env_sel, new_env ); 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; } /*********************************************************************** * SetEnvironmentVariableW (KERNEL32.642) */ BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value ) { LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name ); LPSTR valueA = HEAP_strdupWtoA( GetProcessHeap(), 0, value ); BOOL ret = SetEnvironmentVariableA( nameA, valueA ); HeapFree( GetProcessHeap(), 0, nameA ); HeapFree( GetProcessHeap(), 0, valueA ); return ret; } /*********************************************************************** * ExpandEnvironmentStringsA (KERNEL32.216) * * Note: overlapping buffers are not supported; this is how it should be. */ DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count ) { DWORD len, total_size = 1; /* 1 for terminating '\0' */ LPCSTR p, var; PDB *pdb = PROCESS_Current(); 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; } /*********************************************************************** * ExpandEnvironmentStringsW (KERNEL32.217) */ DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len ) { LPSTR srcA = HEAP_strdupWtoA( GetProcessHeap(), 0, src ); LPSTR dstA = dst ? HeapAlloc( GetProcessHeap(), 0, len ) : NULL; DWORD ret = ExpandEnvironmentStringsA( srcA, dstA, len ); if (dstA) { lstrcpyAtoW( dst, dstA ); HeapFree( GetProcessHeap(), 0, dstA ); } HeapFree( GetProcessHeap(), 0, srcA ); return ret; }