Пример #1
0
// Return temporary directory.
FXString FXSystem::getTempDirectory(){
#ifndef WIN32
  const FXchar* dir;
  if((dir=getenv("TMPDIR"))!=NULL){
    return FXString(dir);
    }
  return FXString("/tmp");
#else
  TCHAR buffer[MAXPATHLEN];
  DWORD len=GetTempPath(MAXPATHLEN,buffer);
  if(1<len && ISPATHSEP(buffer[len-1]) && !ISPATHSEP(buffer[len-2])) len--;
  return FXString(buffer,len);
#endif
  }
Пример #2
0
void EvalMakePath(const void *data, qCtx *ctx, qStr *out, qArgAry *args)
{
	VALID_ARGC("makepath", 2, 2);
	CStr path1 = (*args)[0];
	CStr path2 = (*args)[1];
	fslash(path1.SafeP());
	fslash(path2.SafeP());
#ifdef WIN32
	path1.RTrim(DIRSEP);
	path2.LTrim(DIRSEP);
	path1.RTrim('/');
	path2.LTrim('/');
#else
	path1.RTrim(DIRSEP);
	path2.LTrim(DIRSEP);
#endif

	out->PutS(path1);

#ifdef WIN32
	if (path1.Length() > 0 && !ISPATHSEP(path1.SafeP()[path1.Length()-1]))
#endif
		out->PutC(DIRSEP);
	out->PutS(path2);
}
Пример #3
0
bool _ScanDir(CStr path, int mask, CStr body, qCtx *ctx, qStr *out, DIRSTATE &st)
{
	BOOL bMore; 
	HANDLE hFind; 
	BOOL showdot = false;
  
  	// truncate trailing slashes
	char *b = path.GetBuffer();

	if (!b || !*b) return false;

	char *p = path+path.Length() - 1;
	while (p >= b && ISDIRSEP(*p))
		--p;

	if (p-b+1 > 0) {
		if (*p == ':') {
			showdot = true;
			if (!ISDIRSEP(p[1])) {
				path << '.';
				b = path.GetBuffer();
				p = path+path.Length() - 1;
			} else
				++p;
		}

		st.path = path;

		// truncate path to parent
		while (p >= b && !ISPATHSEP(*p))
			--p;

		if (p >= b) {
			st.path.Grow(p-b+1);
		} else {
			st.path.Grow(0);
		}
	} else {
		st.path = path;
	}

  // read all entries in the directory

	WIN32_FIND_DATA *r = &st.data;
    hFind = FindFirstFile(path, r); 
    bMore = (hFind != (HANDLE) -1); 
    while (bMore &&!st.bquit) { 
    if ((mask & r->dwFileAttributes)
			&& !(r->cFileName[0]=='.'&&r->cFileName[1]=='\0')
			) {
			ctx->Parse(body, out);
		} else if (showdot && r->cFileName[0]=='.'&&r->cFileName[1]=='\0') {
			ctx->Parse(body, out);
		}
		bMore = FindNextFile(hFind, r);
    }
    FindClose(hFind); 

	return true;
} // dir_scan
Пример #4
0
static const char *
keytab_name(const char *name, const char **type, size_t *type_len)
{
    const char *residual;

    residual = strchr(name, ':');

    if (residual == NULL ||
	ISPATHSEP(name[0])
#ifdef _WIN32
        /* Avoid treating <drive>:<path> as a keytab type
         * specification */
        || name + 1 == residual
#endif
        ) {

        *type = "FILE";
        *type_len = strlen(*type);
        residual = name;
    } else {
        *type = name;
        *type_len = residual - name;
        residual++;
    }

    return residual;
}
Пример #5
0
void EvalFileName(const void *data, qCtx *ctx, qStr *out, qArgAry *args)
{
	VALID_ARGC("filename", 1, 1);
	CStr path = (*args)[0];

	if (path) {
	const char *b = path;

	const char *r = b + path.Length() - 1;
	while (r >= b && !(ISPATHSEP(*r))) {
		--r;
	}
	++r;
	out->PutS(r, path.Length() - (r - b));
	}
}
Пример #6
0
// Return the absolute path, based on the specified directory path
// Remove terminating '/' on a path string to simplify a file or directory path
// Thus '/bla/bla////' becomes '/bla/bla'
// Special case : '/' stays to '/'
FXString filePath(const FXString path, const FXString dir)
{
	FXString in=path, out=path;
	while (1)
	{
		if (in[in.length()-1]=='/' && in.length()!=1)
		{
			out=in.trunc(in.length()-1);
			in=out;
		}
		else
			break;
	}
	// If absolute path
	if(ISPATHSEP(out[0]))
		return (out);
	else
		return (dir+PATHSEPSTRING+out);
}
Пример #7
0
void EvalFilePath(const void *data, qCtx *ctx, qStr *out, qArgAry *args)
{
	VALID_ARGC("filepath", 1, 1);
	CStr path = (*args)[0];
	char *b = path.GetBuffer();

	if (b) {
	char *r = b + path.Length() - 1;
	while (r >= b && !(ISPATHSEP(*r))) {
		--r;
	}
	if (r >= b) {
		*r = DIRSEP;
		++r;
		*r = '\0';
		path.Grow(r - b);
		out->PutS(path);
	}
	}
}
Пример #8
0
// Recursively visit files and directories
FXbool FXDirVisitor::traverse(const FXString& path){
  FXStat data;
  if(FXStat::statLink(path,data)){
    if(data.isDirectory()){
      if(enter(path)){
        FXDir directory(path);
        FXString name;
        while(directory.next(name)){
          if(name[0]!='.' || (name[1]!=0 && (name[1]!='.' || name[2]!=0))){ 
            if(!traverse(path+(ISPATHSEP(path.tail())?"":PATHSEPSTRING)+name)) break;
            }
          }
        return leave(path);
        }
      }
    else{
      return visit(path);                // Its a file
      }
    }
  return false;
  }
