VirtualBox

Changeset 2912 in kBuild


Ignore:
Timestamp:
Sep 14, 2016 1:36:15 PM (9 years ago)
Author:
bird
Message:

rewrote kmk_redirect to skip the separate process. Added chache invalidation after directory deletion for addressing kmk rebuild and fetching.

Location:
trunk
Files:
19 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/kBuild/footer-pass2-fetches.kmk

    r2726 r2912  
    362362                ,$$(call MSG_REFETCH,$(target)),$$(call MSG_FETCH,$(target))),$$(call MSG_UNFETCH,$(target)))
    363363        $$(QUIET)$(TEST_EXT) -f $(out).lst -- $$(MAKE) -f $(MAKEFILE) --no-print-directory $(out)_unfetched
     364if $(KBUILD_KMK_REVISION) > 2911
     365        $$(QUIET)kmk_builtin_dircache deleted "$(dir $(out))"
     366endif
    364367        $$(QUIET)$$(if  $$(_TARGET_$(target)_DIGEST),$$(MAKE) -f $(MAKEFILE) --no-print-directory $(out).lst,$$(RMDIR) -p --ignore-fail-on-non-empty --ignore-fail-on-not-exist -- $$(dir $$@))
    365368        $$(QUIET2)$$(if $$(_TARGET_$(target)_DIGEST),$$(APPEND) $$@ "_TARGET_$(target)_DIGEST_PREV := $(_TARGET_$(target)_DIGEST)")
  • trunk/kBuild/header.kmk

    r2895 r2912  
    702702
    703703REDIRECT_EXT:= $(KBUILD_BIN_PATH)/kmk_redirect$(HOSTSUFF_EXE)
     704if $(KBUILD_KMK_REVISION) > 2911
     705REDIRECT_INT:= kmk_builtin_redirect
     706else
    704707REDIRECT_INT:= $(REDIRECT_EXT)
     708endif
    705709REDIRECT    := $(REDIRECT_INT)
    706710
  • trunk/src/kWorker/kWorker.c

    r2906 r2912  
    26382638static _onexit_t __cdecl kwSandbox_msvcrt__onexit(_onexit_t pfnFunc)
    26392639{
    2640     if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
     2640    //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
    26412641    {
    26422642        PKWEXITCALLACK pCallback;
     
    26632663static int __cdecl kwSandbox_msvcrt_atexit(int (__cdecl *pfnFunc)(void))
    26642664{
    2665     if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
     2665    //if (g_Sandbox.pTool->u.Sandboxed.enmHint == KWTOOLHINT_VISUAL_CPP_LINK)
    26662666    {
    26672667        PKWEXITCALLACK pCallback;
     
    74927492    PKWEXITCALLACK              pExitCallback;
    74937493
     7494
     7495    /*
     7496     * First stuff that may cause code to run.
     7497     */
     7498
    74947499    /* Do exit callback first. */
    74957500    pExitCallback = g_Sandbox.pExitCallbackHead;
     
    75147519    }
    75157520
     7521    /* Free left behind FlsAlloc leaks. */
     7522    pLocalStorage = g_Sandbox.pFlsAllocHead;
     7523    g_Sandbox.pFlsAllocHead = NULL;
     7524    while (pLocalStorage)
     7525    {
     7526        PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
     7527        KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
     7528        FlsFree(pLocalStorage->idx);
     7529        kHlpFree(pLocalStorage);
     7530        pLocalStorage = pNext;
     7531    }
     7532
     7533    /* Free left behind TlsAlloc leaks. */
     7534    pLocalStorage = g_Sandbox.pTlsAllocHead;
     7535    g_Sandbox.pTlsAllocHead = NULL;
     7536    while (pLocalStorage)
     7537    {
     7538        PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
     7539        KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
     7540        TlsFree(pLocalStorage->idx);
     7541        kHlpFree(pLocalStorage);
     7542        pLocalStorage = pNext;
     7543    }
     7544
     7545
     7546    /*
     7547     * Then free resources associated with the sandbox run.
     7548     */
    75167549
    75177550#ifdef WITH_TEMP_MEMORY_FILES
     
    75337566#endif
    75347567
     7568    /* Free left behind HeapCreate leaks. */
     7569    pHeap = g_Sandbox.pHeapHead;
     7570    g_Sandbox.pHeapHead = NULL;
     7571    while (pHeap != NULL)
     7572    {
     7573        PKWHEAP pNext = pHeap->pNext;
     7574        KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
     7575        HeapDestroy(pHeap->hHeap);
     7576        pHeap = pNext;
     7577    }
     7578
    75357579    /* Free left behind VirtualAlloc leaks. */
    75367580    pTracker = g_Sandbox.pVirtualAllocHead;
     
    75447588        pTracker = pNext;
    75457589    }
    7546 
    7547     /* Free left behind HeapCreate leaks. */
    7548     pHeap = g_Sandbox.pHeapHead;
    7549     g_Sandbox.pHeapHead = NULL;
    7550     while (pHeap != NULL)
    7551     {
    7552         PKWHEAP pNext = pHeap->pNext;
    7553         KW_LOG(("Freeing HeapCreate leak %p\n", pHeap->hHeap));
    7554         HeapDestroy(pHeap->hHeap);
    7555         pHeap = pNext;
    7556     }
    7557 
    7558     /* Free left behind FlsAlloc leaks. */
    7559     pLocalStorage = g_Sandbox.pFlsAllocHead;
    7560     g_Sandbox.pFlsAllocHead = NULL;
    7561     while (pLocalStorage)
    7562     {
    7563         PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
    7564         KW_LOG(("Freeing leaked FlsAlloc index %#x\n", pLocalStorage->idx));
    7565         FlsFree(pLocalStorage->idx);
    7566         kHlpFree(pLocalStorage);
    7567         pLocalStorage = pNext;
    7568     }
    7569 
    7570     /* Free left behind TlsAlloc leaks. */
    7571     pLocalStorage = g_Sandbox.pTlsAllocHead;
    7572     g_Sandbox.pTlsAllocHead = NULL;
    7573     while (pLocalStorage)
    7574     {
    7575         PKWLOCALSTORAGE pNext = pLocalStorage->pNext;
    7576         KW_LOG(("Freeing leaked TlsAlloc index %#x\n", pLocalStorage->idx));
    7577         TlsFree(pLocalStorage->idx);
    7578         kHlpFree(pLocalStorage);
    7579         pLocalStorage = pNext;
    7580     }
    7581 
    75827590
    75837591    /* Free the environment. */
     
    81848192        /* Optional directory change. */
    81858193        if (   i < argc
    8186             && strcmp(argv[i], "--chdir") == 0)
     8194            && (   strcmp(argv[i], "--chdir") == 0
     8195                || strcmp(argv[i], "-C")      == 0 ) )
    81878196        {
    81888197            i++;
  • trunk/src/kmk/Makefile.kmk

    r2907 r2912  
    8989        kmkbuiltin/strmode.c \
    9090        kmkbuiltin/kbuild_protection.c \
     91        kmkbuiltin/common-env-and-cwd-opt.c \
    9192        getopt.c \
    9293        getopt1.c \
     
    280281        kmkbuiltin/ln.c \
    281282        kmkbuiltin/printf.c \
     283        kmkbuiltin/redirect.c \
    282284        kmkbuiltin/rm.c \
    283285        kmkbuiltin/rmdir.c \
     
    395397
    396398kmk_redirect_TEMPLATE = BIN-KMK
    397 kmk_redirect_DEFS = kmk_builtin_redirect=main
    398399kmk_redirect_SOURCES = \
    399400        kmkbuiltin/redirect.c
  • trunk/src/kmk/dir-nt-bird.c

    r2886 r2912  
    121121                else
    122122                {
    123                     PKFSOBJ pNameObj = kFsCacheLookupRelativeToDirA(g_pFsCache, (PKFSDIR)pDirObj,
    124                                                                     pszName, strlen(pszName), &enmError, NULL);
     123                    PKFSOBJ pNameObj = kFsCacheLookupRelativeToDirA(g_pFsCache, (PKFSDIR)pDirObj, pszName, strlen(pszName),
     124                                                                    0/*fFlags*/, &enmError, NULL);
    125125                    if (pNameObj)
    126126                    {
     
    586586}
    587587
     588/**
     589 * Invalidates a deleted directory so the cache can close handles to it.
     590 *
     591 * Used by kmk_builtin_rm and kmk_builtin_rmdir.
     592 *
     593 * @returns 0 on success, -1 on failure.
     594 * @param   pszDir      The directory to invalidate as deleted.
     595 */
     596int dir_cache_deleted_directory(const char *pszDir)
     597{
     598    if (kFsCacheInvalidateDeletedDirectoryA(g_pFsCache, pszDir))
     599        return 0;
     600    return -1;
     601}
     602
     603
     604int kmk_builtin_dircache(int argc, char **argv, char **envp)
     605{
     606    if (argc >= 2)
     607    {
     608        const char *pszCmd = argv[1];
     609        if (strcmp(pszCmd, "invalidate") == 0)
     610        {
     611            if (argc == 2)
     612            {
     613                dir_cache_invalid_all();
     614                return 0;
     615            }
     616            fprintf(stderr, "kmk_builtin_dircache: the 'invalidate' command takes no arguments!\n");
     617        }
     618        else if (strcmp(pszCmd, "invalidate-missing") == 0)
     619        {
     620            if (argc == 2)
     621            {
     622                dir_cache_invalid_missing ();
     623                return 0;
     624            }
     625            fprintf(stderr, "kmk_builtin_dircache: the 'invalidate-missing' command takes no arguments!\n");
     626        }
     627        else if (strcmp(pszCmd, "volatile") == 0)
     628        {
     629            int i;
     630            for (i = 2; i < argc; i++)
     631                dir_cache_volatile_dir(argv[i]);
     632            return 0;
     633        }
     634        else if (strcmp(pszCmd, "deleted") == 0)
     635        {
     636            int i;
     637            for (i = 2; i < argc; i++)
     638                dir_cache_deleted_directory(argv[i]);
     639            return 0;
     640        }
     641        else
     642            fprintf(stderr, "kmk_builtin_dircache: Invalid command '%s'!\n", pszCmd);
     643    }
     644    else
     645        fprintf(stderr, "kmk_builtin_dircache: No command given!\n");
     646
     647    K_NOREF(envp);
     648    return 2;
     649}
     650
  • trunk/src/kmk/function.c

    r2886 r2912  
    54915491        }
    54925492    }
     5493  else if (strcmp (cmd, "deleted") == 0)
     5494    {
     5495      size_t i;
     5496      for (i = 1; argv[i] != NULL; i++)
     5497        {
     5498          const char *dir = argv[i];
     5499          while (isblank ((unsigned char)*dir))
     5500            dir++;
     5501          if (*dir)
     5502            dir_cache_deleted_directory (dir);
     5503        }
     5504    }
    54935505  else
    54945506    error (reading_file, "Unknown $(dircache-ctl ) command: '%s'", cmd);
  • trunk/src/kmk/kmkbuiltin.c

    r2843 r2912  
    206206    else if (!strcmp(pszCmd, "mv"))
    207207        rc = kmk_builtin_mv(argc, argv, environ);
    208     /*else if (!strcmp(pszCmd, "redirect"))
    209         rc = kmk_builtin_redirect(argc, argv, environ, pPidSpawned);*/
     208    else if (!strcmp(pszCmd, "redirect"))
     209        rc = kmk_builtin_redirect(argc, argv, environ, pChild, pPidSpawned);
    210210    else if (!strcmp(pszCmd, "rm"))
    211211        rc = kmk_builtin_rm(argc, argv, environ);
     
    233233    else if (!strcmp(pszCmd, "sleep"))
    234234        rc = kmk_builtin_sleep(argc, argv, environ);
     235    else if (!strcmp(pszCmd, "dircache"))
     236#ifdef KBUILD_OS_WINDOWS
     237        rc = kmk_builtin_dircache(argc, argv, environ);
     238#else
     239        rc = 0;
     240#endif
    235241    else
    236242    {
  • trunk/src/kmk/kmkbuiltin.h

    r2899 r2912  
    4646extern int kmk_builtin_chmod(int argc, char **argv, char **envp);
    4747extern int kmk_builtin_cmp(int argc, char **argv, char **envp);
     48extern int kmk_builtin_dircache(int argc, char **argv, char **envp);
    4849extern int kmk_builtin_echo(int argc, char **argv, char **envp);
    4950extern int kmk_builtin_expr(int argc, char **argv, char **envp);
     
    5455extern int kmk_builtin_mv(int argc, char **argv, char **envp);
    5556extern int kmk_builtin_printf(int argc, char **argv, char **envp);
     57extern int kmk_builtin_redirect(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned);
    5658extern int kmk_builtin_rm(int argc, char **argv, char **envp);
    5759extern int kmk_builtin_rmdir(int argc, char **argv, char **envp);
     
    7375extern char *kmk_builtin_func_printf(char *o, char **argv, const char *funcname);
    7476
     77/* common-env-and-cwd-opt.c: */
     78extern int kBuiltinOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
     79                             int cVerbosity, const char *pszValue);
     80extern int kBuiltinOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove);
     81extern int kBuiltinOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue);
     82
    7583#endif
    7684
  • trunk/src/kmk/kmkbuiltin/common-env-and-cwd-opt.c

    r2899 r2912  
    11/* $Id$ */
    22/** @file
    3  * kMk Builtin command - submit job to a kWorker.
     3 * kMk Builtin command - Commmon environment and CWD option handling code.
    44 */
    55
     
    2727*   Header Files                                                               *
    2828*******************************************************************************/
    29 #ifdef __APPLE__
    30 # define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */
    31 #endif
    32 #include "make.h"
    33 #include "job.h"
    34 #include "variable.h"
    35 #include "pathstuff.h"
     29#include "config.h"
    3630#include <stdio.h>
    3731#include <stdlib.h>
    3832#include <string.h>
    39 #include <errno.h>
    40 #include <assert.h>
    41 #ifdef HAVE_ALLOCA_H
    42 # include <alloca.h>
    43 #endif
    44 #if defined(_MSC_VER)
    45 # include <ctype.h>
    46 # include <io.h>
    47 # include <direct.h>
    48 # include <process.h>
    49 #else
    50 # include <unistd.h>
    51 #endif
    52 #ifdef KBUILD_OS_WINDOWS
    53 # include "sub_proc.h"
    54 #endif
    55 
    56 #include "kbuild.h"
     33
    5734#include "kmkbuiltin.h"
    5835#include "err.h"
    59 
    60 
    61 /*********************************************************************************************************************************
    62 *   Defined Constants And Macros                                                                                                 *
    63 *********************************************************************************************************************************/
    64 /** Hashes a pid. */
    65 #define KWORKER_PID_HASH(a_pid) ((size_t)(a_pid) % 61)
    66 
    67 
    68 /*********************************************************************************************************************************
    69 *   Structures and Typedefs                                                                                                      *
    70 *********************************************************************************************************************************/
    71 typedef struct WORKERINSTANCE *PWORKERINSTANCE;
    72 typedef struct WORKERINSTANCE
    73 {
    74     /** Pointer to the next worker instance. */
    75     PWORKERINSTANCE         pNext;
    76     /** Pointer to the previous worker instance. */
    77     PWORKERINSTANCE         pPrev;
    78     /** Pointer to the next worker with the same pid hash slot. */
    79     PWORKERINSTANCE         pNextPidHash;
    80     /** 32 or 64. */
    81     unsigned                cBits;
    82     /** The process ID of the kWorker process. */
    83     pid_t                   pid;
    84     union
    85     {
    86         struct
    87         {
    88             /** The exit code. */
    89             int32_t         rcExit;
    90             /** Set to 1 if the worker is exiting. */
    91             uint8_t         bWorkerExiting;
    92             uint8_t         abUnused[3];
    93         } s;
    94         uint8_t             ab[8];
    95     } Result;
    96     /** Number of result bytes read alread.  */
    97     size_t                  cbResultRead;
    98 
    99 #ifdef KBUILD_OS_WINDOWS
    100     /** The process handle. */
    101     HANDLE                  hProcess;
    102     /** The bi-directional pipe we use to talk to the kWorker process. */
    103     HANDLE                  hPipe;
    104     /** For overlapped read (have valid event semaphore). */
    105     OVERLAPPED              OverlappedRead;
    106 #else
    107     /** The socket descriptor we use to talk to the kWorker process. */
    108     int                     fdSocket;
    109 #endif
    110 
    111     /** What it's busy with.  NULL if idle. */
    112     struct child           *pBusyWith;
    113 } WORKERINSTANCE;
    114 
    115 
    116 typedef struct WORKERLIST
    117 {
    118     /** The head of the list.  NULL if empty. */
    119     PWORKERINSTANCE         pHead;
    120     /** The tail of the list.  NULL if empty. */
    121     PWORKERINSTANCE         pTail;
    122     /** Number of list entries. */
    123     size_t                  cEntries;
    124 } WORKERLIST;
    125 typedef WORKERLIST *PWORKERLIST;
    126 
    127 
    128 /*********************************************************************************************************************************
    129 *   Global Variables                                                                                                             *
    130 *********************************************************************************************************************************/
    131 /** List of idle worker.*/
    132 static WORKERLIST           g_IdleList;
    133 /** List of busy workers. */
    134 static WORKERLIST           g_BusyList;
    135 /** PID hash table for the workers.
    136  * @sa KWORKER_PID_HASH() */
    137 static PWORKERINSTANCE      g_apPidHash[61];
    138 
    139 #ifdef KBUILD_OS_WINDOWS
    140 /** For naming the pipes.
    141  * Also indicates how many worker instances we've spawned. */
    142 static unsigned             g_uWorkerSeqNo = 0;
    143 #endif
    144 /** Set if we've registred the atexit handler already. */
    145 static int                  g_fAtExitRegistered = 0;
    146 
    147 /** @var g_cArchBits
    148  * The bit count of the architecture this binary is compiled for. */
    149 /** @var g_szArch
    150  * The name of the architecture this binary is compiled for. */
    151 /** @var g_cArchBits
    152  * The bit count of the alternative architecture. */
    153 /** @var g_szAltArch
    154  * The name of the alternative architecture. */
    155 #if defined(KBUILD_ARCH_AMD64)
    156 static unsigned             g_cArchBits    = 64;
    157 static char const           g_szArch[]     = "amd64";
    158 static unsigned             g_cAltArchBits = 32;
    159 static char const           g_szAltArch[]  = "x86";
    160 #elif defined(KBUILD_ARCH_X86)
    161 static unsigned             g_cArchBits    = 32;
    162 static char const           g_szArch[]     = "x86";
    163 static unsigned             g_cAltArchBits = 64;
    164 static char const           g_szAltArch[]  = "amd64";
    165 #else
    166 # error "Port me!"
    167 #endif
    168 
    169 
    170 
    171 /**
    172  * Unlinks a worker instance from a list.
    173  *
    174  * @param   pList               The list.
    175  * @param   pWorker             The worker.
    176  */
    177 static void kSubmitListUnlink(PWORKERLIST pList, PWORKERINSTANCE pWorker)
    178 {
    179     PWORKERINSTANCE pNext = pWorker->pNext;
    180     PWORKERINSTANCE pPrev = pWorker->pPrev;
    181 
    182     if (pNext)
    183     {
    184         assert(pNext->pPrev == pWorker);
    185         pNext->pPrev = pPrev;
    186     }
    187     else
    188     {
    189         assert(pList->pTail == pWorker);
    190         pList->pTail = pPrev;
    191     }
    192 
    193     if (pPrev)
    194     {
    195         assert(pPrev->pNext == pWorker);
    196         pPrev->pNext = pNext;
    197     }
    198     else
    199     {
    200         assert(pList->pHead == pWorker);
    201         pList->pHead = pNext;
    202     }
    203 
    204     assert(!pList->pHead || pList->pHead->pPrev == NULL);
    205     assert(!pList->pTail || pList->pTail->pNext == NULL);
    206 
    207     assert(pList->cEntries > 0);
    208     pList->cEntries--;
    209 
    210     pWorker->pNext = NULL;
    211     pWorker->pPrev = NULL;
    212 }
    213 
    214 
    215 /**
    216  * Appends a worker instance to the tail of a list.
    217  *
    218  * @param   pList               The list.
    219  * @param   pWorker             The worker.
    220  */
    221 static void kSubmitListAppend(PWORKERLIST pList, PWORKERINSTANCE pWorker)
    222 {
    223     PWORKERINSTANCE pTail = pList->pTail;
    224 
    225     assert(pTail != pWorker);
    226     assert(pList->pHead != pWorker);
    227 
    228     pWorker->pNext = NULL;
    229     pWorker->pPrev = pTail;
    230     if (pTail != NULL)
    231     {
    232         assert(pTail->pNext == NULL);
    233         pTail->pNext = pWorker;
    234     }
    235     else
    236     {
    237         assert(pList->pHead == NULL);
    238         pList->pHead = pWorker;
    239     }
    240     pList->pTail = pWorker;
    241 
    242     assert(pList->pHead->pPrev == NULL);
    243     assert(pList->pTail->pNext == NULL);
    244 
    245     pList->cEntries++;
    246 }
    247 
    248 
    249 /**
    250  * Remove worker from the process ID hash table.
    251  *
    252  * @param   pWorker             The worker.
    253  */
    254 static void kSubmitPidHashRemove(PWORKERINSTANCE pWorker)
    255 {
    256     size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
    257     if (g_apPidHash[idxHash] == pWorker)
    258         g_apPidHash[idxHash] = pWorker->pNext;
    259     else
    260     {
    261         PWORKERINSTANCE pPrev = g_apPidHash[idxHash];
    262         while (pPrev && pPrev->pNext != pWorker)
    263             pPrev = pPrev->pNext;
    264         assert(pPrev != NULL);
    265         if (pPrev)
    266             pPrev->pNext = pWorker->pNext;
    267     }
    268     pWorker->pid = -1;
    269 }
    270 
    271 
    272 /**
    273  * Looks up a worker by its process ID.
    274  *
    275  * @returns Pointer to the worker instance if found. NULL if not.
    276  * @param   pid                 The process ID of the worker.
    277  */
    278 static PWORKERINSTANCE kSubmitFindWorkerByPid(pid_t pid)
    279 {
    280     PWORKERINSTANCE pWorker = g_apPidHash[KWORKER_PID_HASH(pid)];
    281     while (pWorker && pWorker->pid != pid)
    282         pWorker = pWorker->pNextPidHash;
    283     return pWorker;
    284 }
    285 
    286 
    287 /**
    288  * Creates a new worker process.
    289  *
    290  * @returns 0 on success, non-zero value on failure.
    291  * @param   pWorker             The worker structure.  Caller does the linking
    292  *                              (as we might be reusing an existing worker
    293  *                              instance because a worker shut itself down due
    294  *                              to high resource leak level).
    295  * @param   cVerbosity          The verbosity level.
    296  */
    297 static int kSubmitSpawnWorker(PWORKERINSTANCE pWorker, int cVerbosity)
    298 {
    299 #if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
    300     static const char s_szWorkerName[] = "kWorker.exe";
    301 #else
    302     static const char s_szWorkerName[] = "kWorker";
    303 #endif
    304     const char     *pszBinPath = get_kbuild_bin_path();
    305     size_t const    cchBinPath = strlen(pszBinPath);
    306     size_t          cchExectuable;
    307     size_t const    cbExecutableBuf = GET_PATH_MAX;
    308     PATH_VAR(szExecutable);
    309 #define TUPLE(a_sz)     a_sz, sizeof(a_sz) - 1
    310     struct variable *pVarVolatile = lookup_variable(TUPLE("PATH_OUT"));
    311     if (pVarVolatile)
    312     { /* likely */ }
    313     else
    314     {
    315         pVarVolatile = lookup_variable(TUPLE("PATH_OUT_BASE"));
    316         if (!pVarVolatile)
    317             warn("Neither PATH_OUT_BASE nor PATH_OUT was found.");
    318     }
    319 
    320     /*
    321      * Construct the executable path.
    322      */
    323     if (   pWorker->cBits == g_cArchBits
    324         ?  cchBinPath + 1 + sizeof(s_szWorkerName) <= cbExecutableBuf
    325         :  cchBinPath + 1 - sizeof(g_szArch) + sizeof(g_szAltArch) + sizeof(s_szWorkerName) <= cbExecutableBuf )
    326     {
    327 #ifdef KBUILD_OS_WINDOWS
    328         static DWORD        s_fDenyRemoteClients = ~(DWORD)0;
    329         wchar_t             wszPipeName[64];
    330         HANDLE              hWorkerPipe;
    331         SECURITY_ATTRIBUTES SecAttrs = { /*nLength:*/ sizeof(SecAttrs), /*pAttrs:*/ NULL, /*bInheritHandle:*/ TRUE };
    332 #else
    333         int                 aiPair[2] = { -1, -1 };
    334 #endif
    335 
    336         memcpy(szExecutable, pszBinPath, cchBinPath);
    337         cchExectuable = cchBinPath;
    338 
    339         /* Replace the arch bin directory extension with the alternative one if requested. */
    340         if (pWorker->cBits != g_cArchBits)
    341         {
    342             if (   cchBinPath < sizeof(g_szArch)
    343                 || memcmp(&szExecutable[cchBinPath - sizeof(g_szArch) + 1], g_szArch, sizeof(g_szArch) - 1) != 0)
    344                 return errx(1, "KBUILD_BIN_PATH does not end with main architecture (%s) as expected: %s", pszBinPath, g_szArch);
    345             cchExectuable -= sizeof(g_szArch) - 1;
    346             memcpy(&szExecutable[cchExectuable], g_szAltArch, sizeof(g_szAltArch) - 1);
    347             cchExectuable += sizeof(g_szAltArch) - 1;
    348         }
    349 
    350         /* Append a slash and the worker name. */
    351         szExecutable[cchExectuable++] = '/';
    352         memcpy(&szExecutable[cchExectuable], s_szWorkerName, sizeof(s_szWorkerName));
    353 
    354 #ifdef KBUILD_OS_WINDOWS
    355         /*
    356          * Create the bi-directional pipe.  Worker end is marked inheritable, our end is not.
    357          */
    358         if (s_fDenyRemoteClients == ~(DWORD)0)
    359             s_fDenyRemoteClients = GetVersion() >= 0x60000 ? PIPE_REJECT_REMOTE_CLIENTS : 0;
    360         _snwprintf(wszPipeName, sizeof(wszPipeName), L"\\\\.\\pipe\\kmk-%u-kWorker-%u", getpid(), g_uWorkerSeqNo++);
    361         hWorkerPipe = CreateNamedPipeW(wszPipeName,
    362                                        PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE /* win2k sp2+ */,
    363                                        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | s_fDenyRemoteClients,
    364                                        1 /* cMaxInstances */,
    365                                        64 /*cbOutBuffer*/,
    366                                        65536 /*cbInBuffer*/,
    367                                        0 /*cMsDefaultTimeout -> 50ms*/,
    368                                        &SecAttrs /* inherit */);
    369         if (hWorkerPipe != INVALID_HANDLE_VALUE)
    370         {
    371             pWorker->hPipe = CreateFileW(wszPipeName,
    372                                          GENERIC_READ | GENERIC_WRITE,
    373                                          0 /* dwShareMode - no sharing */,
    374                                          NULL /*pSecAttr - no inherit */,
    375                                          OPEN_EXISTING,
    376                                          FILE_FLAG_OVERLAPPED,
    377                                          NULL /*hTemplate*/);
    378             if (pWorker->hPipe != INVALID_HANDLE_VALUE)
    379             {
    380                 pWorker->OverlappedRead.hEvent = CreateEventW(NULL /*pSecAttrs - no inherit*/, TRUE /*bManualReset*/,
    381                                                               TRUE /*bInitialState*/, NULL /*pwszName*/);
    382                 if (pWorker->OverlappedRead.hEvent != NULL)
    383                 {
    384                     char        szHandleArg[32];
    385                     const char *apszArgs[6] =
    386                     {
    387                         szExecutable, "--pipe", szHandleArg,
    388                         pVarVolatile ? "--volatile" : NULL, pVarVolatile ? pVarVolatile->value : NULL,
    389                         NULL
    390                     };
    391                     _snprintf(szHandleArg, sizeof(szHandleArg), "%p", hWorkerPipe);
    392 
    393                     /*
    394                      * Create the worker process.
    395                      */
    396                     pWorker->hProcess = (HANDLE) _spawnve(_P_NOWAIT, szExecutable, apszArgs, environ);
    397                     if ((intptr_t)pWorker->hProcess != -1)
    398                     {
    399                         CloseHandle(hWorkerPipe);
    400                         pWorker->pid = GetProcessId(pWorker->hProcess);
    401                         if (cVerbosity > 0)
    402                             fprintf(stderr, "kSubmit: created %d bit worker %d\n", pWorker->cBits, pWorker->pid);
    403                         return 0;
    404                     }
    405                     err(1, "_spawnve(,%s,,)", szExecutable);
    406                     CloseHandle(pWorker->OverlappedRead.hEvent);
    407                     pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
    408                 }
    409                 else
    410                     errx(1, "CreateEventW failed: %u", GetLastError());
    411                 CloseHandle(pWorker->hPipe);
    412                 pWorker->hPipe = INVALID_HANDLE_VALUE;
    413             }
    414             else
    415                 errx(1, "Opening named pipe failed: %u", GetLastError());
    416             CloseHandle(hWorkerPipe);
    417         }
    418         else
    419             errx(1, "CreateNamedPipeW failed: %u", GetLastError());
    420 
    421 #else
    422         /*
    423          * Create a socket pair.
    424          */
    425         if (socketpair(AF_LOCAL, SOCK_STREAM, 0, aiPair) == 0)
    426         {
    427             pWorker->fdSocket = aiPair[1];
    428         }
    429         else
    430             err(1, "socketpair");
    431 #endif
    432     }
    433     else
    434         errx(1, "KBUILD_BIN_PATH is too long");
    435     return -1;
    436 }
    437 
    438 
    439 /**
    440  * Selects an idle worker or spawns a new one.
    441  *
    442  * @returns Pointer to the selected worker instance.  NULL on error.
    443  * @param   pWorker             The idle worker instance to respawn.
    444  *                              On failure this will be freed!
    445  * @param   cBitsWorker         The worker bitness - 64 or 32.
    446  */
    447 static int kSubmitRespawnWorker(PWORKERINSTANCE pWorker, int cVerbosity)
    448 {
    449     /*
    450      * Clean up after the old worker.
    451      */
    452 #ifdef KBUILD_OS_WINDOWS
    453     DWORD   rcWait;
    454 
    455     /* Close the pipe handle first, breaking the pipe in case it's not already
    456        busted up.  Close the event semaphore too before waiting for the process. */
    457     if (pWorker->hPipe != INVALID_HANDLE_VALUE)
    458     {
    459         if (!CloseHandle(pWorker->hPipe))
    460             warnx("CloseHandle(pWorker->hPipe): %u", GetLastError());
    461         pWorker->hPipe = INVALID_HANDLE_VALUE;
    462     }
    463 
    464     if (!CloseHandle(pWorker->OverlappedRead.hEvent))
    465         warnx("CloseHandle(pWorker->OverlappedRead.hEvent): %u", GetLastError());
    466     pWorker->OverlappedRead.hEvent = INVALID_HANDLE_VALUE;
    467 
    468     /* It's probably shutdown already, if not give it 10 milliseconds before
    469        we terminate it forcefully. */
    470     rcWait = WaitForSingleObject(pWorker->hProcess, 10);
    471     if (rcWait != WAIT_OBJECT_0)
    472     {
    473         BOOL fRc = TerminateProcess(pWorker->hProcess, 127);
    474         rcWait = WaitForSingleObject(pWorker->hProcess, 100);
    475         if (rcWait != WAIT_OBJECT_0)
    476             warnx("WaitForSingleObject returns %u (and TerminateProcess %d)", rcWait, fRc);
    477     }
    478 
    479     if (!CloseHandle(pWorker->hProcess))
    480         warnx("CloseHandle(pWorker->hProcess): %u", GetLastError());
    481     pWorker->hProcess = INVALID_HANDLE_VALUE;
    482 
    483 #else
    484     pid_t   pidWait;
    485     int     rc;
    486 
    487     if (pWorker->fdSocket != -1)
    488     {
    489         if (close(pWorker->fdSocket) != 0)
    490             warn("close(pWorker->fdSocket)");
    491         pWorker->fdSocket = -1;
    492     }
    493 
    494     kill(pWorker->pid, SIGTERM);
    495     pidWait = waitpid(pWorker->pid, &rc, 0);
    496     if (pidWait != pWorker->pid)
    497         warn("waitpid(pWorker->pid,,0)");
    498 #endif
    499 
    500     /*
    501      * Unlink it from the hash table.
    502      */
    503     kSubmitPidHashRemove(pWorker);
    504 
    505     /*
    506      * Respawn it.
    507      */
    508     if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0)
    509     {
    510         /*
    511          * Insert it into the process ID hash table and idle list.
    512          */
    513         size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
    514         pWorker->pNextPidHash = g_apPidHash[idxHash];
    515         g_apPidHash[idxHash] = pWorker;
    516         return 0;
    517     }
    518 
    519     kSubmitListUnlink(&g_IdleList, pWorker);
    520     free(pWorker);
    521     return -1;
    522 }
    523 
    524 
    525 /**
    526  * Selects an idle worker or spawns a new one.
    527  *
    528  * @returns Pointer to the selected worker instance.  NULL on error.
    529  * @param   cBitsWorker         The worker bitness - 64 or 32.
    530  */
    531 static PWORKERINSTANCE kSubmitSelectWorkSpawnNewIfNecessary(unsigned cBitsWorker, int cVerbosity)
    532 {
    533     /*
    534      * Lookup up an idle worker.
    535      */
    536     PWORKERINSTANCE pWorker = g_IdleList.pHead;
    537     while (pWorker)
    538     {
    539         if (pWorker->cBits == cBitsWorker)
    540             return pWorker;
    541         pWorker = pWorker->pNext;
    542     }
    543 
    544     /*
    545      * Create a new worker instance.
    546      */
    547     pWorker = (PWORKERINSTANCE)xcalloc(sizeof(*pWorker));
    548     pWorker->cBits = cBitsWorker;
    549     if (kSubmitSpawnWorker(pWorker, cVerbosity) == 0)
    550     {
    551         /*
    552          * Insert it into the process ID hash table and idle list.
    553          */
    554         size_t idxHash = KWORKER_PID_HASH(pWorker->pid);
    555         pWorker->pNextPidHash = g_apPidHash[idxHash];
    556         g_apPidHash[idxHash] = pWorker;
    557 
    558         kSubmitListAppend(&g_IdleList, pWorker);
    559         return pWorker;
    560     }
    561 
    562     free(pWorker);
    563     return NULL;
    564 }
    565 
    566 
    567 /**
    568  * Composes a JOB mesage for a worker.
    569  *
    570  * @returns Pointer to the message.
    571  * @param   pszExecutable       The executable to run.
    572  * @param   papszArgs           The argument vector.
    573  * @param   papszEnvVars        The environment vector.
    574  * @param   pszCwd              The current directory.
    575  * @param   fWatcomBrainDamage  The wcc/wcc386 workaround.
    576  * @param   papszPostCmdArgs    The post command and it's arguments.
    577  * @param   cPostCmdArgs        Number of post command argument, including the
    578  *                              command.  Zero if no post command scheduled.
    579  * @param   pcbMsg              Where to return the message length.
    580  */
    581 static void *kSubmitComposeJobMessage(const char *pszExecutable, char **papszArgs, char **papszEnvVars,
    582                                       const char *pszCwd, int fWatcomBrainDamage,
    583                                       char **papszPostCmdArgs, uint32_t cPostCmdArgs, uint32_t *pcbMsg)
    584 {
    585     size_t   cbTmp;
    586     uint32_t i;
    587     uint32_t cbMsg;
    588     uint32_t cArgs;
    589     uint32_t cEnvVars;
    590     uint8_t *pbMsg;
    591     uint8_t *pbCursor;
    592 
    593     /*
    594      * Adjust input.
    595      */
    596     if (!pszExecutable)
    597         pszExecutable = papszArgs[0];
    598 
    599     /*
    600      * Calculate the message length first.
    601      */
    602     cbMsg  = sizeof(cbMsg);
    603     cbMsg += sizeof("JOB");
    604     cbMsg += strlen(pszExecutable) + 1;
    605     cbMsg += strlen(pszCwd) + 1;
    606 
    607     cbMsg += sizeof(cArgs);
    608     for (i = 0; papszArgs[i] != NULL; i++)
    609         cbMsg += 1 + strlen(papszArgs[i]) + 1;
    610     cArgs  = i;
    611 
    612     cbMsg += sizeof(cArgs);
    613     for (i = 0; papszEnvVars[i] != NULL; i++)
    614         cbMsg += strlen(papszEnvVars[i]) + 1;
    615     cEnvVars = i;
    616 
    617     cbMsg += 1;
    618 
    619     cbMsg += sizeof(cPostCmdArgs);
    620     for (i = 0; i < cPostCmdArgs; i++)
    621         cbMsg += strlen(papszPostCmdArgs[i]) + 1;
    622 
    623     /*
    624      * Compose the message.
    625      */
    626     pbMsg = pbCursor = xmalloc(cbMsg);
    627 
    628     /* header */
    629     memcpy(pbCursor, &cbMsg, sizeof(cbMsg));
    630     pbCursor += sizeof(cbMsg);
    631     memcpy(pbCursor, "JOB", sizeof("JOB"));
    632     pbCursor += sizeof("JOB");
    633 
    634     /* executable. */
    635     cbTmp = strlen(pszExecutable) + 1;
    636     memcpy(pbCursor, pszExecutable, cbTmp);
    637     pbCursor += cbTmp;
    638 
    639     /* cwd */
    640     cbTmp = strlen(pszCwd) + 1;
    641     memcpy(pbCursor, pszCwd, cbTmp);
    642     pbCursor += cbTmp;
    643 
    644     /* argument */
    645     memcpy(pbCursor, &cArgs, sizeof(cArgs));
    646     pbCursor += sizeof(cArgs);
    647     for (i = 0; papszArgs[i] != NULL; i++)
    648     {
    649         *pbCursor++ = 0; /* Argument expansion flags (MSC, EMX). */
    650         cbTmp = strlen(papszArgs[i]) + 1;
    651         memcpy(pbCursor, papszArgs[i], cbTmp);
    652         pbCursor += cbTmp;
    653     }
    654     assert(i == cArgs);
    655 
    656     /* environment */
    657     memcpy(pbCursor, &cEnvVars, sizeof(cEnvVars));
    658     pbCursor += sizeof(cEnvVars);
    659     for (i = 0; papszEnvVars[i] != NULL; i++)
    660     {
    661         cbTmp = strlen(papszEnvVars[i]) + 1;
    662         memcpy(pbCursor, papszEnvVars[i], cbTmp);
    663         pbCursor += cbTmp;
    664     }
    665     assert(i == cEnvVars);
    666 
    667     /* flags */
    668     *pbCursor++ = fWatcomBrainDamage != 0;
    669 
    670     /* post command */
    671     memcpy(pbCursor, &cPostCmdArgs, sizeof(cPostCmdArgs));
    672     pbCursor += sizeof(cPostCmdArgs);
    673     for (i = 0; i < cPostCmdArgs; i++)
    674     {
    675         cbTmp = strlen(papszPostCmdArgs[i]) + 1;
    676         memcpy(pbCursor, papszPostCmdArgs[i], cbTmp);
    677         pbCursor += cbTmp;
    678     }
    679     assert(i == cPostCmdArgs);
    680 
    681     assert(pbCursor - pbMsg == (size_t)cbMsg);
    682 
    683     /*
    684      * Done.
    685      */
    686     *pcbMsg = cbMsg;
    687     return pbMsg;
    688 }
    689 
    690 
    691 /**
    692  * Sends the job message to the given worker, respawning the worker if
    693  * necessary.
    694  *
    695  * @returns 0 on success, non-zero on failure.
    696  *
    697  * @param   pWorker             The work to send the request to.  The worker is
    698  *                              on the idle list.
    699  * @param   pvMsg               The message to send.
    700  * @param   cbMsg               The size of the message.
    701  * @param   fNoRespawning       Set if
    702  * @param   cVerbosity          The verbosity level.
    703  */
    704 static int kSubmitSendJobMessage(PWORKERINSTANCE pWorker, void const *pvMsg, uint32_t cbMsg, int fNoRespawning, int cVerbosity)
    705 {
    706     int cRetries;
    707 
    708     /*
    709      * Respawn the worker if it stopped by itself and we closed the pipe already.
    710      */
    711 #ifdef KBUILD_OS_WINDOWS
    712     if (pWorker->hPipe == INVALID_HANDLE_VALUE)
    713 #else
    714     if (pWorker->fdSocket == -1)
    715 #endif
    716     {
    717         if (!fNoRespawning)
    718         {
    719             if (cVerbosity > 0)
    720                 fprintf(stderr,  "kSubmit: Respawning worker (#1)...\n");
    721             if (kSubmitRespawnWorker(pWorker, cVerbosity) != 0)
    722                 return 2;
    723         }
    724 
    725     }
    726 
    727     /*
    728      * Restart-on-broken-pipe loop. Necessary?
    729      */
    730     for (cRetries = !fNoRespawning ? 1 : 0; ; cRetries--)
    731     {
    732         /*
    733          * Try write the message.
    734          */
    735         uint32_t        cbLeft = cbMsg;
    736         uint8_t const  *pbLeft = (uint8_t const  *)pvMsg;
    737 #ifdef KBUILD_OS_WINDOWS
    738         DWORD           dwErr;
    739         DWORD           cbWritten;
    740         while (WriteFile(pWorker->hPipe, pbLeft, cbLeft, &cbWritten, NULL /*pOverlapped*/))
    741         {
    742             assert(cbWritten <= cbLeft);
    743             cbLeft -= cbWritten;
    744             if (!cbLeft)
    745                 return 0;
    746 
    747             /* This scenario shouldn't really ever happen. But just in case... */
    748             pbLeft += cbWritten;
    749         }
    750         dwErr = GetLastError();
    751         if (   (   dwErr != ERROR_BROKEN_PIPE
    752                 && dwErr != ERROR_NO_DATA)
    753             || cRetries <= 0)
    754             return errx(1, "Error writing to worker: %u", dwErr);
    755 #else
    756         ssize_t cbWritten
    757         while ((cbWritten = write(pWorker->fdSocket, pbLeft, cbLeft)) >= 0)
    758         {
    759             assert(cbWritten <= cbLeft);
    760             cbLeft -= cbWritten;
    761             if (!cbLeft)
    762                 return 0;
    763 
    764             pbLeft += cbWritten;
    765         }
    766         if (  (   errno != EPIPE
    767                && errno != ENOTCONN
    768                && errno != ECONNRESET))
    769             || cRetries <= 0)
    770             return err(1, "Error writing to worker");
    771 # error "later"
    772 #endif
    773 
    774         /*
    775          * Broken connection. Try respawn the worker.
    776          */
    777         if (cVerbosity > 0)
    778             fprintf(stderr,  "kSubmit: Respawning worker (#2)...\n");
    779         if (kSubmitRespawnWorker(pWorker, cVerbosity) != 0)
    780             return 2;
    781     }
    782 }
    783 
    784 
    785 /**
    786  * Closes the connection on a worker that said it is going to exit now.
    787  *
    788  * This is a way of dealing with imperfect resource management in the worker, it
    789  * will monitor it a little and trigger a respawn when it looks bad.
    790  *
    791  * This function just closes the pipe / socket connection to the worker.  The
    792  * kSubmitSendJobMessage function will see this a trigger a respawn the next
    793  * time the worker is engaged.  This will usually mean there's a little delay in
    794  * which the process can terminate without us having to actively wait for it.
    795  *
    796  * @param   pWorker             The worker instance.
    797  */
    798 static void kSubmitCloseConnectOnExitingWorker(PWORKERINSTANCE pWorker)
    799 {
    800 #ifdef KBUILD_OS_WINDOWS
    801     if (!CloseHandle(pWorker->hPipe))
    802         warnx("CloseHandle(pWorker->hPipe): %u", GetLastError());
    803     pWorker->hPipe = INVALID_HANDLE_VALUE;
    804 #else
    805     if (close(pWorker->fdSocket) != 0)
    806         warn("close(pWorker->fdSocket)");
    807     pWorker->fdSocket = -1;
    808 #endif
    809 }
    810 
    811 
    812 #ifdef KBUILD_OS_WINDOWS
    813 
    814 /**
    815  * Handles read failure.
    816  *
    817  * @returns Exit code.
    818  * @param   pWorker             The worker instance.
    819  * @param   dwErr               The error code.
    820  * @param   pszWhere            Where it failed.
    821  */
    822 static int kSubmitWinReadFailed(PWORKERINSTANCE pWorker, DWORD dwErr, const char *pszWhere)
    823 {
    824     DWORD dwExitCode;
    825 
    826     if (pWorker->cbResultRead == 0)
    827         errx(1, "%s/ReadFile failed: %u", pszWhere, dwErr);
    828     else
    829         errx(1, "%s/ReadFile failed: %u (read %u bytes)", pszWhere, dwErr, pWorker->cbResultRead);
    830     assert(dwErr != 0);
    831 
    832     /* Complete the result. */
    833     pWorker->Result.s.rcExit         = 127;
    834     pWorker->Result.s.bWorkerExiting = 1;
    835     pWorker->cbResultRead            = sizeof(pWorker->Result);
    836 
    837     if (GetExitCodeProcess(pWorker->hProcess, &dwExitCode))
    838     {
    839         if (dwExitCode != 0)
    840             pWorker->Result.s.rcExit = dwExitCode;
    841     }
    842 
    843     return dwErr != 0 ? (int)(dwErr & 0x7fffffff) : 0x7fffffff;
    844 
    845 }
    846 
    847 
    848 /**
    849  * Used by
    850  * @returns 0 if we got the whole result, -1 if I/O is pending, and windows last
    851  *          error on ReadFile failure.
    852  * @param   pWorker             The worker instance.
    853  */
    854 static int kSubmitReadMoreResultWin(PWORKERINSTANCE pWorker, const char *pszWhere)
    855 {
    856     /*
    857      * Set up the result read, telling the sub_proc.c unit about it.
    858      */
    859     while (pWorker->cbResultRead < sizeof(pWorker->Result))
    860     {
    861         DWORD cbRead = 0;
    862 
    863         BOOL fRc = ResetEvent(pWorker->OverlappedRead.hEvent);
    864         assert(fRc); (void)fRc;
    865 
    866         pWorker->OverlappedRead.Offset     = 0;
    867         pWorker->OverlappedRead.OffsetHigh = 0;
    868 
    869         if (!ReadFile(pWorker->hPipe, &pWorker->Result.ab[pWorker->cbResultRead],
    870                      sizeof(pWorker->Result) - pWorker->cbResultRead,
    871                      &cbRead,
    872                      &pWorker->OverlappedRead))
    873         {
    874             DWORD dwErr = GetLastError();
    875             if (dwErr == ERROR_IO_PENDING)
    876                 return -1;
    877             return kSubmitWinReadFailed(pWorker, dwErr, pszWhere);
    878         }
    879 
    880         pWorker->cbResultRead += cbRead;
    881         assert(pWorker->cbResultRead <= sizeof(pWorker->Result));
    882     }
    883     return 0;
    884 }
    885 
    886 #endif /* KBUILD_OS_WINDOWS */
    887 
    888 /**
    889  * Marks the worker active.
    890  *
    891  * On windows this involves setting up the async result read and telling
    892  * sub_proc.c about the process.
    893  *
    894  * @returns Exit code.
    895  * @param   pWorker             The worker instance to mark as active.
    896  * @param   cVerbosity          The verbosity level.
    897  * @param   pChild              The kmk child to associate the job with.
    898  * @param   pPidSpawned         If @a *pPidSpawned is non-zero if the child is
    899  *                              running, otherwise the worker is already done
    900  *                              and we've returned the exit code of the job.
    901  */
    902 static int kSubmitMarkActive(PWORKERINSTANCE pWorker, int cVerbosity, struct child *pChild, pid_t *pPidSpawned)
    903 {
    904 #ifdef KBUILD_OS_WINDOWS
    905     int rc;
    906 #endif
    907 
    908     pWorker->cbResultRead = 0;
    909 
    910 #ifdef KBUILD_OS_WINDOWS
    911     /*
    912      * Setup the async result read on windows.  If we're slow and the worker
    913      * very fast, this may actually get the result immediately.
    914      */
    915 l_again:
    916     rc = kSubmitReadMoreResultWin(pWorker, "kSubmitMarkActive");
    917     if (rc == -1)
    918     {
    919         if (process_kmk_register_submit(pWorker->OverlappedRead.hEvent, (intptr_t)pWorker, pPidSpawned) == 0)
    920         { /* likely */ }
    921         else
    922         {
    923             /* We need to do the waiting here because sub_proc.c has too much to do. */
    924             warnx("Too many processes for sub_proc.c to handle!");
    925             WaitForSingleObject(pWorker->OverlappedRead.hEvent, INFINITE);
    926             goto l_again;
    927         }
    928     }
    929     else
    930     {
    931         assert(rc == 0 || pWorker->Result.s.rcExit != 0);
    932         if (pWorker->Result.s.bWorkerExiting)
    933             kSubmitCloseConnectOnExitingWorker(pWorker);
    934         *pPidSpawned = 0;
    935         return pWorker->Result.s.rcExit;
    936     }
    937 #endif
    938 
    939     /*
    940      * Mark it busy and move it to the active instance.
    941      */
    942     pWorker->pBusyWith = pChild;
    943 #ifndef KBUILD_OS_WINDOWS
    944     *pPidSpawned = pWorker->pid;
    945 #endif
    946 
    947     kSubmitListUnlink(&g_IdleList, pWorker);
    948     kSubmitListAppend(&g_BusyList, pWorker);
    949     return 0;
    950 }
    951 
    952 
    953 #ifdef KBUILD_OS_WINDOWS
    954 
    955 /**
    956  * Retrieve the worker child result.
    957  *
    958  * If incomplete, we restart the ReadFile operation like kSubmitMarkActive does.
    959  *
    960  * @returns 0 on success, -1 if ReadFile was restarted.
    961  * @param   pvUser              The worker instance.
    962  * @param   prcExit             Where to return the exit code.
    963  * @param   piSigNo             Where to return the signal number.
    964  */
    965 int kSubmitSubProcGetResult(intptr_t pvUser, int *prcExit, int *piSigNo)
    966 {
    967     PWORKERINSTANCE pWorker = (PWORKERINSTANCE)pvUser;
    968 
    969     /*
    970      * Get the overlapped result.  There should be one since we're here
    971      * because of a satisfied WaitForMultipleObject.
    972      */
    973     DWORD cbRead = 0;
    974     if (GetOverlappedResult(pWorker->hPipe, &pWorker->OverlappedRead, &cbRead, TRUE))
    975     {
    976         pWorker->cbResultRead += cbRead;
    977         assert(pWorker->cbResultRead <= sizeof(pWorker->Result));
    978 
    979         /* More to be read? */
    980         while (pWorker->cbResultRead < sizeof(pWorker->Result))
    981         {
    982             int rc = kSubmitReadMoreResultWin(pWorker, "kSubmitSubProcGetResult/more");
    983             if (rc == -1)
    984                 return -1;
    985             assert(rc == 0 || pWorker->Result.s.rcExit != 0);
    986         }
    987         assert(pWorker->cbResultRead == sizeof(pWorker->Result));
    988     }
    989     else
    990     {
    991         DWORD dwErr = GetLastError();
    992         kSubmitWinReadFailed(pWorker, dwErr, "kSubmitSubProcGetResult/result");
    993     }
    994 
    995     /*
    996      * Okay, we've got a result.
    997      */
    998     *prcExit = pWorker->Result.s.rcExit;
    999     switch (pWorker->Result.s.rcExit)
    1000     {
    1001         default:                                *piSigNo = 0; break;
    1002         case CONTROL_C_EXIT:                    *piSigNo = SIGINT; break;
    1003         case STATUS_INTEGER_DIVIDE_BY_ZERO:     *piSigNo = SIGFPE; break;
    1004         case STATUS_ACCESS_VIOLATION:           *piSigNo = SIGSEGV; break;
    1005         case STATUS_PRIVILEGED_INSTRUCTION:
    1006         case STATUS_ILLEGAL_INSTRUCTION:        *piSigNo = SIGILL; break;
    1007     }
    1008     if (pWorker->Result.s.bWorkerExiting)
    1009         kSubmitCloseConnectOnExitingWorker(pWorker);
    1010 
    1011     return 0;
    1012 }
    1013 
    1014 
    1015 int kSubmitSubProcKill(intptr_t pvUser, int iSignal)
    1016 {
    1017     return -1;
    1018 }
    1019 
    1020 
    1021 /**
    1022  * Called by process_cleanup when it's done with the worker.
    1023  *
    1024  * @param   pvUser              The worker instance.
    1025  */
    1026 void kSubmitSubProcCleanup(intptr_t pvUser)
    1027 {
    1028     PWORKERINSTANCE pWorker = (PWORKERINSTANCE)pvUser;
    1029     kSubmitListUnlink(&g_BusyList, pWorker);
    1030     kSubmitListAppend(&g_IdleList, pWorker);
    1031 }
    1032 
    1033 #endif /* KBUILD_OS_WINDOWS */
    1034 
    1035 
    1036 /**
    1037  * atexit callback that trigger worker termination.
    1038  */
    1039 static void kSubmitAtExitCallback(void)
    1040 {
    1041     PWORKERINSTANCE pWorker;
    1042     DWORD           msStartTick;
    1043     DWORD           cKillRaids = 0;
    1044 
    1045     /*
    1046      * Tell all the workers to exit by breaking the connection.
    1047      */
    1048     for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1049         kSubmitCloseConnectOnExitingWorker(pWorker);
    1050     for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1051         kSubmitCloseConnectOnExitingWorker(pWorker);
    1052 
    1053     /*
    1054      * Wait a little while for them to stop.
    1055      */
    1056     Sleep(0);
    1057     msStartTick = GetTickCount();
    1058     for (;;)
    1059     {
    1060         /*
    1061          * Collect handles of running processes.
    1062          */
    1063         PWORKERINSTANCE apWorkers[MAXIMUM_WAIT_OBJECTS];
    1064         HANDLE          ahHandles[MAXIMUM_WAIT_OBJECTS];
    1065         DWORD           cHandles = 0;
    1066 
    1067         for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1068             if (pWorker->hProcess != INVALID_HANDLE_VALUE)
    1069             {
    1070                 if (cHandles < MAXIMUM_WAIT_OBJECTS)
    1071                 {
    1072                     apWorkers[cHandles] = pWorker;
    1073                     ahHandles[cHandles] = pWorker->hProcess;
    1074                 }
    1075                 cHandles++;
    1076             }
    1077         for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1078             if (pWorker->hProcess != INVALID_HANDLE_VALUE)
    1079             {
    1080                 if (cHandles < MAXIMUM_WAIT_OBJECTS)
    1081                 {
    1082                     apWorkers[cHandles] = pWorker;
    1083                     ahHandles[cHandles] = pWorker->hProcess;
    1084                 }
    1085                 cHandles++;
    1086             }
    1087         if (cHandles == 0)
    1088             return;
    1089 
    1090         /*
    1091          * Wait for the processes.
    1092          */
    1093         for (;;)
    1094         {
    1095             DWORD cMsElapsed = GetTickCount() - msStartTick;
    1096             DWORD dwWait = WaitForMultipleObjects(cHandles <= MAXIMUM_WAIT_OBJECTS ? cHandles : MAXIMUM_WAIT_OBJECTS,
    1097                                                   ahHandles, FALSE /*bWaitAll*/,
    1098                                                   cMsElapsed < 1000 ? 1000 - cMsElapsed + 16 : 16);
    1099             if (   dwWait >= WAIT_OBJECT_0
    1100                 && dwWait <= WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)
    1101             {
    1102                 size_t idx = dwWait - WAIT_OBJECT_0;
    1103                 CloseHandle(apWorkers[idx]->hProcess);
    1104                 apWorkers[idx]->hProcess = INVALID_HANDLE_VALUE;
    1105 
    1106                 if (cHandles <= MAXIMUM_WAIT_OBJECTS)
    1107                 {
    1108                     /* Restart the wait with the worker removed, or quit if it was the last worker. */
    1109                     cHandles--;
    1110                     if (!cHandles)
    1111                         return;
    1112                     if (idx != cHandles)
    1113                     {
    1114                         apWorkers[idx] = apWorkers[cHandles];
    1115                         ahHandles[idx] = ahHandles[cHandles];
    1116                     }
    1117                     continue;
    1118                 }
    1119                 /* else: Reconstruct the wait array so we get maximum coverage. */
    1120             }
    1121             else if (dwWait == WAIT_TIMEOUT)
    1122             {
    1123                 /* Terminate the whole bunch. */
    1124                 cKillRaids++;
    1125                 if (cKillRaids <= 2)
    1126                 {
    1127                     fprintf(stderr, "kmk/kSubmit: Killing %u lingering worker processe(s)!\n", cHandles);
    1128                     for (pWorker = g_IdleList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1129                         if (pWorker->hProcess != INVALID_HANDLE_VALUE)
    1130                             TerminateProcess(pWorker->hProcess, WAIT_TIMEOUT);
    1131                     for (pWorker = g_BusyList.pHead; pWorker != NULL; pWorker = pWorker->pNext)
    1132                         if (pWorker->hProcess != INVALID_HANDLE_VALUE)
    1133                             TerminateProcess(pWorker->hProcess, WAIT_TIMEOUT);
    1134                 }
    1135                 else
    1136                 {
    1137                     fprintf(stderr, "kmk/kSubmit: Giving up on the last %u worker processe(s). :-(\n", cHandles);
    1138                     break;
    1139                 }
    1140             }
    1141             else
    1142             {
    1143                 /* Some kind of wait error.  Could be a bad handle, check each and remove
    1144                    bad ones as well as completed ones. */
    1145                 size_t idx;
    1146                 fprintf(stderr, "kmk/kSubmit: WaitForMultipleObjects unexpectedly returned %#u (err=%u)\n",
    1147                         dwWait, GetLastError());
    1148                 for (idx = 0; idx < cHandles; idx++)
    1149                 {
    1150                     dwWait = WaitForSingleObject(ahHandles[idx], 0 /*ms*/);
    1151                     if (dwWait != WAIT_TIMEOUT)
    1152                     {
    1153                         CloseHandle(apWorkers[idx]->hProcess);
    1154                         apWorkers[idx]->hProcess = INVALID_HANDLE_VALUE;
    1155                     }
    1156                 }
    1157             }
    1158             break;
    1159         } /* wait loop */
    1160     } /* outer wait loop */
    1161 }
    116236
    116337
     
    118357 * @param   pszValue            The var=value string to apply.
    118458 */
    1185 static int kSubmitOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
    1186                             int cVerbosity, const char *pszValue)
     59int kBuiltinOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars, int cVerbosity, const char *pszValue)
    118760{
    118861    const char *pszEqual = strchr(pszValue, '=');
     
    120073            {
    120174                if (cVerbosity > 0)
    1202                     fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue);
     75                    warnx("replacing '%s' with '%s'", papszEnv[iEnvVar], pszValue);
    120376                free(papszEnv[iEnvVar]);
    1204                 papszEnv[iEnvVar] = xstrdup(pszValue);
     77                papszEnv[iEnvVar] = strdup(pszValue);
     78                if (!papszEnv[iEnvVar])
     79                    return errx(1, "out of memory!");
    120580                break;
    120681            }
     
    121287            {
    121388                *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
    1214                 *ppapszEnv = papszEnv = (char **)xrealloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
    1215             }
    1216             papszEnv[cEnvVars++] = xstrdup(pszValue);
    1217             papszEnv[cEnvVars]   = NULL;
     89                papszEnv = (char **)realloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
     90                if (!papszEnv)
     91                    return errx(1, "out of memory!");
     92                *ppapszEnv = papszEnv;
     93            }
     94            papszEnv[cEnvVars] = strdup(pszValue);
     95            if (!papszEnv[cEnvVars])
     96                return errx(1, "out of memory!");
     97            papszEnv[++cEnvVars]   = NULL;
    121898            *pcEnvVars = cEnvVars;
    121999            if (cVerbosity > 0)
    1220                 fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]);
     100                warnx("added '%s'", papszEnv[iEnvVar]);
    1221101        }
    1222102        else
     
    1228108                {
    1229109                    if (cVerbosity > 0)
    1230                         fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
     110                        warnx("removing duplicate '%s'", papszEnv[iEnvVar]);
    1231111                    free(papszEnv[iEnvVar]);
    1232112                    cEnvVars--;
     
    1255135 * @param   pszVarToRemove      The name of the variable to remove.
    1256136 */
    1257 static int kSubmitOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)
     137int kBuiltinOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)
    1258138{
    1259139    if (strchr(pszVarToRemove, '=') == NULL)
     
    1269149            {
    1270150                if (cVerbosity > 0)
    1271                     fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n"
    1272                             : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
     151                    warnx(!cRemoved ? "removing '%s'" : "removing duplicate '%s'", papszEnv[iEnvVar]);
    1273152                free(papszEnv[iEnvVar]);
    1274153                cEnvVars--;
     
    1282161
    1283162        if (cVerbosity > 0 && !cRemoved)
    1284             fprintf(stderr, "kSubmit: not found '%s'\n", pszVarToRemove);
     163            warnx("not found '%s'", pszVarToRemove);
    1285164    }
    1286165    else
     
    1300179 * @param   pszValue            The --chdir value to apply.
    1301180 */
    1302 static int kSubmitOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)
     181int kBuiltinOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)
    1303182{
    1304183    size_t cchNewCwd = strlen(pszValue);
     
    1360239}
    1361240
    1362 
    1363 static int usage(FILE *pOut,  const char *argv0)
    1364 {
    1365     fprintf(pOut,
    1366             "usage: %s [-Z|--zap-env] [-E|--set <var=val>] [-U|--unset <var=val>]\n"
    1367             "           [-C|--chdir <dir>] [--wcc-brain-damage]\n"
    1368             "           [-3|--32-bit] [-6|--64-bit] [-v]\n"
    1369             "           [-P|--post-cmd <cmd> [args]] -- <program> [args]\n"
    1370             "   or: %s --help\n"
    1371             "   or: %s --version\n"
    1372             "\n"
    1373             "Options:\n"
    1374             "  -Z, --zap-env, -i, --ignore-environment\n"
    1375             "    Zaps the environment. Position dependent.\n"
    1376             "  -E, --set <var>=[value]\n"
    1377             "    Sets an enviornment variable putenv fashion. Position dependent.\n"
    1378             "  -U, --unset <var>\n"
    1379             "    Removes an environment variable. Position dependent.\n"
    1380             "  -C, --chdir <dir>\n"
    1381             "    Specifies the current directory for the program.  Relative paths\n"
    1382             "    are relative to the previous -C option.  Default is getcwd value.\n"
    1383             "  -3, --32-bit\n"
    1384             "    Selects a 32-bit kWorker process. Default: kmk bit count\n"
    1385             "  -6, --64-bit\n"
    1386             "    Selects a 64-bit kWorker process. Default: kmk bit count\n"
    1387             "  --wcc-brain-damage\n"
    1388             "    Works around wcc and wcc386 (Open Watcom) not following normal\n"
    1389             "    quoting conventions on Windows, OS/2, and DOS.\n"
    1390             "  -v,--verbose\n"
    1391             "    More verbose execution.\n"
    1392             "  -P|--post-cmd <cmd> ...\n"
    1393             "    For running a built-in command on the output, specifying the command\n"
    1394             "    and all it's parameters.  Currently supported commands:\n"
    1395             "        kDepObj\n"
    1396             "  -V,--version\n"
    1397             "    Show the version number.\n"
    1398             "  -h,--help\n"
    1399             "    Show this usage information.\n"
    1400             "\n"
    1401             ,
    1402             argv0, argv0, argv0);
    1403     return 1;
    1404 }
    1405 
    1406 
    1407 int kmk_builtin_kSubmit(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned)
    1408 {
    1409     int             rcExit = 0;
    1410     int             iArg;
    1411     unsigned        cAllocatedEnvVars;
    1412     unsigned        iEnvVar;
    1413     unsigned        cEnvVars;
    1414     char          **papszEnv            = NULL;
    1415     const char     *pszExecutable       = NULL;
    1416     const char     *pszCwd              = NULL;
    1417     int             iPostCmd            = argc;
    1418     int             cPostCmdArgs        = 0;
    1419     unsigned        cBitsWorker         = g_cArchBits;
    1420     int             fWatcomBrainDamage  = 0;
    1421     int             cVerbosity          = 0;
    1422     size_t const    cbCwdBuf            = GET_PATH_MAX;
    1423     PATH_VAR(szCwd);
    1424 
    1425     g_progname = argv[0];
    1426 
    1427     /*
    1428      * Create default program environment.
    1429      */
    1430     if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
    1431     { /* likely */ }
    1432     else
    1433         return err(1, "getcwd_fs failed\n");
    1434 
    1435     papszEnv = pChild->environment;
    1436     if (!papszEnv)
    1437         pChild->environment = papszEnv = target_environment(pChild->file);
    1438     cEnvVars = 0;
    1439     while (papszEnv[cEnvVars] != NULL)
    1440         cEnvVars++;
    1441     cAllocatedEnvVars = cEnvVars;
    1442 
    1443     /*
    1444      * Parse the command line.
    1445      */
    1446     for (iArg = 1; iArg < argc; iArg++)
    1447     {
    1448         const char *pszArg = argv[iArg];
    1449         if (*pszArg == '-')
    1450         {
    1451             char chOpt = *++pszArg;
    1452             pszArg++;
    1453             if (chOpt != '-')
    1454             {
    1455                 if (chOpt != '\0')
    1456                 { /* likely */ }
    1457                 else
    1458                 {
    1459                     errx(1, "Incomplete option: '-'");
    1460                     return usage(stderr, argv[0]);
    1461                 }
    1462             }
    1463             else
    1464             {
    1465                 /* '--' indicates where the bits to execute start. */
    1466                 if (*pszArg == '\0')
    1467                 {
    1468                     iArg++;
    1469                     break;
    1470                 }
    1471 
    1472                 if (   strcmp(pszArg, "wcc-brain-damage") == 0
    1473                     || strcmp(pszArg, "watcom-brain-damage") == 0)
    1474                 {
    1475                     fWatcomBrainDamage = 1;
    1476                     continue;
    1477                 }
    1478 
    1479                 /* convert to short. */
    1480                 if (strcmp(pszArg, "help") == 0)
    1481                     chOpt = 'h';
    1482                 else if (strcmp(pszArg, "version") == 0)
    1483                     chOpt = 'V';
    1484                 else if (strcmp(pszArg, "set") == 0)
    1485                     chOpt = 'E';
    1486                 else if (strcmp(pszArg, "unset") == 0)
    1487                     chOpt = 'U';
    1488                 else if (   strcmp(pszArg, "zap-env") == 0
    1489                          || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
    1490                     chOpt = 'Z';
    1491                 else if (strcmp(pszArg, "chdir") == 0)
    1492                     chOpt = 'C';
    1493                 else if (strcmp(pszArg, "post-cmd") == 0)
    1494                     chOpt = 'P';
    1495                 else if (strcmp(pszArg, "32-bit") == 0)
    1496                     chOpt = '3';
    1497                 else if (strcmp(pszArg, "64-bit") == 0)
    1498                     chOpt = '6';
    1499                 else if (strcmp(pszArg, "verbose") == 0)
    1500                     chOpt = 'v';
    1501                 else if (strcmp(pszArg, "executable") == 0)
    1502                     chOpt = 'e';
    1503                 else
    1504                 {
    1505                     errx(1, "Unknown option: '%s'", pszArg - 2);
    1506                     return usage(stderr, argv[0]);
    1507                 }
    1508                 pszArg = "";
    1509             }
    1510 
    1511             do
    1512             {
    1513                 /* Get option value first, if the option takes one. */
    1514                 const char *pszValue = NULL;
    1515                 switch (chOpt)
    1516                 {
    1517                     case 'E':
    1518                     case 'U':
    1519                     case 'C':
    1520                     case 'e':
    1521                         if (*pszArg != '\0')
    1522                             pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
    1523                         else if (++iArg < argc)
    1524                             pszValue = argv[iArg];
    1525                         else
    1526                         {
    1527                             errx(1, "Option -%c requires a value!", chOpt);
    1528                             return usage(stderr, argv[0]);
    1529                         }
    1530                         break;
    1531                 }
    1532 
    1533                 switch (chOpt)
    1534                 {
    1535                     case 'Z':
    1536                     case 'i': /* GNU env compatibility. */
    1537                         for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
    1538                             free(papszEnv[iEnvVar]);
    1539                         papszEnv[0] = NULL;
    1540                         cEnvVars = 0;
    1541                         break;
    1542 
    1543                     case 'E':
    1544                         rcExit = kSubmitOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
    1545                         pChild->environment = papszEnv;
    1546                         if (rcExit == 0)
    1547                             break;
    1548                         return rcExit;
    1549 
    1550                     case 'U':
    1551                         rcExit = kSubmitOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);
    1552                         if (rcExit == 0)
    1553                             break;
    1554                         return rcExit;
    1555 
    1556                     case 'C':
    1557                         rcExit = kSubmitOptChDir(szCwd, cbCwdBuf, pszValue);
    1558                         if (rcExit == 0)
    1559                             break;
    1560                         return rcExit;
    1561 
    1562                     case 'P':
    1563                         if (cPostCmdArgs > 0)
    1564                             return errx(1, "The -P option can only be used once!");
    1565                         if (*pszArg != '\0')
    1566                             return errx(1, "The cmd part of the -P needs to be a separate argument!");
    1567                         iPostCmd = ++iArg;
    1568                         if (iArg >= argc)
    1569                             return errx(1, "The -P option requires a command following it!");
    1570                         while (iArg < argc && strcmp(argv[iArg], "--") != 0)
    1571                             iArg++;
    1572                         cPostCmdArgs = iArg - iPostCmd;
    1573                         iArg--;
    1574                         break;
    1575 
    1576                     case '3':
    1577                         cBitsWorker = 32;
    1578                         break;
    1579 
    1580                     case '6':
    1581                         cBitsWorker = 64;
    1582                         break;
    1583 
    1584                     case 'e':
    1585                         pszExecutable = pszValue;
    1586                         break;
    1587 
    1588                     case 'v':
    1589                         cVerbosity++;
    1590                         break;
    1591 
    1592                     case 'h':
    1593                         usage(stdout, argv[0]);
    1594                         return 0;
    1595 
    1596                     case 'V':
    1597                         return kbuild_version(argv[0]);
    1598                 }
    1599             } while ((chOpt = *pszArg++) != '\0');
    1600         }
    1601         else
    1602         {
    1603             errx(1, "Unknown argument: '%s'", pszArg);
    1604             return usage(stderr, argv[0]);
    1605         }
    1606     }
    1607 
    1608     /*
    1609      * Check that we've got something to execute.
    1610      */
    1611     if (iArg < argc)
    1612     {
    1613         uint32_t        cbMsg;
    1614         void           *pvMsg   = kSubmitComposeJobMessage(pszExecutable, &argv[iArg], papszEnv, szCwd,
    1615                                                            fWatcomBrainDamage, &argv[iPostCmd], cPostCmdArgs, &cbMsg);
    1616         PWORKERINSTANCE pWorker = kSubmitSelectWorkSpawnNewIfNecessary(cBitsWorker, cVerbosity);
    1617         if (pWorker)
    1618         {
    1619             if (!pszExecutable)
    1620                 pszExecutable = argv[iArg];
    1621 
    1622             rcExit = kSubmitSendJobMessage(pWorker, pvMsg, cbMsg, 0 /*fNoRespawning*/, cVerbosity);
    1623             if (rcExit == 0)
    1624                 rcExit = kSubmitMarkActive(pWorker, cVerbosity, pChild, pPidSpawned);
    1625 
    1626             if (!g_fAtExitRegistered)
    1627                 if (atexit(kSubmitAtExitCallback) == 0)
    1628                     g_fAtExitRegistered = 1;
    1629         }
    1630         else
    1631             rcExit = 1;
    1632         free(pvMsg);
    1633     }
    1634     else
    1635     {
    1636         errx(1, "Nothing to executed!");
    1637         rcExit = usage(stderr, argv[0]);
    1638     }
    1639 
    1640     return rcExit;
    1641 }
    1642 
    1643 
    1644 
  • trunk/src/kmk/kmkbuiltin/kSubmit.c

    r2894 r2912  
    11621162
    11631163
    1164 /** The environment variable compare function.
    1165  * We must use case insensitive compare on windows (Path vs PATH).  */
    1166 #ifdef KBUILD_OS_WINDOWS
    1167 # define KSUBMIT_ENV_NCMP   _strnicmp
    1168 #else
    1169 # define KSUBMIT_ENV_NCMP   strncmp
    1170 #endif
    1171 
    1172 
    1173 /**
    1174  * Handles the --set var=value option.
    1175  *
    1176  * @returns 0 on success, non-zero exit code on error.
    1177  * @param   papszEnv            The environment vector.
    1178  * @param   pcEnvVars           Pointer to the variable holding the number of
    1179  *                              environment variables held by @a papszEnv.
    1180  * @param   pcAllocatedEnvVars  Pointer to the variable holding max size of the
    1181  *                              environment vector.
    1182  * @param   cVerbosity          The verbosity level.
    1183  * @param   pszValue            The var=value string to apply.
    1184  */
    1185 static int kSubmitOptEnvSet(char ***ppapszEnv, unsigned *pcEnvVars, unsigned *pcAllocatedEnvVars,
    1186                             int cVerbosity, const char *pszValue)
    1187 {
    1188     const char *pszEqual = strchr(pszValue, '=');
    1189     if (pszEqual)
    1190     {
    1191         char   **papszEnv = *ppapszEnv;
    1192         unsigned iEnvVar;
    1193         unsigned cEnvVars = *pcEnvVars;
    1194         size_t const cchVar = pszEqual - pszValue;
    1195         for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
    1196         {
    1197             char *pszCur = papszEnv[iEnvVar];
    1198             if (   KSUBMIT_ENV_NCMP(pszCur, pszValue, cchVar) == 0
    1199                 && pszCur[cchVar] == '=')
    1200             {
    1201                 if (cVerbosity > 0)
    1202                     fprintf(stderr, "kSubmit: replacing '%s' with '%s'\n", papszEnv[iEnvVar], pszValue);
    1203                 free(papszEnv[iEnvVar]);
    1204                 papszEnv[iEnvVar] = xstrdup(pszValue);
    1205                 break;
    1206             }
    1207         }
    1208         if (iEnvVar == cEnvVars)
    1209         {
    1210             /* Append new variable. We probably need to resize the vector. */
    1211             if ((cEnvVars + 2) > *pcAllocatedEnvVars)
    1212             {
    1213                 *pcAllocatedEnvVars = (cEnvVars + 2 + 0xf) & ~(unsigned)0xf;
    1214                 *ppapszEnv = papszEnv = (char **)xrealloc(papszEnv, *pcAllocatedEnvVars * sizeof(papszEnv[0]));
    1215             }
    1216             papszEnv[cEnvVars++] = xstrdup(pszValue);
    1217             papszEnv[cEnvVars]   = NULL;
    1218             *pcEnvVars = cEnvVars;
    1219             if (cVerbosity > 0)
    1220                 fprintf(stderr, "kSubmit: added '%s'\n", papszEnv[iEnvVar]);
    1221         }
    1222         else
    1223         {
    1224             /* Check for duplicates. */
    1225             for (iEnvVar++; iEnvVar < cEnvVars; iEnvVar++)
    1226                 if (   KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszValue, cchVar) == 0
    1227                     && papszEnv[iEnvVar][cchVar] == '=')
    1228                 {
    1229                     if (cVerbosity > 0)
    1230                         fprintf(stderr, "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
    1231                     free(papszEnv[iEnvVar]);
    1232                     cEnvVars--;
    1233                     if (iEnvVar != cEnvVars)
    1234                         papszEnv[iEnvVar] = papszEnv[cEnvVars];
    1235                     papszEnv[cEnvVars] = NULL;
    1236                     iEnvVar--;
    1237                 }
    1238         }
    1239     }
    1240     else
    1241         return errx(1, "Missing '=': -E %s", pszValue);
    1242 
    1243     return 0;
    1244 }
    1245 
    1246 
    1247 /**
    1248  * Handles the --unset var option.
    1249  *
    1250  * @returns 0 on success, non-zero exit code on error.
    1251  * @param   papszEnv            The environment vector.
    1252  * @param   pcEnvVars           Pointer to the variable holding the number of
    1253  *                              environment variables held by @a papszEnv.
    1254  * @param   cVerbosity          The verbosity level.
    1255  * @param   pszVarToRemove      The name of the variable to remove.
    1256  */
    1257 static int kSubmitOptEnvUnset(char **papszEnv, unsigned *pcEnvVars, int cVerbosity, const char *pszVarToRemove)
    1258 {
    1259     if (strchr(pszVarToRemove, '=') == NULL)
    1260     {
    1261         unsigned     cRemoved = 0;
    1262         size_t const cchVar   = strlen(pszVarToRemove);
    1263         unsigned     cEnvVars = *pcEnvVars;
    1264         unsigned     iEnvVar;
    1265 
    1266         for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
    1267             if (   KSUBMIT_ENV_NCMP(papszEnv[iEnvVar], pszVarToRemove, cchVar) == 0
    1268                 && papszEnv[iEnvVar][cchVar] == '=')
    1269             {
    1270                 if (cVerbosity > 0)
    1271                     fprintf(stderr, !cRemoved ? "kSubmit: removing '%s'\n"
    1272                             : "kSubmit: removing duplicate '%s'\n", papszEnv[iEnvVar]);
    1273                 free(papszEnv[iEnvVar]);
    1274                 cEnvVars--;
    1275                 if (iEnvVar != cEnvVars)
    1276                     papszEnv[iEnvVar] = papszEnv[cEnvVars];
    1277                 papszEnv[cEnvVars] = NULL;
    1278                 cRemoved++;
    1279                 iEnvVar--;
    1280             }
    1281         *pcEnvVars = cEnvVars;
    1282 
    1283         if (cVerbosity > 0 && !cRemoved)
    1284             fprintf(stderr, "kSubmit: not found '%s'\n", pszVarToRemove);
    1285     }
    1286     else
    1287         return errx(1, "Found invalid variable name character '=' in: -U %s", pszVarToRemove);
    1288     return 0;
    1289 }
    1290 
    1291 
    1292 
    1293 /**
    1294  * Handles the --chdir dir option.
    1295  *
    1296  * @returns 0 on success, non-zero exit code on error.
    1297  * @param   pszCwd              The CWD buffer.  Contains current CWD on input,
    1298  *                              modified by @a pszValue on output.
    1299  * @param   cbCwdBuf            The size of the CWD buffer.
    1300  * @param   pszValue            The --chdir value to apply.
    1301  */
    1302 static int kSubmitOptChDir(char *pszCwd, size_t cbCwdBuf, const char *pszValue)
    1303 {
    1304     size_t cchNewCwd = strlen(pszValue);
    1305     size_t offDst;
    1306     if (cchNewCwd)
    1307     {
    1308 #ifdef HAVE_DOS_PATHS
    1309         if (*pszValue == '/' || *pszValue == '\\')
    1310         {
    1311             if (pszValue[1] == '/' || pszValue[1] == '\\')
    1312                 offDst = 0; /* UNC */
    1313             else if (pszCwd[1] == ':' && isalpha(pszCwd[0]))
    1314                 offDst = 2; /* Take drive letter from CWD. */
    1315             else
    1316                 return errx(1, "UNC relative CWD not implemented: cur='%s' new='%s'", pszCwd, pszValue);
    1317         }
    1318         else if (   pszValue[1] == ':'
    1319                  && isalpha(pszValue[0]))
    1320         {
    1321             if (pszValue[2] == '/'|| pszValue[2] == '\\')
    1322                 offDst = 0; /* DOS style absolute path. */
    1323             else if (   pszCwd[1] == ':'
    1324                      && tolower(pszCwd[0]) == tolower(pszValue[0]) )
    1325             {
    1326                 pszValue += 2; /* Same drive as CWD, append drive relative path from value. */
    1327                 cchNewCwd -= 2;
    1328                 offDst = strlen(pszCwd);
    1329             }
    1330             else
    1331             {
    1332                 /* Get current CWD on the specified drive and append value. */
    1333                 int iDrive = tolower(pszValue[0]) - 'a' + 1;
    1334                 if (!_getdcwd(iDrive, pszCwd, cbCwdBuf))
    1335                     return err(1, "_getdcwd(%d,,) failed", iDrive);
    1336                 pszValue += 2;
    1337                 cchNewCwd -= 2;
    1338             }
    1339         }
    1340 #else
    1341         if (*pszValue == '/')
    1342             offDst = 0;
    1343 #endif
    1344         else
    1345             offDst = strlen(pszCwd); /* Relative path, append to the existing CWD value. */
    1346 
    1347         /* Do the copying. */
    1348 #ifdef HAVE_DOS_PATHS
    1349         if (offDst > 0 && pszCwd[offDst - 1] != '/' && pszCwd[offDst - 1] != '\\')
    1350 #else
    1351         if (offDst > 0 && pszCwd[offDst - 1] != '/')
    1352 #endif
    1353              pszCwd[offDst++] = '/';
    1354         if (offDst + cchNewCwd >= cbCwdBuf)
    1355             return errx(1, "Too long CWD: %*.*s%s", offDst, offDst, pszCwd, pszValue);
    1356         memcpy(&pszCwd[offDst], pszValue, cchNewCwd + 1);
    1357     }
    1358     /* else: relative, no change - quitely ignore. */
    1359     return 0;
    1360 }
    1361 
    1362 
    13631164static int usage(FILE *pOut,  const char *argv0)
    13641165{
     
    14011202            ,
    14021203            argv0, argv0, argv0);
    1403     return 1;
     1204    return 2;
    14041205}
    14051206
     
    14141215    char          **papszEnv            = NULL;
    14151216    const char     *pszExecutable       = NULL;
    1416     const char     *pszCwd              = NULL;
    14171217    int             iPostCmd            = argc;
    14181218    int             cPostCmdArgs        = 0;
     
    15031303                else
    15041304                {
    1505                     errx(1, "Unknown option: '%s'", pszArg - 2);
     1305                    errx(2, "Unknown option: '%s'", pszArg - 2);
    15061306                    return usage(stderr, argv[0]);
    15071307                }
     
    15421342
    15431343                    case 'E':
    1544                         rcExit = kSubmitOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
     1344                        rcExit = kBuiltinOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
    15451345                        pChild->environment = papszEnv;
    15461346                        if (rcExit == 0)
     
    15491349
    15501350                    case 'U':
    1551                         rcExit = kSubmitOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);
     1351                        rcExit = kBuiltinOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);
    15521352                        if (rcExit == 0)
    15531353                            break;
     
    15551355
    15561356                    case 'C':
    1557                         rcExit = kSubmitOptChDir(szCwd, cbCwdBuf, pszValue);
     1357                        rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue);
    15581358                        if (rcExit == 0)
    15591359                            break;
  • trunk/src/kmk/kmkbuiltin/redirect.c

    r2839 r2912  
    3030# define _POSIX_C_SOURCE 1 /* 10.4 sdk and unsetenv */
    3131#endif
    32 #include "config.h"
     32#include "make.h"
     33#include <assert.h>
    3334#include <stdio.h>
    3435#include <stdlib.h>
     
    3637#include <errno.h>
    3738#include <fcntl.h>
     39#if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
     40# include <process.h>
     41#endif
    3842#if defined(_MSC_VER)
    3943# include <ctype.h>
    4044# include <io.h>
    41 # include <direct.h>
    42 # include <process.h>
    4345# include "quote_argv.h"
    4446#else
    4547# include <unistd.h>
     48# include <spawn.h>
     49#endif
     50
     51#include <k/kDefs.h>
     52#include <k/kTypes.h>
     53#include "err.h"
     54#include "kbuild_version.h"
     55#include "kmkbuiltin.h"
     56#ifdef KMK
     57# ifdef KBUILD_OS_WINDOWS
     58#  include "sub_proc.h"
     59#  include "pathstuff.h"
     60# endif
     61# include "job.h"
     62# include "variable.h"
    4663#endif
    4764
     
    5471#endif
    5572
    56 
    57 /*********************************************************************************************************************************
    58 *   Global Variables                                                                                                             *
    59 *********************************************************************************************************************************/
    60 /** Number of times the '-v' switch was seen. */
    61 static unsigned g_cVerbosity = 0;
     73#if !defined(KBUILD_OS_WINDOWS) && !defined(KBUILD_OS_OS2)
     74# define USE_POSIX_SPAWN
     75#endif
     76
     77
     78/* String + strlen tuple. */
     79#define TUPLE(a_sz)     a_sz, sizeof(a_sz) - 1
    6280
    6381
     
    107125static int usage(FILE *pOut,  const char *argv0)
    108126{
     127    argv0 = name(argv0);
    109128    fprintf(pOut,
    110             "usage: %s [-[rwa+tb]<fd> <file>] [-c<fd>] [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage] [-v] -- <program> [args]\n"
     129            "usage: %s [-[rwa+tb]<fd> <file>] [-d<fd>=<src-fd>] [-c<fd>]\n"
     130            "           [-Z] [-E <var=val>] [-C <dir>] [--wcc-brain-damage]\n"
     131            "           [-v] -- <program> [args]\n"
    111132            "   or: %s --help\n"
    112133            "   or: %s --version\n"
     
    117138            "   o = stdout\n"
    118139            "   e = stderr\n"
     140            "\n"
     141            "The -d switch duplicate the right hand file descriptor (src-fd) to the left\n"
     142            "hand side one (fd).\n"
    119143            "\n"
    120144            "The -c switch will close the specified file descriptor.\n"
     
    139163            ,
    140164            argv0, argv0, argv0);
    141     return 1;
     165    return 2;
    142166}
    143167
    144168
     169/**
     170 * Decoded file descriptor operations.
     171 */
     172typedef struct REDIRECTORDERS
     173{
     174    enum {
     175        kRedirectOrder_Invalid = 0,
     176        kRedirectOrder_Close,
     177        kRedirectOrder_Open,
     178        kRedirectOrder_Dup,
     179    }           enmOrder;
     180    /** The target file handle. */
     181    int         fdTarget;
     182    /** The source file name, -1 on close only.
     183     * This is an opened file if pszFilename is set.  */
     184    int         fdSource;
     185    /** Whether to remove the file on failure cleanup. */
     186    int         fRemoveOnFailure;
     187    /** The open flags (for O_TEXT/O_BINARY) on windows. */
     188    int         fOpen;
     189    /** The filename - NULL if close only. */
     190    const char *pszFilename;
     191#ifndef USE_POSIX_SPAWN
     192    /** Saved file descriptor. */
     193    int         fdSaved;
     194    /** Saved flags. */
     195    int         fSaved;
     196#endif
     197} REDIRECTORDERS;
     198
     199
     200#ifdef _MSC_VER
     201
     202/**
     203 * Safe way of getting the OS handle of a file descriptor without triggering
     204 * invalid parameter handling.
     205 *
     206 * @returns The handle value if open, INVALID_HANDLE_VALUE if not.
     207 * @param   fd                  The file descriptor in question.
     208 */
     209static HANDLE mscGetOsHandle(int fd)
     210{
     211    intptr_t                    hHandle;
     212    _invalid_parameter_handler  pfnOld = _get_invalid_parameter_handler();
     213    _set_invalid_parameter_handler(ignore_invalid_parameter);
     214    hHandle = _get_osfhandle(fd);
     215    _set_invalid_parameter_handler(pfnOld);
     216    return hHandle != -1 ? (HANDLE)hHandle : INVALID_HANDLE_VALUE;
     217}
     218
     219/**
     220 * Checks if the specified file descriptor is open.
     221 *
     222 * @returns K_TRUE if open, K_FALSE if not.
     223 * @param   fd                  The file descriptor in question.
     224 */
     225static KBOOL mscIsOpenFile(int fd)
     226{
     227    return mscGetOsHandle(fd) != INVALID_HANDLE_VALUE;
     228}
     229
     230/**
     231 * Checks if the native handle is inheritable.
     232 *
     233 * @returns K_TRUE if it is, K_FALSE if it isn't or isn't a valid handle.
     234 * @param   hHandle             The native handle.
     235 */
     236static KBOOL mscIsNativeHandleInheritable(HANDLE hHandle)
     237{
     238    DWORD fFlags = 0;
     239    if (GetHandleInformation(hHandle, &fFlags))
     240        return (fFlags & HANDLE_FLAG_INHERIT) != 0;
     241    return K_FALSE;
     242}
     243
     244/**
     245 * Checks if the file descriptor is inheritable or not.
     246 *
     247 * @returns K_TRUE if it is, K_FALSE if it isn't or isn't a valid descriptor.
     248 * @param   fd                  The file descriptor in question.
     249 */
     250static KBOOL mscIsInheritable(int fd)
     251{
     252    HANDLE hHandle = mscGetOsHandle(fd);
     253    if (hHandle != INVALID_HANDLE_VALUE)
     254        return mscIsNativeHandleInheritable(hHandle);
     255    return K_FALSE;
     256}
     257
     258/**
     259 * A dup3 like function.
     260 *
     261 * @returns fdNew on success, -1 on failure w/ error details written to pStdErr.
     262 * @param   fdSource            The source descriptor.
     263 * @param   fdNew               The new descriptor.
     264 * @param   fFlags              The inherit and text/binary mode flag.
     265 * @param   pStdErr             Working stderr to write error details to.
     266 */
     267static int mscDup3(int fdSource, int fdNew, int fFlags, FILE *pStdErr)
     268{
     269    if (!fFlags & _O_NOINHERIT)
     270    {
     271        /* ASSUMES fFlags doesn't include any changing _O_TEXT/_O_BINARY. */
     272        int fdDup = _dup2(fdSource, fdNew);
     273        if (fdDup != -1)
     274            return fdDup;
     275        fprintf(pStdErr, "%s: _dup2(%d,%d) failed: %s\n", g_progname, fdSource, fdNew, strerror(errno));
     276    }
     277    else
     278    {
     279        HANDLE   hSource = mscGetOsHandle(fdSource);
     280        unsigned cTries  = 0;
     281        int      aFdTries[48];
     282
     283        if (hSource != INVALID_HANDLE_VALUE)
     284        {
     285            HANDLE hCurProc = GetCurrentProcess();
     286            BOOL   fInherit = !(fFlags & _O_NOINHERIT);
     287
     288            /*
     289             * Make sure the old descriptor is closed and can be used again.
     290             */
     291            _invalid_parameter_handler pfnOld = _get_invalid_parameter_handler();
     292            _set_invalid_parameter_handler(ignore_invalid_parameter);
     293            close(fdNew);
     294            _set_invalid_parameter_handler(pfnOld);
     295
     296            /*
     297             * Duplicate the source handle till we've got a match.
     298             */
     299            for (;;)
     300            {
     301                HANDLE hDup = INVALID_HANDLE_VALUE;
     302                if (DuplicateHandle(hCurProc, hSource, hCurProc, &hDup, 0 /* DesiredAccess */,
     303                                    fInherit, DUPLICATE_SAME_ACCESS))
     304                {
     305                    int fdDup = _open_osfhandle((intptr_t)hDup, fFlags);
     306                    if (fdDup != -1)
     307                    {
     308                        if (fdDup == fdNew)
     309                        {
     310                            while (cTries-- > 0)
     311                                close(aFdTries[cTries]);
     312                            return fdDup;
     313                        }
     314
     315                        aFdTries[cTries++] = fdDup;
     316                        if (   fdDup < fdNew
     317                            && cTries < K_ELEMENTS(aFdTries))
     318                            continue;
     319                        fprintf(pStdErr, "%s: mscDup3(%d,%d): giving up! (last fdDup=%d)\n",
     320                                g_progname, fdSource, fdNew, fdDup);
     321                    }
     322                    else
     323                    {
     324                        fprintf(pStdErr, "%s: _open_osfhandle(%#x) failed: %u\n", g_progname, hDup, strerror(errno));
     325                        CloseHandle(hDup);
     326                    }
     327                }
     328                else
     329                    fprintf(pStdErr, "%s: DuplicateHandle(%#x) failed: %u\n", g_progname, hSource, GetLastError());
     330                break;
     331            }
     332
     333            while (cTries-- > 0)
     334                close(aFdTries[cTries]);
     335        }
     336        else
     337            fprintf(pStdErr, "%s: mscDup3(%d,%d): source descriptor is invalid!\n", g_progname, fdSource, fdNew);
     338    }
     339    return -1;
     340}
     341
     342#endif /* _MSC_VER */
     343
     344static KBOOL kRedirectHasConflict(int fd, unsigned cOrders, REDIRECTORDERS *paOrders)
     345{
     346    while (cOrders-- > 0)
     347        if (paOrders[cOrders].fdTarget == fd)
     348            return K_TRUE;
     349    return K_FALSE;
     350}
     351
     352
     353/**
     354 * Creates a file descriptor for @a pszFilename that does not conflict with any
     355 * previous orders.
     356 *
     357 * We need to be careful that there isn't a close or dup targetting the
     358 * temporary file descriptor we return.  Also, we need to take care with the
     359 * descriptor's inheritability.  It should only be inheritable if the returned
     360 * descriptor matches the target descriptor (@a fdTarget).
     361 *
     362 * @returns File descriptor on success, -1 & err/errx on failure.
     363 *
     364 *          The returned file descriptor is not inherited (i.e. close-on-exec),
     365 *          unless it matches @a fdTarget
     366 *
     367 * @param   pszFilename         The filename to open.
     368 * @param   fOpen               The open flags.
     369 * @param   fMode               The file creation mode (if applicable).
     370 * @param   cOrders             The number of orders.
     371 * @param   paOrders            The order array.
     372 * @param   fRemoveOnFailure    Whether to remove the file on failure.
     373 * @param   fdTarget            The target descriptor.
     374 */
     375static int kRedirectOpenWithoutConflict(const char *pszFilename, int fOpen, mode_t fMode,
     376                                        unsigned cOrders, REDIRECTORDERS *paOrders, int fRemoveOnFailure, int fdTarget)
     377{
     378#ifdef _O_NOINHERIT
     379    int const   fNoInherit = _O_NOINHERIT;
     380#elif defined(O_NOINHERIT)
     381    int const   fNoInherit = O_NOINHERIT;
     382#elif defined(O_CLOEXEC)
     383    int const   fNoInherit = O_CLOEXEC;
     384#else
     385# error "port me"
     386#endif
     387    int         aFdTries[32];
     388    int         cTries;
     389
     390    /* Open it first. */
     391    int fdOpened = open(pszFilename, fOpen | fNoInherit, fMode);
     392    if (fdOpened < 0)
     393        return err(9, "open(%s,%#x,) failed", pszFilename, fOpen);
     394
     395    /* Check for conflicts. */
     396    if (!kRedirectHasConflict(fdOpened, cOrders, paOrders))
     397    {
     398        if (fdOpened != fdTarget)
     399            return fdOpened;
     400#ifndef _MSC_VER /* Stupid, stupid MSVCRT!  No friggin way of making a handle inheritable (or not). */
     401        if (fcntl(fdOpened, F_SETFD, FD_CLOEXEC) != -1)
     402            return fdOpened;
     403#endif
     404    }
     405
     406    /*
     407     * Do conflict resolving.
     408     */
     409    cTries = 1;
     410    aFdTries[cTries++] = fdOpened;
     411    while (cTries < K_ELEMENTS(aFdTries))
     412    {
     413        fdOpened = open(pszFilename, fOpen | fNoInherit, fMode);
     414        if (fdOpened >= 0)
     415        {
     416            if (   !kRedirectHasConflict(fdOpened, cOrders, paOrders)
     417#ifdef _MSC_VER
     418                && fdOpened != fdTarget
     419#endif
     420                )
     421            {
     422#ifndef _MSC_VER
     423                if (   fdOpened != fdTarget
     424                    || fcntl(fdOpened, F_SETFD, FD_CLOEXEC) != -1)
     425#endif
     426                {
     427                    while (cTries-- > 0)
     428                        close(aFdTries[cTries]);
     429                    return fdOpened;
     430                }
     431            }
     432
     433        }
     434        else
     435        {
     436            err(9, "open(%s,%#x,) #%u failed", pszFilename, cTries + 1, fOpen);
     437            break;
     438        }
     439        aFdTries[cTries++] = fdOpened;
     440    }
     441
     442    /*
     443     * Give up.
     444     */
     445    if (fdOpened >= 0)
     446        errx(9, "failed to find a conflict free file descriptor for '%s'!", pszFilename);
     447
     448    while (cTries-- > 0)
     449        close(aFdTries[cTries]);
     450    return -1;
     451}
     452
     453#ifndef USE_POSIX_SPAWN
     454
     455/**
     456 * Saves a file handle to one which isn't inherited and isn't affected by the
     457 * file orders.
     458 *
     459 * @returns 0 on success, non-zero exit code on failure.
     460 * @param   pToSave         Pointer to the file order to save the target
     461 *                          descriptor of.
     462 * @param   cOrders         Number of file orders.
     463 * @param   paOrders        The array of file orders.
     464 * @param   ppWorkingStdErr Pointer to a pointer to a working stderr.  This will
     465 *                          get replaced if we're saving stderr, so that we'll
     466 *                          keep having a working one to report failures to.
     467 */
     468static int kRedirectSaveHandle(REDIRECTORDERS *pToSave, unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr)
     469{
     470    int fdToSave = pToSave->fdTarget;
     471    int rcRet    = 10;
     472
     473    /*
     474     * First, check if there's actually handle here that needs saving.
     475     */
     476# ifdef KBUILD_OS_WINDOWS
     477    HANDLE hToSave = mscGetOsHandle(fdToSave);
     478    if (hToSave != INVALID_HANDLE_VALUE)
     479    {
     480        pToSave->fSaved = _setmode(fdToSave, _O_BINARY);
     481        if (pToSave->fSaved != _O_BINARY)
     482            _setmode(fdToSave, pToSave->fSaved);
     483        if (!mscIsNativeHandleInheritable(hToSave))
     484            pToSave->fSaved |= _O_NOINHERIT;
     485    }
     486    if (hToSave != INVALID_HANDLE_VALUE)
     487# else
     488    pToSave->fSaved = fcntl(pToSave->fdTarget, F_GETFD, 0);
     489    if (pToSave->fSaved != -1)
     490# endif
     491    {
     492        /*
     493         * Try up to 32 times to get a duplicate descriptor that doesn't conflict.
     494         */
     495# ifdef KBUILD_OS_WINDOWS
     496        HANDLE hCurProc = GetCurrentProcess();
     497# endif
     498        int aFdTries[32];
     499        int cTries = 0;
     500        do
     501        {
     502            /* Duplicate the handle (windows makes this complicated). */
     503            int fdDup;
     504# ifdef KBUILD_OS_WINDOWS
     505            HANDLE hDup = INVALID_HANDLE_VALUE;
     506            if (!DuplicateHandle(hCurProc, hToSave, hCurProc, &hDup, 0 /* DesiredAccess */,
     507                                FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
     508            {
     509                fprintf(*ppWorkingStdErr, "%s: DuplicateHandle(%#x) failed: %u\n", g_progname, hToSave, GetLastError());
     510                break;
     511            }
     512            fdDup = _open_osfhandle((intptr_t)hDup, pToSave->fSaved | _O_NOINHERIT);
     513            if (fdDup == -1)
     514            {
     515                fprintf(*ppWorkingStdErr, "%s: _open_osfhandle(%#x) failed: %u\n", g_progname, hDup, strerror(errno));
     516                CloseHandle(hDup);
     517                break;
     518            }
     519# else
     520            fdDup = dup(fdToSave);
     521            if (fdDup == -1)
     522            {
     523                fprintf(*ppWorkingStdErr, "%s: dup(%#x) failed: %u\n", g_progname, fdToSave, strerror(errno));
     524                break;
     525            }
     526#endif
     527            /* Is the duplicate usable? */
     528            if (!kRedirectHasConflict(fdDup, cOrders, paOrders))
     529            {
     530                pToSave->fdSaved = fdDup;
     531                if (   *ppWorkingStdErr == stderr
     532                    && fdToSave == fileno(*ppWorkingStdErr))
     533                {
     534                    *ppWorkingStdErr = fdopen(fdDup, "wt");
     535                    if (*ppWorkingStdErr == NULL)
     536                    {
     537                        fprintf(stderr, "%s: fdopen(%d,\"wt\") failed: %s\n", g_progname, fdDup, strerror(errno));
     538                        *ppWorkingStdErr = stderr;
     539                        close(fdDup);
     540                        break;
     541                    }
     542                }
     543                rcRet = 0;
     544                break;
     545            }
     546
     547            /* Not usuable, stash it and try again. */
     548            aFdTries[cTries++] = fdDup;
     549        } while (cTries < K_ELEMENTS(aFdTries));
     550
     551        /*
     552         * Clean up unused duplicates.
     553         */
     554        while (cTries-- > 0)
     555            close(aFdTries[cTries]);
     556    }
     557    else
     558    {
     559        /*
     560         * Nothing to save.
     561         */
     562        pToSave->fdSaved = -1;
     563        rcRet = 0;
     564    }
     565    return rcRet;
     566}
     567
     568
     569/**
     570 * Cleans up the file operation orders.
     571 *
     572 * This does not restore stuff, just closes handles we've opened for the guest.
     573 *
     574 * @param   cOrders         Number of file operation orders.
     575 * @param   paOrders        The file operation orders.
     576 * @param   fFailed         Set if it's a failure.
     577 */
     578static void kRedirectCleanupFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, KBOOL fFailure)
     579{
     580    unsigned i = cOrders;
     581    while (i-- > 0)
     582    {
     583        if (   paOrders[i].enmOrder == kRedirectOrder_Open
     584            && paOrders[i].fdSource != -1)
     585        {
     586            close(paOrders[i].fdSource);
     587            paOrders[i].fdSource = -1;
     588            if (   fFailure
     589                && paOrders[i].fRemoveOnFailure
     590                && paOrders[i].pszFilename)
     591                remove(paOrders[i].pszFilename);
     592        }
     593    }
     594}
     595
     596
     597/**
     598 * Restores the target file descriptors affected by the file operation orders.
     599 *
     600 * @param   cOrders         Number of file operation orders.
     601 * @param   paOrders        The file operation orders.
     602 * @param   ppWorkingStdErr Pointer to a pointer to the working stderr.  If this
     603 *                          is one of the saved file descriptors, we'll restore
     604 *                          it to stderr.
     605 */
     606static void kRedirectRestoreFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr)
     607{
     608    int iSavedErrno = errno;
     609    unsigned i = cOrders;
     610    while (i-- > 0)
     611    {
     612        if (paOrders[i].fdSaved != -1)
     613        {
     614            KBOOL fRestoreStdErr = *ppWorkingStdErr != stderr
     615                                 && paOrders[i].fdSaved == fileno(*ppWorkingStdErr);
     616
     617#ifdef KBUILD_OS_WINDOWS
     618            if (mscDup3(paOrders[i].fdSaved, paOrders[i].fdTarget, paOrders[i].fSaved, *ppWorkingStdErr) != -1)
     619#else
     620            if (dup2(paOrders[i].fdSaved, paOrders[i].fdTarget) != -1)
     621#endif
     622            {
     623                close(paOrders[i].fdSaved);
     624                paOrders[i].fdSaved = -1;
     625
     626                if (fRestoreStdErr)
     627                {
     628                    *ppWorkingStdErr = stderr;
     629                    assert(fileno(stderr) == paOrders[i].fdTarget);
     630                }
     631            }
     632#ifndef KBUILD_OS_WINDOWS
     633            else
     634                fprintf(*ppWorkingStdErr, "%s: dup2(%d,%d) failed: %s",
     635                        g_progname, paOrders[i].fdSaved, paOrders[i].fdTarget, strerror(errno));
     636#endif
     637        }
     638
     639#ifndef KBUILD_OS_WINDOWS
     640        if (paOrders[i].fSaved != -1)
     641        {
     642            if (fcntl(paOrders[i].fdTarget, F_SETFD, paOrders[i].fSaved & FD_CLOEXEC) == -1)
     643                paOrders[i].fSaved = -1;
     644            else
     645                fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_SETFD,%s) failed: %s",
     646                        g_progname, paOrders[i].fdTarget, paOrders[i].fSaved & FD_CLOEXEC ? "FD_CLOEXEC" : "0", strerror(errno));
     647        }
     648#endif
     649    }
     650    errno = iSavedErrno;
     651}
     652
     653
     654/**
     655 * Executes the file operation orders.
     656 *
     657 * @returns 0 on success, exit code on failure.
     658 * @param   cOrders         Number of file operation orders.
     659 * @param   paOrders        File operation orders to execute.
     660 * @param   ppWorkingStdErr Where to return a working stderr (mainly for
     661 *                          kRedirectRestoreFdOrders).
     662 */
     663static int kRedirectExecFdOrders(unsigned cOrders, REDIRECTORDERS *paOrders, FILE **ppWorkingStdErr)
     664{
     665    unsigned i;
     666
     667    *ppWorkingStdErr = stderr;
     668    for (i = 0; i < cOrders; i++)
     669    {
     670        int rcExit = 10;
     671        switch (paOrders[i].enmOrder)
     672        {
     673            case kRedirectOrder_Close:
     674            {
     675                /* If the handle isn't used by any of the following operation,
     676                   just mark it as non-inheritable if necessary. */
     677                int const fdTarget = paOrders[i].fdTarget;
     678                unsigned j;
     679                for (j = i + 1; j < cOrders; j++)
     680                    if (paOrders[j].fdTarget == fdTarget)
     681                        break;
     682# ifdef _MSC_VER
     683                if (j >= cOrders && !mscIsInheritable(fdTarget))
     684                    rcExit = 0;
     685# else
     686                if (j >= cOrders)
     687                {
     688                    paOrders[j].fSaved = fcntl(fdTarget, F_GETFD, 0);
     689                    if (paOrders[j].fSaved != -1)
     690                    {
     691                        if (paOrders[j].fSaved & FD_CLOEXEC)
     692                            rcExit = 0;
     693                        else if (   fcntl(fdTarget, F_SETFD, FD_CLOEXEC) != -1
     694                                 || errno == EBADF)
     695                            rcExit = 0;
     696                        else
     697                            fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_SETFD,FD_CLOEXEC) failed: %s",
     698                                    g_progname, fdTarget, strerror(errno));
     699                    }
     700                    else if (errno == EBADF)
     701                        rcExit = 0;
     702                    else
     703                        fprintf(*ppWorkingStdErr, "%s: fcntl(%d,F_GETFD,0) failed: %s", g_progname, fdTarget, strerror(errno));
     704                }
     705# endif
     706                else
     707                    rcExit = kRedirectSaveHandle(&paOrders[i], cOrders, paOrders, ppWorkingStdErr);
     708                break;
     709            }
     710
     711            case kRedirectOrder_Dup:
     712            case kRedirectOrder_Open:
     713                rcExit = kRedirectSaveHandle(&paOrders[i], cOrders, paOrders, ppWorkingStdErr);
     714                if (rcExit == 0)
     715                {
     716                    if (dup2(paOrders[i].fdSource, paOrders[i].fdTarget) != -1)
     717                        rcExit = 0;
     718                    else
     719                    {
     720                        if (paOrders[i].enmOrder == kRedirectOrder_Open)
     721                            fprintf(*ppWorkingStdErr, "%s: dup2(%d [%s],%d) failed: %s", g_progname, paOrders[i].fdSource,
     722                                    paOrders[i].pszFilename, paOrders[i].fdTarget, strerror(errno));
     723                        else
     724                            fprintf(*ppWorkingStdErr, "%s: dup2(%d,%d) failed: %s",
     725                                    g_progname, paOrders[i].fdSource, paOrders[i].fdTarget, strerror(errno));
     726                        rcExit = 10;
     727                    }
     728                }
     729                break;
     730
     731            default:
     732                fprintf(*ppWorkingStdErr, "%s: error! invalid enmOrder=%d\n", g_progname, paOrders[i].enmOrder);
     733                rcExit = 99;
     734                break;
     735        }
     736
     737        if (rcExit != 0)
     738        {
     739            kRedirectRestoreFdOrders(i, paOrders, ppWorkingStdErr);
     740            return rcExit;
     741        }
     742    }
     743
     744    return 0;
     745}
     746
     747#endif /* !USE_POSIX_SPAWN */
     748
     749
     750/**
     751 * Does the child spawning .
     752 *
     753 * @returns Exit code.
     754 * @param   pszExecutable       The child process executable.
     755 * @param   cArgs               Number of arguments.
     756 * @param   papszArgs           The child argument vector.
     757 * @param   fWatcomBrainDamage  Whether MSC need to do quoting according to
     758 *                              weird Watcom WCC rules.
     759 * @param   papszEnv            The child environment vector.
     760 * @param   pszCwd              The current working directory of the child.
     761 * @param   pszSavedCwd         The saved current working directory.  This is
     762 *                              NULL if the CWD doesn't need changing.
     763 * @param   cOrders             Number of file operation orders.
     764 * @param   paOrders            The file operation orders.
     765 * @param   pFileActions        The posix_spawn file actions.
     766 * @param   cVerbosity          The verbosity level.
     767 * @param   pPidSpawned         Where to return the PID of the spawned child
     768 *                              when we're inside KMK and we're return without
     769 *                              waiting.
     770 * @param   pfIsChildExitCode   Where to indicate whether the return exit code
     771 *                              is from the child or from our setup efforts.
     772 */
     773static int kRedirectDoSpawn(const char *pszExecutable, int cArgs, char **papszArgs, int fWatcomBrainDamage, char **papszEnv,
     774                            const char *pszCwd, const char *pszSavedCwd, unsigned cOrders, REDIRECTORDERS *paOrders,
     775#ifdef USE_POSIX_SPAWN
     776                            posix_spawn_file_actions_t *pFileActions,
     777#endif
     778                            unsigned cVerbosity,
     779#ifdef KMK
     780                            pid_t *pPidSpawned,
     781#endif
     782                            KBOOL *pfIsChildExitCode)
     783{
     784    int     rcExit;
     785    int     i;
     786#ifdef _MSC_VER
     787    char  **papszArgsOriginal = papszArgs;
     788#endif
     789    *pfIsChildExitCode = K_FALSE;
     790
     791#ifdef _MSC_VER
     792    /*
     793     * Do MSC parameter quoting.
     794     */
     795    papszArgs = malloc((cArgs + 1) * sizeof(papszArgs[0]));
     796    if (papszArgs)
     797        memcpy(papszArgs, papszArgsOriginal, (cArgs + 1) * sizeof(papszArgs[0]));
     798    else
     799        return errx(9, "out of memory!");
     800
     801    rcExit = quote_argv(cArgs, papszArgs, fWatcomBrainDamage, 0 /*fFreeOrLeak*/);
     802    if (rcExit == 0)
     803#endif
     804    {
     805        /*
     806         * Display what we're about to execute if we're in verbose mode.
     807         */
     808        if (cVerbosity > 0)
     809        {
     810            for (i = 0; i < cArgs; i++)
     811                warnx("debug: argv[%i]=%s<eos>", i, papszArgs[i]);
     812            for (i = 0; i < (int)cOrders; i++)
     813                switch (paOrders[i].enmOrder)
     814                {
     815                    case kRedirectOrder_Close:
     816                        warnx("debug: close %d\n", paOrders[i].fdTarget);
     817                        break;
     818                    case kRedirectOrder_Dup:
     819                        warnx("debug: dup %d to %d\n", paOrders[i].fdSource, paOrders[i].fdTarget);
     820                        break;
     821                    case kRedirectOrder_Open:
     822                        warnx("debug: open '%s' (%#x) as [%d ->] %d\n",
     823                              paOrders[i].pszFilename, paOrders[i].fOpen, paOrders[i].fdSource, paOrders[i].fdTarget);
     824                        break;
     825                    default:
     826                        warnx("error! invalid enmOrder=%d", paOrders[i].enmOrder);
     827                        assert(0);
     828                        break;
     829                }
     830            if (pszSavedCwd)
     831                warnx("debug: chdir %s\n", pszCwd);
     832        }
     833
     834        /*
     835         * Change working directory if so requested.
     836         */
     837        if (pszSavedCwd)
     838        {
     839            if (chdir(pszCwd) < 0)
     840                rcExit = errx(10, "Failed to change directory to '%s'", pszCwd);
     841        }
     842        if (rcExit == 0)
     843        {
     844#ifndef USE_POSIX_SPAWN
     845            /*
     846             * Execute the file orders.
     847             */
     848            FILE *pWorkingStdErr = NULL;
     849            rcExit = kRedirectExecFdOrders(cOrders, paOrders, &pWorkingStdErr);
     850            if (rcExit == 0)
     851#endif
     852            {
     853#ifdef KMK
     854                /*
     855                 * We're spawning from within kmk.
     856                 */
     857#if defined(KBUILD_OS_WINDOWS)
     858                /* Windows is slightly complicated due to handles and sub_proc.c. */
     859                HANDLE  hProcess = (HANDLE)_spawnvpe(_P_NOWAIT, pszExecutable, papszArgs, papszEnv);
     860                kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
     861                if ((intptr_t)hProcess != -1)
     862                {
     863                    if (process_kmk_register_redirect(hProcess, pPidSpawned) == 0)
     864                    {
     865                        if (cVerbosity > 0)
     866                            warnx("debug: spawned %d", *pPidSpawned);
     867                    }
     868                    else
     869                    {
     870                        DWORD dwTmp;
     871                        warn("sub_proc is out of slots, waiting for child...");
     872                        dwTmp = WaitForSingleObject(hProcess, INFINITE);
     873                        if (dwTmp != WAIT_OBJECT_0)
     874                            warn("WaitForSingleObject failed: %#x\n", dwTmp);
     875
     876                        if (GetExitCodeProcess(hProcess, &dwTmp))
     877                            rcExit = (int)dwTmp;
     878                        else
     879                        {
     880                            warn("GetExitCodeProcess failed: %u\n", GetLastError());
     881                            TerminateProcess(hProcess, 127);
     882                            rcExit = 127;
     883                        }
     884
     885                        CloseHandle(hProcess);
     886                        *pPidSpawned = 0;
     887                        *pfIsChildExitCode = K_TRUE;
     888                    }
     889                }
     890                else
     891                    rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
     892
     893# elif defined(KBUILD_OS_OS2)
     894                *pPidSpawned = _spawnve(_P_NOWAIT, pszExecutable, papszArgs, papszEnv);
     895                kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
     896                if (*pPidSpawned != -1)
     897                {
     898                    if (cVerbosity > 0)
     899                        warnx("debug: spawned %d", *pPidSpawned);
     900                }
     901                else
     902                {
     903                    rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
     904                    *pPidSpawned = 0;
     905                }
     906#else
     907                rcExit = posix_spawnp(pPidSpawned, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
     908                if (rcExit == 0)
     909                {
     910                    if (cVerbosity > 0)
     911                        warnx("debug: spawned %d", *pPidSpawned);
     912                }
     913                else
     914                {
     915                    rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit));
     916                    *pPidSpawned = 0;
     917                }
     918#endif
     919
     920#else  /* !KMK */
     921                /*
     922                 * Spawning from inside the kmk_redirect executable.
     923                 */
     924# if defined(KBUILD_OS_WINDOWS) || defined(KBUILD_OS_OS2)
     925                errno  = 0;
     926                rcExit = (int)_spawnvpe(_P_WAIT, pszExecutable, papszArgs, papszEnv);
     927                kRedirectRestoreFdOrders(cOrders, paOrders, &pWorkingStdErr);
     928                if (rcExit != -1 || errno == 0)
     929                {
     930                    *pfIsChildExitCode = K_TRUE;
     931                    if (cVerbosity > 0)
     932                        warnx("debug: exit code: %d", rcExit);
     933                }
     934                else
     935                    rcExit = err(10, "_spawnvpe(%s) failed", pszExecutable);
     936
     937# else
     938                pid_t pidChild = 0;
     939                rcExit = posix_spawnp(&pidChild, pszExecutable, pFileActions, NULL /*pAttr*/, papszArgs, papszEnvVars);
     940                if (rcExit == 0)
     941                {
     942                    *pfIsChildExitCode = K_TRUE;
     943                    if (cVerbosity > 0)
     944                        warnx("debug: spawned %d", pidChild);
     945
     946                    /* Wait for the child. */
     947                    for (;;)
     948                    {
     949                        pid_t pid = waitpid(pidChild, &rcExit, 0 /*block*/);
     950                        if (pid == pidChild)
     951                        {
     952                            if (cVerbosity > 0)
     953                                warnx("debug: %d exit code: %d", pidChild, rcExit);
     954                            break;
     955                        }
     956                        if (   errno != EINTR
     957#  ifdef ERESTART
     958                            && errno != ERESTART
     959#  endif
     960                           )
     961                        {
     962                            rcExit = err(11, "waitpid failed");
     963                            kill(pidChild, SIGKILL);
     964                            break;
     965                        }
     966                    }
     967                }
     968                else
     969                    rcExit = errx(10, "posix_spawnp(%s) failed: %s", pszExecutable, strerror(rcExit));
     970
     971# endif
     972#endif /* !KMK */
     973            }
     974        }
     975
     976        /*
     977         * Restore the current directory.
     978         */
     979        if (pszSavedCwd)
     980        {
     981            if (chdir(pszSavedCwd) < 0)
     982                warn("Failed to restore directory to '%s'", pszSavedCwd);
     983        }
     984    }
     985#ifdef _MSC_VER
     986    else
     987        rcExit = errx(9, "quite_argv failed: %u", rcExit);
     988
     989    /* Restore the original argv strings, freeing the quote_argv replacements. */
     990    i = cArgs;
     991    while (i-- > 0)
     992        if (papszArgs[i] != papszArgsOriginal[i])
     993            free(papszArgs[i]);
     994    free(papszArgs);
     995#endif
     996    return rcExit;
     997}
     998
     999
     1000/**
     1001 * The function that does almost everything here... ugly.
     1002 */
     1003#ifdef KMK
     1004int kmk_builtin_redirect(int argc, char **argv, char **envp, struct child *pChild, pid_t *pPidSpawned)
     1005#else
    1451006int main(int argc, char **argv, char **envp)
    146 {
    147     int i;
    148     int j;
    149 #if defined(_MSC_VER)
    150     intptr_t rc;
    151 #endif
    152     FILE *pStdErr = stderr;
    153     FILE *pStdOut = stdout;
    154     int fWatcomBrainDamage = 0;
     1007#endif
     1008{
     1009    int             rcExit = 0;
     1010    KBOOL           fChildExitCode = K_FALSE;
     1011#ifdef USE_POSIX_SPAWN
     1012    posix_spawn_file_actions_t FileActions;
     1013#endif
     1014    unsigned        cOrders = 0;
     1015    REDIRECTORDERS  aOrders[32];
     1016
     1017    int             iArg;
     1018    const char     *pszExecutable      = NULL;
     1019    char          **papszEnv           = NULL;
     1020    unsigned        cAllocatedEnvVars;
     1021    unsigned        iEnvVar;
     1022    unsigned        cEnvVars;
     1023    int             fWatcomBrainDamage = 0;
     1024    int             cVerbosity         = 0;
     1025    char           *pszSavedCwd        = NULL;
     1026    size_t const    cbCwdBuf           = GET_PATH_MAX;
     1027    PATH_VAR(szCwd);
     1028#ifdef KBUILD_OS_OS2
     1029    ULONG           ulLibPath;
     1030    char           *apszSavedLibPaths[LIBPATHSTRICT + 1] = { NULL, NULL, NULL, NULL };
     1031#endif
     1032
     1033
     1034    g_progname = argv[0];
     1035
     1036    if (argc <= 1)
     1037        return usage(stderr, argv[0]);
     1038
     1039    /*
     1040     * Create default program environment.
     1041     */
     1042#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
     1043    if (getcwd_fs(szCwd, cbCwdBuf) != NULL)
     1044#else
     1045    if (getcwd(szCwd, cbCwdBuf) != NULL)
     1046#endif
     1047    { /* likely */ }
     1048    else
     1049        return err(9, "getcwd failed");
     1050
     1051#if defined(KMK)
     1052    /* We get it from kmk and just count it:  */
     1053    papszEnv = pChild->environment;
     1054    if (!papszEnv)
     1055        pChild->environment = papszEnv = target_environment(pChild->file);
     1056    cEnvVars = 0;
     1057    while (papszEnv[cEnvVars] != NULL)
     1058        cEnvVars++;
     1059    cAllocatedEnvVars = cEnvVars;
     1060#else
     1061    /* We make a copy and we manage ourselves: */
     1062    cEnvVars = 0;
     1063    while (envp[cEnvVars] != NULL)
     1064        cEnvVars++;
     1065
     1066    cAllocatedEnvVars = cEnvVars + 4;
     1067    papszEnv = malloc((cAllocatedEnvVars + 1) * sizeof(papszEnv));
     1068    if (!papszEnv)
     1069        return errx(9, "out of memory!");
     1070
     1071    iEnvVar = cEnvVars;
     1072    papszEnv[iEnvVar] = NULL;
     1073    while (iEnvVar-- > 0)
     1074    {
     1075        papszEnv[iEnvVar] = strdup(envp[iEnvVar]);
     1076        if (!papszEnv[iEnvVar])
     1077        {
     1078            while (iEnvVar-- > 0)
     1079                free(papszEnv[iEnvVar]);
     1080            free(papszEnv);
     1081            return errx(9, "out of memory!");
     1082        }
     1083    }
     1084#endif
     1085
     1086#ifdef USE_POSIX_SPAWN
     1087    /*
     1088     * Init posix attributes.
     1089     */
     1090    rcExit = posix_spawn_file_actions_init(&FileActions);
     1091    if (rcExit != 0)
     1092        rcExit = errx(9, "posix_spawn_file_actions_init failed: %s", strerror(rcExit));
     1093#endif
    1551094
    1561095    /*
    1571096     * Parse arguments.
    1581097     */
    159     if (argc <= 1)
    160         return usage(pStdErr, name(argv[0]));
    161     for (i = 1; i < argc; i++)
     1098    for (iArg = 1; rcExit == 0 && iArg < argc; iArg++)
    1621099    {
    163         if (argv[i][0] == '-')
     1100        char *pszArg = argv[iArg];
     1101        if (*pszArg == '-')
    1641102        {
    165             int fd;
    166             int fdOpened;
    167             int fOpen;
    168             char *psz = &argv[i][1];
    169             if (*psz == '-')
    170             {
    171                 /* '--' ? */
    172                 if (!psz[1])
    173                 {
    174                     i++;
     1103            int         fd;
     1104            char        chOpt;
     1105            const char *pszValue;
     1106
     1107            chOpt = *++pszArg;
     1108            pszArg++;
     1109            if (chOpt == '-')
     1110            {
     1111                /* '--' indicates where the bits to execute start. */
     1112                if (*pszArg == '\0')
     1113                {
     1114                    iArg++;
    1751115                    break;
    1761116                }
    1771117
    178                 /* convert to short. */
    179                 if (!strcmp(psz, "-help"))
    180                     psz = "h";
    181                 else if (!strcmp(psz, "-version"))
    182                     psz = "V";
    183                 else if (!strcmp(psz, "-env"))
    184                     psz = "E";
    185                 else if (!strcmp(psz, "-chdir"))
    186                     psz = "C";
    187                 else if (!strcmp(psz, "-zap-env"))
    188                     psz = "Z";
    189                 else if (!strcmp(psz, "-close"))
    190                     psz = "c";
    191                 else if (!strcmp(psz, "-wcc-brain-damage"))
     1118                if (   strcmp(pszArg, "wcc-brain-damage") == 0
     1119                    || strcmp(pszArg, "watcom-brain-damage") == 0)
    1921120                {
    1931121                    fWatcomBrainDamage = 1;
    1941122                    continue;
    1951123                }
     1124
     1125                /* convert to short. */
     1126                if (strcmp(pszArg, "help") == 0)
     1127                    chOpt = 'h';
     1128                else if (strcmp(pszArg, "version") == 0)
     1129                    chOpt = 'V';
     1130                else if (   strcmp(pszArg, "set") == 0
     1131                         || strcmp(pszArg, "env") == 0)
     1132                    chOpt = 'E';
     1133                else if (strcmp(pszArg, "unset") == 0)
     1134                    chOpt = 'U';
     1135                else if (   strcmp(pszArg, "zap-env") == 0
     1136                         || strcmp(pszArg, "ignore-environment") == 0 /* GNU env compatibility. */ )
     1137                    chOpt = 'Z';
     1138                else if (strcmp(pszArg, "chdir") == 0)
     1139                    chOpt = 'C';
     1140                else if (strcmp(pszArg, "close") == 0)
     1141                    chOpt = 'c';
     1142                else if (strcmp(pszArg, "verbose") == 0)
     1143                    chOpt = 'v';
     1144                else
     1145                {
     1146                    errx(2, "Unknown option: '%s'", pszArg - 2);
     1147                    rcExit = usage(stderr, argv[0]);
     1148                    break;
     1149                }
     1150                pszArg = "";
    1961151            }
    1971152
    1981153            /*
    199              * Deal with the obligatory help and version switches first.
     1154             * Deal with the obligatory help and version switches first to get them out of the way.
    2001155             */
    201             if (*psz == 'h')
    202             {
    203                 usage(pStdOut, name(argv[0]));
    204                 return 0;
    205             }
    206             if (*psz == 'V')
    207             {
    208                 printf("kmk_redirect - kBuild version %d.%d.%d (r%u)\n"
    209                        "Copyright (C) 2007-2012 knut st. osmundsen\n",
    210                        KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH,
    211                        KBUILD_SVN_REV);
    212                 return 0;
    213             }
     1156            if (chOpt == 'h')
     1157            {
     1158                usage(stdout, argv[0]);
     1159                rcExit = -1;
     1160                break;
     1161            }
     1162            if (chOpt == 'V')
     1163            {
     1164                kbuild_version(argv[0]);
     1165                rcExit = -1;
     1166                break;
     1167            }
     1168
     1169            /*
     1170             * Get option value first, if the option takes one.
     1171             */
     1172            if (   chOpt == 'E'
     1173                || chOpt == 'U'
     1174                || chOpt == 'C'
     1175                || chOpt == 'c'
     1176                || chOpt == 'd'
     1177                || chOpt == 'e')
     1178            {
     1179                if (*pszArg != '\0')
     1180                    pszValue = pszArg + (*pszArg == ':' || *pszArg == '=');
     1181                else if (++iArg < argc)
     1182                    pszValue = argv[iArg];
     1183                else
     1184                {
     1185                    errx(2, "syntax error: Option -%c requires a value!", chOpt);
     1186                    rcExit = usage(stderr, argv[0]);
     1187                    break;
     1188                }
     1189            }
     1190            else
     1191                pszValue = NULL;
    2141192
    2151193            /*
    2161194             * Environment switch?
    2171195             */
    218             if (*psz == 'E')
    219             {
    220                 psz++;
    221                 if (*psz == ':' || *psz == '=')
    222                     psz++;
    223                 else
    224                 {
    225                     if (i + 1 >= argc)
     1196            if (chOpt == 'E')
     1197            {
     1198                const char *pchEqual = strchr(pszValue, '=');
     1199#ifdef KBUILD_OS_OS2
     1200                if (   strncmp(pszValue, TUPLE("BEGINLIBPATH=")) == 0
     1201                    || strncmp(pszValue, TUPLE("ENDLIBPATH=")) == 0
     1202                    || strncmp(pszValue, TUPLE("LIBPATHSTRICT=")) == 0)
     1203                {
     1204                    ULONG   ulVar  = *pszValue == 'B' ? BEGIN_LIBPATH
     1205                                   : *pszValue == 'E' ? END_LIBPATH
     1206                                   :                    LIBPATHSTRICT;
     1207                    APIRET  rc;
     1208                    if (apszSavedLibPaths[ulVar] == NULL)
    2261209                    {
    227                         fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]);
    228                         return 1;
     1210                        /* The max length is supposed to be 1024 bytes. */
     1211                        apszSavedLibPaths[ulVar] = calloc(1024 * 2);
     1212                        if (apszSavedLibPaths[ulVar])
     1213                        {
     1214                            rc = DosQueryExtLIBPATH(apszSavedLibPaths[ulVar], ulVar);
     1215                            if (rc)
     1216                            {
     1217                                rcExit = errx(9, "DosQueryExtLIBPATH(,%u) failed: %lu", ulVar, rc);
     1218                                free(apszSavedLibPaths[ulVar]);
     1219                                apszSavedLibPaths[ulVar] = NULL;
     1220                            }
     1221                        }
     1222                        else
     1223                            rcExit = errx(9, "out of memory!");
    2291224                    }
    230                     psz = argv[++i];
    231                 }
    232 #ifdef __OS2__
    233                 if (    !strncmp(psz, "BEGINLIBPATH=",  sizeof("BEGINLIBPATH=") - 1)
    234                     ||  !strncmp(psz, "ENDLIBPATH=",    sizeof("ENDLIBPATH=") - 1)
    235                     ||  !strncmp(psz, "LIBPATHSTRICT=", sizeof("LIBPATHSTRICT=") - 1))
    236                 {
    237                     ULONG ulVar = *psz == 'B' ? BEGIN_LIBPATH
    238                                 : *psz == 'E' ? END_LIBPATH
    239                                 :               LIBPATHSTRICT;
    240                     const char *pszVal = strchr(psz, '=') + 1;
    241                     APIRET rc = DosSetExtLIBPATH(pszVal, ulVar);
    242                     if (rc)
     1225                    if (rcExit == 0)
    2431226                    {
    244                         fprintf(pStdErr, "%s: error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu\n",
    245                                 name(argv[0]), pszVal, pszVal - psz - 1, psz, ulVar, rc);
    246                         return 1;
     1227                        rc = DosSetExtLIBPATH(pchEqual + 1, ulVar);
     1228                        if (rc)
     1229                            rcExit = errx(9, "error: DosSetExtLibPath(\"%s\", %.*s (%lu)): %lu",
     1230                                          pchEqual, pchEqual - pszValue, pchEqual + 1, ulVar, rc);
    2471231                    }
    248                 }
    249                 else
    250 #endif /* __OS2__ */
    251                 {
    252                     const char *pchEqual = strchr(psz, '=');
    253                     if (pchEqual && pchEqual[1] != '\0')
     1232                    continue;
     1233                }
     1234#endif /* KBUILD_OS_OS2 */
     1235
     1236                /* We differ from kSubmit here and use putenv sematics. */
     1237                if (pchEqual)
     1238                {
     1239                    if (pchEqual[1] != '\0')
    2541240                    {
    255                         if (putenv(psz))
    256                         {
    257                             fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno));
    258                             return 1;
    259                         }
     1241                        rcExit = kBuiltinOptEnvSet(&papszEnv, &cEnvVars, &cAllocatedEnvVars, cVerbosity, pszValue);
     1242#ifdef KMK
     1243                        pChild->environment = papszEnv;
     1244#endif
    2601245                    }
    2611246                    else
    2621247                    {
    263                         size_t cchVar = pchEqual ? (size_t)(pchEqual - psz) : strlen(psz);
    264                         char *pszCopy = (char *)malloc(cchVar + 2);
    265                         memcpy(pszCopy, psz, cchVar);
    266 
    267 #if defined(_MSC_VER) || defined(__OS2__)
    268                         pszCopy[cchVar] = '=';
    269                         pszCopy[cchVar + 1] = '\0';
    270                         if (putenv(pszCopy))
     1248                        char *pszCopy = strdup(pszValue);
     1249                        if (pszCopy)
    2711250                        {
    272                             fprintf(pStdErr, "%s: error: putenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno));
    273                             return 1;
     1251                            pszCopy[pchEqual - pszValue] = '\0';
     1252                            rcExit = kBuiltinOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszCopy);
     1253                            free(pszCopy);
    2741254                        }
    275 #else
    276                         pszCopy[cchVar] = '\0';
    277                         if (unsetenv(pszCopy))
    278                         {
    279                             fprintf(pStdErr, "%s: error: unsetenv(\"%s\"): %s\n", name(argv[0]), pszCopy, strerror(errno));
    280                             return 1;
    281                         }
    282 #endif
    283                         free(pszCopy);
     1255                        else
     1256                            rcExit = errx(1, "out of memory!");
    2841257                    }
    285                 }
     1258                    continue;
     1259                }
     1260                /* Simple unset. */
     1261                chOpt = 'U';
     1262            }
     1263
     1264            /*
     1265             * Unset environment variable.
     1266             */
     1267            if (chOpt == 'U')
     1268            {
     1269#ifdef KBUILD_OS_OS2
     1270                if (   strcmp(pszValue, "BEGINLIBPATH") == 0
     1271                    || strcmp(pszValue, "ENDLIBPATH") == 0
     1272                    || strcmp(pszValue, "LIBPATHSTRICT") == 0)
     1273                    rcExit = errx(2, "error: '%s' cannot be unset, only set to an empty value using -E/--set.", pszValue);
     1274                else
     1275#endif
     1276                    rcExit = kBuiltinOptEnvUnset(papszEnv, &cEnvVars, cVerbosity, pszValue);
     1277                continue;
     1278            }
     1279
     1280            /*
     1281             * Zap environment switch?
     1282             */
     1283            if (   chOpt == 'Z'
     1284                || chOpt == 'i' /* GNU env compatibility. */ )
     1285            {
     1286                for (iEnvVar = 0; iEnvVar < cEnvVars; iEnvVar++)
     1287                    free(papszEnv[iEnvVar]);
     1288                papszEnv[0] = NULL;
     1289                cEnvVars = 0;
    2861290                continue;
    2871291            }
     
    2901294             * Change directory switch?
    2911295             */
    292             if (*psz == 'C')
    293             {
    294                 psz++;
    295                 if (*psz == ':' || *psz == '=')
    296                     psz++;
     1296            if (chOpt == 'C')
     1297            {
     1298                if (pszSavedCwd == NULL)
     1299                    pszSavedCwd = strdup(szCwd);
     1300                if (pszSavedCwd)
     1301                    rcExit = kBuiltinOptChDir(szCwd, cbCwdBuf, pszValue);
    2971302                else
    298                 {
    299                     if (i + 1 >= argc)
    300                     {
    301                         fprintf(pStdErr, "%s: syntax error: no argument for %s\n", name(argv[0]), argv[i]);
    302                         return 1;
    303                     }
    304                     psz = argv[++i];
    305                 }
    306                 if (!chdir(psz))
    307                     continue;
    308 #ifdef _MSC_VER
    309                 {
    310                     /* drop trailing slash if any. */
    311                     size_t cch = strlen(psz);
    312                     if (    cch > 2
    313                         &&  (psz[cch - 1] == '/' || psz[cch - 1] == '\\')
    314                         &&  psz[cch - 1] != ':')
    315                     {
    316                         int rc2;
    317                         char *pszCopy = strdup(psz);
    318                         do  pszCopy[--cch] = '\0';
    319                         while (    cch > 2
    320                                &&  (pszCopy[cch - 1] == '/' || pszCopy[cch - 1] == '\\')
    321                                &&  pszCopy[cch - 1] != ':');
    322                         rc2 = chdir(pszCopy);
    323                         free(pszCopy);
    324                         if (!rc2)
    325                             continue;
    326                     }
    327                 }
    328 #endif
    329                 fprintf(pStdErr, "%s: error: chdir(\"%s\"): %s\n", name(argv[0]), psz, strerror(errno));
    330                 return 1;
    331             }
    332 
    333             /*
    334              * Zap environment switch?
    335              * This is a bit of a hack.
    336              */
    337             if (*psz == 'Z')
    338             {
    339                 unsigned j = 0;
    340                 while (envp[j] != NULL)
    341                     j++;
    342                 while (j-- > 0)
    343                 {
    344                     char *pszEqual = strchr(envp[j], '=');
    345                     char *pszCopy;
    346 
    347                     if (pszEqual)
    348                         *pszEqual = '\0';
    349                     pszCopy = strdup(envp[j]);
    350                     if (pszEqual)
    351                         *pszEqual = '=';
    352 
    353 #if defined(_MSC_VER) || defined(__OS2__)
    354                     putenv(pszCopy);
    355 #else
    356                     unsetenv(pszCopy);
    357 #endif
    358                     free(pszCopy);
    359                 }
     1303                    rcExit = err(9, "out of memory!");
    3601304                continue;
    3611305            }
     1306
    3621307
    3631308            /*
    3641309             * Verbose operation switch?
    3651310             */
    366             if (*psz == 'v')
    367             {
    368                 g_cVerbosity++;
     1311            if (chOpt == 'v')
     1312            {
     1313                cVerbosity++;
    3691314                continue;
    3701315            }
    3711316
    3721317            /*
    373              * Close the specified file descriptor (no stderr/out/in aliases).
     1318             * Executable image other than the first argument following '--'.
    3741319             */
    375             if (*psz == 'c')
    376             {
    377                 psz++;
    378                 if (!*psz)
    379                 {
    380                     i++;
    381                     if (i >= argc)
     1320            if (chOpt == 'e')
     1321            {
     1322                pszExecutable = pszValue;
     1323                continue;
     1324            }
     1325
     1326            /*
     1327             * Okay, it is some file descriptor opearation.  Make sure we've got room for it.
     1328             */
     1329            if (cOrders + 1 < K_ELEMENTS(aOrders))
     1330            {
     1331                aOrders[cOrders].fdTarget         = -1;
     1332                aOrders[cOrders].fdSource         = -1;
     1333                aOrders[cOrders].fOpen            = 0;
     1334                aOrders[cOrders].fRemoveOnFailure = 0;
     1335                aOrders[cOrders].pszFilename      = NULL;
     1336#ifndef USE_POSIX_SPAWN
     1337                aOrders[cOrders].fdSaved          = -1;
     1338#endif
     1339            }
     1340            else
     1341            {
     1342                rcExit = errx(2, "error: too many file actions (max: %d)", K_ELEMENTS(aOrders));
     1343                break;
     1344            }
     1345
     1346            if (chOpt == 'c')
     1347            {
     1348                /*
     1349                 * Close the specified file descriptor (no stderr/out/in aliases).
     1350                 */
     1351                char *pszTmp;
     1352                fd = (int)strtol(pszValue, &pszTmp, 0);
     1353                if (pszTmp == pszValue || *pszTmp != '\0')
     1354                    rcExit = errx(2, "error: failed to convert '%s' to a number", pszValue);
     1355                else if (fd < 0)
     1356                    rcExit = errx(2, "error: negative fd %d (%s)", fd, pszValue);
     1357                else
     1358                {
     1359                    aOrders[cOrders].enmOrder = kRedirectOrder_Close;
     1360                    aOrders[cOrders].fdTarget = fd;
     1361                    cOrders++;
     1362#ifdef USE_POSIX_SPAWN
     1363                    rcExit = posix_spawn_file_actions_addclose(&FileActions, fd);
     1364                    if (rcExit != 0)
     1365                        rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
     1366#endif
     1367                }
     1368            }
     1369            else if (chOpt == 'd')
     1370            {
     1371                /*
     1372                 * Duplicate file handle.  Value is fdTarget=fdSource
     1373                 */
     1374                char *pszEqual;
     1375                fd = (int)strtol(pszValue, &pszEqual, 0);
     1376                if (pszEqual == pszValue)
     1377                    rcExit = errx(2, "error: failed to convert target descriptor of '-d %s' to a number", pszValue);
     1378                else if (fd < 0)
     1379                    rcExit = errx(2, "error: negative target descriptor %d ('-d %s')", fd, pszValue);
     1380                else if (*pszEqual != '=')
     1381                    rcExit = errx(2, "syntax error: expected '=' to follow target descriptor: '-d %s'", pszValue);
     1382                else
     1383                {
     1384                    char *pszEnd;
     1385                    int fdSource = (int)strtol(++pszEqual, &pszEnd, 0);
     1386                    if (pszEnd == pszEqual || *pszEnd != '\0')
     1387                        rcExit = errx(2, "error: failed to convert source descriptor of '-d %s' to a number", pszValue);
     1388                    else if (fdSource < 0)
     1389                        rcExit = errx(2, "error: negative source descriptor %d ('-d %s')", fdSource, pszValue);
     1390                    else
    3821391                    {
    383                         fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0]));
    384                         return 1;
     1392                        aOrders[cOrders].enmOrder = kRedirectOrder_Dup;
     1393                        aOrders[cOrders].fdTarget = fd;
     1394                        aOrders[cOrders].fdSource = fdSource;
     1395                        cOrders++;
     1396#ifdef USE_POSIX_SPAWN
     1397                        rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdSource, fd);
     1398                        if (rcExit != 0)
     1399                            rcExit = errx(2, "posix_spawn_file_actions_addclose(%d) failed: %s", fd, strerror(rcExit));
     1400#endif
    3851401                    }
    386                     psz = argv[i];
    387                 }
    388 
    389                 fd = (int)strtol(psz, &psz, 0);
    390                 if (!fd || *psz)
    391                 {
    392                     fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]);
    393                     return 1;
    394 
    395                 }
    396                 if (fd < 0)
    397                 {
    398                     fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]);
    399                     return 1;
    400                 }
    401                 /** @todo deal with stderr */
    402                 safeCloseFd(fd);
    403                 continue;
    404             }
    405 
    406             /*
    407              * Parse a file descriptor argument.
    408              */
    409 
    410             /* mode */
    411             switch (*psz)
    412             {
    413                 case 'r':
    414                     psz++;
    415                     if (*psz == '+')
     1402                }
     1403            }
     1404            else
     1405            {
     1406                /*
     1407                 * Open file as a given file descriptor.
     1408                 */
     1409                int fdOpened;
     1410                int fOpen;
     1411
     1412                /* mode */
     1413                switch (chOpt)
     1414                {
     1415                    case 'r':
     1416                        chOpt = *pszArg++;
     1417                        if (chOpt == '+')
     1418                        {
     1419                            fOpen = O_RDWR;
     1420                            chOpt = *pszArg++;
     1421                        }
     1422                        else
     1423                            fOpen = O_RDONLY;
     1424                        break;
     1425
     1426                    case 'w':
     1427                        chOpt = *pszArg++;
     1428                        if (chOpt == '+')
     1429                        {
     1430                            fOpen = O_RDWR | O_CREAT | O_TRUNC;
     1431                            chOpt = *pszArg++;
     1432                        }
     1433                        else
     1434                            fOpen = O_WRONLY | O_CREAT | O_TRUNC;
     1435                        aOrders[cOrders].fRemoveOnFailure = 1;
     1436                        break;
     1437
     1438                    case 'a':
     1439                        chOpt = *pszArg++;
     1440                        if (chOpt == '+')
     1441                        {
     1442                            fOpen = O_RDWR | O_CREAT | O_APPEND;
     1443                            chOpt = *pszArg++;
     1444                        }
     1445                        else
     1446                            fOpen = O_WRONLY | O_CREAT | O_APPEND;
     1447                        break;
     1448
     1449                    case 'i': /* make sure stdin is read-only. */
     1450                        fOpen = O_RDONLY;
     1451                        break;
     1452
     1453                    case '+':
     1454                        rcExit = errx(2, "syntax error: Unexpected '+' in '%s'", argv[iArg]);
     1455                        continue;
     1456
     1457                    default:
     1458                        fOpen = O_RDWR | O_CREAT | O_TRUNC;
     1459                        aOrders[cOrders].fRemoveOnFailure = 1;
     1460                        break;
     1461                }
     1462
     1463                /* binary / text modifiers */
     1464                switch (chOpt)
     1465                {
     1466                    case 'b':
     1467                        chOpt = *pszArg++;
     1468                    default:
     1469#ifdef O_BINARY
     1470                        fOpen |= O_BINARY;
     1471#elif defined(_O_BINARY)
     1472                        fOpen |= _O_BINARY;
     1473#endif
     1474                        break;
     1475
     1476                    case 't':
     1477#ifdef O_TEXT
     1478                        fOpen |= O_TEXT;
     1479#elif defined(_O_TEXT)
     1480                        fOpen |= _O_TEXT;
     1481#endif
     1482                        chOpt = *pszArg++;
     1483                        break;
     1484
     1485                }
     1486
     1487                /* convert to file descriptor number */
     1488                switch (chOpt)
     1489                {
     1490                    case 'i':
     1491                        fd = 0;
     1492                        break;
     1493
     1494                    case 'o':
     1495                        fd = 1;
     1496                        break;
     1497
     1498                    case 'e':
     1499                        fd = 2;
     1500                        break;
     1501
     1502                    case '0':
     1503                        if (*pszArg == '\0')
     1504                        {
     1505                            fd = 0;
     1506                            break;
     1507                        }
     1508                    case '1':
     1509                    case '2':
     1510                    case '3':
     1511                    case '4':
     1512                    case '5':
     1513                    case '6':
     1514                    case '7':
     1515                    case '8':
     1516                    case '9':
     1517                        pszValue = pszArg - 1;
     1518                        fd = (int)strtol(pszValue, &pszArg, 0);
     1519                        if (pszArg == pszValue)
     1520                            rcExit = errx(2, "error: failed to convert '%s' to a number", argv[iArg]);
     1521                        else if (fd < 0)
     1522                            rcExit = errx(2, "error: negative fd %d (%s)", fd, argv[iArg]);
     1523                        else
     1524                            break;
     1525                        continue;
     1526
     1527                    /*
     1528                     * Invalid argument.
     1529                     */
     1530                    default:
     1531                        rcExit = errx(2, "error: failed to convert '%s' ('%s') to a file descriptor", pszArg, argv[iArg]);
     1532                        continue;
     1533                }
     1534
     1535                /*
     1536                 * Check for the filename.
     1537                 */
     1538                if (*pszArg != '\0')
     1539                {
     1540                    if (*pszArg != ':' && *pszArg != '=')
    4161541                    {
    417                         fOpen = O_RDWR;
    418                         psz++;
    419                     }
    420                     else
    421                         fOpen = O_RDONLY;
    422                     break;
    423 
    424                 case 'w':
    425                     psz++;
    426                     if (*psz == '+')
    427                     {
    428                         psz++;
    429                         fOpen = O_RDWR | O_CREAT | O_TRUNC;
    430                     }
    431                     else
    432                         fOpen = O_WRONLY | O_CREAT | O_TRUNC;
    433                     break;
    434 
    435                 case 'a':
    436                     psz++;
    437                     if (*psz == '+')
    438                     {
    439                         psz++;
    440                         fOpen = O_RDWR | O_CREAT | O_APPEND;
    441                     }
    442                     else
    443                         fOpen = O_WRONLY | O_CREAT | O_APPEND;
    444                     break;
    445 
    446                 case 'i': /* make sure stdin is read-only. */
    447                     fOpen = O_RDONLY;
    448                     break;
    449 
    450                 case '+':
    451                     fprintf(pStdErr, "%s: syntax error: Unexpected '+' in '%s'\n", name(argv[0]), argv[i]);
    452                     return 1;
    453 
    454                 default:
    455                     fOpen = O_RDWR | O_CREAT | O_TRUNC;
    456                     break;
    457             }
    458 
    459             /* binary / text modifiers */
    460             switch (*psz)
    461             {
    462                 case 'b':
    463 #ifdef O_BINARY
    464                     fOpen |= O_BINARY;
    465 #endif
    466                     psz++;
    467                     break;
    468 
    469                 case 't':
    470 #ifdef O_TEXT
    471                     fOpen |= O_TEXT;
    472 #endif
    473                     psz++;
    474                     break;
    475 
    476                 default:
    477 #ifdef O_BINARY
    478                     fOpen |= O_BINARY;
    479 #endif
    480                     break;
    481 
    482             }
    483 
    484             /* convert to file descriptor number */
    485             switch (*psz)
    486             {
    487                 case 'i':
    488                     fd = 0;
    489                     psz++;
    490                     break;
    491 
    492                 case 'o':
    493                     fd = 1;
    494                     psz++;
    495                     break;
    496 
    497                 case 'e':
    498                     fd = 2;
    499                     psz++;
    500                     break;
    501 
    502                 case '0':
    503                     if (!psz[1])
    504                     {
    505                         fd = 0;
    506                         psz++;
     1542                        rcExit = errx(2, "syntax error: characters following the file descriptor: '%s' ('%s')",
     1543                                      pszArg, argv[iArg]);
    5071544                        break;
    5081545                    }
    509                 case '1':
    510                 case '2':
    511                 case '3':
    512                 case '4':
    513                 case '5':
    514                 case '6':
    515                 case '7':
    516                 case '8':
    517                 case '9':
    518                     fd = (int)strtol(psz, &psz, 0);
    519                     if (!fd)
     1546                    pszArg++;
     1547                }
     1548                else if (++iArg < argc)
     1549                    pszArg = argv[iArg];
     1550                else
     1551                {
     1552                    rcExit = errx(2, "syntax error: missing filename argument.");
     1553                    break;
     1554                }
     1555
     1556                /*
     1557                 * Open the file.  We could've used posix_spawn_file_actions_addopen here,
     1558                 * but that means complicated error reporting.  So, since we need to do
     1559                 * this for windows anyway, just do it the same way everywhere.
     1560                 */
     1561                fdOpened = kRedirectOpenWithoutConflict(pszArg, fOpen, 0666, cOrders, aOrders,
     1562                                                        aOrders[cOrders].fRemoveOnFailure, fd);
     1563                if (fdOpened >= 0)
     1564                {
     1565                    aOrders[cOrders].enmOrder    = kRedirectOrder_Open;
     1566                    aOrders[cOrders].fdTarget    = fd;
     1567                    aOrders[cOrders].fdSource    = fdOpened;
     1568                    aOrders[cOrders].fOpen       = fOpen;
     1569                    aOrders[cOrders].pszFilename = pszArg;
     1570                    cOrders++;
     1571
     1572#ifdef USE_POSIX_SPAWN
     1573                    if (fdOpened != fdSource)
    5201574                    {
    521                         fprintf(pStdErr, "%s: error: failed to convert '%s' to a number\n", name(argv[0]), argv[i]);
    522                         return 1;
    523 
     1575                        rcExit = posix_spawn_file_actions_adddup2(&FileActions, fdOpened, fd);
     1576                        if (rcExit != 0)
     1577                            rcExit = err(9, "posix_spawn_file_actions_adddup2(,%d [%s], %d) failed: %s",
     1578                                         fdOpened, fd, pszArg, strerror(rcExit));
    5241579                    }
    525                     if (fd < 0)
    526                     {
    527                         fprintf(pStdErr, "%s: error: negative fd %d (%s)\n", name(argv[0]), fd, argv[i]);
    528                         return 1;
    529                     }
    530                     break;
    531 
    532                 /*
    533                  * Invalid argument.
    534                  */
    535                 default:
    536                     fprintf(pStdErr, "%s: error: failed to convert '%s' ('%s') to a file descriptor\n", name(argv[0]), psz, argv[i]);
    537                     return 1;
    538             }
    539 
    540             /*
    541              * Check for the filename.
    542              */
    543             if (*psz)
    544             {
    545                 if (*psz != ':' && *psz != '=')
    546                 {
    547                     fprintf(pStdErr, "%s: syntax error: characters following the file descriptor: '%s' ('%s')\n", name(argv[0]), psz, argv[i]);
    548                     return 1;
    549                 }
    550                 psz++;
    551             }
    552             else
    553             {
    554                 i++;
    555                 if (i >= argc)
    556                 {
    557                     fprintf(pStdErr, "%s: syntax error: missing filename argument.\n", name(argv[0]));
    558                     return 1;
    559                 }
    560                 psz = argv[i];
    561             }
    562 
    563             /*
    564              * Setup the redirection.
    565              */
    566             if (fd == fileno(pStdErr))
    567             {
    568                 /*
    569                  * Move stderr to a new location, making it close on exec.
    570                  * If pStdOut has already teamed up with pStdErr, update it too.
    571                  */
    572                 FILE *pNew;
    573                 fdOpened = dup(fileno(pStdErr));
    574                 if (fdOpened == -1)
    575                 {
    576                     fprintf(pStdErr, "%s: error: failed to dup stderr (%d): %s\n", name(argv[0]), fileno(pStdErr), strerror(errno));
    577                     return 1;
    578                 }
    579 #ifdef _MSC_VER
    580                 /** @todo figure out how to make the handle close-on-exec. We'll simply close it for now.
    581                  * SetHandleInformation + set FNOINHERIT in CRT.
    582                  */
    583 #else
    584                 if (fcntl(fdOpened, F_SETFD, FD_CLOEXEC) == -1)
    585                 {
    586                     fprintf(pStdErr, "%s: error: failed to make stderr (%d) close-on-exec: %s\n", name(argv[0]), fdOpened, strerror(errno));
    587                     return 1;
    588                 }
    589 #endif
    590 
    591                 pNew = fdopen(fdOpened, "w");
    592                 if (!pNew)
    593                 {
    594                     fprintf(pStdErr, "%s: error: failed to fdopen the new stderr (%d): %s\n", name(argv[0]), fdOpened, strerror(errno));
    595                     return 1;
    596                 }
    597                 if (pStdOut == pStdErr)
    598                     pStdOut = pNew;
    599                 pStdErr = pNew;
    600             }
    601             else if (fd == 1 && pStdOut != pStdErr)
    602                 pStdOut = pStdErr;
    603 
    604             /*
    605              * Close and open the new file descriptor.
    606              */
    607             safeCloseFd(fd);
    608 #if defined(_MSC_VER)
    609             if (!strcmp(psz, "/dev/null"))
    610                 psz = (char *)"nul";
    611 #endif
    612             fdOpened = open(psz, fOpen, 0666);
    613             if (fdOpened == -1)
    614             {
    615                 fprintf(pStdErr, "%s: error: failed to open '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno));
    616                 return 1;
    617             }
    618             if (fdOpened != fd)
    619             {
    620                 /* move it (dup2 returns 0 on MSC). */
    621                 if (dup2(fdOpened, fd) == -1)
    622                 {
    623                     fprintf(pStdErr, "%s: error: failed to dup '%s' as %d: %s\n", name(argv[0]), psz, fd, strerror(errno));
    624                     return 1;
    625                 }
    626                 close(fdOpened);
     1580#endif
     1581                }
    6271582            }
    6281583        }
    6291584        else
    6301585        {
    631             fprintf(pStdErr, "%s: syntax error: Invalid argument '%s'.\n", name(argv[0]), argv[i]);
    632             return usage(pStdErr, name(argv[0]));
     1586            errx(2, "syntax error: Invalid argument '%s'.", argv[iArg]);
     1587            rcExit = usage(stderr, argv[0]);
    6331588        }
    6341589    }
     1590    if (!pszExecutable)
     1591        pszExecutable = argv[iArg];
    6351592
    6361593    /*
    6371594     * Make sure there's something to execute.
    6381595     */
    639     if (i >= argc)
     1596    if (rcExit == 0 && iArg < argc)
    6401597    {
    641         fprintf(pStdErr, "%s: syntax error: nothing to execute!\n", name(argv[0]));
    642         return usage(pStdErr, name(argv[0]));
     1598        /*
     1599         * Do the spawning in a separate function (main is far to large as it is by now).
     1600         */
     1601        rcExit = kRedirectDoSpawn(pszExecutable, argc - iArg, &argv[iArg], fWatcomBrainDamage, papszEnv, szCwd, pszSavedCwd,
     1602#ifdef USE_POSIX_SPAWN
     1603                                  cOrders, aOrders, &FileActions, cVerbosity,
     1604#else
     1605                                  cOrders, aOrders, cVerbosity,
     1606#endif
     1607#ifdef KMK
     1608                                  pPidSpawned,
     1609#endif
     1610                                  &fChildExitCode);
    6431611    }
    644 
    645 #if defined(_MSC_VER)
    646     if (fileno(pStdErr) != 2) /* no close-on-exec flag on windows */
     1612    else if (rcExit == 0)
    6471613    {
    648         fclose(pStdErr);
    649         pStdErr = NULL;
     1614        errx(2, "syntax error: nothing to execute!");
     1615        rcExit = usage(stderr, argv[0]);
    6501616    }
    651 
    652     /* MSC is a PITA since it refuses to quote the arguments... */
    653     quote_argv(argc - i, &argv[i], fWatcomBrainDamage, 0 /*fFreeOrLeak*/);
    654     if (g_cVerbosity > 0)
    655         for (j = i; j < argc; j++)
    656             fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", j - i, argv[j]);
    657     rc = _spawnvp(_P_WAIT, argv[i], &argv[i]);
    658     if (rc == -1 && pStdErr)
    659     {
    660         fprintf(pStdErr, "%s: error: _spawnvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno));
    661         rc = 1;
    662     }
    663     return rc;
    664 #else
    665     if (g_cVerbosity > 0)
    666         for (j = i; j < argc; j++)
    667             fprintf(pStdErr, "kmk_redirect: debug: argv[%i]=%s<eos>\n", j - i, argv[j]);
    668     execvp(argv[i], &argv[i]);
    669     fprintf(pStdErr, "%s: error: _execvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno));
    670     return 1;
    671 #endif
     1617    /* Help and version sets rcExit to -1. Change it to zero. */
     1618    else if (rcExit == -1)
     1619        rcExit = 0;
     1620
     1621    /*
     1622     * Cleanup.
     1623     */
     1624    if (pszSavedCwd)
     1625        free(pszSavedCwd);
     1626    kRedirectCleanupFdOrders(cOrders, aOrders, rcExit != 0 && !fChildExitCode);
     1627#ifdef USE_POSIX_SPAWN
     1628    posix_spawn_file_actions_destroy(&FileActions);
     1629#endif
     1630#ifndef KMK
     1631    iEnvVar = cEnvVars;
     1632    while (iEnvVar-- > 0)
     1633        free(papszEnv[iEnvVar]);
     1634    free(papszEnv);
     1635#endif
     1636#ifdef KBUILD_OS_OS2
     1637    for (ulLibPath = 0; ulLibPath < K_ELEMENTS(apszSavedLibPaths); ulLibPath++)
     1638        if (apszSavedLibPaths[ulLibPath] != NULL)
     1639        {
     1640            APIRET rc = DosSetExtLIBPATH(apszSavedLibPaths[ulLibPath], ulLibPath);
     1641            if (rc != 0)
     1642                warnx("DosSetExtLIBPATH('%s',%u) failed with %u when restoring the original values!",
     1643                      apszSavedLibPaths[ulLibPath], ulLibPath, rc);
     1644            free(apszSavedLibPaths[ulLibPath])
     1645        }
     1646#endif
     1647
     1648    return rcExit;
    6721649}
    6731650
  • trunk/src/kmk/kmkbuiltin/rm.c

    r2713 r2912  
    394394                                                (void)printf("%s\n",
    395395                                                    p->fts_path);
     396#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
     397                                        if (rval == 0) {
     398                                            extern int dir_cache_deleted_directory(const char *pszDir);
     399                                            dir_cache_deleted_directory(p->fts_accpath);
     400                                        }
     401#endif
    396402                                        continue;
    397403                                }
    398                                 operation = "mkdir";
     404                                operation = "rmdir";
    399405                                break;
    400406
  • trunk/src/kmk/kmkbuiltin/rmdir.c

    r2466 r2912  
    6060# include "mscfakes.h"
    6161#endif
     62#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
     63extern int dir_cache_deleted_directory(const char *pszDir);
     64#endif
    6265
    6366static int rm_path(char *);
     
    135138                                continue;
    136139                        /* (only ignored doesn't exist errors fall thru) */
    137                 } else if (vflag) {
    138                         printf("%s\n", *argv);
     140                } else {
     141#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
     142                        dir_cache_deleted_directory(*argv);
     143#endif
     144                        if (vflag) {
     145                                printf("%s\n", *argv);
     146                        }
    139147                }
    140148                if (pflag)
     
    178186
    179187                if (rmdir(path) < 0) {
    180                         if (ignore_fail_on_non_empty && (errno == ENOTEMPTY || errno == EPERM || errno == EACCES || errno == EINVAL || errno == EEXIST))
     188                        if (   ignore_fail_on_non_empty
     189                            && (   errno == ENOTEMPTY || errno == EPERM || errno == EACCES || errno == EINVAL || errno == EEXIST))
    181190                                break;
    182191                        if (!ignore_fail_on_not_exist || errno != ENOENT) {
     
    185194                        }
    186195                }
     196#if defined(KMK) && defined(KBUILD_OS_WINDOWS)
     197                else {
     198                        dir_cache_deleted_directory(path);
     199                }
     200#endif
    187201                if (vflag)
    188202                        printf("%s\n", path);
  • trunk/src/kmk/make.h

    r2886 r2912  
    985985extern void dir_cache_invalid_missing (void);
    986986extern int dir_cache_volatile_dir (const char *dir);
     987extern int dir_cache_deleted_directory(const char *pszDir);
    987988# endif
    988989#endif
  • trunk/src/kmk/w32/include/sub_proc.h

    r2844 r2912  
    5050#ifdef KMK
    5151EXTERN_DECL(int process_kmk_register_submit, (HANDLE hEvent, intptr_t clue, pid_t *pPid));
     52EXTERN_DECL(int process_kmk_register_redirect, (HANDLE hProcess, pid_t *pPid));
    5253#endif
    5354
  • trunk/src/kmk/w32/subproc/sub_proc.c

    r2886 r2912  
    199199
    200200#ifdef KMK
     201
    201202/**
    202203 * Interface used by kmkbuiltin/kSubmit.c to register stuff going down in a
     
    223224        return -1;
    224225}
    225 #endif
     226
     227/**
     228 * Interface used by kmkbuiltin/kRedirect.c to register a spawned process.
     229 *
     230 * @returns 0 on success, -1 if there are too many sub-processes already.
     231 * @param   hProcess            The process handle.
     232 * @param   pPid                Where to return the pid that job.c expects.
     233 */
     234int
     235process_kmk_register_redirect(HANDLE hProcess, pid_t *pPid)
     236{
     237        if (proc_index < MAXIMUM_WAIT_OBJECTS) {
     238                sub_process *pSubProc = (sub_process *)xcalloc(sizeof(*pSubProc));
     239                pSubProc->enmType = kRegular;
     240                pSubProc->pid     = (intptr_t)hProcess;
     241
     242                proc_array[proc_index++] = pSubProc;
     243                *pPid = (intptr_t)pSubProc;
     244                return 0;
     245        }
     246        return -1;
     247}
     248
     249#endif /* KMK */
    226250
    227251/*
  • trunk/src/lib/nt/kFsCache.c

    r2879 r2912  
    19161916 * @param   pCache              The cache.
    19171917 * @param   chLetter            The uppercased drive letter.
     1918 * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
    19181919 * @param   penmError           Where to return details as to why the lookup
    19191920 *                              failed.
    19201921 */
    1921 static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KFSLOOKUPERROR *penmError)
     1922static PKFSOBJ kFswCacheLookupDrive(PKFSCACHE pCache, char chLetter, KU32 fFlags, KFSLOOKUPERROR *penmError)
    19221923{
    19231924    KU32 const          uHash = chLetter - 'A';
     
    19521953                return pCur;
    19531954            kHlpAssert(pCur->bObjType == KFSOBJ_TYPE_MISSING);
    1954             if (kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
     1955            if (   (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
     1956                || kFsCacheRefreshMissingIntermediateDir(pCache, pCur, penmError))
    19551957                return pCur;
    19561958            return NULL;
    19571959        }
     1960    }
     1961
     1962    if (fFlags & KFSCACHE_LOOKUP_F_NO_INSERT)
     1963    {
     1964        *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND; /* close enough */
     1965        return NULL;
    19581966    }
    19591967
     
    21992207 * @param   pCache              The cache.
    22002208 * @param   pszPath             The path.
     2209 * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
    22012210 * @param   poff                Where to return the root dire.
    22022211 * @param   penmError           Where to return details as to why the lookup
    22032212 *                              failed.
    22042213 */
    2205 static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
     2214static PKFSOBJ kFswCacheLookupUncShareA(PKFSCACHE pCache, const char *pszPath, KU32 fFlags,
     2215                                        KU32 *poff, KFSLOOKUPERROR *penmError)
    22062216{
    22072217#if 0 /* later */
     
    22502260 * @param   pCache              The cache.
    22512261 * @param   pwszPath            The path.
     2262 * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
    22522263 * @param   poff                Where to return the root dire.
    22532264 * @param   penmError           Where to return details as to why the lookup
    22542265 *                              failed.
    22552266 */
    2256 static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 *poff, KFSLOOKUPERROR *penmError)
     2267static PKFSOBJ kFswCacheLookupUncShareW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 fFlags,
     2268                                        KU32 *poff, KFSLOOKUPERROR *penmError)
    22572269{
    22582270#if 0 /* later */
     
    23042316 * @param   pszPath             The path to walk.
    23052317 * @param   cchPath             The length of the path.
     2318 * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
    23062319 * @param   penmError           Where to return details as to why the lookup
    23072320 *                              failed.
     
    23102323 *                              found problem.  Optional.
    23112324 */
    2312 PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath,
     2325PKFSOBJ kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags,
    23132326                                     KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
    23142327{
     
    23452358         * Do we need to populate or refresh this directory first?
    23462359         */
    2347         if (   pParent->fPopulated
     2360        if (   !pParent->fNeedRePopulating
     2361            && pParent->fPopulated
    23482362            && (   pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
    23492363                || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
    23502364        { /* likely */ }
    2351         else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
     2365        else if (   (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
     2366                 || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
    23522367        { /* likely */ }
    23532368        else
     
    23652380        else
    23662381        {
    2367             if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
     2382            if (    (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
     2383                && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
    23682384                pChild = kFsCacheCreateMissingA(pCache, pParent, &pszPath[off], offEnd - off, penmError);
    23692385            if (cchSlashes == 0 || offEnd + cchSlashes >= cchPath)
     
    23882404                || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
    23892405                || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
     2406                || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
    23902407                || kFsCacheRefreshMissing(pCache, pChild, penmError) )
    23912408            { /* likely */ }
     
    24092426        }
    24102427        else if (   pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
    2411                  || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
     2428                 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
     2429                 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
    24122430        {
    24132431            *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
     
    24442462 * @param   pszPath             The path to walk.  No dot-dot bits allowed!
    24452463 * @param   cchPath             The length of the path.
     2464 * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
    24462465 * @param   penmError           Where to return details as to why the lookup
    24472466 *                              failed.
     
    24502469 *                              found problem.  Optional.
    24512470 */
    2452 PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath,
     2471PKFSOBJ kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
    24532472                                     KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
    24542473{
     
    24852504         * Do we need to populate or refresh this directory first?
    24862505         */
    2487         if (   pParent->fPopulated
     2506        if (   !pParent->fNeedRePopulating
     2507            && pParent->fPopulated
    24882508            && (   pParent->Obj.uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
    24892509                || pParent->Obj.uCacheGen == pCache->auGenerations[pParent->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN]) )
    24902510        { /* likely */ }
    2491         else if (kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
     2511        else if (   (fFlags & (KFSCACHE_LOOKUP_F_NO_INSERT | fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH))
     2512                 || kFsCachePopuplateOrRefreshDir(pCache, pParent, penmError))
    24922513        { /* likely */ }
    24932514        else
     
    25052526        else
    25062527        {
    2507             if (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
     2528            if (    (pCache->fFlags & KFSCACHE_F_MISSING_OBJECTS)
     2529                && !(fFlags & KFSCACHE_LOOKUP_F_NO_INSERT))
    25082530                pChild = kFsCacheCreateMissingW(pCache, pParent, &pwszPath[off], offEnd - off, penmError);
    25092531            if (cwcSlashes == 0 || offEnd + cwcSlashes >= cwcPath)
     
    25282550                || pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
    25292551                || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
     2552                || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
    25302553                || kFsCacheRefreshMissing(pCache, pChild, penmError) )
    25312554            { /* likely */ }
     
    25492572        }
    25502573        else if (   pChild->uCacheGen == KFSOBJ_CACHE_GEN_IGNORE
    2551                  || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
     2574                 || pChild->uCacheGen == pCache->auGenerationsMissing[pChild->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
     2575                 || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH) )
     2576
    25522577        {
    25532578            *penmError = KFSLOOKUPERROR_PATH_COMP_NOT_FOUND;
     
    25832608 * @param   pszPath             The path to walk. No dot-dot bits allowed!
    25842609 * @param   cchPath             The length of the path.
     2610 * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
    25852611 * @param   penmError           Where to return details as to why the lookup
    25862612 *                              failed.
     
    25892615 *                              found problem.  Optional.
    25902616 */
    2591 static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath,
     2617static PKFSOBJ kFsCacheLookupAbsoluteA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
    25922618                                       KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
    25932619{
     
    26092635        offEnd = 2;
    26102636        kHlpAssert(IS_SLASH(pszPath[2]));
    2611         pRoot = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), penmError);
     2637        pRoot = kFswCacheLookupDrive(pCache, toupper(pszPath[0]), fFlags, penmError);
    26122638    }
    26132639    else if (   IS_SLASH(pszPath[0])
    26142640             && IS_SLASH(pszPath[1]) )
    2615         pRoot = kFswCacheLookupUncShareA(pCache, pszPath, &offEnd, penmError);
     2641        pRoot = kFswCacheLookupUncShareA(pCache, pszPath, fFlags, &offEnd, penmError);
    26162642    else
    26172643    {
     
    26402666                                    ? pCache->auGenerations[       pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
    26412667                                    : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
     2668            || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
    26422669            || kFsCacheRefreshObj(pCache, pRoot, penmError))
    26432670            return kFsCacheObjRetainInternal(pRoot);
     
    26612688     */
    26622689    return kFsCacheLookupRelativeToDirA(pCache, (PKFSDIR)pRoot, &pszPath[offEnd + cchSlashes],
    2663                                         cchPath - offEnd - cchSlashes, penmError, ppLastAncestor);
     2690                                        cchPath - offEnd - cchSlashes, fFlags, penmError, ppLastAncestor);
    26642691}
    26652692
     
    26772704 * @param   pwszPath            The path to walk.
    26782705 * @param   cwcPath             The length of the path (in wchar_t's).
     2706 * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
    26792707 * @param   penmError           Where to return details as to why the lookup
    26802708 *                              failed.
     
    26832711 *                              found problem.  Optional.
    26842712 */
    2685 static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath,
     2713static PKFSOBJ kFsCacheLookupAbsoluteW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
    26862714                                       KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
    26872715{
     
    27062734        offEnd = 2;
    27072735        kHlpAssert(IS_SLASH(pwszPath[2]));
    2708         pRoot = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), penmError);
     2736        pRoot = kFswCacheLookupDrive(pCache, toupper(pwszPath[0]), fFlags, penmError);
    27092737    }
    27102738    else if (   IS_SLASH(pwszPath[0])
    27112739             && IS_SLASH(pwszPath[1]) )
    2712         pRoot = kFswCacheLookupUncShareW(pCache, pwszPath, &offEnd, penmError);
     2740        pRoot = kFswCacheLookupUncShareW(pCache, pwszPath, fFlags, &offEnd, penmError);
    27132741    else
    27142742    {
     
    27372765                                    ? pCache->auGenerations[       pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN]
    27382766                                    : pCache->auGenerationsMissing[pRoot->fFlags & KFSOBJ_F_USE_CUSTOM_GEN])
     2767            || (fFlags & KFSCACHE_LOOKUP_F_NO_REFRESH)
    27392768            || kFsCacheRefreshObj(pCache, pRoot, penmError))
    27402769            return kFsCacheObjRetainInternal(pRoot);
     
    27582787     */
    27592788    return kFsCacheLookupRelativeToDirW(pCache, (PKFSDIR)pRoot, &pwszPath[offEnd + cwcSlashes],
    2760                                         cwcPath - offEnd - cwcSlashes, penmError, ppLastAncestor);
     2789                                        cwcPath - offEnd - cwcSlashes, fFlags, penmError, ppLastAncestor);
    27612790}
    27622791
     
    27722801 * @param   pszPath             The path.
    27732802 * @param   cchPath             The length of the path.
     2803 * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
    27742804 * @param   penmError           Where to return details as to why the lookup
    27752805 *                              failed.
     
    27782808 *                              found problem.  Optional.
    27792809 */
    2780 static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath,
     2810static PKFSOBJ kFsCacheLookupSlowA(PKFSCACHE pCache, const char *pszPath, KU32 cchPath, KU32 fFlags,
    27812811                                   KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
    27822812{
     
    27902820        && cchFull < sizeof(szFull))
    27912821    {
    2792         PKFSOBJ pFsObj;
    27932822        KFSCACHE_LOG2(("kFsCacheLookupSlowA(%s)\n", pszPath));
    2794         pFsObj = kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, penmError, ppLastAncestor);
    2795 
    2796 #if 0 /* No need to do this until it's actually queried. */
    2797         /* Cache the resulting path. */
    2798         if (   pFsObj
    2799             || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
    2800             || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
    2801         {
    2802             KU32 uHashPath = kFsCacheStrHash(szFull);
    2803             kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
    2804                                             uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
    2805         }
    2806 #endif
    2807         return pFsObj;
     2823        return kFsCacheLookupAbsoluteA(pCache, szFull, cchFull, fFlags, penmError, ppLastAncestor);
    28082824    }
    28092825
     
    28252841 * @param   pwszPath            The path.
    28262842 * @param   cwcPath             The length of the path (in wchar_t's).
     2843 * @param   fFlags              Lookup flags, KFSCACHE_LOOKUP_F_XXX.
    28272844 * @param   penmError           Where to return details as to why the lookup
    28282845 *                              failed.
     
    28312848 *                              found problem.  Optional.
    28322849 */
    2833 static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath,
     2850static PKFSOBJ kFsCacheLookupSlowW(PKFSCACHE pCache, const wchar_t *pwszPath, KU32 wcwPath, KU32 fFlags,
    28342851                                   KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor)
    28352852{
     
    28432860        && cwcFull < KFSCACHE_CFG_MAX_PATH)
    28442861    {
    2845         PKFSOBJ pFsObj;
    28462862        KFSCACHE_LOG2(("kFsCacheLookupSlowA(%ls)\n", pwszPath));
    2847         pFsObj = kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, penmError, ppLastAncestor);
    2848 
    2849 #if 0 /* No need to do this until it's actually queried. */
    2850         /* Cache the resulting path. */
    2851         if (   pFsObj
    2852             || (pCache->fFlags & KFSCACHE_F_MISSING_PATHS)
    2853             || *penmError == KFSLOOKUPERROR_UNSUPPORTED)
    2854         {
    2855             KU32 uHashPath = kFsCacheStrHash(szFull);
    2856             kFsCacheCreatePathHashTabEntryA(pCache, pFsObj, pszPath, cchPath, uHashPath,
    2857                                             uHashPath % K_ELEMENTS(pCache->apAnsiPaths), *penmError);
    2858         }
    2859 #endif
    2860         return pFsObj;
     2863        return kFsCacheLookupAbsoluteW(pCache, wszFull, cwcFull, fFlags, penmError, ppLastAncestor);
    28612864    }
    28622865
     
    28822885    {
    28832886        if (pHashEntry->fAbsolute)
    2884             pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
     2887            pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
    28852888                                                         &pHashEntry->enmError, &pLastAncestor);
    28862889        else
    2887             pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
     2890            pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
    28882891                                                     &pHashEntry->enmError, &pLastAncestor);
    28892892    }
     
    29002903                kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
    29012904                if (pHashEntry->fAbsolute)
    2902                     pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
     2905                    pHashEntry->pFsObj = kFsCacheLookupAbsoluteA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
    29032906                                                                 &pHashEntry->enmError, &pLastAncestor);
    29042907                else
    2905                     pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath,
     2908                    pHashEntry->pFsObj = kFsCacheLookupSlowA(pCache, pHashEntry->pszPath, pHashEntry->cchPath, 0 /*fFlags*/,
    29062909                                                             &pHashEntry->enmError, &pLastAncestor);
    29072910            }
     
    29432946    {
    29442947        if (pHashEntry->fAbsolute)
    2945             pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
     2948            pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
    29462949                                                         &pHashEntry->enmError, &pLastAncestor);
    29472950        else
    2948             pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
     2951            pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
    29492952                                                     &pHashEntry->enmError, &pLastAncestor);
    29502953    }
     
    29612964                kFsCacheObjRelease(pCache, pHashEntry->pFsObj);
    29622965                if (pHashEntry->fAbsolute)
    2963                     pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
     2966                    pHashEntry->pFsObj = kFsCacheLookupAbsoluteW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
    29642967                                                                 &pHashEntry->enmError, &pLastAncestor);
    29652968                else
    2966                     pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath,
     2969                    pHashEntry->pFsObj = kFsCacheLookupSlowW(pCache, pHashEntry->pwszPath, pHashEntry->cwcPath, 0 /*fFlags*/,
    29672970                                                             &pHashEntry->enmError, &pLastAncestor);
    29682971            }
     
    30693072            && !kFsCacheHasDotDotA(pchPath, cchPath) )
    30703073        {
    3071             pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, penmError, &pLastAncestor);
     3074            pFsObj = kFsCacheLookupAbsoluteA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
    30723075            fAbsolute = K_TRUE;
    30733076        }
    30743077        else
    30753078        {
    3076             pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, penmError, &pLastAncestor);
     3079            pFsObj = kFsCacheLookupSlowA(pCache, pchPath, cchPath, 0 /*fFlags*/, penmError, &pLastAncestor);
    30773080            fAbsolute = K_FALSE;
    30783081        }
     
    31773180            && !kFsCacheHasDotDotW(pwcPath, cwcPath) )
    31783181        {
    3179             pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, penmError, &pLastAncestor);
     3182            pFsObj = kFsCacheLookupAbsoluteW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
    31803183            fAbsolute = K_TRUE;
    31813184        }
    31823185        else
    31833186        {
    3184             pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, penmError, &pLastAncestor);
     3187            pFsObj = kFsCacheLookupSlowW(pCache, pwcPath, cwcPath, 0 /*fFlags*/, penmError, &pLastAncestor);
    31853188            fAbsolute = K_FALSE;
    31863189        }
     
    39803983        return K_TRUE;
    39813984    }
     3985    return K_FALSE;
     3986}
     3987
     3988
     3989/**
     3990 * Invalidates a deleted directory, ANSI version.
     3991 *
     3992 * @returns K_TRUE if found and is a non-root directory. Otherwise K_FALSE.
     3993 * @param   pCache              The cache.
     3994 * @param   pszDir              The directory.
     3995 */
     3996KBOOL kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir)
     3997{
     3998    KU32            cchDir = (KU32)kHlpStrLen(pszDir);
     3999    KFSLOOKUPERROR  enmError;
     4000    PKFSOBJ         pFsObj;
     4001
     4002    /* Is absolute without any '..' bits? */
     4003    if (   cchDir >= 3
     4004        && (   (   pszDir[1] == ':'    /* Drive letter */
     4005                && IS_SLASH(pszDir[2])
     4006                && IS_ALPHA(pszDir[0]) )
     4007            || (   IS_SLASH(pszDir[0]) /* UNC */
     4008                && IS_SLASH(pszDir[1]) ) )
     4009        && !kFsCacheHasDotDotA(pszDir, cchDir) )
     4010        pFsObj = kFsCacheLookupAbsoluteA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
     4011                                         &enmError, NULL);
     4012    else
     4013        pFsObj = kFsCacheLookupSlowA(pCache, pszDir, cchDir, KFSCACHE_LOOKUP_F_NO_INSERT | KFSCACHE_LOOKUP_F_NO_REFRESH,
     4014                                     &enmError, NULL);
     4015    if (pFsObj)
     4016    {
     4017        /* Is directory? */
     4018        if (pFsObj->bObjType == KFSOBJ_TYPE_DIR)
     4019        {
     4020            if (pFsObj->pParent != &pCache->RootDir)
     4021            {
     4022                PKFSDIR pDir = (PKFSDIR)pFsObj;
     4023                KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: %s hDir=%p\n", pszDir, pDir->hDir));
     4024                if (pDir->hDir != INVALID_HANDLE_VALUE)
     4025                {
     4026                    g_pfnNtClose(pDir->hDir);
     4027                    pDir->hDir = INVALID_HANDLE_VALUE;
     4028                }
     4029                pDir->fNeedRePopulating = K_TRUE;
     4030                pDir->Obj.uCacheGen = pCache->auGenerations[pDir->Obj.fFlags & KFSOBJ_F_USE_CUSTOM_GEN] - 1;
     4031                kFsCacheObjRetainInternal(&pDir->Obj);
     4032                return K_TRUE;
     4033            }
     4034            KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a root directory was deleted! %s\n", pszDir));
     4035        }
     4036        else
     4037            KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: Trying to invalidate a non-directory: bObjType=%d %s\n",
     4038                          pFsObj->bObjType, pszDir));
     4039        kFsCacheObjRetainInternal(pFsObj);
     4040    }
     4041    else
     4042        KFSCACHE_LOG(("kFsCacheInvalidateDeletedDirectoryA: '%s' was not found\n", pszDir));
    39824043    return K_FALSE;
    39834044}
  • trunk/src/lib/nt/kFsCache.h

    r2868 r2912  
    457457PKFSOBJ     kFsCacheLookupA(PKFSCACHE pCache, const char *pszPath, KFSLOOKUPERROR *penmError);
    458458PKFSOBJ     kFsCacheLookupW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError);
    459 PKFSOBJ     kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath,
     459PKFSOBJ     kFsCacheLookupRelativeToDirA(PKFSCACHE pCache, PKFSDIR pParent, const char *pszPath, KU32 cchPath, KU32 fFlags,
    460460                                         KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor);
    461 PKFSOBJ     kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath,
     461PKFSOBJ     kFsCacheLookupRelativeToDirW(PKFSCACHE pCache, PKFSDIR pParent, const wchar_t *pwszPath, KU32 cwcPath, KU32 fFlags,
    462462                                         KFSLOOKUPERROR *penmError, PKFSOBJ *ppLastAncestor);
    463463PKFSOBJ     kFsCacheLookupWithLengthA(PKFSCACHE pCache, const char *pchPath, KSIZE cchPath, KFSLOOKUPERROR *penmError);
     
    466466PKFSOBJ     kFsCacheLookupNoMissingW(PKFSCACHE pCache, const wchar_t *pwszPath, KFSLOOKUPERROR *penmError);
    467467
     468/** @name KFSCACHE_LOOKUP_F_XXX - lookup flags
     469 * @{ */
     470/** No inserting new cache entries.
     471 * This effectively prevent directories from being repopulated too.  */
     472#define KFSCACHE_LOOKUP_F_NO_INSERT     KU32_C(1)
     473/** No refreshing cache entries. */
     474#define KFSCACHE_LOOKUP_F_NO_REFRESH    KU32_C(2)
     475/** @} */
    468476
    469477KU32        kFsCacheObjRelease(PKFSCACHE pCache, PKFSOBJ pObj);
     
    485493void        kFsCacheInvalidateCustomBoth(PKFSCACHE pCache);
    486494KBOOL       kFsCacheSetupCustomRevisionForTree(PKFSCACHE pCache, PKFSOBJ pRoot);
    487 
    488 #endif
     495KBOOL       kFsCacheInvalidateDeletedDirectoryA(PKFSCACHE pCache, const char *pszDir);
     496
     497#endif
  • trunk/src/lib/quote_argv.c

    r2894 r2912  
    8080 * Arguments": http://msdn.microsoft.com/en-us/library/a1y7w461.aspx
    8181 *
     82 * @returns 0 on success, -1 if out of memory.
    8283 * @param   argc                The argument count.
    8384 * @param   argv                The argument vector.
     
    9091 *                              Suggest doing the latter if it's main()'s argv.
    9192 */
    92 void quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak)
     93int quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak)
    9394{
    9495    int i;
     
    122123            size_t cchNew       = fComplicated ? cchOrg * 2 + 2 : cchOrg + 2;
    123124            char  *pszNew       = (char *)malloc(cchNew + 1 /*term*/ + 3 /*passthru hack*/);
     125            if (!pszNew)
     126                return -1;
    124127
    125128            argv[i] = pszNew;
     
    208211
    209212    /*for (i = 0; i < argc; i++) fprintf(stderr, "argv[%u]=%s;;\n", i, argv[i]);*/
     213    return 0;
    210214}
    211215
  • trunk/src/lib/quote_argv.h

    r2851 r2912  
    3434
    3535#include "mytypes.h"
    36 extern void quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak);
     36extern int quote_argv(int argc, char **argv, int fWatcomBrainDamage, int fFreeOrLeak);
    3737
    3838#endif
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette