/*$Header: /p/tcsh/cvsroot/tcsh/win32/support.c,v 1.16 2014/08/17 02:56:37 amold Exp $*/ /*- * Copyright (c) 1980, 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * support.c * various routines to do exec, etc. * */ #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include "ntport.h" #include "sh.err.h" #include "sh.h" #include "nt.const.h" #pragma warning(disable:6001) DWORD gdwPlatform,gdwVersion; unsigned short __nt_really_exec = 0,__nt_child_nohupped =0; DWORD gdwStackSize = 524288;//0.5 MB void path_slashify(char *pstr) { while(*pstr) { #ifdef DSPMBYTE if (Ismbyte1(*pstr) && *(pstr + 1)) pstr ++; else #endif /* DSPMBYTE */ if (*pstr == '\\') *pstr = '/'; pstr++; } } void do_nothing(const wchar_t *p1, const wchar_t *p2, const wchar_t*p3, unsigned int p4, uintptr_t p5) { UNREFERENCED_PARAMETER(p1); UNREFERENCED_PARAMETER(p2); UNREFERENCED_PARAMETER(p3); UNREFERENCED_PARAMETER(p4); UNREFERENCED_PARAMETER(p5); } void nt_init(void) { #ifdef SECURE_CD { char temp[512];/*FIXBUF*/ extern char gcurr_drive; if(!GetCurrentDirectory(512,temp)) ExitProcess((DWORD)-1); gcurr_drive=temp[0]; } #endif SECURE_CD _set_invalid_parameter_handler(do_nothing); init_stdio(); nt_init_signals(); nt_term_init(); init_hb_subst(); setlocale(LC_ALL,""); init_shell_dll(); init_plister(); fork_init(); init_clipboard(); return; } void nt_cleanup(void){ nt_term_cleanup(); nt_cleanup_signals(); cleanup_netbios(); } void caseify_pwd(char *curwd) { char *sp, *dp, p,*s; WIN32_FIND_DATA fdata; HANDLE hFind; if (gdwPlatform !=VER_PLATFORM_WIN32_NT) return; if (*curwd == '\\' && (!curwd[1] || curwd[1] == '\\')) return; sp = curwd +3; dp = curwd +3; do { p= *sp; if (p && p != '\\'){ sp++; continue; } else { *sp = 0; hFind = FindFirstFile(curwd,&fdata); *sp = p; if (hFind != INVALID_HANDLE_VALUE) { FindClose(hFind); s = fdata.cFileName; while(*s) { *dp++ = *s++; } dp++; sp = dp; } else { sp++; dp = sp; } } sp++; }while(p != 0); } static char defcwd[MAX_PATH]; char * forward_slash_get_cwd(char * path, size_t maxlen) { char *ptemp; Char *vp; int rc ; if ((path == NULL) || (maxlen == 0)) { path = &defcwd[0]; maxlen = MAX_PATH; } rc = GetCurrentDirectory((DWORD)maxlen,path); if (rc > maxlen) { errno = ERANGE; return NULL; } vp = varval(STRNTcaseifypwd); if (vp != STRNULL) { caseify_pwd(path); } ptemp=path; path_slashify(ptemp); return path; } void getmachine (void) { char temp[256]; char *vendor, *ostype; OSVERSIONINFO osver; SYSTEM_INFO sysinfo; memset(&osver,0,sizeof(osver)); memset(&sysinfo,0,sizeof(sysinfo)); vendor = "Microsoft"; tsetenv(STRVENDOR,str2short(vendor)); osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); #pragma warning(suppress:4996 28159) //deprecated function if (!GetVersionEx(&osver)) { MessageBox(NULL,"GetVersionEx failed in getmachine", "tcsh",MB_ICONHAND); ExitProcess(0xFF); } GetSystemInfo(&sysinfo); if(osver.dwPlatformId == VER_PLATFORM_WIN32_NT) { char *ostr; ostype = "WindowsNT"; ostr = "Windows NT"; (void)StringCbPrintf(temp,(int)sizeof(temp),"%s %u.%u Build %u (%s)", ostr, osver.dwMajorVersion, osver.dwMinorVersion, osver.dwBuildNumber, osver.szCSDVersion[0]?osver.szCSDVersion:""); tsetenv(STRHOSTTYPE,str2short(temp)); } else if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { ostype = "Windows9x"; (void)StringCbPrintf(temp,(int)sizeof(temp), "Win9x %u.%u:%u",osver.dwMajorVersion,osver.dwMinorVersion, LOWORD(osver.dwBuildNumber)); tsetenv(STRHOSTTYPE,str2short(temp)); } else { ostype = "WindowsWhoKnows"; MessageBox(NULL,"Unknown platform","tcsh",MB_ICONHAND); } tsetenv(STROSTYPE,str2short(ostype)); switch (sysinfo.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_INTEL: if ( ( sysinfo.wProcessorLevel < 3) || ( sysinfo.wProcessorLevel > 9) ) sysinfo.wProcessorLevel = 3; (void)StringCbPrintf(temp,(int)sizeof(temp), "i%u86",sysinfo.wProcessorLevel); break; case PROCESSOR_ARCHITECTURE_ALPHA: (void)StringCbPrintf(temp,(int)sizeof(temp),"Alpha"); break; case PROCESSOR_ARCHITECTURE_MIPS: (void)StringCbPrintf(temp,(int)sizeof(temp),"Mips"); break; case PROCESSOR_ARCHITECTURE_PPC: (void)StringCbPrintf(temp,(int)sizeof(temp),"PPC"); break; case PROCESSOR_ARCHITECTURE_AMD64: (void)StringCbPrintf(temp,(int)sizeof(temp),"AMD64"); break; default: (void)StringCbPrintf(temp,(int)sizeof(temp),"Unknown"); break; } tsetenv(STRMACHTYPE,str2short(temp)); } void nt_exec(char *prog, char**args) { nt_execve(prog,args,NULL); } void nt_execve(char *prog, char**args, char**envir ) { STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE htemp; BOOL bRet; DWORD type=0; DWORD dwCreationflags; unsigned int priority; char *argv0= NULL; char *cmdstr, *cmdend ; char *originalPtr; unsigned int cmdsize,cmdlen; char *p2; char **savedargs; int retries=0; int hasdot =0; int is_winnt ; UNREFERENCED_PARAMETER(envir); memset(&si,0,sizeof(si)); savedargs = args; /* * This memory is not freed because we are exec()ed and will * not be alive long. */ originalPtr = cmdstr= heap_alloc(MAX_PATH<<2); is_winnt = (gdwPlatform != VER_PLATFORM_WIN32_WINDOWS); cmdsize = MAX_PATH<<2; p2 = cmdstr; cmdlen = 0; cmdlen += copy_quote_and_fix_slashes(prog,cmdstr,&hasdot); p2 += cmdlen; /* If the command was not quoted , skip initial character we left for quote */ if (*cmdstr != '"') { *cmdstr = 'A'; cmdstr++; cmdsize--; } *p2 = 0; cmdend = p2; if (!is_winnt){ argv0 = NULL; goto win95_directly_here; } else { argv0 = heap_alloc(MAX_PATH); /* not freed */ (void)StringCbPrintf(argv0,MAX_PATH,"%s",prog); } retry: bRet=GetBinaryType(argv0,&type); dprintf("binary type for %s is %d\n",argv0,bRet); // // For NT, append .EXE and retry // if (is_winnt && !bRet ) { /* Don't append .EXE if it could be a script file */ if (GetLastError() == ERROR_BAD_EXE_FORMAT){ errno = ENOEXEC; if (!__nt_only_start_exes) try_shell_ex(args,1,FALSE); //can't throw on error return; } else if ( retries ){ if ( ( (argv0[0] == '\\') ||(argv0[0] == '/') ) && ( (argv0[1] == '\\') ||(argv0[1] == '/') ) && (!args[1]) ) if (!__nt_only_start_exes) try_shell_ex(args,1,FALSE); errno = ENOENT; } if (retries > 1){ return; } // Try uppercase once and then lower case // if (!retries) { (void)StringCbPrintf(argv0,MAX_PATH,"%s.exe",prog); } else { (void)StringCbPrintf(argv0,MAX_PATH,"%s.EXE",prog); /* fix for clearcase */ } retries++; goto retry; } win95_directly_here: si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; htemp= (HANDLE)_get_osfhandle(0); DuplicateHandle(GetCurrentProcess(),htemp,GetCurrentProcess(), &si.hStdInput,0,TRUE,DUPLICATE_SAME_ACCESS); htemp= (HANDLE)_get_osfhandle(1); DuplicateHandle(GetCurrentProcess(),htemp,GetCurrentProcess(), &si.hStdOutput,0,TRUE,DUPLICATE_SAME_ACCESS); htemp= (HANDLE)_get_osfhandle(2); DuplicateHandle(GetCurrentProcess(),htemp,GetCurrentProcess(), &si.hStdError,0,TRUE,DUPLICATE_SAME_ACCESS); args++; // the first arg is the command dprintf("nt_execve calling c_a_a_q"); if(!concat_args_and_quote(args,&originalPtr,&cmdstr,&cmdlen,&cmdend, &cmdsize)) { dprintf("concat_args_and_quote failed\n"); heap_free(originalPtr); errno = ENOMEM; goto fail_return; } *cmdend = 0; dwCreationflags = GetPriorityClass(GetCurrentProcess()); if (__nt_child_nohupped) { dwCreationflags |= DETACHED_PROCESS; } priority = GetThreadPriority(GetCurrentThread()); (void)fix_path_for_child(); if (is_winnt) dwCreationflags |= CREATE_SUSPENDED; re_cp: dprintf("argv0 %s cmdstr %s\n",argv0,cmdstr); bRet = CreateProcessA(argv0, cmdstr, NULL, NULL, TRUE, // need this for redirecting std handles dwCreationflags, NULL, NULL, &si, &pi); if (!bRet){ if (GetLastError() == ERROR_BAD_EXE_FORMAT) { if (!__nt_only_start_exes) try_shell_ex(savedargs,1,FALSE); errno = ENOEXEC; } else if (GetLastError() == ERROR_INVALID_PARAMETER) { /* can't get invalid parameter, so this must be * the case when we exceed the command length limit. */ errno = ENAMETOOLONG; } else { errno = ENOENT; } if (!is_winnt && !hasdot) { //append '.' to the end if needed (void)StringCbCat(cmdstr,cmdsize,"."); hasdot=1; goto re_cp; } } else{ int gui_app ; char guivar[50]; if (GetEnvironmentVariable("TCSH_NOASYNCGUI",guivar,50)) gui_app=0; else { if (is_winnt || hasdot) gui_app= is_gui(argv0); else gui_app = is_9x_gui(prog); } if (is_winnt && !SetThreadPriority(pi.hThread,priority) ) { priority =GetLastError(); } if (is_winnt) ResumeThread(pi.hThread); errno= 0; if (__nt_really_exec||__nt_child_nohupped || gui_app){ ExitProcess(0); } else { DWORD exitcode=0; WaitForSingleObject(pi.hProcess,INFINITE); (void)GetExitCodeProcess(pi.hProcess,&exitcode); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); /* * If output was redirected to /dev/clipboard, * we need to close the pipe handles */ if (is_dev_clipboard_active) { CloseHandle((HANDLE)_get_osfhandle(0)); CloseHandle((HANDLE)_get_osfhandle(1)); CloseHandle((HANDLE)_get_osfhandle(2)); CloseHandle(si.hStdInput); CloseHandle(si.hStdOutput); CloseHandle(si.hStdError); WaitForSingleObject(ghdevclipthread,60*1000); } ExitProcess(exitcode); } } fail_return: CloseHandle(si.hStdInput); CloseHandle(si.hStdOutput); CloseHandle(si.hStdError); return; } /* This function from Mark Tucker (mtucker@fiji.sidefx.com) */ int quoteProtect(char *dest, char *src,unsigned long destsize) { char *prev, *curr; for (curr = src; *curr; curr++) { // Protect " from MS-DOS expansion if (*curr == '"') { // Now, protect each preceeding backslash for (prev = curr-1; prev >= src && *prev == '\\'; prev--) { *dest++ = '\\'; destsize--; if(destsize == 0) return ERROR_BUFFER_OVERFLOW; } *dest++ = '\\'; destsize--; if(destsize == 0) return ERROR_BUFFER_OVERFLOW; } *dest++ = *curr; destsize--; if(destsize == 0) return ERROR_BUFFER_OVERFLOW; } *dest = 0; return NO_ERROR; } int gethostname(char *buf, int len) { GetComputerName(buf,(DWORD*)&len); return 0; } int nt_chdir (char *path) { char *tmp = path; if (gdwPlatform !=VER_PLATFORM_WIN32_NT) { while(*tmp) { if (*tmp == '/') *tmp = '\\'; tmp++; } } return _chdir(path); } void WINAPI uhef( EXCEPTION_POINTERS *lpep) { ExitProcess(lpep->ExceptionRecord->ExceptionCode); } extern BOOL CreateWow64Events(DWORD,HANDLE*,HANDLE*,BOOL); // load kernel32 and look for iswow64. if not found, assume FALSE BOOL bIsWow64Process = FALSE; void init_wow64(void) { HMODULE hlib; //BOOL (WINAPI *pfnIsWow64)(HANDLE,BOOL*); FARPROC pfnIsWow64; bIsWow64Process = FALSE; hlib = LoadLibrary("kernel32.dll"); if (!hlib) { return; } pfnIsWow64 = GetProcAddress(hlib,"IsWow64Process"); if (!pfnIsWow64) { FreeLibrary(hlib); return; } if (!pfnIsWow64(GetCurrentProcess(),&bIsWow64Process) ) bIsWow64Process = FALSE; FreeLibrary(hlib); return; } extern void mainCRTStartup(void *); /* * heap_init() MUST NOT be moved outside the entry point. Sometimes child * processes may load random DLLs not loaded by the parent and * use the heap address reserved for fmalloc() in the parent. This * causes havoc as no dynamic memory can then be inherited. * */ extern void heap_init(void); #include void silly_entry(void *peb) { char * path1=NULL; int rc; char temp[MAX_PATH+5]; char buf[MAX_PATH]; char ptr1[MAX_PATH]; char ptr2[MAX_PATH]; char ptr3[MAX_PATH]; OSVERSIONINFO osver; osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); #pragma warning(suppress:4996 28159) //deprecated function if (!GetVersionEx(&osver)) { MessageBox(NULL,"GetVersionEx failed","tcsh",MB_ICONHAND); ExitProcess(0xFF); } gdwVersion = osver.dwMajorVersion; if(gdwVersion < 6) // no wow64 hackery for vista. { init_wow64(); } #ifdef _M_IX86 // look at the explanation in fork.c for why we do these steps. if (bIsWow64Process) { HANDLE h64Parent,h64Child; char *stk, *end; DWORD mb = (1<<20); // if we found the events, then we're the product of a fork() if (CreateWow64Events(GetCurrentProcessId(), &h64Parent,&h64Child,TRUE)) { if (!h64Parent || !h64Child) return; // tell parent we're rolling SetEvent(h64Child); if(WaitForSingleObject(h64Parent,FORK_TIMEOUT) != WAIT_OBJECT_0) { return; } // if __forked is 0, we shouldn't have found the events if (!__forked) return; } // now create the stack if (!__forked) { stk = VirtualAlloc(NULL,mb+65536,MEM_COMMIT,PAGE_READWRITE); if (!stk) { dprintf("virtual alloc in parent failed %d\n",GetLastError()); return; } end = stk + mb + 65536; end -= sizeof(char*); __fork_stack_begin = end; __asm {mov esp,end }; set_stackbase(end); heap_init(); } else { // child process stk = (char*)__fork_stack_begin + sizeof(char*)- mb - 65536; dprintf("begin is 0x%08x\n",stk); end = VirtualAlloc(stk, mb+65536 , MEM_RESERVE , PAGE_READWRITE); if (!end) { rc = GetLastError(); dprintf("virtual alloc 1 in child failed %d\n",rc); return; } stk = VirtualAlloc(end, mb+65536 , MEM_COMMIT , PAGE_READWRITE); if (!stk) { rc = GetLastError(); dprintf("virtual alloc 2 in child failed %d\n",rc); return; } end = stk + mb + 65536; __asm {mov esp, end}; set_stackbase(end); SetEvent(h64Child); CloseHandle(h64Parent); CloseHandle(h64Child); } } #endif _M_IX86 SetFileApisToOEM(); if (!bIsWow64Process) heap_init(); /* If home is set, we only need to change '\' to '/' */ rc = GetEnvironmentVariable("HOME",buf,MAX_PATH); if (rc && (rc < MAX_PATH)){ path_slashify(buf); (void)SetEnvironmentVariable("HOME",buf); goto skippy; } memset(ptr1,0,MAX_PATH); memset(ptr2,0,MAX_PATH); memset(ptr3,0,MAX_PATH); if(osver.dwPlatformId == VER_PLATFORM_WIN32_NT) { GetEnvironmentVariable("USERPROFILE",ptr1,MAX_PATH); GetEnvironmentVariable("HOMEDRIVE",ptr2,MAX_PATH); GetEnvironmentVariable("HOMEPATH",ptr3,MAX_PATH); ptr1[MAX_PATH -1] = ptr2[MAX_PATH-1] = ptr3[MAX_PATH-1]= 0; #pragma warning(disable:4995) if (!ptr1[0] || osver.dwMajorVersion <4) { wsprintfA(temp, "%s%s",ptr2[0]?ptr2:"C:",ptr3[0]?ptr3:"\\"); } else if (osver.dwMajorVersion >= 4) { wsprintfA(temp, "%s",ptr1); } #pragma warning(default:4995) } else if (osver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { rc = GetWindowsDirectory(ptr1,MAX_PATH); if (rc > MAX_PATH) { MessageBox(NULL,"This should never happen","tcsh",MB_ICONHAND); ExitProcess(0xFF); } (void)StringCbPrintf(temp,sizeof(temp),"%s",ptr1); } else { MessageBox(NULL,"Unknown platform","tcsh",MB_ICONHAND); } path_slashify(temp); SetEnvironmentVariable("HOME",temp); skippy: gdwPlatform = osver.dwPlatformId; rc = GetEnvironmentVariable("Path",path1,0); if ( rc !=0 && path1) { path1 =heap_alloc(rc); GetEnvironmentVariable("Path",path1,rc); SetEnvironmentVariable("Path",NULL); /*SetEnvironmentVariable("PATH",NULL);*/ SetEnvironmentVariable("PATH",path1); heap_free(path1); } mainCRTStartup(peb); } /* * Copy source into target, quote if it has space, also converting '/' to '\'. * * hasdot is set to 1 if source ends in a file extension * return value is the length of the string copied. */ int copy_quote_and_fix_slashes(char *source,char *target, int *hasdot ) { int len ; int hasspace; char *save; char *ptr; save = target; /* leave space for quote */ len = 1; target++; hasspace = 0; while(*source) { if (*source == '/') *source = '\\'; else if (*source == ' ') hasspace = 1; *target++ = *source; source++; len++; } ptr = target;//source; while( (ptr > save ) && (*ptr != '\\')) { if (*ptr == '.') *hasdot = 1; ptr--; } if (hasspace) { *save = '"'; *target = '"'; len++; } return len; } /* * This routine is a replacement for the old, horrible strcat() loop * that was used to turn the argv[] array into a string for CreateProcess(). * It's about a zillion times faster. * -amol 2/4/99 */ char *concat_args_and_quote(char **args, char **poriginalPtr,char **cstr, unsigned int *clen, char **cend, unsigned int *cmdsize) { unsigned int argcount, arglen, cmdlen; char *tempptr, *cmdend ,*cmdstr; short quotespace = 0; short quotequote = 0; short noquoteprotect = 0; char *tempquotedbuf; unsigned long tqlen = 256; int rc; dprintf("entering concat_args_and_quote\n"); tempquotedbuf = heap_alloc(tqlen); if(!tempquotedbuf) { return NULL; } noquoteprotect = (short)(varval(STRNTnoquoteprotect) != STRNULL); /* quotespace hack needed since execv() would have separated args, but createproces doesnt -amol 9/14/96 */ cmdend= *cend; cmdstr = *cstr; cmdlen = *clen; argcount = 0; while (*args) { *cmdend++ = ' '; cmdlen++; tempptr = *args; arglen = 0; argcount++; //dprintf("args is %s\n",*args); if (!*tempptr) { *cmdend++ = '"'; *cmdend++ = '"'; } while(*tempptr) { if (*tempptr == ' ' || *tempptr == '\t') quotespace = 1; else if (*tempptr == '"') quotequote = 1; tempptr++; arglen++; } if (arglen + cmdlen +4 > *cmdsize) { // +4 is if we have to quote tempptr = heap_realloc(*poriginalPtr,*cmdsize<<1); if(!tempptr) return NULL; // If it's not the same heap block, re-adjust the pointers. if (tempptr != *poriginalPtr) { cmdstr = tempptr + (cmdstr - *poriginalPtr); cmdend = tempptr + (cmdend- *poriginalPtr); *poriginalPtr = tempptr; } *cmdsize <<=1; } if (quotespace) *cmdend++ = '"'; if ((noquoteprotect == 0) && quotequote){ tempquotedbuf[0]=0; tempptr = &tempquotedbuf[0]; rc = quoteProtect(tempquotedbuf,*args,tqlen); while(rc == ERROR_BUFFER_OVERFLOW) { char *tmp = tempquotedbuf; tempquotedbuf = heap_realloc(tempquotedbuf,tqlen <<1); if(!tempquotedbuf) { heap_free(tmp); return NULL; } tqlen <<= 1; tempptr = &tempquotedbuf[0]; rc = quoteProtect(tempquotedbuf,*args,tqlen); } while (*tempptr) { *cmdend = *tempptr; cmdend++; tempptr++; } cmdlen +=2; } else { tempptr = *args; while(*tempptr) { *cmdend = *tempptr; cmdend++; tempptr++; } } if (quotespace) { *cmdend++ = '"'; cmdlen +=2; } cmdlen += arglen; args++; } *clen = cmdlen; *cend = cmdend; *cstr = cmdstr; heap_free(tempquotedbuf); return cmdstr; } char *fix_path_for_child(void) { char *ptr; Char *vp; char *pathstr; char *oldpath; long len; vp = varval(STRNTlamepathfix); if (vp != STRNULL) { len = GetEnvironmentVariable("PATH",NULL,0); oldpath = heap_alloc(len+1); pathstr = heap_alloc(len+1); if(!oldpath || !pathstr) { return NULL; } len = GetEnvironmentVariable("PATH",oldpath,len+1); #pragma warning(suppress:6102) memcpy(pathstr,oldpath,len); ptr = pathstr; while(*ptr) { if (*ptr == '/') *ptr = '\\'; ptr++; } SetEnvironmentVariable("PATH",pathstr); heap_free(pathstr); return oldpath; //freed in restore_path; } else return NULL; } void restore_path(char *oldpath) { if (oldpath) { SetEnvironmentVariable("PATH",oldpath); heap_free(oldpath); } }