Пример #9
0
// Return the absolute path, based on the current directory path
// Remove terminating '/' on a path string to simplify a file or directory path
// Thus '/bla/bla////' becomes '/bla/bla'
// Special case : '/' stays to '/'
FXString filePath(const FXString path)
{
	FXString in=path, out=path;
	while (1)
	{
		if (in[in.length()-1]=='/' && in.length()!=1)
		{
			out=in.trunc(in.length()-1);
			in=out;
		}
		else
			break;
	}
	FXString dir=FXSystem::getCurrentDirectory();

	// If absolute path
	if(ISPATHSEP(out[0]))
		return (out);
	else
		return (dir+PATHSEPSTRING+out);
}
Пример #10
0
int access(const char *filename, int mode)
{
	char extension[_MAX_FNAME];
	DWORD attribs;
	size_t extlen;
	const char *extptr;
	const char *begin, *end;
	int isx, hasext, trypathext = 0;

	/* once: get default PATHEXT or use empty exts */
	if (!*exts) {
		DWORD rc;
		/* not initialized */
		rc = GetEnvironmentVariable("PATHEXT", exts, sizeof(exts));
		if ((rc == 0) || (rc >= sizeof(exts)))
			*exts = 0;
	}

	if (!filename) {
		errno = ENOENT;
		return (-1);
	}
	/* search for the extension starting at the end */
	extptr = filename + strlen(filename) - 1;
	hasext = 0;
	while (extptr > filename && !ISPATHSEP(*extptr)) {
		if (*extptr == '.' && *(extptr - 1) != ':' && !ISPATHSEP(*(extptr - 1))) {
			hasext++;
			break;
		}
		extptr--;
	}

	if (hasext) 
		attribs = get_file_attributes(filename, extptr, hasext, mode, &isx);
	else
		attribs = get_file_attributes(filename, "", hasext, mode, &isx);

	/* if mode != X_OK or file exists or filename already has an extension ignore PATHEXT */
	if ((mode != X_OK) || (attribs != (DWORD)-1) || hasext) {
		begin = ".";
		end = "";
	} else {
		/* dir/file name not found and no extension */
		begin = exts;
		end = exts;
		trypathext = 1;
	}

	while (*begin) {
		if (trypathext) {
			extlen = pgetext(&begin, &end, extension, sizeof(extension));
			if (!*begin)
				break;
			if (extlen)
				attribs = get_file_attributes(filename, extension, hasext, mode, &isx);
			else
				attribs = (DWORD)(-1);
		}
		if (attribs != (DWORD)(-1)) {
			/* file or directory found */
			if (mode & X_OK) {
				if (attribs & FILE_ATTRIBUTE_DIRECTORY)
					break;
				/* appending pathext may find a directory ! */
				if (trypathext || isx)
					return (0);
				break;
			} else if ((mode & W_OK) && (attribs & FILE_ATTRIBUTE_READONLY)) {
				break;
			}
			/* R_OK is always OK */
			return (0);
		}
		begin = pgetnext(&begin, &end);
	}

	if (attribs == (DWORD)(-1))
		errno = ENOENT;
	else
		errno = EACCES;
	return (-1);
}
Пример #11
0
// Hit the accept button or enter in text field
long FileSelector::onCmdAccept(FXObject*,FXSelector,void*)
{
    FXSelector sel=accept->getSelector();
    FXObject *tgt=accept->getTarget();

    // Get (first) filename
    FXString path=getFilename();
	
	// If filename is empty, we get the current directory
	if(path.empty())
	{
		path=list->getDirectory();
        filename->setText(path);
	}
	
    // Only do something if a selection was made
    if(!path.empty())
    {
        // Is directory?
        if(::isDirectory(path))
        {
            // In directory mode:- we got our answer!
            if(selectmode==SELECT_FILE_DIRECTORY || selectmode==SELECT_FILE_MULTIPLE_ALL || selectmode==SELECT_FILE_MIXED)
            {
                if(tgt)
                   tgt->handle(accept,FXSEL(SEL_COMMAND,sel),(void*)1);
                return 1;
            }

            // Hop over to that directory
            list->setDirectory(path);
            pathlink->setPath(list->getDirectory());
            pathtext->setText(list->getDirectory());
            filename->setText(FXString::null);
            return 1;
        }

        // Get directory part of path
        FXString dir=FXPath::directory(path);

        // In file mode, directory part of path should exist
        if(::isDirectory(dir))
        {

            // In any mode, existing directory part is good enough
            if(selectmode==SELECT_FILE_ANY)
            {
                if(tgt)
                    tgt->handle(accept,FXSEL(SEL_COMMAND,sel),(void*)1);
                return 1;
            }

            // In existing mode, the whole filename must exist and be a file
            else if(selectmode==SELECT_FILE_EXISTING)
            {
                if(::isFile(path))
                {
                    if(tgt)
                        tgt->handle(accept,FXSEL(SEL_COMMAND,sel),(void*)1);
                    return 1;
                }
            }

            // In multiple mode, return if all selected files exist
            else if(selectmode==SELECT_FILE_MULTIPLE)
            {
                for(FXint i=0; i<list->getNumItems(); i++)
                {
                    if(list->isItemSelected(i) && list->isItemFile(i))
                    {
                        if(tgt)
                            tgt->handle(accept,FXSEL(SEL_COMMAND,sel),(void*)1);
                        return 1;
                    }
                }
            }

            // Multiple files and/or directories
            else
            {
                for(FXint i=0; i<list->getNumItems(); i++)
                {
                    if(list->isItemSelected(i) && list->getItemFilename(i)!="..")
                    {
                        if(tgt)
                            tgt->handle(accept,FXSEL(SEL_COMMAND,sel),(void*)1);
                        return 1;
                    }
                }
            }
        }

        // Go up to the lowest directory which still exists
        while(!FXPath::isTopDirectory(dir) && !::isDirectory(dir))
            dir=FXPath::upLevel(dir);

        // Switch as far as we could go
        list->setDirectory(dir);
        pathlink->setPath(list->getDirectory());
        pathtext->setText(list->getDirectory());

        // Put the tail end back for further editing
        if(ISPATHSEP(path[dir.length()]))
            path.erase(0,dir.length()+1);
        else
            path.erase(0,dir.length());

        // Replace text box with new stuff
        filename->setText(path);
        filename->selectAll();
    }

    return 1;
}
Пример #12
0
// Perform match
static bool domatch(const char *pattern,const char *string,FXuint flags){
  register const char *p=pattern;
  register const char *q=string;
  register const char *s;
  register char c,cs,ce,cc,neg;
  register int level;
  while((c=*p++)!='\0'){
    switch(c){
      case '?':
        if(*q=='\0') return false;
        if((flags&FILEMATCH_FILE_NAME) && ISPATHSEP(*q)) return false;
        if((flags&FILEMATCH_PERIOD) && (*q=='.') && ((q==string) || ((flags&FILEMATCH_FILE_NAME) && ISPATHSEP(*(q-1))))) return false;
        q++;
        break;
      case '*':
        c=*p;
        while(c=='*') c=*++p;
        if((flags&FILEMATCH_PERIOD) && (*q=='.') && ((q==string) || ((flags&FILEMATCH_FILE_NAME) && ISPATHSEP(*(q-1))))) return false;
        if(c=='\0'){    // Optimize for case of trailing '*'
          if(flags&FILEMATCH_FILE_NAME){ for(s=q; *s; s++){ if(ISPATHSEP(*s)) return false; } }
          return 1;
          }
        while(!domatch(p,q,flags&~FILEMATCH_PERIOD)){
          if((flags&FILEMATCH_FILE_NAME) && ISPATHSEP(*q)) return false;
          if(*q++=='\0') return false;
          }
        return 1;
      case '[':
        if(*q=='\0') return false;
        if((flags&FILEMATCH_PERIOD) && (*q=='.') && ((q==string) || ((flags&FILEMATCH_FILE_NAME) && ISPATHSEP(*(q-1))))) return false;
        cc=FOLD(*q);
        neg=((*p=='!') || (*p=='^'));
        if(neg) p++;
        c=*p++;
        do{
          if(c=='\\' && !(flags&FILEMATCH_NOESCAPE)) c=*p++;
          cs=ce=FOLD(c);
          if(c=='\0') return false;
          c=*p++;
          c=FOLD(c);
          if((flags&FILEMATCH_FILE_NAME) && ISPATHSEP(c)) return false;
          if(c=='-' && *p!=']'){
            c = *p++;
            if(c=='\\' && !(flags&FILEMATCH_NOESCAPE)) c=*p++;
            if(c=='\0') return false;
            ce=FOLD(c);
            c=*p++;
            }
          if(((FXuchar)cs)<=((FXuchar)cc) && ((FXuchar)cc)<=((FXuchar)ce)) goto match;
          }
        while(c!=']');
        if(!neg) return false;
        q++;
        break;
match:  while(c!=']'){
          if(c=='\0') return false;
          c=*p++;
          if(c=='\\' && !(flags&FILEMATCH_NOESCAPE)) p++;
          }
        if(neg) return false;
        q++;
        break;
      case '(':
nxt:    if(domatch(p,q,flags)) return true;
        for(level=0; *p && 0<=level; ){
          switch(*p++){
            case '\\': if(*p) p++; break;
            case '(': level++; break;
            case ')': level--; break;
            case '|':
            case ',': if (level==0) goto nxt;
            }
          }
        return false;
      case ')':
        break;
      case '|':
      case ',':
        for(level=0; *p && 0<=level; ){
          switch(*p++){
            case '\\': if(*p) p++; break;
            case '(': level++; break;
            case ')': level--; break;
            }
          }
        break;
      case '\\':
        if(*p && !(flags&FILEMATCH_NOESCAPE)) c=*p++;   // Trailing escape represents itself
      default:
        if(FOLD(c)!=FOLD(*q)) return false;
        q++;
        break;
      }
    }
  return (*q=='\0') || (ISPATHSEP(*q) && (flags&FILEMATCH_LEADING_DIR));
  }
Пример #13
0
// List all the files in directory
FXint FXDir::listFiles(FXString*& filelist,const FXString& path,const FXString& pattern,FXuint flags){
  FXDir dir(path);

  // Initialize to empty
  filelist=NULL;

  // Get directory stream pointer
  if(dir.isOpen()){
    FXuint    mode=(flags&CaseFold)?(FILEMATCH_FILE_NAME|FILEMATCH_NOESCAPE|FILEMATCH_CASEFOLD):(FILEMATCH_FILE_NAME|FILEMATCH_NOESCAPE);
    FXString *newlist;
    FXint     size=0;
    FXint     count=0;
    FXString  pathname;
    FXString  name;
    FXStat    data;

    // Loop over directory entries
    while(dir.next(name)){

      // Build full pathname
      pathname=path;
      if(!ISPATHSEP(pathname.tail())) pathname+=PATHSEPSTRING;
      pathname+=name;

      // Get info on file
      if(!FXStat::statFile(pathname,data)) continue;

#ifdef WIN32

      // Filter out files; a bit tricky...
      if(!data.isDirectory() && ((flags&NoFiles) || (data.isHidden() && !(flags&HiddenFiles)) || (!(flags&AllFiles) && !FXPath::match(pattern,name,mode)))) continue;

      // Filter out directories; even more tricky!
      if(data.isDirectory() && ((flags&NoDirs) || (data.isHidden() && !(flags&HiddenDirs)) || ((name[0]=='.' && (name[1]==0 || (name[1]=='.' && name[2]==0))) && (flags&NoParent)) || (!(flags&AllDirs) && !FXPath::match(pattern,name,mode)))) continue;

#else

      // Filter out files; a bit tricky...
      if(!data.isDirectory() && ((flags&NoFiles) || (name[0]=='.' && !(flags&HiddenFiles)) || (!(flags&AllFiles) && !FXPath::match(pattern,name,mode)))) continue;

      // Filter out directories; even more tricky!
      if(data.isDirectory() && ((flags&NoDirs) || (name[0]=='.' && !(flags&HiddenDirs)) || ((name[0]=='.' && (name[1]==0 || (name[1]=='.' && name[2]==0))) && (flags&NoParent)) || (!(flags&AllDirs) && !FXPath::match(pattern,name,mode)))) continue;

#endif

      // Grow list
      if(count+1>=size){
        size=size?(size<<1):256;
        newlist=new FXString [size];
        for(int i=0; i<count; i++){
          newlist[i].adopt(filelist[i]);
          }
        delete [] filelist;
        filelist=newlist;
        }

      // Add to list
      filelist[count++].adopt(name);
      }
    return count;
    }
  return 0;
  }
Пример #14
0
int nt_execve(const char *prog, const char *const *args, const char *const *envir)
{
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	enum {none, directex, shellex} execmode;
	DWORD exitcode;
	DWORD dwCreationflags;
	int priority;
	char *argv0;
	char *cmdstr, *cmdend;
	unsigned int cmdsize;
	size_t prognamelen, cmdlen;
	int hasext;
	char extension[_MAX_FNAME];
	const char *begin, *end, *extptr;
	static char exts[MAX_PATH];

	UNREFERENCED_PARAMETER(envir);

	/* get default PATHEXT or use empty exts */
	if (!*exts) {
		DWORD rc;
		/* not initialized */
		rc = GetEnvironmentVariable("PATHEXT", exts, sizeof(exts));
		if ((rc == 0) || (rc >= sizeof(exts)))
			/* if error or PATHEXT too big will retry at the next call */
			*exts = 0;
	}

	/* if prog has an extension initialize begin end to skip PATHEXT search */
	prognamelen = strlen(prog);
	extptr = prog + prognamelen - 1;
	hasext = 0;
	while (extptr > prog && !ISPATHSEP(*extptr)) {
		if (*extptr == '.' && *(extptr - 1) != ':' && !ISPATHSEP(*(extptr - 1))) {
			hasext++;
			break;
		}
		extptr--;
	}
	if (hasext) {
		begin = ".";
		end = "";
		strcpy(extension, extptr);
	} else {
		begin = exts;
		end = exts;
		*extension = '\0';
	}

	argv0 = (char *)heap_alloc(MAX_PATH);
	/* (prognamelen + 1) does not really matter, argv0 is '\0' filled */
	memcpy(argv0, prog, prognamelen + 1);

	errno = 0;
	execmode = none;
	/* NOTE: loops over PATHEXT if no extension found */
	while (*begin) {
		size_t extlen;
		if (GetBinaryType(argv0, &exitcode)) {
			/* exists and is executable
			   NOTE: an "xxx.exe" without a correct PE header (i.e. a text file)
			   has type "DOS binary", but execution will generate a WOW error */
			execmode = directex;
			break;
		}
		if (GetLastError() == ERROR_BAD_EXE_FORMAT) {
			/* exists but is not "executable" */
			execmode = shellex;
			break;
		}
		if (hasext)
			break;
		/* get next PATHEXT extension */
		while (*begin && (*begin != '.'))
			begin++;
		while (*end && (*end != ';'))
			end++;
		if (!*begin)
			break;
		extlen = end - begin;
		if (extlen < sizeof(extension)) {
			memcpy(extension, begin, extlen);
			extension[extlen] = '\0';
			/* prognamelen ignores the last '\r' if present */
			memcpy(argv0, prog, prognamelen);
			/* update argv0 adding the extension to prog */
			memcpy(argv0 + prognamelen, extension, extlen + 1);
		}
		begin = end;
		/* skip sequences of ';' */
		while (*end && *end == ';')
			end++;
	};

	cmdstr = (char *)heap_alloc(MAX_PATH << 2);
	cmdsize = MAX_PATH << 2;
	cmdlen = 0;
	cmdend = cmdstr;

	dbgprintf(PR_VERBOSE, "%s(): execute [%s] extension=[%s] mode=%d hasext=%d\n", __FUNCTION__, argv0, extension, execmode, hasext);
	/* skip over program name */
	args++;

	/* the file (after PATHEXT search) exists, but it's not "executable" */
	if (execmode == shellex) {
		/* if prog had no extension or has the extension associated to shell scripts */
		if ((hasext == 0 && *extension == '\0') || is_shell_script(extension)) {
			int res = process_shebang(argv0, (const char *const *)&cmdstr, &cmdlen, &cmdend, &cmdsize);
			if (res < 0) {
				execmode = none;
			} else if (res == 0) {
				char *newargv[2];
				cmdlen = copy_quote_and_fix_slashes(gModuleName, cmdstr);
				cmdend = cmdstr + cmdlen;
				newargv[0] = path_to_slash(argv0);
				newargv[1] = NULL;
				concat_args_and_quote((const char *const *)newargv, &cmdstr, &cmdlen, &cmdend, &cmdsize);
				*cmdend = 0;
				argv0 = gModuleName;
				execmode = directex;
			} else {
				cmdend = cmdstr + cmdlen;
				execmode = directex;
			}
		} else {
			unsigned long shflags = 0L;
			/* if the file extension is in pathext, use the same console
			   and wait for child. StrStrI() is from shlwapi */
			if (StrStrI(exts, extension))
				shflags = SEE_MASK_NO_CONSOLE | SEE_MASK_NOCLOSEPROCESS;
			if (try_shell_ex(argv0, args, shflags, &cmdstr, &cmdsize))
				return (0);
			/* ShellExecute failed, the file has an unknown extension, but it
			    may be a shell script with a shebang */
			if (process_shebang(argv0, (const char *const *)&cmdstr, &cmdlen, &cmdend, &cmdsize) > 0) {
				cmdend = cmdstr + cmdlen;
				execmode = directex;
			} else {
				/* the file extension is NOT known and the file has NO shebang:
				   returns EPERM, see NOTES */
				errno = EPERM;
				return (-1);
			}
		}
	} else if (execmode == directex) {
		cmdlen = copy_quote_and_fix_slashes(prog, cmdstr);
		cmdend = cmdstr + cmdlen;
	}
	if (execmode == none) {
		/* error: prog not found even after trying PATHEXT extensions */
		errno = ENOENT;
		return (-1);
	}

	concat_args_and_quote(args, &cmdstr, &cmdlen, &cmdend, &cmdsize);
	if (*cmdstr == ' ') {
		/* if we left a ' ' for the quote and there is no quote */
		cmdstr++;
		cmdlen--;
	}
	*cmdend = 0;

	init_startupinfo(&si);
	dwCreationflags = GetPriorityClass(GetCurrentProcess());
	priority = GetThreadPriority(GetCurrentThread());

#if defined(W32DEBUG)
	/* DebugView output is very difficult to read with overlong lines */
	if (cmdlen < 128)
		dbgprintf(PR_EXEC, "%s(): CreateProcess(%s, ..) cmdstr=[%s]\n", __FUNCTION__, argv0, cmdstr);
	else {
		char shortbuf[128+4];
		memcpy(shortbuf, cmdstr, 128);
		memcpy(shortbuf + 128, "...", 4);
		dbgprintf(PR_EXEC, "nt_execve(): CreateProcess(%s, ..) cmdstr=[%s]\n", argv0, shortbuf);
	}
#endif
	if (!CreateProcess(argv0, cmdstr, NULL, NULL,
			   TRUE, // need this for redirecting std handles
			   dwCreationflags | CREATE_SUSPENDED,
			   NULL, NULL, &si, &pi)) {
                exitcode = GetLastError();
		if (exitcode == ERROR_BAD_EXE_FORMAT) {
			dbgprintf(PR_ERROR, "!!! CreateProcess(%s, ..) error BAD_EXE_FORMAT in %s\n", argv0, __FUNCTION__);
			errno  = ENOEXEC;
		} else if (exitcode == ERROR_INVALID_PARAMETER) {
			dbgprintf(PR_ERROR, "!!! CreateProcess(%s, ..) error INVALID_PARAMETER in %s, cmdstr len=%u\n", argv0, __FUNCTION__, strlen(cmdstr));
			/* exceeded command line */
			/* return NOT found, ENAMETOOLONG is correct but not understood by
			   the shell that will retry with another path ... */
			errno = ENOENT;
		} else {
			dbgprintf(PR_ERROR, "!!! CreateProcess(%s, ..) error %ld in %s\n", argv0, exitcode, __FUNCTION__);
			errno = ENOENT;
		}
		goto fail_return;
	} else {
		exitcode = 0;
		if (!SetThreadPriority(pi.hThread, priority))
			dbgprintf(PR_ERROR, "!!! SetThreadPriority(0x%p) failed, error %ld\n", pi.hThread, GetLastError());
		ResumeThread(pi.hThread);
		if (!is_gui(argv0)) {
			if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0)
				dbgprintf(PR_ERROR, "!!! error %ld waiting for process %ld\n", GetLastError(), pi.dwProcessId);
			if (!GetExitCodeProcess(pi.hProcess, &exitcode))
				dbgprintf(PR_ERROR, "!!! GetExitCodeProcess(0x%p, ..) error %ld in %s\n", pi.hProcess, GetLastError(), __FUNCTION__);
		}
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
		close_si_handles();
		/* @@@@ should wait for the clipboard ?
		if (is_dev_clipboard_active) {
			CloseHandle((HANDLE)_get_osfhandle(0));
			CloseHandle((HANDLE)_get_osfhandle(1));
			CloseHandle((HANDLE)_get_osfhandle(2));
			...
			WaitForSingleObject(ghdevclipthread,60*1000);
			}
		*/
		dbgprintf(PR_ALL, "--- %s(): Exec'd process %ld terminated with exitcode %ld\n", __FUNCTION__, pi.dwProcessId, exitcode);
		exec_exit((int)exitcode);
	}

fail_return:
        heap_free(cmdstr);
	close_si_handles();
	exec_exit(-1);
	return (-1);
}